opencv的基本數據結構(一)(轉)


從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;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM