【问题标题】:Deep Copy of OpenCV cv::MatOpenCV cv::Mat 的深拷贝
【发布时间】:2014-03-06 17:43:57
【问题描述】:

复制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。为什么?还有一些时候它不起作用。 (原代码太长但也可以简化为上述情况)。在那些时候,赋值运算符似乎实际上是“浅”的复制。为什么?

非常感谢!

【问题讨论】:

  • 1.这取决于所需的语义。 2.不,cv::Mat使用了某种引用计数,所以调用方收到的对象是有效的。
  • 问题 3 说“有时”某事“似乎”会发生。我有限的大脑无法计算出这样的问题:-)

标签: c++ opencv deep-copy


【解决方案1】:

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

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);
...
}

【讨论】:

    【解决方案2】:

    我使用 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 的原因。

    【讨论】:

      【解决方案3】:

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

      另一方面 cv::Mat::clone 将提供一个“深拷贝”,它为矩阵分配一个全新的内存块来驻留,如果您正在对您的图像进行转换,这可能很有用然而,可能想要撤消,更多的内存分配/释放会增加所需的资源量。

      希望这对某人有所帮助。

      【讨论】:

        猜你喜欢
        • 2012-04-12
        • 2015-01-13
        • 2011-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多