從2001年以來,opencv的函數庫一直是基於C接口構建的,因此在opencv1.0版本中,一般使用IplImage的C結構體在內存中存儲圖像,因此,我們在很多較經典的書籍或者開源項目中依然可見IplImage。但是用其存儲圖像的時候必須在退出前將圖像內存手動release掉,即添加語句cvReleaseImage(&IplImage);,否則會造成內存泄漏。Mat類帶來了自動的內存管理,同時它的操作也更加簡單,比如用imshow顯示圖像,imread讀取圖像等等,跟Matlab有點接近。下面是將圖像容器類mat轉化成IplImage結構體的一種方法,最后別忘了cvReleaseImage(&pImg)。
Mat frame,frame1; IplImage* pImg; frame=capture.read(); frame1=frame.clone(); pImg=cvCreateImage(cvSize(frame.cols,frame.rows),8,3); pImg->imageData=(char*)frame1.data;
...
Mat是一個類,它由兩個數據部分組成:矩陣頭和一個指向存儲所有像素值的矩陣的指針。其中矩陣頭包含了矩陣的尺寸、存儲方法、儲存地址等信息,由此可以看出矩陣頭所占的內存很小,通常是一個常數值,而具體存儲所有像素值的矩陣則非常大。因此,在程序中傳遞圖像並創建副本時,大的開銷是由矩陣造成的,而不是信息頭。其實上面給的mat轉IplImage結構體就是一個例子,顯然復制圖像會增加算法的復雜度,降低程序的性能。形象點說,在一個班級里,矩陣頭就相當於存儲了班級里有多少人、男女比多少、平均身高等信息,而矩陣就儲存了班級中所有同學的所有基本信息,每一個同學就相當於是一個像素矩陣中的一個元素,那么根據不同的存儲方法就得到了不同的元素表示方法,顯然矩陣要比矩陣頭復雜得多。
為了解決上述代碼存在的問題。opencv使用了引用計數機制,其思路就是讓每個Mat對象有自己的信息頭,但是共享同一個矩陣。也就是讓矩陣指針指向同一地址,共用一片內存來實現。復制圖像的時候只是復制了矩陣頭的信息和矩陣指針,並不是復制了整個矩陣。例如下面這段代碼:
Mat A,C;//僅創建信息頭部分 A= imread("1.jpg",CV_LOAD_IMAGE_COLOR);//這里為矩陣開辟內存 Mat B(A);//使用拷貝構造函數 C=A;
這段代碼中,A、B和C都是Mat類型,它們都指向同一個也是唯一一個數據矩陣。雖然它們的信息頭不同,但通過任何一個對象所做的改變也會影響其它對象。而通過clone()或者copyTo()來復制一個圖像,就包括了矩陣本身,也因此,改變復制對象的內容並不會改變源矩陣,例如frame1顯然是復制frame,因此對frame1的操作並不改變frame。
Mat的常見屬性
屬性 | 說明 |
---|---|
data | uchar型的指針。Mat類分為了兩個部分:矩陣頭和指向矩陣數據部分的指針,data就是指向矩陣數據的指針。 |
dims | 矩陣的維度,例如5*6矩陣是二維矩陣,則dims=2,三維矩陣dims=3. |
rows | 矩陣的行數 |
cols | 矩陣的列數 |
size | 矩陣的大小,size(cols,rows) |
channels() | 矩陣元素擁有的通道數,例如常見的彩色圖像,每一個像素由RGB三個通道組成 |
type() | 表示了矩陣中元素的類型以及矩陣的通道個數,它是一系列的預定義的常量,其命名規則為CV_(位數)+(數據類型)+(通道數)如:,CV_8UC3 |
depth() | 矩陣中元素的一個通道的數據類型,這個值和type是相關的。例如 type為 CV_8UC3,一個3通道的16位的有符號整數。那么,depth則是CV_8UC |
elemSize() | 矩陣一個元素占用的字節數(不區分通道,即多個通道的總和) |
elemSize1() | 矩陣一個元素每個通道占用的字節數(區分通道,單個通道的值) |
flags | 一個int型數字,保存了許多有用的信息,flags說明; |
創建Mat的方式:
1、Mat M
創建一個矩陣頭,沒有數據。
2、Mat M(2,2,CV_8UC3,Scalar(0,0,1));
2*2大小的矩陣,每個元素為3通道8位無符號整數,如下:
0 0 1 0 0 1
0 0 1 0 0 1
3、Mat M(2,2,CV_8UC1,Scalar(10));
與上面類似:
10 10
10 10
4、Mat M(2,2,CV_8UC1,Scalar::all(0));
5、以逗號分隔符初始化賦值:Mat M = (Mat_(3,3) << 1,2,3,4,5,6,7,8,9);
6、為已經存在的IplImage指針創建信息頭,例如:
IplImage* img=cvLoadImage("1.jpg",1); Mat mtx(img);//轉換IplImage*為Mat
7、利用create()函數
Mat M;
M.create(4,4,CV_8UC(2)); cout<<"M="M<<endl;
但是這種方法不能為矩陣設初值,只是在改變尺寸時重新為矩陣數據開辟內存而已。
8、Mat還有一些matlab式函數用來創建和初始化矩陣:
Mat E = Mat::eye(4,4,CV_64F); //單位矩陣
Mat Z = Mat::zeros(4,4,CV_32F); //0矩陣
Mat O = Mat::ones(4,4,CV_8UC1); //1矩陣
9、為已經存在的對象創建新的信息頭
Mat E=Mat::eye(4,4,CV_64F); Mat Row=E.row(1).clone(); cout<<Row<<endl;