2014-02-09 152 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將提供一個「深層拷貝」,它爲矩陣分配一個全新的內存塊,如果您正在對圖像進行轉換,這會非常有用。然而,你可能想撤消,更多的內存分配/釋放會增加所需資源的數量。

希望這可以幫助別人。