私は徹底的に検索しましたが、これに対する簡単な答えは見つかりませんでした。
Opencv行列(_cv::Mat
_)を関数の引数として渡すと、スマートポインターが渡されます。関数内の入力行列を変更すると、関数スコープ外の行列も変更されます。
マトリックスをconst参照として渡すことで、関数内で変更されないことを読みました。しかし、簡単な例はそれを示しています:
_void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
Output = Input;
Output += 1;
}
int main( int argc, char** argv ){
cv::Mat A = cv::Mat::ones(3,3,CV_8U);
std::cout<<"A = \n"<<A<<"\n\n";
cv::Mat B;
sillyFunc(A,B);
std::cout<<"A = \n"<<A<<"\n\n";
std::cout<<"B = \n"<<B<<"\n\n";
}
_
_const cv::Mat&
_として送信された場合でも、明らかにA
は変更されます。
関数_I2
_内では_I1
_のスマートポインターの単純なコピーであり、したがって_I2
_の変更は_I1
_を変更するため、これは驚くことではありません。
私を困惑させているのは、関数への引数として_cv::Mat
_、_const cv::Mat
_、_const cv::Mat&
_または_cv::Mat&
_を送信することの実際的な違いがわからないことです。
これをオーバーライドする方法は知っています(_Output = Input;
_をOutput = Input.clone();
に置き換えることで問題が解決します)、それでも上記の違いを理解していません。
みんなありがとう!
それは、OpenCVが Automatic Memory Management を使用しているためです。
OpenCVはすべてのメモリを自動的に処理します。
まず、
std::vector
、Mat
、および関数とメソッドで使用されるその他のデータ構造には、必要に応じて基になるメモリバッファーの割り当てを解除するデストラクターがあります。これは、Mat
の場合のように、デストラクタが常にバッファの割り当てを解除するとは限らないことを意味します。データ共有の可能性を考慮します。デストラクタは、マトリックスデータバッファに関連付けられた参照カウンタをデクリメントします。参照カウンタがゼロになった場合、つまり同じバッファを参照する構造が他にない場合にのみ、バッファの割り当てが解除されます。 同様に、Mat
インスタンスがコピーされると、実際のデータは実際にはコピーされません。代わりに、同じデータの別の所有者がいることを記憶するために、参照カウンターが増分されます。マトリックスデータの完全なコピーを作成するMat::clone
メソッドもあります。
つまり、2つのcv::Mat
sが異なるものを指すようにするには、それらに別々にメモリを割り当てる必要があります。たとえば、以下は期待どおりに機能します。
void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
Output = Input.clone(); // Input, Output now have seperate memory
Output += 1;
}
P.S:cv::Mat
には、参照カウンターを指すint* refcount
が含まれます。詳細については、 メモリ管理と参照カウント をご覧ください。
Mat
は、行列/画像の特性(行と列の番号、データ型など)およびデータへのポインターを保持する構造です。したがって、同じデータに対応するMat
の複数のインスタンスを持つことを妨げるものは何もありません。Mat
は、Mat
の特定のインスタンスが破棄されたときにデータの割り当てを解除する必要があるかどうかを示す参照カウントを保持します。
cv::Mat
、const cv::Mat
、const cv::Mat&
、またはcv::Mat&
を関数の引数として送信する場合の違い:cv::Mat Input
:Input
のヘッダーのコピーを渡します。そのヘッダーは、この関数の外部では変更されませんが、関数内で変更できます。例えば:
void sillyFunc(cv::Mat Input, cv::Mat& Output){
Input = cv::Mat::ones(4, 4, CV_32F); // OK, but only changed within the function
//...
}
const cv::Mat Input
:Input
のヘッダーのコピーを渡します。そのヘッダーは、関数の外部または内部では変更されません。例えば:
void sillyFunc(const cv::Mat Input, cv::Mat& Output){
Input = cv::Mat::ones(4, 4, CV_32F); // Error, even when changing within the function
//...
}
const cv::Mat& Input
:Input
のヘッダーの参照を渡します。 Input
のヘッダーが関数の外部または内部で変更されないことを保証します。例えば:
void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
Input = cv::Mat::ones(4, 4, CV_32F); // Error when trying to change the header
...
}
cv::Mat& Input
:Input
のヘッダーの参照を渡します。 Input
のヘッダーへの変更は、関数の外部および内部で発生します。例えば:
void sillyFunc(cv::Mat& Input, cv::Mat& Output){
Input = cv::Mat::ones(4, 4, CV_32F); // totally OK and does change
...
}
PS2:4つの状況すべて(cv::Mat
、const cv::Mat
、const cv::Mat&
またはcv::Mat&
)、Matのヘッダーへのアクセスのみが制限され、Matが指すデータへのアクセスは制限されません。たとえば、4つの状況すべてでデータを変更でき、そのデータは実際に関数の外部および内部で変更されます。
/*** will work for all the four situations ***/
//void sillyFunc(cv::Mat Input){
//void sillyFunc(const cv::Mat Input){
//void sillyFunc(const cv::Mat &Input){
void sillyFunc(cv::Mat &Input){
Input.data[0] = 5; // its data will be changed here
}
スマートポインターを参照として渡すと、スマートポインターのコピーコンストラクターが呼び出されないため、理論的には処理時間を節約できます。