關於 Mat ,首先要知道的是你不必再手動地
(1)爲其開闢空間
(2)在不需要時立即將空間釋放
但手動地做還是可以的:大多數OpenCV函數仍會手動地爲輸出數據開闢空間。當傳遞一個已經存在的 Mat 對象時,開闢好的矩陣空間會被重用。也就是說,我們每次都使用大小正好的內存來完成任務。
Mat是OpenCV裏最基本的一個類,由兩個數據部分組成:矩陣頭(包含矩陣尺寸,存儲方法,存儲地址等信息)和一個指向存儲矩陣所有像素值的指針。矩陣頭的尺寸是常數值,但矩陣本身的尺寸會依圖像的不同而不同,通常比矩陣頭的尺寸大數個數量級。因此,當在程序中傳遞圖像並創建拷貝時,大的開銷是由矩陣造成的,而不是信息頭。OpenCV是一個圖像處理庫,囊括了大量的圖像處理函數,爲了解決問題通常要使用庫中的多個函數,因此在函數中傳遞圖像是家常便飯。同時不要忘了我們正在討論的是計算量很大的圖像處理算法,因此,除非萬不得已,我們不應該拷貝大的圖像,因爲這會降低程序速度。
爲了搞定這個問題,OpenCV使用引用計數機制。其思路是讓每個 Mat 對象有自己的信息頭,但共享同一個矩陣。這通過讓矩陣指針指向同一地址而實現。而拷貝構造函數則只拷貝信息頭和矩陣指針,而不拷貝矩陣。
Mat A, C; // 只創建信息頭部分
A =imread(argv[1], CV_LOAD_IMAGE_COLOR); // 這裏爲矩陣開闢內存
Mat B(A); // 使用拷貝構造函數
C = A; // 賦值運算符
以上代碼中的所有Mat對象最終都指向同一個也是唯一一個數據矩陣。雖然它們的信息頭不同,但通過任何一個對象所做的改變也會影響其它對象。實際上,不同的對象只是訪問相同數據的不同途徑而已。
如果現有成員,後面才能確定參數:
mat.creat(rows,cols, CV_8UC1);
這裏還要提及一個比較棒的功能:你可以創建只引用部分數據的信息頭。比如想要創建一個感興趣區域( ROI),你只需要創建包含邊界信息的信息頭:
Mat D (A, Rect(10,10, 100, 100) ); // using a rectangle
Mat E =A(Range:all(), Range(1,3)); // using row and column boundaries
現在你也許會問,如果矩陣屬於多個 Mat 對象,那麼當不再需要它時誰來負責清理?簡單的回答是:最後一個使用它的對象。通過引用計數機制來實現。無論什麼時候有人拷貝了一個 Mat 對象的信息頭,都會增加矩陣的引用次數;反之當一個頭被釋放之後,這個計數被減一;當計數值爲零,矩陣會被清理。但某些時候你仍會想拷貝矩陣本身(不只是信息頭和矩陣指針),這時可以使用函數 clone() 或者 copyTo() 。
Mat F = A.clone();
Mat G;
A.copyTo(G);
現在改變 F 或者 G 就不會影響 Mat 信息頭所指向的矩陣。總結一下,你需要記住的是
①OpenCV函數中輸出圖像的內存分配是自動完成的(如果不特別指定的話)。
②使用OpenCV的C++接口時不需要考慮內存釋放問題。
③賦值運算符和拷貝構造函數( ctor)只拷貝信息頭。
④使用函數 clone() 或者 copyTo() 來拷貝一副圖像的矩陣。
對於二維多通道圖像,首先要定義其尺寸,即行數和列數,然後,需要指定存儲元素的數據類型以及每個矩陣點的通道數。爲此,依據下面的規則有多種定義
CV_[The number of bits peritem][Signed or Unsigned][Type Prefix]C[The channel number]
CV_<bit_depth>(S|U|F)C<number_of_channels>
①bit_depth
比特數---代表8bite,16bites,32bites,64bites
舉個例子:
如果你現在創建了一個存儲--灰度圖片的Mat對象,這個圖像的大小爲寬100,高100,那麼,現在這張灰度圖片中有10000個像素點,它每一個像素點在內存空間所佔的空間大小是8bite,8位--所以它對 應的就是CV_8
②S|U|F
S--代表---signedint---有符號整形
U--代表--unsignedint--無符號整形
F--代表--float---------單精度浮點型
③C<number_of_channels>----代表---一張圖片的通道數,比如:
1--灰度圖片--grayImg---是--單通道圖像
2--RGB彩色圖像---------是--3通道圖像
3--帶Alph通道的RGB圖像--是--4通道圖像