メモリ管理

新しいインタフェースを利用する場合,ほとんどのメモリ解放やメモリ確保処理は必要な時に自動で行われます.

ここで,まず最初に, Mat()SparseMat() ,その他のクラスは, コピーされたメモリバッファを必要なときに解放するデストラクタを持つ,ということを述べておきます.

そして,この「必要なとき」というのは,デストラクタはデータが共有される可能性を考慮しており,常にバッファを解放しているわけではない事を意味します. つまり,デストラクタでは,内部データの参照カウンタがデクリメントされ,参照カウンタが 0 になった(他の構造が同じバッファを参照していない)場合に限りそのデータが解放されます. この様な参照カウンタを含む構造がコピーされる場合,通常は内部データはコピーされずにヘッダのみが複製されます.そして,同じデータに別の所有者がいる事を記憶するために参照カウンタがインクリメントされます. Mat のように,ユーザが確保したデータを参照することができる構造も存在します.この場合,参照カウンタは NULL ポインタとなり,参照カウントは行われません.もちろんデストラクタはデータを解放しないので,ユーザが手動で解放するべきです.この機構ついては,この章の最初のサンプルで説明しました:

// IplImage を確保し,共有ポインタクラスでラップします.
Ptr<IplImage> iplimg = cvLoadImage(...);

// IplImage データに対する Mat ヘッダを作成します.
// データはコピーしません.
// また,参照カウンタの値は NULL になります.
Mat img(iplimg);
...
// このブロックの最後で,img のデストラクタが呼ばれますが,
// 参照カウンタは NULL ポインタなので,
// データを解放しようとはしません.
//
// Ptr<IplImage> のデストラクタが呼ばれて
// 参照カウンタがデクリメントされ,それが 0 になると,
// この場合はデストラクタが cvReleaseImage() を呼び出します.

コピー動作については上述のパラグラフで触れましたが,これについてはさらに詳しく議論する価値があります. 新しい OpenCV の構造はデフォルトで,浅いコピー,いわゆる O(1) (つまり,定数時間)での代入演算を行います. これにより,巨大なデータ構造を関数に渡したり(しかし,例えば const Mat& を渡せば, Mat を処理するよりもさらに高速です), 戻したり(例えば,前述の findHomography() の例を参照してください), OpenCV や STL のコンテナに保存したりすることが可能となり,これらは非常に効率的に行われます. また一方で,新しいデータ構造の多くは,オブジェクトの完全なコピーを作成する clone() メソッドを提供します.以下にこの例を示します:

// 8Mb の大きな行列を作成します.
Mat A(1000, 1000, CV_64F);

// 同じ行列に対する別のヘッダを作成します.
// この処理は行列のサイズによらず一瞬で行われます.
Mat B = A;
// A の 3 行目に対するヘッダを作成します.データのコピーは行われません.
Mat C = B.row(3);
// ここで,この行列の別のコピーを作成します.
Mat D = B.clone();
// B の 5 行目を C にコピー,つまり A の 5 行目を A の 3 行目にコピーします.
B.row(5).copyTo(C);
// ここで, A と D のデータを共有します.
// 以降も,修正された A は B と C に参照されます.
A = D;
// B を(メモリバッファを参照しない)空の行列にしても,
// 修正された A は,まだ C から参照されています.
// C は単に元の A の 1 つの行を表します.
B.release();

// 最後に C の完全なコピーを作成します.
// その結果,修正された大きな行列はどこからも参照されないので,解放されます.
C = C.clone();

新しいデータ構造のメモリ管理は,自動で行われるので簡単です.しかし,もし IplImageCvMat ,あるいは別の C のデータ構造を多用している場合でも,直ちに Mat() に移行しなければならないわけではありません.既に述べたクラステンプレート Ptr() (これは, Boost や C++ TR1 の 共有ポインタ shared_ptr に似ています)を利用して,メモリ管理を自動化することができます. これは,任意のオブジェクトへのポインタをラップして,そのオブジェクトのフィールドへの透過的なアクセスを提供し,さらに参照カウンタを関連付けます. このクラスのインスタンスは,元のポインタを引数に取る任意の関数に渡すことができます. オブジェクトを正しく解放するためには,専用の Ptr<T>::delete_obj() メソッドを用意する必要があります. この特別なメソッドは,古い形式の OpenCV 構造体にも既に存在します.例えば:

// cxoperations.hpp:
...
template<> inline Ptr<IplImage>::delete_obj() {
    cvReleaseImage(&obj);
}
...

詳細や別の使用例については, Ptr() の説明を参照してください.

前のトピックへ

名前空間 cv と関数の命名

次のトピックへ

メモリ管理 パート2.自動的なデータ確保

このページ