web-dev-qa-db-ja.com

「const cv :: Mat&」、「cv :: Mat&」、「cv :: Mat」、または「const cv :: Mat」を関数パラメーターとして使用する違い

私は徹底的に検索しましたが、これに対する簡単な答えは見つかりませんでした。

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();に置き換えることで問題が解決します)、それでも上記の違いを理解していません。

みんなありがとう!

29
CV_User

それは、OpenCVが Automatic Memory Management を使用しているためです。

OpenCVはすべてのメモリを自動的に処理します。

まず、std::vectorMat、および関数とメソッドで使用されるその他のデータ構造には、必要に応じて基になるメモリバッファーの割り当てを解除するデストラクターがあります。これは、Matの場合のように、デストラクタが常にバッファの割り当てを解除するとは限らないことを意味します。データ共有の可能性を考慮します。デストラクタは、マトリックスデータバッファに関連付けられた参照カウンタをデクリメントします。参照カウンタがゼロになった場合、つまり同じバッファを参照する構造が他にない場合にのみ、バッファの割り当てが解除されます。 同様に、Matインスタンスがコピーされると、実際のデータは実際にはコピーされません。代わりに、同じデータの別の所有者がいることを記憶するために、参照カウンターが増分されます。マトリックスデータの完全なコピーを作成するMat::cloneメソッドもあります。

つまり、2つのcv::Matsが異なるものを指すようにするには、それらに別々にメモリを割り当てる必要があります。たとえば、以下は期待どおりに機能します。

void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
    Output = Input.clone(); // Input, Output now have seperate memory
    Output += 1;
}

P.Scv::Matには、参照カウンターを指すint* refcountが含まれます。詳細については、 メモリ管理と参照カウント をご覧ください。

Matは、行列/画像の特性(行と列の番号、データ型など)およびデータへのポインターを保持する構造です。したがって、同じデータに対応するMatの複数のインスタンスを持つことを妨げるものは何もありません。 Matは、Matの特定のインスタンスが破棄されたときにデータの割り当てを解除する必要があるかどうかを示す参照カウントを保持します。


cv::Matconst cv::Matconst cv::Mat&、またはcv::Mat&を関数の引数として送信する場合の違い:

  1. cv::Mat InputInputのヘッダーのコピーを渡します。そのヘッダーは、この関数の外部では変更されませんが、関数内で変更できます。例えば:

    void sillyFunc(cv::Mat Input, cv::Mat& Output){
        Input = cv::Mat::ones(4, 4, CV_32F); // OK, but only changed within the function
        //...
    }
    
  2. const cv::Mat InputInputのヘッダーのコピーを渡します。そのヘッダーは、関数の外部または内部では変更されません。例えば:

    void sillyFunc(const cv::Mat Input, cv::Mat& Output){
        Input = cv::Mat::ones(4, 4, CV_32F); // Error, even when changing within the function
        //...
    }
    
  3. const cv::Mat& InputInputのヘッダーの参照を渡します。 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
        ...
    }
    
  4. cv::Mat& InputInputのヘッダーの参照を渡します。 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::Matconst cv::Matconst 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
}
56
herohuyongtao

スマートポインターを参照として渡すと、スマートポインターのコピーコンストラクターが呼び出されないため、理論的には処理時間を節約できます。

0
ivg