OpenCV cv :: Mat的深层副本

时间:2014-02-09 13:18:22

标签: c++ opencv deep-copy

复制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. 但为什么呢?这不是一个深刻的副本。

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

    非常感谢!

3 个答案:

答案 0 :(得分:20)

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

Mat a=b.clone(); 

如果你想复制矩阵来替换来自另一个矩阵的数据(为了避免重新分配内存),请使用:

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

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

如果你想从函数使用引用得到结果,它会更快:

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

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

答案 1 :(得分:7)

我一直在使用OpenCV一段时间,而cv :: Mat也让我很困惑,所以我做了一些阅读。

cv :: Mat是一个标题,指向保存实际图像数据的*数据指针。它还实现了引用计数。它保存当前指向该*数据指针的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 :(得分:2)

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

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

希望这有助于某人。