2016-11-10 11 views
4

私のプログラムでは、いくつかの画像を読み込み、そこからいくつかの特徴を抽出し、cv::Matを使ってこれらの特徴を保存します。私はcv::Matが約720Mbであるサイズ(行x cols)700.000 x 256であると知っているイメージの数に基づいています。しかし、私が約400.000 x 256(400Mb)のプログラムを実行して、それを追加しようとすると、単に致命的なエラーでクラッシュします。誰も確かに400Mbがcv::Matの記憶容量の限界であることを確認できますか?より多くの問題をチェックする必要がありますか?この問題を克服する方法はありますか?C++ OpenCVの最大記憶容量

+0

これはありません。利用可能なメモリを割り当てることができるはずです。より多くの問題を確認する必要があります。 –

+0

@RobertPrévostしかし、 'cv :: Mat :: push_back()'はシステムに十分なRAMがある間に "outOfMemory"エラー例外をスローします。 – DimChtz

+0

'cv :: Mat :: push_back'はおそらく少なくとも同じサイズの別の行列を割り当てます。データを2倍にするのに十分なスペースがありますか? –

答えて

2

push_backを使用して、ソースコードを掘る:

、新しい要素のための十分なスペースがあるかどうかではない場合、それは/ ための空間(current_size * 3 + 1)を用いて、マトリックスを再割り当てチェック2see here)。あなたの例では、約400,000 * 256(合計102,400,000要素)で別の割り当てを試みるので、307,200,001/2 = 153,600,000要素の領域を割り当てようとします。しかし、これを移動するためには、新しい領域を割り当てる必要があり、その後matrix.cppからデータ

をコピーします。だから、

Mat m(dims, size.p, type()); 
size.p[0] = r; 
if(r > 0) 
{ 
    Mat mpart = m.rowRange(0, r); 
    copyTo(mpart); 
} 

*this = m; 

、本質的に:

  1. はデフォルトを使用して、新しい行列を割り当て新しく作成されたすべての要素のコンストラクタ
  2. データをコピーして古いデータを削除します。
  3. この行列の新しいヘッダーを作成します。十分な列あなたのケースでは、それは(60万+40万)のための十分なスペースを必要とする、という意味(旧割り当てられたメモリを解放する)新たに割り当てられたデータへ
  4. ポイントこの要素

* 256 - データの1ギガバイト4バイトの整数を使用します。また、1行の補助行列を作成します。この場合は600,000列になり、余分な2,400,000バイトを占めます。

したがって、600,000列に達すると、900,000x256要素(900Mb)+ 600,000x256要素(600Mb)+ 600,000(〜3.4Mb)を割り当てようとします。したがって、(push_backを使用して)このように割り当てるだけで、いくつかの再割り当てが行われます。

他の言葉:マトリックスのおおよそのサイズを知っているので、reserveを使用する必要があります。数倍高速です(再割り当てやコピーを避ける)。

また、回避策として、転置された行列に挿入してから、処理が完了した後に再度転置することもできます。

この質問にはmalloc/memcpyの代わりにreallocを使用しないでください。

1

以下のようにマトリックスを作成しました。約700 MBのCV_8UC4を使用しました。まったく問題ありません。だから、400Mbは限界ではありません。 700Mbは制限ではありません。 2倍(1400000行、1.4Gb)で試してみましたが、それでも限界はありませんでした(私のデフォルト画像ビューアでは結果のBMPファイルを表示できませんでした)。この問題を克服するための

const unsigned int N_rows = 700000; 
const unsigned int N_cols = 256; 
cv::Mat m(N_rows, N_cols, CV_8UC4); 
for (int r = 0; r < m.rows; ++r) 
{ 
    for (int c = 0; c < m.cols; ++c) 
    { 
     m.data[(r*N_cols + c) * 4] = c % 256; 
    } 
} 
cv::imwrite("test.bmp", m); 

可能な方法:

  • を確認するために、多分少し余分に、冒頭でcv::Matに十分なスペースを割り当てます。再割り当てによって問題が発生した場合は、これが役立ちます。
  • あなたのアプリケーションが32ビットである場合、あなたのコメントが示唆しているように、64ビットに変換してメモリー制限を増やしてください。
  • 32ビットアプリケーションでも、720Mbは問題ではありません。しかし、おそらくあなたのアプリケーションは他のものにもっと多くのメモリを使います。そうであれば、イメージを別のプロセスに移動して、別個の2Gbを取得することができます。しかし、プロセス間通信は苦痛です。
  • まだメモリに収まらないファイルを扱う必要がある場合は、メモリマップされたファイルを調べてください。OpenCVには少なくともいくつかのサポートがありますが、それ以上は使用できません。
  • 必要なものに応じて、小さな行列のコレクションを使用して、読み書き/分割/結合します。
+0

それはおそらく、 'cv :: Mat :: push_back()'のようなものです。 – DimChtz

+0

あなたが事前にサイズを知っていれば、とにかくそれをあらかじめ割り当てておくべきでしょう。 – Headcrab

1

cv::Matのサイズには厳密な制限はありません。利用可能な限りメモリを割り当てることができます。

ここには、cv::Mat::push_backを何度も実行しているときにデータポインタに何が起こるかを示す小さなプログラムがあります。 rowscolsの値で再生すると、最終的にメモリ不足の例外がスローされる前に、1つまたは複数の値がa.dataとして出力される可能性があります。

#include <opencv2/opencv.hpp> 
int main() 
{ 
    int rows = 1, cols = 10000; 
    cv::Mat a(rows, cols, CV_8UC1); 
    while(1) { 
    a.push_back(cv::Mat(1, cols, CV_8UC1)); 
    std::cout << (size_t) a.data << std::endl; 
    } 
} 

実際には、上記のコードが行と列のさまざまな値に対して何を行うかに関するアロケータに依存します。したがって、aの小さい初期サイズと大きい初期サイズを考慮する必要があります。

C++ 11 std::vectorと同様に、cv::Matの要素は連続しています。基盤となるデータへのアクセスは、cv::Mat::dataメンバーから取得できます。 std::vector::push_backまたはcv::Mat::push_backを継続的に呼び出すと、基になるメモリが再割り当てされることがあります。この場合、メモリは新しいアドレスに移動されなければならず、古いメモリから新しいメモリに移動するにはメモリの約2倍の量が必要になることがあります。

+0

さて、私は 'cv :: Mat :: push_back'を呼び出すたびに1行を追加します。これは合計700.000コールです。私は 'cv :: Mat :: push_back'の総呼び出しを減らす必要があると思います。 – DimChtz

+2

私が正しく覚えていれば、動的成長を可能にする連続した構造のためのかなり一般的なメモリ割り当て戦略は、スペースを使い果たしたときに前のサイズの2倍を割り当てることです。これはおそらく、移動するのに要する時間のおよそ3分の1を意味します。古いサイズの2倍の新しいメモリブロック+古いメモリブロック。データがコピーされるまで削除できません。 – Headcrab

+0

@Headcrab 'cv :: Mat'と連続していないデータを使用することはできますか? – DimChtz

関連する問題