OpenCV自動內存管理
目前版本的OpenCV是自動處理所有自己的內存的,雖然這么說也不是很嚴謹。OpenCV在2.0版本中引入了一個新的C++接口,利用自動內存管理給出了解決問題的新方法。使用這個方法,開發者不需要糾結在管理內存上,而且你的代碼會變得簡潔。
以 Mat為例 ,首先現在沒必要再手動地(1)為其開辟空間(2)在不需要時立即將空間釋放。但手動地做還是可以的:大多數OpenCV函數仍會手動地為輸出數據開辟空間。當傳遞一個已經存在的 Mat 對象時,開辟好的矩陣空間會被重用。也就是說,我們每次都使用大小正好的內存來完成任務。基本上講 Mat 是一個類,由兩個數據部分組成:矩陣頭(包含矩陣尺寸,存儲方法,存儲地址等信息)和一個指向存儲所有像素值的矩陣(根據所選存儲方法的不同矩陣可以是不同的維數)的指針。矩陣頭的尺寸是常數值,但矩陣本身的尺寸會依圖像的不同而不同,通常比矩陣頭的尺寸大數個數量級。因此,當在程序中傳遞圖像並創建拷貝時,大的開銷是由矩陣造成的,而不是信息頭。OpenCV是一個圖像處理庫,囊括了大量的圖像處理函數,為了解決問題通常要使用庫中的多個函數,因此在函數中傳遞圖像是家常便飯。同時不要忘了我們正在討論的是計算量很大的圖像處理算法,因此,除非萬不得已,我們不應該拷貝 大 的圖像,因為這會降低程序速度。
為了搞定這個問題,OpenCV使用引用計數機制。其思路是讓每個 Mat 對象有自己的信息頭,但共享同一個矩陣。這通過讓矩陣指針指向同一地址而實現。而拷貝構造函數則 只拷貝信息頭和矩陣指針 ,而不拷貝矩陣。
1 Mat A, C; // 只創建信息頭部分
2 A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 這里為矩陣開辟內存
3 Mat B(A); // 使用拷貝構造函數
4 C = A; // 賦值運算符
以上代碼中的所有Mat對象最終都指向同一個也是唯一一個數據矩陣。雖然它們的信息頭不同,但通過任何一個對象所做的改變也會影響其它對象。實際上,不同的對象只是訪問相同數據的不同途徑而已。這里還要提及一個比較棒的功能:你可以創建只引用部分數據的信息頭。比如想要創建一個感興趣區域( ROI ),你只需要創建包含邊界信息的信息頭:
1 Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
2 Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries
現在你也許會問,如果矩陣屬於多個 Mat 對象,那么當不再需要它時誰來負責清理?簡單的回答是:最后一個使用它的對象。通過引用計數機制來實現。無論什么時候有人拷貝了一個 Mat 對象的信息頭,都會增加矩陣的引用次數;反之當一個頭被釋放之后,這個計數被減一;當計數值為零,矩陣會被清理。但某些時候你仍會想拷貝矩陣本身(不只是信息頭和矩陣指針),這時可以使用函數 clone() 或者 copyTo() 。
1 Mat F = A.clone(); 2 Mat G; 3 A.copyTo(G);
現在改變 F 或者 G 就不會影響 Mat 信息頭所指向的矩陣。
總結
1、OpenCV函數中輸出圖像的內存分配是自動完成的(如果不特別指定的話)。
2、使用OpenCV的C++接口時不需要考慮內存釋放問題。
3、賦值運算符和拷貝構造函數( ctor )只拷貝信息頭。
4、使用函數 clone() 或者 copyTo() 來拷貝一副圖像的矩陣。
上述部分內容參考了OpenCV的中文教程。(http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/mat%20-%20the%20basic%20image%20container/mat%20-%20the%20basic%20image%20container.html#matthebasicimagecontainer)