Mat是OpenCV最基本的數據結構,Mat即矩陣(Matrix)的縮寫,Mat數據結構主要包含2部分:Header(矩陣頭)和Pointer(矩陣指針)。Header中主要包含矩陣的大小,存儲方式,存儲地址等信息;Pointer中存儲指向像素值的指針。矩陣頭的大小是一個常數,不會隨着圖像的大小而改變,但是保存圖像像素數據的矩陣則會隨着圖像的大小而改變,通常數據量會很大,比矩陣頭大幾個數量級。因此,OpenCV使用了引用次數,當進行圖像複製和傳遞時,不再複製整個Mat數據,而只是複製矩陣頭和指向像素矩陣的指針。
OpenCV中的內存分配是自動完成(不是特別指定的話),使用OpenCV的C++ 接口不需要考慮內存釋放問題。當Mat實例化後,分配內存;當對象離開作用域後,分配的內存自動釋放。
1.1 image storage method
在一個彩色圖像中,圖像數據緩衝區中的前三個字節對應圖像左上角像素的三個通道值,接下來的三個字節對應第一行的第二個像素,以此類推。一個寬爲W、高爲H的圖像需要一個大小由W X H X3個uchar構成的內存塊。但是,出於效率考慮,每行會填補一些額外的像素。這是因爲,如果行的長度是4或8的倍數,一些多媒體處理芯片(如Intel的MMX架構)可以更高效的處理圖像。這些額外的像素不會被顯示或者保存,填補的值將被忽略。OpenCV將填補後一行的長度指定爲關鍵字。如果圖像沒有對行進行填補,那麼圖像的有效寬度就等於圖像的真實寬度。
成員變量cols代表圖像的寬度(圖像的列數),rows代表圖像的高度,step代表以字節爲單位的圖像的有效寬度。即使你的圖像的元素類型不是uchar,step仍然代表着行的字節數。像素的大小可以由elemSize函數得到:對於一個三通道的short型矩陣(CV_16SC3),elemSize返回6。圖像的通道數可以channels方法得到,灰度爲1,彩色爲3。total函數返回矩陣的像素個數。
1.2 isContinuous
Mat提供了一個檢測圖像是否連續的函數isContinuous()。當圖像連通時,就可以把圖像完全展開,看成是一行。
1.3 Deep / Shallow Copies(深拷貝和淺拷貝)
1. shallow copy
Mat實現了引用計數以及淺拷貝。引用計數的作用是隻有當所有引用內存數據的對象都被析構後,內存纔會釋放。淺拷貝是指當圖像之間進行賦值時,圖像數據並未發生複製,而是兩個對象都指向同一塊內存塊。
cv::Mat a; // creates just the header parts(創建矩陣頭) a = cv::imread("test.jpg"); // allocate matrix(分配矩陣內存),讀入圖像 cv::Mat b = a; // Assignment operator(賦值運算符),複製 /* a,b獨立矩陣頭,但是其矩陣指針指向同一個矩陣,即其中任何一個矩陣數據改變都會影響另外一個當Mat對象每被複制一次時,就會將引用計數加1,故其引用計數ref count爲2 */
2. deep copy
Sometimes you will want to copy the matrix itself too, so OpenCV provides the clone() and copyTo() functions.
cv::Mat c = a.clone(); cv::Mat d; a.copyTo(d); /*上面代碼中的c,d各自擁有自己的矩陣,改變自己的矩陣數據不會相互影響。*/
#include<iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> using namespace std; using namespace cv; int main(int argc, char *argv[]) { Mat m1 = (Mat_<double>(3,3) << 1, 2, 3, 4, 5, 6, 7, 8, 9); cout << "yunfung_opencv_learn_test:" <<"\n\n"; cout << "m1_refcount=" << m1.u->refcount <<endl; Mat m2 = m1; // m1.u->refcount ,not add 1 cout << "m1_refcount=" << m1.u->refcount <<" "<<"m2_refcount=" << m1.u->refcount<<endl; Mat m3 = m1.clone(); // m1.u->refcount ,not add 1 cout << "m1_refcount=" << m1.u->refcount <<endl; m2.release(); //if using m1.release(),no m1.u->refcount cout << "m1_refcount=" << m1.u->refcount <<" "<<"m2_refcount=" << m1.u->refcount<<endl; }