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





