web-dev-qa-db-ja.com

OpenCV cv :: Matのディープコピー

Mat::copyTo()はディープコピーですが、代入演算子はディープコピーではないことがわかります。私の質問:

  1. cv::Mat func()などの関数からcv::Matを返すにはどうすればよいですか?

  2. ドキュメントによると、cv::Matを返すと、その関数のcv::Matのローカルコピーが返された後、その関数のcv::Matのローカルコピーが破棄されるため、返される値を受け入れるため、使用できません。関数の外側は、ランダムなアドレスを指している必要があります。奇妙なことは、(ほとんどの場合)正しく動作することです。たとえば、次のように機能します。

    cv::Mat CopyOneImage(const cv::Mat& orgImage)
    {
    
        cv::Mat image;
        orgImage.copyTo(image);
        return image;
    
    }
    
    int main()
    {
    
        std::string orgImgName("a.jpg");        
        cv::Mat orgImage;
        orgImage = cv::imread(orgImgName);
    
        cv::Mat aCopy;
        aCopy = CopyOneImage(orgImage);
    
        return 1;
    }
    

しかし、なぜ?ディープコピーではありません。

質問3.また、割り当て演算子もディープコピーのように見える場合があります。

    int main()
    {

        std::string orgImgName("a.jpg");        
        cv::Mat orgImage;
        orgImage = cv::imread(orgImgName);

        cv::Mat aCopy;
        orgImage.copyTo(aCopy);

        cv::Mat copyCopy1;
        copyCopy1 = aCopy;

        cv::namedWindow("smallTest", 1);
        cv::imshow("smallTest", copyCopy1);
        uchar key = (uchar)cv::waitKey();

        cv::Mat orgImage2 = cv::imread("b.jpg");
        orgImage2.copyTo(aCopy);

        cv::imshow("smallTest", copyCopy1);
        return 1;
    }

次に、2つのディスプレイに同じ画像a.jpgが表示されます。どうして?また、他の場合には機能しません。 (元のコードは長すぎますが、上記の場合に単純化することもできます)。当時、割り当て演算子は実際には「浅い」コピーのようです。どうして?

どうもありがとう!

25
flyinggip

割り当ての使用は、マトリックスコピーの最良の方法ではないと思います。マトリックスの新しい完全なコピーが必要な場合は、次を使用します。

Mat a=b.clone(); 

他のマトリックスからのデータを置き換えるためにマトリックスをコピーしたい場合(メモリの再割り当てを避けるため):

Mat a(b.size(),b.type());
b.copyTo(a);

1つのマトリックスを他のマトリックスに割り当てると、マトリックスデータへのスマートポインターの参照のカウンターが1つ増加し、マトリックスを解放すると(コードブロックを離れるときに暗黙的に実行できます)、1つ減少します。ゼロに等しくなると、割り当てられたメモリの割り当てが解除されます。

関数使用リファレンスから結果を取得したい場合は、より高速です:

void Func(Mat& input,Mat& output)
{
 somefunc(input,output);
}

int main(void)
{
...
  Mat a=Mat(.....);
  Mat b=Mat(.....);
  Func(a,b);
...
}
22
Andrey Smorodov

私はしばらくの間OpenCVを使用してきましたが、cv :: Matも私を混乱させたので、読書をしました。

cv :: Matは、実際の画像データを保持する* dataポインターを指すヘッダーです。また、参照カウントも実装します。現在* dataポインターを指している_cv::Mat_ヘッダーの数を保持します。したがって、次のような通常のコピーを実行する場合:

_cv::Mat b; 
cv::Mat a = b;
_

abのデータをポイントし、その参照カウントがインクリメントされます。同時に、以前にbでポイントされたデータの参照カウントが減らされます(減分後に0の場合、メモリは解放されます)。

質問1:プログラムによって異なります。詳細については、この質問を参照してください: is-cvmat-class-flawed-by-design

質問2:関数は値で戻ります。つまり、_return image_はマットをコピーし、参照カウントを増やし(現在はref_count = 2)、新しいマットを返します。関数が終了すると、イメージは破棄され、ref_countは1つ減ります。ただし、ref_countが0ではないため、メモリは解放されません。したがって、返されるcv :: Matはランダムなメモリ位置を指していません。

質問3:同様のことが起こります。 orgImage2.copyTo(aCopy);と言うと、aCopyが指すデータのref_countは減少します。次に、コピーされる新しいデータを保存するために新しいメモリが割り当てられます。そのため、_copyCopy1_はこれを行ったときに変更されませんでした。

7
Indika Pathi

C++ 11を見てください std :: shared_ptr 参照カウンターcv :: Matを使用することで、同じ方法で効果的に動作します。自動的に解放されます。つまり、メモリの割り当てが解除され、cv :: Matは使用できなくなります。これは事実上「浅いコピー」であり、大量のメモリの割り当て/割り当て解除でリソースを節約します。

一方、cv :: Mat :: cloneは、マトリックスの常駐する新しいメモリブロック全体を割り当てる「ディープコピー」を提供します。これは、必要な画像に変換を行う場合に役立ちます。ただし、元に戻すと、メモリの割り当て/割り当て解除により多くのリソースが必要になります。

これが誰かを助けることを願っています。

2
user3102241