2014-02-09 154 views
17

复制cv::Mat的行为令我困惑。OpenCV cv深层副本::垫

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

非常感谢!

+3

1.它取决于所需的语义。 2.不,'cv :: Mat'使用某种引用计数,所以在呼叫方收到的对象将是有效的。 – juanchopanza

+0

问题3说“有时”某些事情“似乎”发生。我有限的大脑不能计算这样的问题:-) – juanchopanza

回答

17

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

Mat a=b.clone(); 

如果你想复制矩阵替换数据从enother矩阵(为避免内存重新分配)使用方法:

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

我已经使用了OpenCV的,而现在和CV ::垫困惑我也一样,所以我做了一些阅读。

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计数(现在ref_count = 2)并返回新的Mat。当函数结束时,图像将被销毁并且ref_count将被减少1。但是由于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将提供一个“深层拷贝”,它为矩阵分配一个全新的内存块,如果您正在对图像进行转换,这会非常有用。然而,你可能想撤消,更多的内存分配/释放会增加所需资源的数量。

希望这可以帮助别人。