OpenCV的基本數(shù)據(jù)類型
OpenCV提供了多種基本數(shù)據(jù)類型。雖然這些數(shù)據(jù)類型在C語言中不是基本類型,但結(jié)構(gòu)都很簡單,可將它們作為原子類型。可以在“…/OpenCV/cxcore/include”
目錄下的cxtypes.h文件中查看其詳細定義。
數(shù)據(jù)類型中最簡單的就是CvPoint。CvPoint是一個包含integer類型成員x和y的簡單結(jié)構(gòu)體。CvPoint有兩個變體類型:CvPoint2D32f和CvPoint3D32f。前者同樣有兩個成員x,y,但它們是浮點類型;而后者卻多了一個浮點類型的成員z。
CvSize類型與CvPoint非常相似,但它的數(shù)據(jù)成員是integer類型的width和height。如果希望使用浮點類型,則選用CvSize的變體類型CvSize2D32f。
CvRect類型派生于CvPoint和CvSize,它包含4個數(shù)據(jù)成員:x,y,width和height。(正如你所想的那樣,該類型是一個復(fù)合類型)。
下一個(但不是最后一個)是包含4個整型成員的CvScalar類型,當內(nèi)存不是問題時,CvScalar經(jīng)常用來代替1,2或者3個實數(shù)成員(在這個情況下,不需要的分量被忽略)。CvScalar有一個單獨的成員val,它是一個指向4個雙精度浮點數(shù)數(shù)組的指針。
所有這些數(shù)據(jù)類型具有以其名稱來定義的構(gòu)造函數(shù),例如cvSize()。(構(gòu)造函數(shù)通常具有與結(jié)構(gòu)類型一樣的名稱,只是首字母不大寫)。記住,這是C而不是C++,所以這些構(gòu)造函數(shù)只是內(nèi)聯(lián)函數(shù),它們首先提取參數(shù)列表,然后返回被賦予相關(guān)值的結(jié)構(gòu)。??????????????????????????????????????????????????????????????????????????????????????????????????????????????????
各數(shù)據(jù)類型的內(nèi)聯(lián)構(gòu)造函數(shù)被列在下表中:cvPoint(),cvSize(),cvRect()和cvScalar()。這些結(jié)構(gòu)都十分有用,因為它們不僅使代碼更容易編寫,而且也更易于閱讀。假設(shè)要在(5,10)和(20,30)之間畫一個白色矩形,只需簡單調(diào)用:
?
cvRectangle(
????myImg,
????cvPoint(5,10),
????cvPoint(20,30),
????cvScalar(255,255,255)
);
?
表3-1:points, size, rectangles和calar三元組的結(jié)構(gòu)
結(jié)構(gòu)
成員
意義
CvPoint
int x, y
圖像中的點
CvPoint2D32f
float x, y
二維空間中的點
CvPoint3D32f
float x, y, z
三維空間中的點
CvSize
int width, height
圖像的尺寸
CvRect
int x, y, width, height
圖像的部分區(qū)域
CvScalar
double val[4]
RGBA 值
cvScalar是一個特殊的例子:它有3個構(gòu)造函數(shù)。第一個是cvScalar(),它需要一個、兩個、三個或者四個參數(shù)并將這些參數(shù)傳遞給數(shù)組val[]中的相應(yīng)元素。第二個構(gòu)造函數(shù)是cvRealScalar(),它需要一個參數(shù),它被傳遞給給val[0],而val[]數(shù)組別的值被賦為0。最后一個有所變化的是cvScalarAll(),它需要一個參數(shù)并且val[]中的4個元素都會設(shè)置為這個參數(shù)。
矩陣和圖像類型
下圖為我們展示了三種圖像的類或結(jié)構(gòu)層次結(jié)構(gòu)。使用OpenCV時,會頻繁遇到IplImage數(shù)據(jù)類型,IplImage是我們用來為通常所說的“圖像”進行編碼的基本結(jié)構(gòu)。這些圖像可能是灰度,彩色,4通道的(RGB+alpha),其中每個通道可以包含任意的整數(shù)或浮點數(shù)。因此,該類型比常見的、易于理解的3通道8位RGB圖像更通用。
OpenCV提供了大量實用的圖像操作符,包括縮放圖像,單通道提取,找出特定通道最大最小值,兩個圖像求和,對圖像進行閾值操作,等等。
圖3-1:雖然OpenCV是由C語言實現(xiàn)的,但它使用的結(jié)構(gòu)體也是遵循面向?qū)ο蟮乃枷朐O(shè)計的。實際上,IplImage由CvMat派生,而CvMat由CvArr派生
在開始探討圖像細節(jié)之前,我們需要先了解另一種數(shù)據(jù)類型CvMat,OpenCV的矩陣結(jié)構(gòu)。雖然OpenCV完全由C語言實現(xiàn),但CvMat和IplImage之間的關(guān)系就如同C++中的繼承關(guān)系。實質(zhì)上,IplImage可以被視為從CvMat中派生的。因此,在試圖了解復(fù)雜的派生類之前,最好先了解基本的類。第三個類CvArr,可以被視為一個抽象基類,CvMat由它派生。在函數(shù)原型中,會經(jīng)常看到CvArr(更準確地說,CvArr*),當它出現(xiàn)時,便可以將CvMat*或IplImage*傳遞到程序。
??? CvMat矩陣結(jié)構(gòu):
在開始學習矩陣的相關(guān)內(nèi)容之前,我們需要知道兩件事情。第一,在OpenCV中沒有向量(vector)結(jié)構(gòu)。任何時候需要向量,都只需要一個列矩陣(如果需要一個轉(zhuǎn)置或者共軛向量,則需要一個行矩陣)。第二,OpenCV矩陣的概念與我們在線性代數(shù)課上學習的概念相比,更抽象,尤其是矩陣的元素,并非只能取簡單的數(shù)值類型。例如,一個用于新建一個二維矩陣的例程具有以下原型:
cvMat* cvCreateMat ( int rows, int cols, int type );
這里type可以是任何預(yù)定義類型,預(yù)定義類型的結(jié)構(gòu)如下:CV_?(S|U|F)C。于是,矩陣的元素可以是32位浮點型數(shù)據(jù)(CV_32FC1),或者是無符號的8位三元組的整型數(shù)據(jù)(CV_8UC3),或者是無數(shù)的其他類型的元素。一個CvMat的元素不一定就是個單一的數(shù)字。在矩陣中可以通過單一(簡單)的輸入來表示多值,這樣我們可以在一個三原色圖像上描繪多重色彩通道。對于一個包含RGB通道的簡單圖像,大多數(shù)的圖像操作將分別應(yīng)用于每一個通道(除非另有說明)。
?
IplImage數(shù)據(jù)結(jié)構(gòu)
從本質(zhì)上講,它是一個CvMat對象,但它還有其他一些成員變量將矩陣解釋為圖像。這個結(jié)構(gòu)最初被定義為Intel圖像處理庫(IPL)的一部分。IplImage結(jié)構(gòu)的準確定義如例3-10所示。
例3-10:IplImage結(jié)構(gòu)
?
typedef struct _IplImage?{
??int?nSize;
??int?ID;
??int?nChannels;
??int?alphaChannel;
??int?depth;
??char colorModel[4];
??char channelSeq[4];
??int?dataOrder;
??int?origin;
??int?align;
??int?width;
??int?height;
??struct _IplROI*?roi;
??struct _IplImage*?maskROI;
??void*?imageId;
??struct _IplTileInfo*?tileInfo;
??int?imageSize;
??char*?imageData;
??int?widthStep;
??int?BorderMode[4];
??int?BorderConst[4];
??char*?imageDataOrigin;
}?IplImage;
?
我們試圖討論這些變量的某些功能。有些變量不是很重要,但是有些變量非常重要,有助于我們理解OpenCV解釋和處理圖像的方式。
width和height這兩個變量很重要,其次是depth和nchannals。depth變量的值取自ipl.h中定義的一組數(shù)據(jù),但與在矩陣中看到的對應(yīng)變量不同。因為在圖像中,我們往往將深度和通道數(shù)分開處理,而在矩陣中,我們往往同時表示它們。可用的深度值如表3-2所示。????????????????????????????????????????????????????????????????????????????????????????????????表3-2:OpenCV圖像類型
宏
圖像像素類型
IPL_DEPTH_8U
無符號8位整數(shù) (8u)
IPL_DEPTH_8S
有符號 8位整數(shù)(8s)
IPL_DEPTH_16S
有符號16位整數(shù)(16s)
IPL_DEPTH_32S
有符號32位整數(shù)(32s)
IPL_DEPTH_32F
32位浮點數(shù)單精度(32f)
IPL_DEPTH_64F
64位浮點數(shù)雙精度(64f)
通道數(shù)nChannels可取的值是1,2,3或4。
隨后兩個重要成員是origin和dataOrder。origin變量可以有兩種取值:IPL_ORIGIN_TL 或者 IPL_ORIGIN_BL,分別設(shè)置坐標原點的位置于圖像的左上角或者左下角。
在計算機視覺領(lǐng)域,一個重要的錯誤來源就是原點位置的定義不統(tǒng)一。具體而言,圖像的來源、操作系統(tǒng)、編解碼器和存儲格式等因素都可以影響圖像坐標原點的選取。舉例來說,你或許認為自己正在從圖像上面的臉部附近取樣,但實際上卻在圖像下方的裙子附近取樣。避免此類現(xiàn)象發(fā)生的最好辦法是在最開始的時候檢查一下系統(tǒng),在所操作的圖像塊的地方畫點東西試試。
dataOrder的取值可以是IPL_DATA_ORDER_PIXEL或IPL_DATA_ORDER_PLANE,前者指明數(shù)據(jù)是將像素點不同通道的值交錯排在一起(這是常用的交錯排列方式),后者是把所有像素同通道值排在一起,形成通道平面,再把平面排列起來。
參數(shù)widthStep與前面討論過的CvMat中的step參數(shù)類似,包括相鄰行的同列點之間的字節(jié)數(shù)。僅憑變量width是不能計算這個值的,因為為了處理過程更高效每行都會用固定的字節(jié)數(shù)來對齊;因此在第i行末和第i+1行開始處可能會有些冗于字節(jié)。參數(shù)imageData包含一個指向第一行圖像數(shù)據(jù)的指針。如果圖像中有些獨立的平面(如當dataOrder = IPL_DATA_ORDER_PLANE)那么把它們作為單獨的圖像連續(xù)擺放,總行數(shù)為height和nChannels的乘積。但通常情況下,它們是交錯的,使得行數(shù)等于高度,而且每一行都有序地包含交錯的通道。
最后還有一個實用的重要參數(shù)——?感興趣的區(qū)域(ROI),實際上它是另一個IPL/IPP 結(jié)構(gòu)IplROI的實例。IplROI包含xOffset,yOffset,height,width和coi成員變量,其中COI代表channel of interest(感興趣的通道)。ROI的思想是:一旦設(shè)定ROI,通常作用于整幅圖像的函數(shù)便會只對ROI所表示的子圖像進行操作。如果IplImage變量中設(shè)置了ROI,則所有的OpenCV函數(shù)就會使用該ROI變量。如果COI被設(shè)置成非0值,則對該圖像的操作就只作用于被指定的通道上了。不幸的是,許多OpenCV函數(shù)都忽略參數(shù)COI。
OpenCV提供了多種基本數(shù)據(jù)類型。雖然這些數(shù)據(jù)類型在C語言中不是基本類型,但結(jié)構(gòu)都很簡單,可將它們作為原子類型??梢栽凇啊?OpenCV/cxcore/include”