OpenCV cv :: Mat的深拷贝

问题描述 投票:26回答:3

复制cv::Mat的行为使我感到困惑。

我从文档中了解到,Mat::copyTo()是深层副本,而赋值运算符则不是。我的问题:

  1. 我应该怎么做才能从函数中返回cv::Mat,例如:cv::Mat func()

  2. 根据文档,如果我返回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;
    }

然后两个显示器显示相同的图像a.jpg。为什么?在其他时候,它不起作用。 (原始代码太长,但也可以简化为上述情况)。在那些时候,赋值运算符似乎实际上是在“浅”复制。为什么?

非常感谢!

c++ opencv deep-copy
3个回答
23
投票

我认为,使用赋值不是矩阵复制的最佳方法。如果要矩阵的新完整副本,请使用:

Mat a=b.clone(); 

如果要复制矩阵以替换另一个矩阵中的数据(以避免内存重新分配),请使用:

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

当您将一个矩阵分配给另一个矩阵时,智能指针对矩阵数据的引用计数器将增加一,当您释放矩阵时(可以在离开代码块时隐式完成),它的值将减一。当它等于零时,分配的内存被释放。

如果要从函数中使用引用获取结果,则速度更快:

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

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

7
投票

我已经使用OpenCV一段时间了,而cv :: Mat也使我感到困惑,所以我做了一些阅读。

cv :: Mat是一个标头,它指向保存实际图像数据的* data指针。它还实现了引用计数。它保存当前指向该* data指针的cv::Mat标头的数量。因此,当您进行常规复制时,例如:

cv::Mat b; 
cv::Mat a = b;

a将指向b的数据,并且其引用计数将增加。同时,先前由b指向的数据的引用计数将减少(如果减少后内存为0,则将释放内存)。

问题1:取决于您的程序。有关更多详细信息,请参考此问题:is-cvmat-class-flawed-by-design

问题2:函数按值返回。这意味着return image将复制Mat并增加引用计数(现在ref_count = 2)并返回新的Mat。函数结束时,图像将被破坏,ref_count将减少一。但是由于ref_count不为0,所以不会释放内存。因此返回的cv :: Mat并不指向随机的内存位置。

问题3:发生了类似的事情。当您说orgImage2.copyTo(aCopy);时,由aCopy指向的数据的ref_count将减少。然后分配新的内存以存储将要复制的新数据。因此,这就是为什么您执行此操作时未修改copyCopy1的原因。


2
投票

看看c ++ 11 std::shared_ptr以相同的方式有效地工作,通过使用引用计数器cv :: Mat可以聪明地记住每次引用指针时,一旦计数达到0,它将自动释放,即内存释放,并且cv :: Mat不再可用。这实际上是一个“浅表副本”,可以节省分配/取消分配大量内存的资源。

另一方面,cv :: Mat :: clone将提供一个“深层副本”,该副本为要驻留的矩阵分配一个全新的内存块,如果您要转换为可以要撤消操作,更多的内存分配/释放会增加所需的资源量。

希望这对某人有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.