php網(wǎng)站開發(fā)過(guò)程免費(fèi)下載b站視頻軟件
OpenCV的基本數(shù)據(jù)類型...2
矩陣和圖像類型...4
CvMat矩陣結(jié)構(gòu)...4
矩陣數(shù)據(jù)的存取...8
點(diǎn)的數(shù)組...13
IplImage數(shù)據(jù)結(jié)構(gòu)...14
訪問(wèn)圖像數(shù)據(jù)...17
對(duì)ROI和widthStep的補(bǔ)充...18
矩陣和圖像操作...21
cvAbs,cvAbsDiff和cvAbsDiffS.23
cvAdd,cvAddS, cvAddWeighted和alpha融合...23
cvAnd和cvAndS.26
cvAvg.27
cvAvgSdv.27
cvCalcCovarMatrix.28
cvCmp和cvCmpS.29
cvConvertScale.30
cvConvertScaleAbs.31
cvCopy.31
cvCountNonZero.32
cvCrossProduct32
cvCvtColor32
cvDet35
cvDiv.35
cvDotProduct35
cvEigenVV.36
cvFlip.36
cvGEMM..37
cvGetCol和cvGetCols.38
cvGetDiag.38
cvGetDims和cvGetDimSize.39
cvGetRow和cvGetRows.39
cvGetSize.40
cvGetSubRect40
cvInRange和cvInRangeS.41
cvInvert41
cvMax和cvMaxS.43
cvMerge.44
cvMin和cvMinS.44
cvMinMaxLoc.44
cvMul45
cvNot45
cvNorm..46
cvNormalize.47
cvOr和cvOrS.48
cvReduce.48
cvRepeat49
cvScale.49
cvSet 和 cvSetZero.50
cvSetIdentity.50
cvSolve.50
cvSplit51
cvSub,cvSubS和cvSubRS.51
cvSum..52
cvSVD..53
cvSVBkSb.53
cvTrace.54
cvTranspose與cvT.54
cvXor和cvXorS.55
cvZero.55
繪圖...55
直線...55
圓形和橢圓...57
多邊形...58
字體和文字...59
數(shù)據(jù)存儲(chǔ)...62
集成性能基元...66
驗(yàn)證安裝...66
小結(jié)...67
練習(xí)...67
?
?
OpenCV的基本數(shù)據(jù)類型
OpenCV提供了多種基本數(shù)據(jù)類型。雖然這些數(shù)據(jù)類型在C語(yǔ)言中不是基本類型,但結(jié)構(gòu)都很簡(jiǎn)單,可將它們作為原子類型??梢栽凇?em>…/OpenCV/cxcore/include”目錄下的cxtypes.h文件中查看其詳細(xì)定義。
在這些數(shù)據(jù)類型中最簡(jiǎn)單的就是CvPoint。CvPoint是一個(gè)包含integer類型成員x和y的簡(jiǎn)單結(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)存不是問(wèn)題時(shí),CvScalar經(jīng)常用來(lái)代替1,2或者3個(gè)實(shí)數(shù)成員(在這個(gè)情況下,不需要的分量被忽略)。CvScalar有一個(gè)單獨(dú)的成員val,它是一個(gè)指向4個(gè)雙精度浮點(diǎn)數(shù)數(shù)組的指針。
所有這些數(shù)據(jù)類型具有以其名稱來(lái)定義的構(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)。?????????????????????????????????????? ???????????????????????????????????????? ???????????????????????? 【31】
各數(shù)據(jù)類型的內(nèi)聯(lián)構(gòu)造函數(shù)被列在表3-1中:cvPointXXX(),cvSize(),cvRect()和cvScalar()。這些結(jié)構(gòu)都十分有用,因?yàn)樗鼈儾粌H使代碼更容易編寫,而且也更易于閱讀。假設(shè)要在(5,10)和(20,30)之間畫一個(gè)白色矩形,只需簡(jiǎn)單調(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è)元素都會(huì)設(shè)置為這個(gè)參數(shù)。
矩陣和圖像類型
圖3-1為我們展示了三種圖像的類或結(jié)構(gòu)層次結(jié)構(gòu)。使用OpenCV時(shí),會(huì)頻繁遇到IplImage數(shù)據(jù)類型,第2章已經(jīng)出現(xiàn)多次。IplImage是我們用來(lái)為通常所說(shuō)的“圖像”進(jìn)行編碼的基本結(jié)構(gòu)。這些圖像可能是灰度,彩色,4通道的(RGB+alpha),其中每個(gè)通道可以包含任意的整數(shù)或浮點(diǎn)數(shù)。因此,該類型比常見(jiàn)的、易于理解的3通道8位RGB圖像更通用。
OpenCV提供了大量實(shí)用的圖像操作符,包括縮放圖像,單通道提取,找出特定通道最大最小值,兩個(gè)圖像求和,對(duì)圖像進(jìn)行閾值操作,等等。本章我們將仔細(xì)介紹這類操作。?????????????????????????????????????? ???????????????????????????????????????????????? ???????????????????????? 【32】
圖3-1:雖然OpenCV是由C語(yǔ)言實(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語(yǔ)言實(shí)現(xiàn),但CvMat和IplImage之間的關(guān)系就如同C++中的繼承關(guān)系。實(shí)質(zhì)上,IplImage可以被視為從CvMat中派生的。因此,在試圖了解復(fù)雜的派生類之前,最好先了解基本的類。第三個(gè)類CvArr,可以被視為一個(gè)抽象基類,CvMat由它派生。在函數(shù)原型中,會(huì)經(jīng)常看到CvArr(更準(zhǔn)確地說(shuō),CvArr*),當(dāng)它出現(xiàn)時(shí),便可以將CvMat*或IplImage*傳遞到程序。
CvMat矩陣結(jié)構(gòu)
在開始學(xué)習(xí)矩陣的相關(guān)內(nèi)容之前,我們需要知道兩件事情。第一,在OpenCV中沒(méi)有向量(vector)結(jié)構(gòu)。任何時(shí)候需要向量,都只需要一個(gè)列矩陣(如果需要一個(gè)轉(zhuǎn)置或者共軛向量,則需要一個(gè)行矩陣)。第二,OpenCV矩陣的概念與我們?cè)诰€性代數(shù)課上學(xué)習(xí)的概念相比,更抽象,尤其是矩陣的元素,并非只能取簡(jiǎn)單的數(shù)值類型。例如,一個(gè)用于新建一個(gè)二維矩陣的例程具有以下原型:
cvMat* cvCreateMat ( int rows, intcols, int type );
這里type可以是任何預(yù)定義類型,預(yù)定義類型的結(jié)構(gòu)如下:CV_<bit_depth> (S|U|F)C<number_of_channels>。于是,矩陣的元素可以是32位浮點(diǎn)型數(shù)據(jù)(CV_32FC1),或者是無(wú)符號(hào)的8位三元組的整型數(shù)據(jù)(CV_8UC3),或者是無(wú)數(shù)的其他類型的元素。一個(gè)CvMat的元素不一定就是個(gè)單一的數(shù)字。在矩陣中可以通過(guò)單一(簡(jiǎn)單)的輸入來(lái)表示多值,這樣我們可以在一個(gè)三原色圖像上描繪多重色彩通道。對(duì)于一個(gè)包含RGB通道的簡(jiǎn)單圖像,大多數(shù)的圖像操作將分別應(yīng)用于每一個(gè)通道(除非另有說(shuō)明)。
實(shí)質(zhì)上,正如例3-1所示,CvMat的結(jié)構(gòu)相當(dāng)簡(jiǎn)單,(可以自己打開文件…/opencv/cxcore/include/cxtypes.h查看)。矩陣由寬度(width)、高度(height)、類型(type)、行數(shù)據(jù)長(zhǎng)度(step,行的長(zhǎng)度用字節(jié)表示而不是整型或者浮點(diǎn)型長(zhǎng)度)和一個(gè)指向數(shù)據(jù)的指針構(gòu)成(現(xiàn)在我們還不能討論更多的東西)??梢酝ㄟ^(guò)一個(gè)指向CvMat的指針訪問(wèn)這些成員,或者對(duì)于一些普通元素,使用現(xiàn)成的訪問(wèn)方法。例如,為了獲得矩陣的大小,可通過(guò)調(diào)用函數(shù)vGetSize(CvMat*),返回一個(gè)CvSize結(jié)構(gòu),便可以獲取任何所需信息,或者通過(guò)獨(dú)立訪問(wèn)高度和寬度,結(jié)構(gòu)為matrix->height 和matrix->width。??? ???????????????????????????????????????????????????????????????? 【33~34】
例3-1:CvMat結(jié)構(gòu):矩陣頭
typedefstruct CvMat {
???int type;
???int step;
???int* refcount;??? // for internal use only
???union {
???????uchar*? ptr;
???????short*? s;
???????int*??? i;
???????float*? fl;
???????double* db;
???} data;
???union {
???????int rows;
???????int height;
???};
???union {
???????int cols;
???????int width;
};
}CvMat;
此類信息通常被稱作矩陣頭。很多程序是區(qū)分矩陣頭和數(shù)據(jù)體的,后者是各個(gè)data成員所指向的內(nèi)存位置。
矩陣有多種創(chuàng)建方法。最常見(jiàn)的方法是用cvCreateMat(),它由多個(gè)原函數(shù)組成,如cvCreateMatHeader()和cvCreateData()。cvCreateMatHeader()函數(shù)創(chuàng)建CvMat結(jié)構(gòu),不為數(shù)據(jù)分配內(nèi)存,而cvCreateData()函數(shù)只負(fù)責(zé)數(shù)據(jù)的內(nèi)存分配。有時(shí),只需要函數(shù)cvCreateMatHeader(),因?yàn)橐岩蚱渌碛煞峙淞舜鎯?chǔ)空間,或因?yàn)檫€不準(zhǔn)備分配存儲(chǔ)空間。第三種方法是用函數(shù)cvCloneMat (CvMat*),它依據(jù)一個(gè)現(xiàn)有矩陣創(chuàng)建一個(gè)新的矩陣。當(dāng)這個(gè)矩陣不再需要時(shí),可以調(diào)用函數(shù)cvReleaseMat(CvMat*)釋放它。 ???????????????????????????????????????? ????????【34】
例3-2概述了這些函數(shù)及其密切相關(guān)的其他函數(shù)。
例3-2:矩陣的創(chuàng)建和釋放
//Createa new rows by cols matrix of type 'type'.
//
CvMat*cvCreateMat( int rows, int cols, int type );
//Createonly matrix header without allocating data
//
CvMat*cvCreateMatHeader( int rows, int cols, int type );
//Initializeheader on existiong CvMat structure
//
CvMat*cvInitMatHeader(
?CvMat* mat,
?int?? rows,
?int?? cols,
?int?? type,
?void* data = NULL,
?int?? step = CV_AUTOSTEP
);
//LikecvInitMatHeader() but allocates CvMat as well.
//
CvMatcvMat(
?int?? rows,
?int?? cols,
?int?? type,
?void* data = NULL
);
//Allocatea new matrix just like the matrix 'mat'.
//
CvMat*cvCloneMat( const cvMat* mat );
//Free the matrix 'mat', both header and data.
//
voidcvReleaseMat( CvMat** mat );
與很多OpenCV結(jié)構(gòu)類似,有一種構(gòu)造函數(shù)叫cvMat,它可以創(chuàng)建CvMat結(jié)構(gòu),但實(shí)際上不分配存儲(chǔ)空間,僅創(chuàng)建頭結(jié)構(gòu)(與cvInitMatHeader()類似)。這些方法對(duì)于存取到處散放的數(shù)據(jù)很有作用,可以將矩陣頭指向這些數(shù)據(jù),實(shí)現(xiàn)對(duì)這些數(shù)據(jù)的打包,并用操作矩陣的函數(shù)去處理這些數(shù)據(jù),如例3-3所示。
例3-3:用固定數(shù)據(jù)創(chuàng)建一個(gè)OpenCV矩陣
//Createan OpenCV Matrix containing some fixed data.
//
floatvals[] = { 0.866025, -0.500000, 0.500000, 0.866025 };
CvMatrotmat;
cvInitMatHeader(
?&rotmat,
?2,
?2,
?CV_32FC1,
?vals
);
一旦我們創(chuàng)建了一個(gè)矩陣,便可用它來(lái)完成很多事情。最簡(jiǎn)單的操作就是查詢數(shù)組定義和數(shù)據(jù)訪問(wèn)等。為查詢矩陣,我們可以使用函數(shù)cvGetElemType(const CvArr* arr),cvGetDims(const CvArr* arr, int* sizes=NULL)和cvGet- DimSize(const CvArr* arr,int index)。第一個(gè)返回一個(gè)整型常數(shù),表示存儲(chǔ)在數(shù)組里的元素類型(它可以為CV_8UC1和CV_64FC4等類型)。第二個(gè)取出數(shù)組以及一個(gè)可選擇的整型指針,它返回維數(shù)(我們當(dāng)前的實(shí)例是二維,但是在后面我們將遇到的N維矩陣對(duì)象)。如果整型指針不為空,它將存儲(chǔ)對(duì)應(yīng)數(shù)組的高度和寬度(或者N維數(shù))。最后的函數(shù)通過(guò)一個(gè)指示維數(shù)的整型數(shù)簡(jiǎn)單地返回矩陣在那個(gè)維數(shù)上矩陣的大小。??? ???????????????????????????????????????????????????????????????????????????????? 【35~36】
矩陣數(shù)據(jù)的存取
訪問(wèn)矩陣中的數(shù)據(jù)有3種方法:簡(jiǎn)單的方法、麻煩的方法和恰當(dāng)?shù)姆椒ā?/p>
簡(jiǎn)單的方法
從矩陣中得到一個(gè)元素的最簡(jiǎn)單的方法是利用宏CV_MAT_ELEM()。這個(gè)宏(參見(jiàn)??????????????????????????????????例3-4)傳入矩陣、待提取的元素的類型、行和列數(shù)4個(gè)參數(shù),返回提取出的元素????????????????????????????的值。
例3-4:利用CV_MAT_ELEM()宏存取矩陣
CvMat*mat = cvCreateMat( 5, 5, CV_32FC1 );
floatelement_3_2 = CV_MAT_ELEM( *mat, float, 3, 2 );
更進(jìn)一步,還有一個(gè)與此宏類似的宏,叫CV_MAT_ELEM_PTR()。CV_MAT_ELEM_ PTR()(參見(jiàn)例3-5)傳入矩陣、待返回元素的行和列號(hào)這3個(gè)參數(shù),返回指向這個(gè)元素的指針。該宏和CV_MAT_ELEM()宏的最重要的區(qū)別是后者在指針解引用之前將其轉(zhuǎn)化成指定的類型。如果需要同時(shí)讀取數(shù)據(jù)和設(shè)置數(shù)據(jù),可以直接調(diào)用CV_MAT_ELEM_PTR()。但在這種情況下,必須自己將指針轉(zhuǎn)化成恰當(dāng)?shù)??????????????????????類型。
例3-5:利用宏CV_MAT_ELEM_PTR()為矩陣設(shè)置一個(gè)數(shù)值
CvMat*mat = cvCreateMat( 5, 5, CV_32FC1 );
floatelement_3_2 = 7.7;
*((float*)CV_MAT_ELEM_PTR( *mat, 3, 2 ) ) = element_3_2;
【36】
遺撼的是,這些宏在每次調(diào)用的時(shí)候都重新計(jì)算指針。這意味著要查找指向矩陣基本元素?cái)?shù)據(jù)區(qū)的指針、計(jì)算目標(biāo)數(shù)據(jù)在矩陣中的相對(duì)地址,然后將相對(duì)位置與基本位置相加。所以,即使這些宏容易使用,但也不是存取矩陣的最佳方法。在計(jì)劃順序訪問(wèn)矩陣中的所有元素時(shí),這種方法的缺點(diǎn)尤為突出。下面我們將講述怎么運(yùn)用最好的方法完成這個(gè)重要任務(wù)。
麻煩的方法
在“簡(jiǎn)單的方法”中討論的兩個(gè)宏僅僅適用于訪問(wèn)1維或2維的數(shù)組(回憶一下,1維的數(shù)組,或者稱為“向量”實(shí)際只是一個(gè)n×1維矩陣)。OpenCV提供了處理多維數(shù)組的機(jī)制。事實(shí)上,OpenCV可以支持普通的N維的數(shù)組,這個(gè)N值可以取值為任意大的數(shù)。
為了訪問(wèn)普通矩陣中的數(shù)據(jù),我們可以利用在例3-6和例3-7中列舉的cvPtr*D和cvGet*D…等函數(shù)族。cvPtr*D家族包括cvPtr1D(), cvPtr2D(), cvPtr3D()和cvPtrND()…。這三個(gè)函數(shù)都可接收CvArr*類型的矩陣指針參數(shù),緊隨其后的參數(shù)是表示索引的整數(shù)值,最后是一個(gè)可選的參數(shù),它表示輸出值的類型。函數(shù)返回一個(gè)指向所需元素的指針。對(duì)于cvPtrND()來(lái)說(shuō),第二個(gè)參數(shù)是一個(gè)指向一個(gè)整型數(shù)組的指針,這個(gè)數(shù)組中包含索引的合適數(shù)字。后文會(huì)再次介紹此函數(shù)(在這之后的原型中,也會(huì)看到一些可選參數(shù),必要時(shí)會(huì)有講解)。
例3-6:指針訪問(wèn)矩陣結(jié)構(gòu)
uchar*cvPtr1D(
?const CvArr* ? arr,
?int????????? idx0,
?int*???????? ??? type =NULL
);
uchar*cvPtr2D(
?const CvArr* ? arr,
?int????????? idx0,
?int????????? idx1,
?int*???????? ??? type =NULL
);
uchar*cvPtr3D(
?const CvArr* ? arr,
?int????????? idx0,
?int????????? idx1,
?int????????? idx2,
?int*???????? ??? type =NULL
);
uchar*cvPtrND(
?const CvArr* ? arr,
?int*???????? ??? idx,
?int*???????? ???type??????????? = NULL,
?int?????????create_node???? = 1,
?unsigned*??? ?? precalc_hashval = NULL
);?????????????????????????????????????????????????????????????????【37~38】
如果僅僅是讀取數(shù)據(jù),可用另一個(gè)函數(shù)族cvGet*D。如例3-7所示,該例與例3-6類似,但是返回矩陣元素的實(shí)際值。
例3-7:CvMat和IPlImage元素函數(shù)
doublecvGetReal1D( const CvArr* arr, int idx0 );
doublecvGetReal2D( const CvArr* arr, int idx0, int idx1 );
doublecvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 );
doublecvGetRealND( const CvArr* arr, int* idx );
CvScalarcvGet1D( const CvArr* arr, int idx0 );
CvScalarcvGet2D( const CvArr* arr, int idx0, int idx1 );
CvScalarcvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 );
CvScalarcvGetND( const CvArr* arr, int* idx );
cvGet*D中有四個(gè)函數(shù)返回的是整型的,另外四個(gè)的返回值是CvScalar類型的。這意味著在使用這些函數(shù)的時(shí)候,會(huì)有很大的空間浪費(fèi)。所以,只是在你認(rèn)為用這些函數(shù)比較方便和高效率的時(shí)候才用它們,否則,最好用cvPtr*D。
用cvPtr*D()函數(shù)族還有另外一個(gè)原因,即可以用這些指針函數(shù)訪問(wèn)矩陣中的特定的點(diǎn),然后由這個(gè)點(diǎn)出發(fā),用指針的算術(shù)運(yùn)算得到指向矩陣中的其他數(shù)據(jù)的指針。在多通道的矩陣中,務(wù)必記住一點(diǎn):通道是連續(xù)的,例如,在一個(gè)3通道2維的表示紅、綠、藍(lán)(RGB)矩陣中。矩陣數(shù)據(jù)如下存儲(chǔ)rgbrgbrgb . . .。所以,要將指向該數(shù)據(jù)類型的指針移動(dòng)到下一通道,我們只需要將其增加1。如果想訪問(wèn)下一個(gè)“像素”或者元素集,我們只需要增加一定的偏移量,使其與通道數(shù)相等。
另一個(gè)需要知道的技巧是矩陣數(shù)組的step元素(參見(jiàn)例3-1和例3-3),step是矩陣中行的長(zhǎng)度,單位為字節(jié)。在那些結(jié)構(gòu)中,僅靠cols或width是無(wú)法在矩陣的不同行之間移動(dòng)指針的,出于效率的考慮,矩陣或圖像的內(nèi)存分配都是4字節(jié)的整數(shù)倍。所以,三個(gè)字節(jié)寬度的矩陣將被分配4個(gè)字節(jié),最后一個(gè)字節(jié)被忽略。因此,如果我們得到一個(gè)字節(jié)指針,該指針指向數(shù)據(jù)元素,那么我們可以用step和這個(gè)指針相加以使指針指向正好在我們的點(diǎn)的下一行元素。如果我們有一個(gè)整型或者浮點(diǎn)型的矩陣,對(duì)應(yīng)的有整型和浮點(diǎn)型的指針指向數(shù)據(jù)區(qū)域,我們將讓step/4與指針相加來(lái)移到下一行,對(duì)雙精度型的,我們讓step/8與指針相加(這里僅僅考慮了C將自動(dòng)地將差值與我們添加的數(shù)據(jù)類型的字節(jié)數(shù)?????????????????相乘)。???????????????????????????????????? ???????????????????????????????????????? ???????????????????????????????????????? 【38】
例3-8中的cvSet*D和cvGet*D多少有些相似,它通過(guò)一次函數(shù)調(diào)用為一個(gè)矩陣或圖像中的元素設(shè)置值,函數(shù)cvSetReal*D()和函數(shù)cvSet*D()可以用來(lái)設(shè)置矩陣或者圖像中元素的數(shù)值。
例3-8:為CvMat或者IplImage元素設(shè)定值的函數(shù)
voidcvSetReal1D( CvArr* arr, int idx0, double value );
voidcvSetReal2D( CvArr* arr, int idx0, int idx1, double value );
voidcvSetReal3D(
?CvArr* arr,
?int idx0,
?int idx1,
?int idx2,
?double value
);
voidcvSetRealND( CvArr* arr, int* idx, double value );
voidcvSet1D( CvArr* arr, int idx0, CvScalar value );
voidcvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value );
voidcvSet3D(
?CvArr* arr,
?int idx0,
?int idx1,
?int idx2,
?CvScalar value
);
voidcvSetND( CvArr* arr, int* idx, CvScalar value );
為了方便,我們也可以使用cvmSet()和cvmGet(),這兩個(gè)函數(shù)用于處理浮點(diǎn)型單通道矩陣,非常簡(jiǎn)單。
double cvmGet( const CvMat* mat, introw, int col )
void cvmSet( CvMat* mat, int row, intcol, double value )
以下函數(shù)調(diào)用cvmSet():
cvmSet( mat, 2, 2, 0.5000 );
等同于cvSetReal2D函數(shù)調(diào)用:
cvSetReal2D( mat, 2, 2, 0.5000 );
恰當(dāng)?shù)姆椒?/h5>
從以上所有那些訪問(wèn)函數(shù)來(lái)看,你可能會(huì)想,沒(méi)有必要再介紹了。實(shí)際上,這些set和get函數(shù)很少派上用場(chǎng)。大多數(shù)時(shí)侯,計(jì)算機(jī)視覺(jué)是一種運(yùn)算密集型的任務(wù),因而你想盡量利用最有效的方法做事。毋庸置疑,通過(guò)這些函數(shù)接口是不可能做到十分高效的。相反地,應(yīng)該定義自己的指針計(jì)算并且在矩陣中利用自己的方法。如果打算對(duì)數(shù)組中的每一個(gè)元素執(zhí)行一些操作,使用自己的指針是尤為重要的(假設(shè)沒(méi)有可以為你執(zhí)行任務(wù)的OpenCV函數(shù))。
要想直接訪問(wèn)矩陣,其實(shí)只需要知道一點(diǎn),即數(shù)據(jù)是按光柵掃描順序存儲(chǔ)的,列(“x”)是變化最快的變量。通道是互相交錯(cuò)的,這意味著,對(duì)于一個(gè)多通道矩陣來(lái)說(shuō),它們變化的速度仍然比較快。例3-9顯示了這一過(guò)程。???????????? 【39~40】
例3-9:累加一個(gè)三通道矩陣中的所有元素
floatsum( const CvMat* mat ) {
?float s = 0.0f;
?for(int row=0; row<mat->rows; row++ ) {
???const float* ptr=(const float*)(mat->data.ptr + row * mat->step);
???for( col=0; col<mat->cols; col++ ) {
?????s += *ptr++;
???}
?}
?return( s );
}
計(jì)算指向矩陣的指針時(shí),記住一點(diǎn):矩陣的元素data是一個(gè)聯(lián)合體。所以,對(duì)這個(gè)指針解引用的時(shí)候,必須指明結(jié)構(gòu)體中的正確的元素以便得到正確的指針類型。然后,為了使指針產(chǎn)生正確的偏移,必須用矩陣的行數(shù)據(jù)長(zhǎng)度(step)元素。我們以前曾提過(guò),行數(shù)據(jù)元素的是用字節(jié)來(lái)計(jì)算的。為了安全,指針最好用字節(jié)計(jì)算,然后分配恰當(dāng)?shù)念愋?#xff0c;如浮點(diǎn)型。CvMat結(jié)構(gòu)中為了兼容IplImage結(jié)構(gòu),有寬度和高度的概念,這個(gè)概念已經(jīng)被最新的行和列取代。最后要注意,我們?yōu)槊啃卸贾匦掠?jì)算了ptr,而不是簡(jiǎn)單地從開頭開始,爾后每次讀的時(shí)候累加指針。這看起來(lái)好像很繁瑣,但是因?yàn)镃vMat數(shù)據(jù)指針可以指向一個(gè)大型數(shù)組中的ROI,所以無(wú)法保證數(shù)據(jù)會(huì)逐行連續(xù)存取。
點(diǎn)的數(shù)組
有一個(gè)經(jīng)常提到但又必須理解的問(wèn)題是,包含多維對(duì)象的多維數(shù)組(或矩陣)和包含一維對(duì)象的高維數(shù)組之間的不同。例如,假設(shè)有n個(gè)三維的點(diǎn),你想將這些點(diǎn)傳遞到參數(shù)類型為CvMat*的一些OpenCV函數(shù)中。對(duì)此,有四種顯而易見(jiàn)的方式,記住,這些方法不一定等價(jià)。一是用一個(gè)二維數(shù)組,數(shù)組的類型是CV32FC1,有n行,3列(n×3)。類似地,也可以用一個(gè)3行n列(3×n)的二維數(shù)組。也可以用一個(gè)n行1列(n×1)的數(shù)組或者1行n列(1×n)的數(shù)組,數(shù)組的類型是CV32FC3。這些例子中,有些可以自由轉(zhuǎn)換(這意味著只需傳遞一個(gè),另一個(gè)便可可以計(jì)算得到),有的則不能。要想理解原因,可以參考圖3-2中的內(nèi)存布局???????????????????????????情況。
從圖中可以看出,在前三種方式中,點(diǎn)集以同樣的方式被映射到內(nèi)存。但最后一種方式則不同。對(duì)N維數(shù)組的c維點(diǎn),情況變得更為復(fù)雜。需要記住的最關(guān)鍵的一點(diǎn)是,給定點(diǎn)的位置可以由以下公式計(jì)算出來(lái)。
圖3-2:有10個(gè)點(diǎn),每個(gè)點(diǎn)由3個(gè)浮點(diǎn)數(shù)表示,這10個(gè)點(diǎn)被放在4個(gè)結(jié)構(gòu)稍有不同的數(shù)組中。前3種情況下,內(nèi)存布局情況是相同的,但最后一種情況下,內(nèi)存布局不同
其中,Ncols?和Nchannels分別表示列數(shù)和通道數(shù)。總的來(lái)說(shuō),從這個(gè)公式中可以看出一點(diǎn),一個(gè)c維對(duì)象的N維數(shù)組和一個(gè)一維對(duì)象的(N+c)維數(shù)組不同。至于N=1(即把向量描繪成n×1或者1×n數(shù)組),有一個(gè)特殊之處(即圖3-2顯示的等值)值得注意,如果考慮到性能,可以在有些情況下用到它。
關(guān)于OpenCV的數(shù)據(jù)類型,如CvPoint2D和CvPoint2D32f,我們要說(shuō)明的最后一點(diǎn)是:這些數(shù)據(jù)類型被定義為C結(jié)構(gòu),因此有嚴(yán)格定義的內(nèi)存布局。具體說(shuō)來(lái),由整型或者浮點(diǎn)型組成的結(jié)構(gòu)是順序型的通道。那么,對(duì)于一維的C語(yǔ)言的對(duì)象數(shù)組來(lái)說(shuō),其數(shù)組元素都具有相同的內(nèi)存布局,形如CV32FC2的n×1或者1×n數(shù)組。這和申請(qǐng)CvPoint3D32f類型的數(shù)組結(jié)構(gòu)也是相同的。????????????????????? ?【41】
IplImage數(shù)據(jù)結(jié)構(gòu)
掌握了前面的知識(shí),再來(lái)討論IplImage數(shù)據(jù)結(jié)構(gòu)就比較容易了。從本質(zhì)上講,它是一個(gè)CvMat對(duì)象,但它還有其他一些成員變量將矩陣解釋為圖像。這個(gè)結(jié)構(gòu)最初被定義為Intel圖像處理庫(kù)(IPL)的一部分。IplImage結(jié)構(gòu)的準(zhǔn)確定義如例3-10所示。
例3-10:IplImage結(jié)構(gòu)
typedefstruct _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;
我們?cè)噲D討論這些變量的某些功能。有些變量不是很重要,但是有些變量非常重要,有助于我們理解OpenCV解釋和處理圖像的方式。
width和height這兩個(gè)變量很重要,其次是depth和nchannals。depth變量的值取自ipl.h中定義的一組數(shù)據(jù),但與在矩陣中看到的對(duì)應(yīng)變量不同。因?yàn)樵趫D像中,我們往往將深度和通道數(shù)分開處理,而在矩陣中,我們往往同時(shí)表示它們??捎玫纳疃戎等绫?-2所示。??????????????????????????????????????? ?????????????????????????????????????? ????????【42】
表3-2:OpenCV圖像類型
宏 | 圖像像素類型 |
IPL_DEPTH_8U | 無(wú)符號(hào)8位整數(shù) (8u) |
IPL_DEPTH_8S | 有符號(hào) 8位整數(shù)(8s) |
IPL_DEPTH_16S | 有符號(hào)16位整數(shù)(16s) |
IPL_DEPTH_32S | 有符號(hào)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ī)視覺(jué)領(lǐng)域,一個(gè)重要的錯(cuò)誤來(lái)源就是原點(diǎn)位置的定義不統(tǒng)一。具體而言,圖像的來(lái)源、操作系統(tǒng)、編解碼器和存儲(chǔ)格式等因素都可以影響圖像坐標(biāo)原點(diǎn)的選取。舉例來(lái)說(shuō),你或許認(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ò)排列方式),后者是把所有像素同通道值排在一起,形成通道平面,再把平面排列起來(lái)。
參數(shù)widthStep與前面討論過(guò)的CvMat中的step參數(shù)類似,包括相鄰行的同列點(diǎn)之間的字節(jié)數(shù)。僅憑變量width是不能計(jì)算這個(gè)值的,因?yàn)闉榱颂幚磉^(guò)程更高效每行都會(huì)用固定的字節(jié)數(shù)來(lái)對(duì)齊;因此在第i行末和第i+1行開始處可能會(huì)有些冗于字節(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ù)便會(huì)只對(duì)ROI所表示的子圖像進(jìn)行操作。如果IplImage變量中設(shè)置了ROI,則所有的OpenCV函數(shù)就會(huì)使用該ROI變量。如果COI被設(shè)置成非0值,則對(duì)該圖像的操作就只作用于被指定的通道上了。不幸的是,許多OpenCV函數(shù)都忽略參數(shù)COI。
訪問(wèn)圖像數(shù)據(jù)
通常,我們需要非常迅速和高效地訪問(wèn)圖像中的數(shù)據(jù)。這意味著我們不應(yīng)受制于存取函數(shù)(如cvSet*D之類)。實(shí)際上,我們想要用最直接的方式訪問(wèn)圖像內(nèi)的數(shù)據(jù)?,F(xiàn)在,應(yīng)用已掌握的IplImage內(nèi)部結(jié)構(gòu)的知識(shí),我們知道怎樣做才是最佳的???????????????方法。
雖然OpenCV中有很多優(yōu)化函數(shù)幫助我們完成大多數(shù)的圖像處理的任務(wù),但是還有一些任務(wù),庫(kù)中沒(méi)有預(yù)先包裝好的函數(shù)可以幫我們解決。例如,如果我們有一個(gè)三通道HSV圖像[Smith78],在色度保持不變的情況下,我們要設(shè)置每個(gè)點(diǎn)的飽和度和高度為255(8位圖像的最大值),我們可以使用指針遍歷圖像,類似于例3-9中的矩陣遍歷。然而,有一些細(xì)微的不同,是源于IplImage和CvMat結(jié)構(gòu)的差異。例3-11演示了最高效的方法。
例3-11:僅最大化HSV圖像“S”和“V”部分
voidsaturate_sv( IplImage* img ) {
?for( int y=0; y<img->height; y++ ) {
???uchar* ptr = (uchar*) (
?????img->imageData + y * img->widthStep
???);
???for( int x=0; x<img->width; x++ ) {
?????ptr[3*x+1] = 255;
?????ptr[3*x+2] = 255;
???}
?}
}
在以上程序中,我們用指針ptr指向第y行的起始位置。接著,我們從指針中析出飽和度和高度在x維的值。因?yàn)檫@是一個(gè)三通道圖像,所以C通道在x行的位置是3*x+c。
與CvMat的成員data相比,IplImage和CvMat之間的一個(gè)重要度別在于imageData。CvMat的data元素類型是聯(lián)合類型,所以你必須說(shuō)明需要使用的指針類型。imageData指針是字節(jié)類型指針(uchar * )。我們已經(jīng)知道是種類型的指針指向的數(shù)據(jù)是uchar類型的,這意味著,在圖像上進(jìn)行指針運(yùn)算時(shí),你可以簡(jiǎn)單地增加widthStep(也以字節(jié)為單位),而不必關(guān)心實(shí)際數(shù)據(jù)類型。在這里重新說(shuō)明一下:當(dāng)要處理的是矩陣時(shí),必須對(duì)偏移并進(jìn)行調(diào)整,因?yàn)閿?shù)據(jù)指針可能是非字節(jié)類型;當(dāng)要處理的是圖像時(shí),可以直接使用偏移,因?yàn)閿?shù)據(jù)指針總是字節(jié)類型,因此當(dāng)你要用到它的時(shí)候要清楚是怎么回事。
對(duì)ROI和widthStep的補(bǔ)充
ROI和widthStep在實(shí)際工作中有很重要的作用,在很多情況下,使用它們會(huì)提高計(jì)算機(jī)視覺(jué)代碼的執(zhí)行速度。這是因?yàn)樗鼈冊(cè)试S對(duì)圖像的某一小部分進(jìn)行操作,而不是對(duì)整個(gè)圖像進(jìn)行運(yùn)算。在OpenCV中,普遍支持ROI和widthStep,函數(shù)的操作被限于感興趣區(qū)域。要設(shè)置或取消ROI,就要使用cvSetImageROI()和cvResetImageROI()函數(shù)。如果想設(shè)置ROI,可以使用函數(shù)cvSetImageROI(),并為其傳遞一個(gè)圖像指針和矩形。而取消ROI,只需要為函數(shù)cvResetImageROI()傳遞一個(gè)圖像指針。
void cvSetImageROI( IplImage* image,CvRect rect );
void cvResetImageROI( IplImage* image);
為了解釋ROI的用法,我們假設(shè)要加載一幅圖像并修改一些區(qū)域,如例3-12的代碼,讀取了一幅圖像,并設(shè)置了想要的ROI的x,y,width和height的值,最后將ROI區(qū)域中像素都加上一個(gè)整數(shù)。本例程中通過(guò)內(nèi)聯(lián)的cvRect()構(gòu)造函數(shù)設(shè)置ROI。通過(guò)cvResetImageROI()函數(shù)釋放ROI是非常重要的,否則,將忠實(shí)地只顯示ROI區(qū)域。
例3-12:用imageROI來(lái)增加某范圍的像素
//roi_add <image> <x> <y> <width> <height><add>
#include<cv.h>
#include<highgui.h>
intmain(int argc, char** argv)
{
???IplImage* src;
???if( argc == 7 && ((src=cvLoadImage(argv[1],1)) != 0 ))
???{
???????int x = atoi(argv[2]);
???????int y = atoi(argv[3]);
???????int width = atoi(argv[4]);
???????int height = atoi(argv[5]);
???????int add = atoi(argv[6]);
???????cvSetImage ROI(src, cvRect(x,y,width,height));
???????cvAddS(src, cvScalar(add),src);
???????cvResetImageROI(src);
???????cvNamedWindow( "Roi_Add", 1 );
???????cvShowImage( "Roi_Add", src );
???????cvWaitKey();
???}
???return 0;
}
使用例3-12中的代碼把ROI集中于一張貓的臉部,并將其藍(lán)色通道增加150后的效果如圖3-3所示。?????????????????????????????????????????????????????????????????????????????????? 【45~46】
圖3-3:在貓臉上用ROI增加150像素的效果
通過(guò)巧妙地使用widthStep,我們可以達(dá)到同樣的效果。要做到這一點(diǎn),我們創(chuàng)建另一個(gè)圖像頭,讓它的width和height的值等于interest_rect的width和height的值。我們還需要按interest_rect起點(diǎn)設(shè)置圖像起點(diǎn)(左上角或者左下角)。下一步,我們?cè)O(shè)置子圖像的widthStep與較大的interest_img相同。這樣,即可在子圖像中逐行地步進(jìn)到大圖像里子區(qū)域中下一行開始處的合適位置。最后設(shè)置子圖像的imageDate指針指向興趣子區(qū)域的開始,如例3-13所示。
例3-13:利用其他widthStep方法把interest_img的所有像素值增加1
//Assuming IplImage *interest_img; and
?//?CvRect interest_rect;
?//?Use widthStep to get a region of interest
?//
?//(Alternate method)
?//
?IplImage*sub_img = cvCreateImageHeader(
??cvSize(
?????interest_rect.width,
?????interest_rect.height
??),
??interest_img->depth,
??interest_img->nChannels
?);
?sub_img->origin= interest_img->origin;
?sub_img->widthStep= interest_img->widthStep;
?sub_img->imageData= interest_img->imageData +
??interest_rect.y * interest_img->widthStep?? +
??interest_rect.x * interest_img->nChannels;
?cvAddS(sub_img, cvScalar(1), sub_img );
?cvReleaseImageHeader(&sub_img);
看起來(lái)設(shè)置和重置ROI更方便一些,為什么還要使用widthStep?原因在于有些時(shí)候在處理的過(guò)程中,想在操作過(guò)程中設(shè)置和保持一幅圖像的多個(gè)子區(qū)域處于活動(dòng)狀態(tài),但是ROI只能串行處理并且必須不斷地設(shè)置和重置。
最后,我們要在此提到一個(gè)詞——?掩碼或模板,在代碼示例中cvAddS()函數(shù)允許第四個(gè)參數(shù)默認(rèn)值為空:const CvArr*mask=NULL。這是一個(gè)8位單通道數(shù)組,它允許把操作限制到任意形狀的非0像素的掩碼區(qū),如果ROI隨著掩碼或模板變化,進(jìn)程將會(huì)被限制在ROI和掩碼的交集區(qū)域。掩碼或模板只能在指定了其圖像的函數(shù)中使用。
矩陣和圖像操作
表3-3列出了一些操作矩陣圖像的函數(shù),其中的大部分對(duì)于圖像處理非常有效。它們實(shí)現(xiàn)了圖像處理中的基本操作,例如對(duì)角化、矩陣變換以及一些更復(fù)雜的諸如計(jì)算圖像的統(tǒng)計(jì)操作。??????????????????????????????????????? ???????????????????????????????????????? ???????????????? 【47】
表3-3:矩陣和圖像基本操作
函數(shù)名稱 | 描述 |
cvAbs | 計(jì)算數(shù)組中所有元素的絕對(duì)值 |
cvAbsDiff | 計(jì)算兩個(gè)數(shù)組差值的絕對(duì)值 |
???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ? 續(xù)表
函數(shù)名稱 | 描述 |
cvAbsDiffS | 計(jì)算數(shù)組和標(biāo)量差值的絕對(duì)值 |
cvAdd | 兩個(gè)數(shù)組的元素級(jí)的加運(yùn)算 |
cvAddS | 一個(gè)數(shù)組和一個(gè)標(biāo)量的元素級(jí)的相加運(yùn)算 |
cvAddWeighted | 兩個(gè)數(shù)組的元素級(jí)的加權(quán)相加運(yùn)算(alpha融合) |
cvAvg | 計(jì)算數(shù)組中所有元素的平均值 |
cvAvgSdv | 計(jì)算數(shù)組中所有元素的絕對(duì)值和標(biāo)準(zhǔn)差 |
cvCalcCovarMatrix | 計(jì)算一組n維空間向量的協(xié)方差 |
cvCmp | 對(duì)兩個(gè)數(shù)組中的所有元素運(yùn)用設(shè)置的比較操作 |
cvCmpS | 對(duì)數(shù)組和標(biāo)量運(yùn)用設(shè)置的比較操作 |
cvConvertScale | 用可選的縮放值轉(zhuǎn)換數(shù)組元素類型 |
cvConvertScaleAbs | 計(jì)算可選的縮放值的絕對(duì)值之后再轉(zhuǎn)換數(shù)組元素的類型 |
cvCopy | 把數(shù)組中的值復(fù)制到另一個(gè)數(shù)組中 |
cvCountNonZero | 計(jì)算數(shù)組中非0值的個(gè)數(shù) |
cvCrossProduct | 計(jì)算兩個(gè)三維向量的向量積(叉積) |
cvCvtColor | 將數(shù)組的通道從一個(gè)顏色空間轉(zhuǎn)換另外一個(gè)顏色空間 |
cvDet | 計(jì)算方陣的行列式 |
cvDiv | 用另外一個(gè)數(shù)組對(duì)一個(gè)數(shù)組進(jìn)行元素級(jí)的除法運(yùn)算 |
cvDotProduct | 計(jì)算兩個(gè)向量的點(diǎn)積 |
cvEigenVV | 計(jì)算方陣的特征值和特征向量 |
cvFlip | 圍繞選定軸翻轉(zhuǎn) |
cvGEMM | 矩陣乘法 |
cvGetCol | 從一個(gè)數(shù)組的列中復(fù)制元素 |
cvGetCols | 從數(shù)據(jù)的相鄰的多列中復(fù)制元素值 |
cvGetDiag | 復(fù)制數(shù)組中對(duì)角線上的所有元素 |
cvGetDims | 返回?cái)?shù)組的維數(shù) |
cvGetDimSize | 返回一個(gè)數(shù)組的所有維的大小 |
cvGetRow | 從一個(gè)數(shù)組的行中復(fù)制元素值 |
cvGetRows | 從一個(gè)數(shù)組的多個(gè)相鄰的行中復(fù)制元素值 |
cvGetSize | 得到二維的數(shù)組的尺寸,以CvSize返回 |
cvGetSubRect | 從一個(gè)數(shù)組的子區(qū)域復(fù)制元素值 |
cvInRange | 檢查一個(gè)數(shù)組的元素是否在另外兩個(gè)數(shù)組中的值的范圍內(nèi) |
cvInRangeS | 檢查一個(gè)數(shù)組的元素的值是否在另外兩個(gè)標(biāo)量的范圍內(nèi) |
???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ? 續(xù)表
函數(shù) 名稱 | 描述 |
cvInvert | 求矩陣的轉(zhuǎn)置 |
cvMahalonobis | 計(jì)算兩個(gè)向量間的馬氏距離 |
cvMax | 在兩個(gè)數(shù)組中進(jìn)行元素級(jí)的取最大值操作 |
cvMaxS | 在一個(gè)數(shù)組和一個(gè)標(biāo)量中進(jìn)行元素級(jí)的取最大值操作 |
cvMerge | 把幾個(gè)單通道圖像合并為一個(gè)多通道圖像 |
cvMin | 在兩個(gè)數(shù)組中進(jìn)行元素級(jí)的取最小值操作 |
cvMinS | 在一個(gè)數(shù)組和一個(gè)標(biāo)量中進(jìn)行元素級(jí)的取最小值操作 |
cvMinMaxLoc | 尋找數(shù)組中的最大最小值 |
cvMul | 計(jì)算兩個(gè)數(shù)組的元素級(jí)的乘積 |
cvNot | 按位對(duì)數(shù)組中的每一個(gè)元素求反 |
cvNorm | 計(jì)算兩個(gè)數(shù)組的正態(tài)相關(guān)性 |
cvNormalize | 將數(shù)組中元素進(jìn)行規(guī)一化 |
cvOr | 對(duì)兩個(gè)數(shù)組進(jìn)行按位或操作 |
cvOrS | 在數(shù)組與標(biāo)量之間進(jìn)行按位或操作 |
cvReduce | 通過(guò)給定的操作符將二維數(shù)組約簡(jiǎn)為向量 |
cvRepeat | 以平鋪的方式進(jìn)行數(shù)組復(fù)制 |
cvSet | 用給定值初始化數(shù)組 |
cvSetZero | 將數(shù)組中所有元素初始化為0 |
cvSetIdentity | 將數(shù)組中對(duì)角線上的元素設(shè)為1,其他置0 |
cvSolve | 求出線性方程組的解 |
cvSplit | 將多通道所組分割成多個(gè)單通道數(shù)組 |
cvSub | 兩個(gè)數(shù)組元素級(jí)的相減 |
cvSubS | 元素級(jí)的從數(shù)組中減去標(biāo)量 |
cvSubRS | 元素級(jí)的從標(biāo)量中減去數(shù)組 |
cvSum | 對(duì)數(shù)組中的所有元素求和 |
cvSVD | 二維矩陣的奇異值分解 |
cvSVBkSb | 奇異值回代計(jì)算 |
cvTrace | 計(jì)算矩陣跡 |
cvTranspose | 矩陣的轉(zhuǎn)置運(yùn)算 |
cvXor | 對(duì)兩個(gè)數(shù)組進(jìn)行按位異或操作 |
cvXorS | 在數(shù)組和標(biāo)量之間進(jìn)行按位異或操作 |
cvZero | 將所有數(shù)組中的元素置為0 |
cvAbs,cvAbsDiff和cvAbsDiffS
void cvAbs(
??? const CvArr* ?src,
???const??????? ??? dst
);
void cvAbsDiff(
?? const CvArr* ??src1,
?? const CvArr* ??src2,
??const?????? ????? dst
);
void cvAbsDiffS(
??? const CvArr* ?src,
???CvScalar???? ??? value,
???const??????? ??? dst
);
【50】
這些函數(shù)計(jì)算一個(gè)數(shù)組的絕對(duì)值或數(shù)組和其他對(duì)象的差值的絕對(duì)值,cvAbs()函數(shù)計(jì)算src里的值的絕對(duì)值,然后把結(jié)果寫到dst;cvAbsDiff()函數(shù)會(huì)先從src1減去src2,然后將所得差的絕對(duì)值寫到dst;除了從所有src元素減掉的數(shù)是常標(biāo)量值外,可以看到cvAbsDiffS()函數(shù)同cvAbsDiff()函數(shù)基本相同。
cvAdd,cvAddS, cvAddWeighted和alpha融合
void cvAdd(
??? const CvArr*?? src1,
??? const CvArr*?? src2,
???CvArr*?????? ???? dst,
??? const CvArr*?? mask = NULL
);
void cvAddS(
??? const CvArr*?? src,
???CvScalar???? ???? value,
???CvArr*?????? ???? dst,
??? const CvArr*?? mask = NULL
);
void cvAddWeighted(
??? const CvArr*?? src1,
???double?????? ???? alpha,
??? const CvArr*?? src2,
???double?????? ???? beta,
???double?????? ???? gamma,
???CvArr*?????? ???? dst
);
cvAdd()是一個(gè)簡(jiǎn)單的加法函數(shù),它把src1里的所有元素同src2里的元素對(duì)應(yīng)進(jìn)行相加,然后把結(jié)果放到dst,如果mask沒(méi)有被設(shè)為NULL,那么由mask中非零元素指定的dst元素值在函數(shù)執(zhí)行后不變。cvAddS()與cvAdd()非常相似,惟一不同的是被加的數(shù)量標(biāo)量value。
cvAddWeighted()函數(shù)同cvAdd()類似,但是被寫入dst的結(jié)果是經(jīng)過(guò)下面的公式得出的:
????????????????【50】
這個(gè)函數(shù)可用來(lái)實(shí)現(xiàn)alpha?融合 [Smith79; Porter84];也就是說(shuō),它可以用于一個(gè)圖像同另一個(gè)圖像的融合,函數(shù)的形式如下:
void? cvAddWeighted(
??? const CvArr* src1,
???double?????? ?alpha,
??? const CvArr* src2,
???double?????? beta,
???double?????? gamma,
???CvArr*?????? dst
);
在函數(shù)cvAddWeighted()中我們有兩個(gè)源圖像,分別是src1和src2。這些圖像可以是任何類型的像素,只要它們屬于同一類型即可。它們還可以有一個(gè)或三個(gè)通道(灰度或彩色),同樣也要保持類型一致。結(jié)果圖像dst,也必須同src1和src2是相同的像素類型。這些圖像可能是不同尺寸,但是它們的ROI必須統(tǒng)一尺寸,否則OpenCV就會(huì)產(chǎn)生錯(cuò)誤,參數(shù)alpha是src1的融合強(qiáng)度,beta是src2的融合強(qiáng)度,alpha融合公式如下:
可以通過(guò)設(shè)置α從0到1區(qū)間取值,β?= 1 –?α,γ為0,將前面公式轉(zhuǎn)換為標(biāo)準(zhǔn)alpha融合方程。這就得出下式:
但是,在加權(quán)融合圖像,以及目標(biāo)圖像的附加偏移參數(shù)γ方面, cvAddWeighted()提供了更大的靈活性。一般而言,你或許想讓alpha和beta不小于0,并且兩者之和不大于1,gamma的設(shè)置取決于像素所要調(diào)整到的平均或最大值。例3-14展示了alpha融合的用法。
例3-14:src2中alpha融合ROI以(0,0)開始,src1中ROI以(x,y)開始
//alphablend <imageA> <image B> <x> <y> <width><height>
//?????????????<alpha><beta>
#include<cv.h>
#include<highgui.h>
intmain(int argc, char** argv)
{
???IplImage *src1, *src2;
???if( argc == 9 && ((src1=cvLoadImage(argv[1],1)) != 0
???????)&&((src2=cvLoadImage(argv[2],1)) != 0 ))
???{
???????int x = atoi(argv[3]);
???????int y = atoi(argv[4]);
???????int width = atoi(argv[5]);
???????int height = atoi(argv[6]);
???????double alpha = (double)atof(argv[7]);
???????double beta? = (double)atof(argv[8]);
???????cvSetImage ROI(src1, cvRect(x,y,width,height));
???????cvSetImageROI(src2, cvRect(0,0,width,height));
???????cvAddWeighted(src1, alpha, src2, beta,0.0,src1);
???????cvResetImageROI(src1);
???????cvNamedWindow( "Alpha_blend", 1 );
???????cvShowImage( "Alpha_blend", src1 );
???????cvWaitKey();
???}
???return 0;
}??????????????????????????????????????????????????????????????????【51~52】
例3-14中的代碼用兩個(gè)源圖像:初始的(src1)和待融合的(src2)。它從矩形的ROI中讀取src1,然后將同樣大小的ROI應(yīng)用到src2中,這一次設(shè)在原始位置,它從命令行讀入alpha和beta的級(jí)別但是把gamma設(shè)為0。Alpha融合使用函數(shù)cvAddWeighted(),結(jié)果被放到src1并顯示,例子輸出如圖3-4所示,一個(gè)小孩的臉同一個(gè)貓的臉和身體被融合到了一起,值得注意的是,代碼采用相同的ROI,像圖3-3的例子一樣。這次我們使用了ROI作為目標(biāo)融合區(qū)域。
圖3-4:一個(gè)小孩的臉被alpha融合到一只貓的臉上
cvAnd和cvAndS
void cvAnd(
??? const CvArr* ?src1,
??? const CvArr* ?src2,
???CvArr*?????? ??? dst,
??? const CvArr* ?mask = NULL
);
void cvAndS(
??? const CvArr* ?src1,
???CvScalar???? ?? value,
???CvArr*?????? ??? dst,
??? const CvArr* ?mask = NULL
);
這兩個(gè)函數(shù)在src1數(shù)組上做按位與運(yùn)算,在cvAnd()中每個(gè)dst元素都是由相應(yīng)的src1和src2兩個(gè)元素進(jìn)行位與運(yùn)算得出的。在cvAndS()中,位與運(yùn)算由常標(biāo)量value得出。同一般函數(shù)一樣,如果mask是非空,就只計(jì)算非0 mask元素所對(duì)應(yīng)的dst元素。
盡管支持所有的數(shù)據(jù)類型,但是對(duì)于cvAnd()來(lái)說(shuō),src1和src2要保持相同的數(shù)據(jù)類型。如果元素都是浮點(diǎn)型的,則使用該浮點(diǎn)數(shù)的按位表示。?????????????????????? 【52】
cvAvg
CvScalar cvAvg(
??? const CvArr* ?arr,
??? const CvArr* ?mask = NULL
);
cvAvg()計(jì)算數(shù)組arr的平均像素值,如果mask為非空,那么平均值僅由那些mask值為非0的元素相對(duì)應(yīng)的像素算出。
此函數(shù)還有別名cvMean(),但不推薦使用。
cvAvgSdv
cvAvgSdv(
??? const CvArr* ?arr,
???CvScalar*??? ?? mean,
???CvScalar*??? ?? std_dev,
??? const CvArr* ?mask???? = NULL
);?????????????????????????????????????????????????????????????????【53】
此函數(shù)同cvAvg()類似,但除了求平均,還可以計(jì)算像素的標(biāo)準(zhǔn)差。
函數(shù)現(xiàn)在有不再使用的別名cvMean_StdDev()。
cvCalcCovarMatrix
void cvCalcCovarMatrix(
??? const CvArr** vects,
???int?????????? count,
???CvArr*??????? cov_mat,
???CvArr*??????? avg,
???int?????????? flags
);
給定一些向量,假定這些向量表示的點(diǎn)是高斯分布,cvCalcCovarMatrix()將計(jì)算這些點(diǎn)的均值和協(xié)方差矩陣。這當(dāng)然可以運(yùn)用到很多方面,并且OpenCV有很多附加的flags值,在特定的環(huán)境下會(huì)起作用(參見(jiàn)表3-4)。這些標(biāo)志可以用標(biāo)準(zhǔn)的布爾或操作組合到一起。
表3-4:cvCalcCovarMatrix()可能用到的標(biāo)志參數(shù)的值
標(biāo)志參數(shù)的具體標(biāo)志值 | 意義 |
CV_COVAR_NORMAL | 計(jì)算均值和協(xié)方差 |
CV_COVAR_SCRAMBLED | 快速PCA“Scrambled”協(xié)方差 |
CV_COVAR_USE_AVERAGE | 輸入均值而不是計(jì)算均值 |
CV_COVAR_SCALE | 重新縮放輸出的協(xié)方差矩陣 |
在所有情況下,在vects中是OpenCV指針數(shù)組(即一個(gè)指向指針數(shù)組的指針),并有一個(gè)指示多少數(shù)組的參數(shù)count。在所有情況下,結(jié)果將被置于cov_mat,但是avg的確切含義取決于標(biāo)志的值(參見(jiàn)表3-4)。
標(biāo)識(shí)CV_COVAR_NORMAL和CV_COVAR_SCRAMBLED是相互排斥的;只能使用其中一種,不能兩者同時(shí)使用。如果為CV_COVAR_NORMAL,函數(shù)便只計(jì)算該點(diǎn)的均值和協(xié)方差。
因此,標(biāo)準(zhǔn)的協(xié)方差由長(zhǎng)度為n的m個(gè)向量計(jì)算,其中被定義為平均向量的第n個(gè)元素,由此產(chǎn)生的協(xié)方差矩陣是一個(gè)n?×n矩陣, 比例z是一個(gè)可選的縮放比例,除非使用CV_COVAR_SCALE標(biāo)志,否則它將被設(shè)置為1。???????? 【54】
如果是CV_COVAR_SCRAMBLED標(biāo)志,cvCalcCovarMatrix ()將如下計(jì)算:
這種矩陣不是通常的協(xié)方差矩陣(注意轉(zhuǎn)置運(yùn)算符的位置),這種矩陣的計(jì)算來(lái)自同樣長(zhǎng)度為n的m個(gè)向量,但由此而來(lái)的協(xié)方差矩陣是一個(gè)m×m矩陣。這種矩陣是用在一些特定的算法中,如針對(duì)非常大的向量的快速PCA分析法(人臉識(shí)別可能會(huì)用到此運(yùn)算)。
如果已知平均向量,則使用標(biāo)志CV_COVAR_USE_AVG,在這種情況下,參數(shù)avg用來(lái)作為輸入而不是輸出,從而減少計(jì)算時(shí)間。
最后,標(biāo)志CV_COVAR_SCALE用于對(duì)計(jì)算得到的協(xié)方差矩陣進(jìn)行均勻縮放。這是前述方程的比例z,同標(biāo)志CV_COVAR_NORMAL一起使用時(shí),應(yīng)用的縮放比例將是1.0 /m(或等效于1.0/count)。如果不使用CV_COVAR_SCRAMBLED,那么z的值將會(huì)是1.0/n(向量長(zhǎng)度的倒數(shù)),cvCalcCovarMatrix()的輸入輸出矩陣都應(yīng)該是浮點(diǎn)型,結(jié)果矩陣cov_mat的大小應(yīng)當(dāng)是n×n?或者?m×m,這取決于計(jì)算的是標(biāo)準(zhǔn)協(xié)方差還是scrambled的協(xié)方差。應(yīng)當(dāng)指出的是,在vects中輸入的“向量”并不一定要是一維的;它們也可以是兩維對(duì)象(例如圖像)。
cvCmp和cvCmpS
void cvCmp(
??? const CvArr*?? src1,
??? const CvArr*?? src2,
???CvArr*?????? ???? dst,
???int?????????????? cmp_op
);
void cvCmpS(
??? constCvArr*??? src,
???double?????? ???? value,
???CvArr*?????? ???? dst,
???int?????????????? cmp_op
);
這兩個(gè)函數(shù)都是進(jìn)行比較操作,比較兩幅圖像相應(yīng)的像素值或?qū)⒔o定圖像的像素值與某常標(biāo)量值進(jìn)行比較。cvCmp()和cvCmpS()的最后一個(gè)參數(shù)的比較操作符可以是表3-5所列出的任意一個(gè)。???????????????????????????????????? ???????????????????????????????????????? 【55】
表3-5:cvCmp()和cvCmpS()使用的cmp_op值以及由此產(chǎn)生的比較操作
cmp_op的值 | 比較方法 |
CV_CMP_EQ | (src1i == src2i) |
CV_CMP_GT | (src1i > src2i) |
CV_CMP_GE | (src1i >= src2i) |
CV_CMP_LT | (src1i < src2i) |
CV_CMP_LE | (src1i <= src2i) |
CV_CMP_NE | (src1i != src2i) |
表3-5列出的比較操作都是通過(guò)相同的函數(shù)實(shí)現(xiàn)的,只需傳遞合適的參數(shù)來(lái)說(shuō)明你想怎么做,這些特殊的功能操作只能應(yīng)用于單通道的圖像。
這些比較功能適用于這樣的應(yīng)用程序,當(dāng)你使用某些版本的背景減法并想對(duì)結(jié)果進(jìn)行掩碼處理但又只從圖像中提取變化區(qū)域信息時(shí)(如從安全監(jiān)控?cái)z像機(jī)看一段視????????????頻流)。
cvConvertScale
void cvConvertScale(
??? const CvArr* src,
???CvArr*?????? ? dst,
???double?????? ? scale = 1.0,
???double?????? ? shift = 0.0
);
cvConvertScale()函數(shù)實(shí)際上融多種功能于一體,它能執(zhí)行幾個(gè)功能中的任意之一,如果需要,也可以一起執(zhí)行多個(gè)功能。第一個(gè)功能是將源圖像的數(shù)據(jù)類型轉(zhuǎn)變成目標(biāo)圖像的數(shù)據(jù)類型。例如,如果我們有一個(gè)8位的RGB灰度圖像并想把它變?yōu)?6位有符號(hào)的圖像,就可以調(diào)用函數(shù)cvConvertScale()來(lái)做這個(gè)工作。
cvConvertScale()的第二個(gè)功能是對(duì)圖像數(shù)據(jù)執(zhí)行線性變換。在轉(zhuǎn)換成新的數(shù)據(jù)類型之后,每個(gè)像素值將乘以scale值,然后將shift值加到每個(gè)像素上。
至關(guān)重要的是要記住,盡管在函數(shù)名稱中“Convert”在“Scale”之前,但執(zhí)行這些操作的順序?qū)嶋H上是相反的。具體來(lái)說(shuō),在數(shù)據(jù)類型轉(zhuǎn)變之前,與scale相乘和shift的相加已經(jīng)發(fā)生了。??????????????????????????????????????????????????????????????????? 【56】
如果只是傳遞默認(rèn)值(scale = 1.0和shift = 0.0),則不必?fù)?dān)心性能; OpenCV足夠聰明,能意識(shí)到這種情況而不會(huì)在無(wú)用的操作上浪費(fèi)處理器的時(shí)間。澄清一下(如果你想添加一些),OpenCV還提供了宏指令cvConvert(),該指令同cvConvertScale()一樣,但是通常只適用于scale 和 shift 參數(shù)設(shè)為默認(rèn)????????????????值時(shí)。
對(duì)于所有數(shù)據(jù)類型和任何數(shù)量通道cvConvertScale ()都適用,但是源圖像和目標(biāo)圖像的通道數(shù)量必須相同。(如果你想實(shí)現(xiàn)彩色圖像與灰度圖的相互轉(zhuǎn)換,可以使用cvCvtColor(),之后我們將會(huì)提到。)???????????????????????????????????????? 【56~57】
cvConvertScaleAbs
void cvConvertScaleAbs(
??? const CvArr* src,
???CvArr*?????? ? dst,
???double???? ??? scale = 1.0,
???double?????? ? shift = 0.0
);
cvConvertScaleAbs()與cvConvertScale()基本相同,區(qū)別是dst圖像元素是結(jié)果數(shù)據(jù)的絕對(duì)值。具體說(shuō)來(lái),cvConvertScaleAbs()先縮放和平移,然后算出絕對(duì)值,最后進(jìn)行數(shù)據(jù)類型的轉(zhuǎn)換。
cvCopy
void cvCopy(
??? const CvArr* src,
???CvArr*?????? ? dst,
??? const CvArr* mask =NULL
);
用于將一個(gè)圖像復(fù)制到另一個(gè)圖像。cvCopy()函數(shù)要求兩個(gè)數(shù)組具有相同的數(shù)據(jù)類型、相同的大小和相同的維數(shù)。可以使用它來(lái)復(fù)制稀疏矩陣,但這樣做時(shí),不支持mask。對(duì)于非稀疏矩陣和圖像,mask如果為非空,則只對(duì)與mask中與非0值相對(duì)應(yīng)的dst中的像素賦值。
cvCountNonZero
int cvCountNonZero( const CvArr* arr );
cvCountNonZero()返回?cái)?shù)組arr中非0像素的個(gè)數(shù)。
cvCrossProduct
void cvCrossProduct(
const CvArr* src1,
const CvArr* src2,
CvArr* dst
);
這個(gè)函數(shù)的主要功能是計(jì)算兩個(gè)三維向量的叉積[Lagrange1773]。無(wú)論向量是行或者列的形式函數(shù)都支持。(實(shí)際上對(duì)于單通道矩陣,行向量和列向量的數(shù)據(jù)在內(nèi)存中的排列方式完全相同)。src1和src2都必須是單道數(shù)組,同時(shí)dst也必須是單道的,并且長(zhǎng)度應(yīng)精確為3。所有這些陣列的數(shù)據(jù)類型都要一致。??????????????? 【57】
cvCvtColor
void cvCvtColor(
??? const CvArr* src,
??? CvArr* dst,
??? int code
);
此前介紹的幾個(gè)函數(shù)用于把一個(gè)數(shù)據(jù)類型轉(zhuǎn)換成另一個(gè)數(shù)據(jù)類型,原始圖像和目標(biāo)圖像的通道數(shù)目應(yīng)保持一致。另外一個(gè)函數(shù)是cvCvtColor(),當(dāng)數(shù)據(jù)類型一致時(shí),它將圖像從一個(gè)顏色空間(通道的數(shù)值)轉(zhuǎn)換到另一個(gè)[Wharton71]。具體轉(zhuǎn)換操作由參數(shù)code來(lái)指定,表3-6列出了此參數(shù)可能的值。
表3-6:cvCvtColor()的轉(zhuǎn)換
轉(zhuǎn)換代碼? ???????? ???????? ?? 解釋 | |
CV_BGR2RGB CV_RGB2BGR CV_RGBA2BGRA CV_BGRA2RGBA | 在RGB或BGR色彩空間之間轉(zhuǎn)換(包括或者不包括alpha 通道) |
CV_RGB2RGBA CV_BGR2BGRA | 在 RGB或BGR圖像中加入alpha 通道 |
CV_RGBA2RGB CV_BGRA2BGR | 從 RGB或BGR圖像中刪除alpha 通道 |
CV_RGB2BGRA CV_RGBA2BGR CV_BGRA2RGB CV_BGR2RGBA | 加入或者移除alpha通道時(shí),轉(zhuǎn)換RGB到BGR 色彩空間 |
CV_RGB2GRAY CV_BGR2GRAY | 轉(zhuǎn)換RGB或者BGR色彩空間為灰度空間 |
CV_GRAY2RGB CV_GRAY2BGR CV_RGBA2GRAY CV_BGRA2GRAY | 轉(zhuǎn)換灰度為RGB或者BGR色彩空間(在進(jìn)程中選擇移除alpha通道) |
CV_GRAY2RGBA CV_GRAY2BGRA | 轉(zhuǎn)換灰度為RGB或者BGR色彩空間并且加入alpha通道 |
CV_RGB2BGR565 CV_BGR2BGR565 CV_BGR5652RGB CV_BGR5652BGR CV_RGBA2BGR565 CV_BGRA2BGR565 CV_BGR5652RGBA CV_BGR5652BGRA | 在從RGB或者BGR色彩空間轉(zhuǎn)換到BGR565彩色圖畫時(shí),選擇加入或者移除 alpha通道 (16位圖) |
CV_GRAY2BGR565 CV_BGR5652GRAY | 轉(zhuǎn)換灰度為BGR565彩色圖像或者反變換(16位圖) |
???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ? 續(xù)表
轉(zhuǎn)換代碼? ???????? ???????? ?? 解釋 | |
CV_RGB2BGR555 CV_BGR2BGR555 CV_BGR5552RGB CV_BGR5552BGR CV_RGBA2BGR555 CV_BGRA2BGR555 | 在從RGB或者BGR色彩空間轉(zhuǎn)換到BGR555色彩空間時(shí),選擇加入或者移除 alpha通道(16位圖) |
CV_BGR5552RGBA CV_BGR5552BGRA | ? |
CV_GRAY2BGR555 CV_BGR5552GRAY | 轉(zhuǎn)換灰度到BGR555色彩空間或者反變換(16位圖) |
CV_RGB2XYZ CV_BGR2XYZ CV_XYZ2RGB CV_XYZ2BGR | 轉(zhuǎn)換RGB或者BGR色彩空間到CIE XYZ色彩空間或者反變換(Rec 709和D65 白點(diǎn)) |
CV_RGB2YCrCb CV_BGR2YCrCb CV_YCrCb2RGB CV_YCrCb2BGR | 轉(zhuǎn)換RGB 或者BGR色彩空間到luma-chroma (aka YCC)色彩空間 |
CV_RGB2HSV CV_BGR2HSV CV_HSV2RGB CV_HSV2BGR | 轉(zhuǎn)換RGB或者BGR色彩空間到HSV(hue,saturation,value)色彩空間或反變換 |
CV_RGB2HLS CV_BGR2HLS CV_HLS2RGB CV_HLS2BGR | 轉(zhuǎn)換RGB或者BGR色彩空間到HLS(hue,Lightness,saturation)色彩空間或反變換 |
CV_RGB2Lab CV_BGR2Lab CV_Lab2RGB CV_Lab2BGR | 轉(zhuǎn)換RGB或者BGR色彩空間到CIE LAB色彩空間或反變換 |
???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ? 續(xù)表
轉(zhuǎn)換代碼? ?????????? 解釋 | |
CV_RGB2Luv CV_BGR2Luv CV_Luv2RGB CV_Luv2BGR | 轉(zhuǎn)換RGB或者BGR色彩空間到CIE Luv色彩空間 |
CV_BayerBG2RGB CV_BayerGB2RGB | 轉(zhuǎn)換Bayer模式(單通道)到RGB或者BGR色彩空間 |
CV_BayerRG2RGB CV_BayerGR2RGB CV_BayerBG2BGR CV_BayerGB2BGR CV_BayerRG2BGR CV_BayerGR2BGR | ? |
這里不再進(jìn)一步闡述CIE色彩空間中Bayer模式的細(xì)節(jié),但許多這樣的轉(zhuǎn)換是很有意義的。我們的目的是,了解OpenCV能夠在哪些色彩空間進(jìn)行轉(zhuǎn)換,這對(duì)用戶來(lái)說(shuō)很重要。
色彩空間轉(zhuǎn)換都用到以下約定:8位圖像范圍是0~255,16位圖像范圍是0~65536,浮點(diǎn)數(shù)的范圍是0.0~1.0。黑白圖像轉(zhuǎn)換為彩色圖像時(shí),最終圖像的所有通道都是相同的;但是逆變換(例如RGB或BGR到灰度),灰度值的計(jì)算使用加權(quán)公式:
Y=(0.299)R+(0.587)G+(0.114)B
就HSV色彩模式或者HLS色彩模式來(lái)說(shuō),色調(diào)通常是在0~360之間。在8位圖中,這可能出現(xiàn)問(wèn)題,因此,轉(zhuǎn)換到HSV色彩模式,并以8位圖的形式輸出時(shí),色調(diào)應(yīng)該除以2。
cvDet
double cvDet(const CvArr* mat);
cvDet()用于計(jì)算一個(gè)方陣的行列式。這個(gè)數(shù)組可以是任何數(shù)據(jù)類型,但它必須是單通道的,如果是小的矩陣,則直接用標(biāo)準(zhǔn)公式計(jì)算。然而對(duì)于大型矩陣,這樣就不是很有效,行列式的計(jì)算使用高斯消去法。
值得指出的是,如果已知一個(gè)矩陣是對(duì)稱正定的,也可以通過(guò)奇異值分解的策略來(lái)解決。欲了解更多信息,請(qǐng)參閱“cvSVD”一節(jié)。但這個(gè)策略是將U和V設(shè)置為NULL,然后矩陣W的乘積就是所求正定矩陣。
cvDiv
void cvDiv(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
double scale = 1
);
cvDiv是一個(gè)實(shí)現(xiàn)除法的簡(jiǎn)單函數(shù);它用src2除以src1中對(duì)應(yīng)元素,然后把最終的結(jié)果存到dst中。如果mask非空,那么dst中的任何與mask中0元素相對(duì)應(yīng)的元素都不改變。如果對(duì)數(shù)組中所有元素求倒數(shù),則可以設(shè)置src1為NULL,函數(shù)將假定src1是一個(gè)元素全為1的數(shù)組。
cvDotProduct
double cvDotProduct(
??? const CvArr* src1,
??? const CvArr* src2
);
【58~60】
這個(gè)函數(shù)主要計(jì)算兩個(gè)N維向量的點(diǎn)積[Lagrange1773]。與叉積函數(shù)相同,點(diǎn)積函數(shù)也不太關(guān)注向量是行或者是列的形式。src1和src2都應(yīng)該是單通道的數(shù)組,并且數(shù)組的數(shù)據(jù)類型應(yīng)該一致。
cvEigenVV
double cvEigenVV(
??? CvArr* mat,
??? CvArr* evects,
??? CvArr* evals,
??? double eps = 0
);
對(duì)對(duì)稱矩陣mat,cvEigenVV()會(huì)計(jì)算出該矩陣的特征值和相應(yīng)的特征向量。函數(shù)實(shí)現(xiàn)的是雅可比方法[Bronshtein97],對(duì)于小的矩陣是非常高效的,雅可比方法需要一個(gè)停止參數(shù),它是最終矩陣中偏離對(duì)角線元素最大尺寸??蛇x參數(shù)eps用于設(shè)置這個(gè)值。在計(jì)算的過(guò)程中,所提供的矩陣mat的數(shù)據(jù)空間被用于計(jì)算,所以,它的值會(huì)在調(diào)用函數(shù)后改變。函數(shù)返回時(shí),你會(huì)在evects中找到以行順序保存的特征向量。對(duì)應(yīng)的特征值被存儲(chǔ)到evals中。特征向量的次序以對(duì)應(yīng)特征值的重要性按降序排列。該cvEigenVV()函數(shù)要求所有三個(gè)矩陣具有浮點(diǎn)???????????????????????類型。
正如cvDet()(前述),如果被討論的向量是已知的對(duì)稱和正定矩陣,那么最好使用SVD計(jì)算mat的特征值和特征向量。
cvFlip
void cvFlip(
??? const CvArr* src,
??? CvArr* dst = NULL,
??? int flip_mode = 0
);
本函數(shù)是將圖像繞著在X軸或Y軸或者繞著X軸或Y軸上同時(shí)旋轉(zhuǎn)。當(dāng)參數(shù)flip_mode被設(shè)置為0的時(shí)候,圖像只會(huì)繞X軸旋轉(zhuǎn)。???????????????????????? ???????????????? 【61】
flip_mode被設(shè)置為正值時(shí)(例如,+1),圖像會(huì)圍繞Y軸旋轉(zhuǎn),如果被設(shè)置成負(fù)值(例如,-1),圖像會(huì)圍繞X軸和Y軸旋轉(zhuǎn)。
在Win32運(yùn)行視頻處理系統(tǒng)時(shí),你會(huì)發(fā)現(xiàn)自己經(jīng)常使用此功能來(lái)進(jìn)行圖像格式變換,也就是坐標(biāo)原點(diǎn)在左上角和左下角的變換。
cvGEMM
double cvGEMM(
??? const CvArr* src1,
??? const CvArr* src2,
??? double alpha,
??? const CvArr* src3,
??? double beta,
??? CvArr* dst,
??? int tABC = 0
);
廣義矩陣乘法(generalized matrixmultiplicatipm,GEMM)在OpenCV中是由cvGEMM()來(lái)實(shí)現(xiàn)的,可實(shí)現(xiàn)矩陣乘法、轉(zhuǎn)置后相乘、比例縮放等。最常見(jiàn)的情況下,cvGEMM()計(jì)算如下:
其中A,B和C分別是矩陣src1,src2和src3,α和β是數(shù)值系數(shù),op()是附在矩陣上的可選轉(zhuǎn)置。參數(shù)src3可以設(shè)置為空。在這種情況下,不會(huì)參與計(jì)算。轉(zhuǎn)置將由可選參數(shù)tABC來(lái)控制,它的值可以是0或者(通過(guò)布爾OR操作)CV_GEMM_A_T、CV_GEMM_B_T和CV_GEMM_C_T的任何組合(每一個(gè)標(biāo)志都有一個(gè)矩陣轉(zhuǎn)換相對(duì)應(yīng))。
過(guò)去的OpenCV包含cvMatMul()和cvMatMulAdd()方法,但是,它們很容易和cvMul()混淆,其實(shí)它們的功能是完全不一樣的(即兩個(gè)數(shù)組的元素與元素相乘)。這個(gè)函數(shù)以宏的形式繼續(xù)存在,它們直接調(diào)用cvGEMM()。兩者對(duì)應(yīng)關(guān)系如表3-7所示。
表3-7:cvGEMM()一般用法的宏別名
cvMatMul(A,B, D) | cvGEMM(A,B,1,NULL,0,D,0) |
cvMatMulAdd(A,B,C,D) | cvGEMM(A,B,1,C,1,D,0) |
只有大小符合約束的矩陣才能進(jìn)行乘法運(yùn)算,并且所有的數(shù)據(jù)類型都應(yīng)該是浮點(diǎn)型。cvGEMM()函數(shù)支持雙通道矩陣,在這種情況下,它將雙通道視為一個(gè)復(fù)數(shù)的兩個(gè)部分。
cvGetCol和cvGetCols
CvMat*cvGetCol(
???const CvArr* arr,
???CvMat* submat,
???int col
);
CvMat*cvGetCols(
???const CvArr* arr,
???CvMat* submat,
???int start_col,
???int end_col
);
cvGetCol()函數(shù)被用作提取矩陣中的某一列,并把它以向量的形式返回(即只有一列的矩陣)。在這種情況下,矩陣指針submat將被修改為指向arr中的特定列,必須指出的是,該指針在修改過(guò)程中并未涉及內(nèi)存的分配或數(shù)據(jù)的復(fù)制;submat的內(nèi)容僅僅是作了簡(jiǎn)單修改以使它正確地指出arr中所選擇的列。它支持所有數(shù)據(jù)類型。
cvGetCols()函數(shù)的工作原理與cvGetCols完全一致,區(qū)別只在于前者將選擇從start_col到end_col之間的所有列。這兩個(gè)函數(shù)都返回一個(gè)與被調(diào)用的特定列或者多列(即,submat)相對(duì)應(yīng)的頭指針。
cvGetDiag
CvMat* cvGetDiag(
??? const CvArr* arr,
??? CvMat*submat,
??? int diag= 0
);
cvGetDiag ()類似于cvGetCol();它能從一個(gè)矩陣選擇某一條對(duì)角線并將其作為向量返回。submat是一個(gè)矩陣類型的頭指針。函數(shù)cvGetDiag()將填充該向量頭指針中的各分量,以使用指向arr中的正確信息。注意,調(diào)用cvGetDiag()會(huì)修改輸入的頭指針,將數(shù)據(jù)指針指向arr對(duì)角線上的數(shù)據(jù),實(shí)際上,并沒(méi)有復(fù)制arr的數(shù)據(jù)??蛇x參數(shù)diag表明submat指向哪一條對(duì)角線的。如果diag被設(shè)置為默認(rèn)值0,主對(duì)角線將被選中。如果diag大于0,則始于(diag,0)的對(duì)角線將被選中,如果diag小于0,則始于(0,-diag)的對(duì)角線將被選中。cvGetDiag()并不要求矩陣arr是方陣,但是數(shù)組submat長(zhǎng)度必須與輸入數(shù)組的尺寸相匹配。當(dāng)該函數(shù)被調(diào)用時(shí),最終的返回結(jié)果與輸入的submat相同。
cvGetDims和cvGetDimSize
int cvGetDims(
??? const CvArr* arr,
??? int* sizes=NULL
);
int cvGetDimSize(
??? const CvArr* arr,
??? int index
);????????????????????????????????????????????????????????????????【63】
您一定還記得OpenCV中的矩陣維數(shù)可以遠(yuǎn)遠(yuǎn)大于2。函數(shù)cvGetDims()返回指定數(shù)組的維數(shù)并可返回每一個(gè)維數(shù)的大小。如果數(shù)組sizes非空,那么大小將被寫入sizes。如果使用了參數(shù)sizes,它應(yīng)該是一個(gè)指向n個(gè)整數(shù)的指針,這里的n指維數(shù)。如果無(wú)法事先獲知維數(shù),為了安全起見(jiàn),可以把sizes大小指定為CV_MAX_DIM。
函數(shù)cvGetDimSize()返回一個(gè)由index參數(shù)指定的某一維大小。如果這個(gè)數(shù)組是矩陣或者圖像,那么cvGetDims()將一直返回為2。對(duì)于矩陣和圖像,由cvGetDims()返回的sizes的次序?qū)⒖偸窍仁切袛?shù)然后是列數(shù)。
cvGetRow和cvGetRows
CvMat* cvGetRow(
??? const CvArr* arr,
??? CvMat* submat,
??? int row
);
CvMat* cvGetRows(
??? const CvArr* arr,
??? CvMat*submat,
??? int start_row,
??? int end_row
);
cvGetRow()獲取矩陣中的一行讓它作為向量(僅有一行的矩陣)返回。跟cvGetCol()類似,矩陣頭指針submat將被修改為指向arr中的某個(gè)特定行,并且對(duì)該頭指針的修改不涉及內(nèi)存的分配和數(shù)據(jù)的復(fù)制;submat的內(nèi)容僅是作為適當(dāng)?shù)男薷囊允顾_地指向arr中所選擇的行。該指針?biāo)袛?shù)據(jù)類型。
cvGetRows()函數(shù)的工作原理與cvGetRow()完全一致,區(qū)別只在于前者將選擇從start_row到end_row之間的所有行。這兩個(gè)函數(shù)都返回一個(gè)的頭指針,指向特定行或者多個(gè)行。
cvGetSize
CvSize cvGetSize( const CvArr* arr );
它與cvGetDims()密切相關(guān),cvGetDims()返回一個(gè)數(shù)組的大小。主要的不同是cvGetSize()是專為矩陣和圖像設(shè)計(jì)的,這兩種對(duì)象的維數(shù)總是2。其尺寸可以以CvSize結(jié)構(gòu)的形式返回,例如當(dāng)創(chuàng)建一個(gè)新的大小相同的矩陣或圖像時(shí),使用此函數(shù)就很方便。???????????????????????????????????????????????????????????????????????????????? ???????????????????????? 【64】
cvGetSubRect
cvGetSubRect
CvSize cvGetSubRect(
??? const CvArr* arr,
??? CvArr* submat,
??? CvRect rect
);
cvGetSubRect()與cvGetColumns()或cvGetRows()非常類似,區(qū)別在于cvGetSubRect()通過(guò)參數(shù)rect在數(shù)組中選擇一個(gè)任意的子矩陣。與其他選擇數(shù)組子區(qū)域的函數(shù)的函數(shù)一樣,submat僅僅是一個(gè)被cvGetSubRect()函數(shù)填充的頭,它將指向用戶期望的子矩陣數(shù)據(jù),這里并不涉及內(nèi)存分配和數(shù)據(jù)的復(fù)制。
cvInRange和cvInRangeS
void cvInRange(const CvArr* src,
??? const CvArr* lower,
??? const CvArr* upper,
???CvArr*?????? dst
);
void cvInRangeS(
??? const CvArr* src,
???CvScalar???? lower,
???CvScalar???? upper,
???CvArr*?????? dst
);
這兩個(gè)函數(shù)可用于檢查圖像中像素的灰度是否屬于某一指定范圍。cvInRange()檢查,src的每一個(gè)像素點(diǎn)是否落在lower和upper范圍中。如果src的值大于或者等于lower值,并且小于upper值,那么dst中對(duì)應(yīng)的對(duì)應(yīng)值將被設(shè)置為0xff;否則,dst的值將被設(shè)置為0。
cvInRangeS()的原理與之完全相同,但src是與lower和upper中的一組常量值(類型CvScala)進(jìn)行比較。對(duì)于這兩個(gè)函數(shù),圖像src可以是任意類型;如果圖像有多個(gè)通道,那么每一種通道都將被分別處理。注意,dst的尺寸和通道數(shù)必須與src一致,且必須為8位的圖像。
cvInvert
double cvInvert(
??? const CvArr* src,
??? CvArr* dst,
??? Int method = CV_LU
);
cvInvert()求取保存在src中的矩陣的逆并把結(jié)果保存在dst中。這個(gè)函數(shù)支持使用多種方法來(lái)計(jì)算矩陣的逆(見(jiàn)表3-8),但默認(rèn)采取的是高斯消去法。該函數(shù)的返回值與所選用的方法有關(guān)。??? ???????????????????????????????????????????????????????????????????????? 【65】
表3-8:cvInvert()函數(shù)中指定方法的參數(shù)值
方法的參數(shù)值 | 含義 |
CV_LU | 高斯消去法 (LU 分解) |
CV_SVD | 奇異值分解(SVD) |
CV_SVD_SYM | 對(duì)稱矩陣的SVD |
就高斯消去法(method=CV_LU)來(lái)說(shuō),當(dāng)函數(shù)執(zhí)行完畢,src的行列式將被返回。如果行列式是0,那么事實(shí)上不進(jìn)行求逆操作,并且數(shù)組dst將被設(shè)置為全0。
就CV_SVD或者CV_SVD_SYM,來(lái)說(shuō),返回值是矩陣的逆條件數(shù)(最小特征值跟最大特征值的比例)。如果src是奇異的,那么cvInvert()在SVD模式中將進(jìn)行偽逆計(jì)算。
cvMahalonobis
CvSize cvMahalonobis(
??? const CvArr* vec1,
??? const CvArr* vec2,
???CvArr*?????? mat
);
Mahalonobis距離(Mahal)被定義為一點(diǎn)和高斯分布中心之間的向量距離,該距離使用給定分布的協(xié)方差矩陣的逆作為歸一化標(biāo)準(zhǔn)。參見(jiàn)圖3-5。直觀上,這是與基礎(chǔ)統(tǒng)計(jì)學(xué)中的標(biāo)準(zhǔn)分?jǐn)?shù)(Z-score)類似,某一點(diǎn)到分布中心的距離是以該分布的方差作為單位。馬氏距離則是該思路在高維空間中的推廣。
cvMahalonobis()計(jì)算的公式如下:
假設(shè)向量vec1對(duì)應(yīng)x點(diǎn),向量vec2是分布的均值。mat是協(xié)方差矩陣的逆。
實(shí)際上,這個(gè)協(xié)方差矩陣通常用cvCalcCovarMatrix()(前面所述)來(lái)進(jìn)行計(jì)算,然后用cvInvert()來(lái)求逆。使用SV_SVD方法求逆是良好的程序設(shè)計(jì)習(xí)慣,因?yàn)槠渲幸粋€(gè)特征值為0的分布這種情況在所難免!
圖3-5:數(shù)據(jù)在2D空間分布,3個(gè)疊加在一起的橢圓分別對(duì)應(yīng)到分布中心的馬氏距離為1.0,2.0和3.0所有點(diǎn)
cvMax和cvMaxS
void cvMax(
??? const CvArr* src1,
??? const CvArr* src2,
???CvArr*?????? dst
);
void cvMaxS(
??? const CvArr* src,
???double?????? value,
???CvArr*?????? dst
);?????????????????????????????????????????????????????????????????【66】
cvMax()計(jì)算數(shù)組src1和src2中相對(duì)應(yīng)的每像素一對(duì)中的最大值。而cvMaxS(),將數(shù)組src與常量參數(shù)value進(jìn)行比較。通常,如果mask非空,那么只有與非0參數(shù)相對(duì)應(yīng)的dst中的元素參與計(jì)算。
cvMerge
void cvMerge(
??? const CvArr* src0,
??? const CvArr* src1,
??? const CvArr* src2,
??? const CvArr* src3,
??? CvArr* dst
);?????????????????????????????????????????????????????????????????【67】
cvMerge()是cvSplit()的逆運(yùn)算。數(shù)組src0,src1,src2,和src3將被合并到數(shù)組dst中。當(dāng)然,dst應(yīng)該與源數(shù)組具有相同的數(shù)據(jù)類型和尺寸,但它可以有兩個(gè),三個(gè)或四個(gè)道道。未使用的源圖像參數(shù)可設(shè)置為NULL。
cvMin和cvMinS
void cvMin(
??? const CvArr* src1,
??? const CvArr* src2,
??? CvArr* dst
);
void cvMinS(
??? const CvArr* src,
??? double value,
??? CvArr* dst
);
cvMin()計(jì)算數(shù)組src1和src2中相對(duì)應(yīng)的每一對(duì)像素中的最小值。而cvMaxS(),將數(shù)組src與常量標(biāo)量value進(jìn)行比較。同樣的,如果mask非空的話,那么只有與mask的非0參數(shù)相對(duì)應(yīng)的dst中的元素進(jìn)行計(jì)算。
cvMinMaxLoc
void cvMinMaxLoc(
??? const CvArr* arr,
???double*????? min_val,
???double*????? max_val,
???CvPoint*???? min_loc = NULL,
???CvPoint*???? max_loc = NULL,
??? const CvArr*mask??? = NULL
);
該例程找出數(shù)組arr中的最大值和最小值,并且(有選擇性地)返回它們的地址。計(jì)算出的最大值和最小值賦值給max_val和min_val?;蛘?#xff0c;如果極值的位置參數(shù)非空,那極值的位置便會(huì)寫入min_loc和max_loc。
通常,如果參數(shù)mask非空,那么只有圖像arr中與參數(shù)mask中的非零的像素相對(duì)應(yīng)的部分才被考慮。cvMinMaxLoc()例程僅僅處理單通道數(shù)組,如果有一個(gè)多通道的數(shù)組,則應(yīng)該使用cvSetCOI()來(lái)對(duì)某個(gè)特定通道進(jìn)行設(shè)置。
cvMul
void cvMul(
??? const CvArr* src1,
??? const CvArr* src2,
??? CvArr* dst,
??? double scale=1
);?????????????????????????????????????????????????????????????????【68】
cvMul()是一個(gè)簡(jiǎn)單的乘法函數(shù)。它將src1中的元素與src2中相對(duì)應(yīng)的元素相乘,然后把結(jié)果賦給dst。如果mask是空,那么與其中0元素相對(duì)應(yīng)的dst元素都不會(huì)因此操作而改變。OpenCV中沒(méi)有函數(shù)cvMulS(),因?yàn)樵摴δ芤呀?jīng)由函數(shù)cvScale()或cvCvtScale()提供。
除此之外,有一件事情要記住:cvMul()執(zhí)行的是元素之間的乘法。有時(shí)候,在進(jìn)行矩陣相乘時(shí),可能會(huì)錯(cuò)誤使用cvMul(),但這無(wú)法奏效;記住,cvGEMM()才是處理矩陣乘法的函數(shù),而不是cvMul()。
cvNot
void cvNot(
??? const CvArr* src,
???CvArr*?????? dst
);
函數(shù)cvNot()會(huì)將src中的每一個(gè)元素的每一位取反,然后把結(jié)果賦給dst。因此,一個(gè)值為0x00的8位圖像將被映射到0xff,而值為0x83的圖像將被映射到0x7c。
cvNorm
double cvNorm(
??? const CvArr* arr1,
??? const CvArr*arr2? = NULL,
??? int? norm_type= CV_L2,
??? const CvArr*mask? = NULL
??? );
這一函數(shù)可于計(jì)算一個(gè)數(shù)組的各種范數(shù),當(dāng)為該函數(shù)提供了兩個(gè)數(shù)組作為參數(shù)時(shí),可選用各種不同的公式來(lái)計(jì)算相對(duì)的距離。在前一種情況下,計(jì)算的范數(shù)如表3-9所示。
表3-9:當(dāng)arr2=NULL時(shí),對(duì)于不同的norm_type由cvNorm()計(jì)算范數(shù)的公式
norm_type | 結(jié)果 |
CV_C | |
CV_L1 | |
CV_L2 |
如果第二個(gè)數(shù)組參數(shù)arr2非空,那么范數(shù)的計(jì)算將使用不同的公式,就像兩個(gè)數(shù)組之間的距離。前三種情況的計(jì)算公式如表3-10所示,這些范數(shù)是絕對(duì)范數(shù);在后三個(gè)情況下,將會(huì)根據(jù)第二個(gè)數(shù)組arr2的幅度進(jìn)行重新調(diào)整。?【69~70】
表3-10:arr2非空,且norm_type不同值時(shí)函數(shù)cvNorm()計(jì)算范數(shù)的計(jì)算
公式
norm_type | 結(jié)果 |
CV_C | |
CV_L1 | |
CV_L2 | |
CV_RELATIVE_C | |
CV_ RELATIVE_L1 | |
CV_ RELATIVE_L2 |
在所有情況下,arr1和arr2必須具有相同的大小和通道數(shù)。當(dāng)通道數(shù)大于1時(shí),將會(huì)對(duì)所有通道一起計(jì)算范數(shù)(即是說(shuō),在表3-9和表3-10中,不僅是針對(duì)x和y,也針對(duì)通道數(shù)求和)。
cvNormalize
cvNormalize(
??? const CvArr* src,
???CvArr*?????? ? dst,
???double?????? ?a???????? = 1.0,
???double????? ?? ???b???????? = 0.0,
??? int?????????????? norm_type?? = CV_L2,
??? const CvArr*mask????? ??? = NULL
);
與許多OpenCV函數(shù)一樣,cvNormalize()的功能比表面看更多。根據(jù)norm_type的不同值,圖像src將被規(guī)范化,或者以其他方式映射到dst的一個(gè)特定的范圍內(nèi)。表3-11列舉了norm_type可能出現(xiàn)的值。
表3-11:函數(shù)cvNormalize()的參數(shù)norm_type可能的值
norm_type | 結(jié)果 |
CV_C | |
CV_L1 |
???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ? 續(xù)表
norm_type | 結(jié)果 |
CV_L2 | |
CV_MINMAX | 映射到[a, b]的范圍上 |
【70】
計(jì)算C范數(shù)時(shí),數(shù)組src將被進(jìn)行比例變標(biāo),使其中絕對(duì)值最大的值等于a。當(dāng)計(jì)算L1范數(shù)成L2范數(shù)時(shí),該數(shù)組也將被縮放,如使其范數(shù)為a。如果norm_type的值設(shè)置為CV_MINMAX,那么將會(huì)對(duì)數(shù)組的所有的值進(jìn)行轉(zhuǎn)化,使它們線性映射到a和b之間(包括a和b)。
與以前一樣,如果參數(shù)mask非空,那么只有與掩碼非0值對(duì)應(yīng)的像素會(huì)對(duì)范數(shù)的計(jì)算有貢獻(xiàn),并且只有那些像素會(huì)被cvNormalize()改變。
cvOr和cvOrS
void cvOr(
??? const CvArr* src1,
??? const CvArr* src2,
???CvArr*?????? ? dst,
??? const CvArr*mask=NULL
);
void cvOrS(
??? const CvArr* src,
???CvScalar???? ? value,
???CvArr*?????? ? dst,
??? const CvArr*mask? = NULL
);
這兩個(gè)函數(shù)將對(duì)數(shù)組src1進(jìn)行按位或計(jì)算。在函數(shù)cvOr()中,dst中的每一個(gè)元素都是是由src1和src2中相對(duì)應(yīng)的元素按位做或運(yùn)算的結(jié)果。在cvOrS()函數(shù)中,將對(duì)src和常量value進(jìn)行或運(yùn)算。像往常一樣,如果mask非空,則只計(jì)算dst中與mask中非0元素對(duì)應(yīng)的元素。
該函數(shù)支持所有的數(shù)據(jù)類型,但在cvOr()中,src1和src2必須有相同的數(shù)據(jù)類型。如果數(shù)組元素是浮點(diǎn)類型,則使用浮點(diǎn)按位表示形式。
cvReduce
CvSize cvReduce(
??? const CvArr* src,
???CvArr*?????? ? dst,
???int????????? ????? dim,
???int????????? ????? op = CV_REDUCE_SUM
);
約簡(jiǎn)是指使用一些op所代表的組合規(guī)則,對(duì)輸入的矩陣src的每一行(或列)進(jìn)行系統(tǒng)的轉(zhuǎn)化,使之成為成向量dst,直到只剩一行(或列)為止(見(jiàn)表3-12)。參數(shù)op決定如何進(jìn)行約簡(jiǎn),總結(jié)如表3-13所示。?????????????????????????????????????? ???????????????? 【71】
表3-12:參數(shù)op在cvReduce()中所代表的轉(zhuǎn)化操作
op的值 | 結(jié)果 |
CV_REDUCE_SUM | 計(jì)算所有向量的總和 |
CV_REDUCE_AVG | 計(jì)算所有向量的平均值 |
CV_REDUCE_MAX | 計(jì)算所有向量中的最大值 |
CV_REDUCE_MIN | 計(jì)算所有向量中的最小值 |
表3-13:參數(shù)dim在cvReduce()中控制轉(zhuǎn)化的方向
dim的值 | 結(jié)果 |
+1 | 合并成一行 |
0 | 合并成一列 |
-1 | 轉(zhuǎn)化成對(duì)應(yīng)的dst |
cvReduce()支持浮點(diǎn)型的多通道數(shù)組。它也允許在dst中使用比src更高精度的數(shù)據(jù)類型。這關(guān)鍵在于要有正確的CV_REDUCE_SUM和CV_REDUCE_AVG參數(shù),否則那里可能有溢出和累積問(wèn)題。
cvRepeat
void cvRepeat(
??? const CvArr* src,
???CvArr*?????? ? dst
);
這一函數(shù)是將src的內(nèi)容復(fù)制到dst中,重復(fù)多次,直到dst沒(méi)有多余的空間。具體而言,dst相對(duì)于src可以是任何大小。它可能比src大或小,它們?cè)诖笮『途S數(shù)之間不需要有任何的數(shù)值關(guān)系。
cvScale
void cvScale(
??? const CvArr* src,
???CvArr*?????? ? dst,
???double?????? ? scale
);
從宏觀上講,函數(shù)cvScale()實(shí)際上是cvConvertScale()的一個(gè)宏,它會(huì)將shift參數(shù)設(shè)置為0.0。因此,它可以用來(lái)重新調(diào)整數(shù)組的內(nèi)容,并且可以將參數(shù)從一種數(shù)據(jù)類型轉(zhuǎn)換為另一種。
cvSet和cvSetZero
void cvSet(
???CvArr*?????? ? arr,
???CvScalar???? ? value,
??? const CvArr*mask?? = NULL
);?????????????????????????????????????????????????????????????????【72】
這些函數(shù)能將數(shù)組的所有通道的所有值設(shè)置為指定的參數(shù)value。該cvSet()函數(shù)接受一個(gè)可選的參數(shù):如果提供參數(shù),那么只有那些與參數(shù)mask中非0值對(duì)應(yīng)的像素將被設(shè)置為指定的值。函數(shù)cvSetZero()僅僅是cvSet(0.0)別名。
cvSetIdentity
void cvSetIdentity( CvArr* arr );
cvSetIdentity()將會(huì)把數(shù)組中除了行數(shù)與列數(shù)相等以外的所有元素的值都設(shè)置為0;行數(shù)與列數(shù)相等的元素的值都設(shè)置為1。cvSetIdentity()支持所有數(shù)據(jù)類型,甚至不要求數(shù)組的行數(shù)與列數(shù)相等。
cvSolve
int cvSolve(
??? const CvArr* src1,
??? const CvArr* src2,
???CvArr*?????? ? dst,
???int????????? ????? method = CV_LU
);
基于cvInvert()函數(shù)cvSolve()為求解線性方程組提供了一條捷徑。它的計(jì)算公式如下:
C=argminx||A·X-B||
其中A是一個(gè)由src1指定的方陣,B是向量src2,然后C是由cvSolve()計(jì)算的結(jié)果,目標(biāo)是尋找一個(gè)最優(yōu)的向量X。最優(yōu)結(jié)果向量X將返回給dst。cvInvert()支持同樣的方法(前述);不過(guò)只支持浮點(diǎn)類型的數(shù)據(jù)。該函數(shù)將會(huì)返回一個(gè)整型值,當(dāng)返回的值是一個(gè)非0值的話,這表明它能夠找到一個(gè)解。
應(yīng)當(dāng)指出的是,cvSolve()可以用來(lái)解決超定的線性方程組。超定系統(tǒng)將使用所謂的偽逆方法進(jìn)行解決,它是使用SVD方法找到方程組的最小二乘解的。
cvSplit
void cvSplit(
??? const CvArr* src,
???CvArr*?????? ? dst0,
???CvArr*?????? ? dst1,
???CvArr*?????? ? dst2,
???CvArr*?????? ? dst3
);
有些時(shí)候處理多通道圖像時(shí)不是很方便。在這種情況下,可以利用cvSplit()分別復(fù)制每個(gè)通道到多個(gè)單通道圖像。如果需要,cvSplit()函數(shù)將復(fù)制src的各個(gè)通道到圖像dst0,dst1,dst2和dst3中。目標(biāo)圖像必須與源圖像在大小和數(shù)據(jù)類型上相匹配,當(dāng)然也應(yīng)該是單通道的圖像。
如果源圖像少于4個(gè)通道(這種情況經(jīng)常出現(xiàn)),那么傳遞給cvSplit()的不必要的目標(biāo)參數(shù)可設(shè)置為NULL。???????????????????????????????????????? ???????????????????????????????????????? 【73】
cvSub,cvSubS和cvSubRS
void cvSub(
??? const CvArr* src1,
??? const CvArr* src2,
???CvArr*?????? ? dst,
??? const CvArr*mask? = NULL
);
void cvSubS(
??? const CvArr* src,
???CvScalar???? ? value,
???CvArr*?????? ? dst,
??? const CvArr*mask? = NULL
);
void cvSubRS(
??? const CvArr* src,
???CvScalar???? ? value,
???CvArr*??? ???? ??? dst,
??? const CvArr*mask? = NULL
);
cvSub()是一個(gè)簡(jiǎn)單的減法函數(shù),它對(duì)數(shù)組src2和src1對(duì)應(yīng)的元素進(jìn)行減法運(yùn)算,然后把結(jié)果賦給dst。如果數(shù)組mask非空,那么dst中元素對(duì)應(yīng)位置的mask中的0元素不會(huì)因此而改變。相關(guān)的函數(shù)cvSubS()執(zhí)行相類似的功能,但它會(huì)對(duì)src的每一個(gè)元素減去一個(gè)常量value。函數(shù)cvSubRS()的功能和cvSubS()相似,但不是src的每個(gè)元素減去一個(gè)常量,而是常量減去的src中的每一元素。
cvSum
CvScalar cvSum(
??? CvArr* arr
);? ?????? ??? ??? ?????? ??? ??? ?????? ??? ??? ?????? 【75】
cvSum()計(jì)算數(shù)組arr各個(gè)通道的所有的像素的總和。注意,函數(shù)的返回類型是CvScalar,這意味著cvSum()提供多通道數(shù)組計(jì)算。在這種情況下,每個(gè)通道的和都會(huì)賦給類型為CvScalar的返回值中相應(yīng)的分量。
cvSVD
void cvSVD(
??? CvArr* A,
??? CvArr* W,
??? CvArr* U????= NULL,
??? CvArr*V???? = NULL,
???int??? flags = 0
);
奇異值分解(SVD)是將一個(gè)m×n的矩陣A按如下公式分解:
A=U·W·VT
其中,W是一個(gè)對(duì)角矩陣,U和V分別是m×m和n×n的酉矩陣。當(dāng)然,矩陣W也是一個(gè)m×n的矩陣,所以在這里的“對(duì)角線”是指任何行數(shù)和列數(shù)不相等的位置的元素值一定是0。因?yàn)閃必須是對(duì)角矩陣,OpenCV允許它表示為一個(gè)m×n階矩陣或一個(gè)n×1向量(在這種情況下,該向量將只包含對(duì)角線上的“奇異”值)。
對(duì)于函數(shù)cvSVD()來(lái)說(shuō),U和V是可選參數(shù),如果它們的值設(shè)置為NULL,則不會(huì)返回它們的內(nèi)容。最后的參數(shù)flags可以是表3-14所示三個(gè)選項(xiàng)中任何一個(gè)或全部(視情況進(jìn)行布爾型或計(jì)算合并)。
表3-14:cvSVD()中flags參數(shù)的取值
參數(shù) | 結(jié)果 |
CV_SVD_MODIFY_A | 允許改變矩陣A |
CV_SVD_U_T | 返回UT而不是U |
CV_SVD_V_T | 返回VT而不是V |
cvSVBkSb
void cvSVBkSb(
??? const CvArr* W,
??? const CvArr* U,
??? const CvArr* V,
??? const CvArr* B,
??? CvArr* X,
???int??? flags = 0
);
這個(gè)函數(shù)一般不會(huì)被直接調(diào)用。與剛才所描述的cvSVD()一起,本函數(shù)構(gòu)成了基于SVD的方法cvInvert()和cvSolve()的基礎(chǔ)。也就是說(shuō),如果你想自己實(shí)現(xiàn)矩陣求逆,可用這兩個(gè)函數(shù)(這可以為你節(jié)省在cvInvert()或cvSolve()中為臨時(shí)矩陣分配的一大堆內(nèi)存空間)。???????????????????????????? ???????????????????????????????????????? ???????????????? 【75】
函數(shù)cvSVBkSb()對(duì)矩陣A進(jìn)行反向替代計(jì)算,A已分解為矩陣U,W和V(即SVD)的結(jié)構(gòu)中描述出來(lái)。矩陣X的結(jié)果可由如下公式計(jì)算得出:
X=V·W*·UT·B
矩陣B是可選的,如果設(shè)置為NULL,它將會(huì)被忽略。當(dāng)時(shí)矩陣W*中的對(duì)角線元素定義如下:
ε這個(gè)值是一個(gè)奇異性閾值,一個(gè)非常小的數(shù)值,通常與W的對(duì)角線元素的總和成正比(即)。
cvTrace
CvScalar cvTrace( const CvArr* mat );
矩陣的跡是對(duì)角線元素的總和。在OpenCV中,該功能在函數(shù)cvGetDiag()基礎(chǔ)上實(shí)現(xiàn),因此輸入的數(shù)組不需要是方陣。同樣支持多通道數(shù)組,但是數(shù)組mat必須是浮點(diǎn)類型。
cvTranspose與cvT
void cvTranspose(
??? const CvArr* src,
???CvArr*?????? dst
);
cvTranspose()將src中每一個(gè)元素的值復(fù)制到dst中行號(hào)與列號(hào)相調(diào)換的位置上。這個(gè)函數(shù)不支持多通道數(shù)組;然而,如果你用兩通道數(shù)組表示復(fù)數(shù),那么記住一點(diǎn):cvTranspose()不執(zhí)行復(fù)共軛(依靠函數(shù)cvXorS()是實(shí)現(xiàn)該功能的一個(gè)快速方法,它可以直接翻轉(zhuǎn)數(shù)組中虛數(shù)部分中的符號(hào)位)。宏cvT()是函數(shù)cvTranspose()的縮寫。
cvXor和cvXorS
void cvXor(
??? const CvArr* src1,
??? const CvArr* src2,
??? CvArr* dst,
??? const CvArr*mask=NULL
);
void cvXorS(
??? const CvArr* src,
??? CvScalar value,
??? CvArr* dst,
??? const CvArr*mask=NULL
);?????????????????????????????????????????????????????????????????【76】
這兩個(gè)函數(shù)在數(shù)組src1上按位進(jìn)行異或(XOR)運(yùn)算。在函數(shù)cvXor()中,dst的每個(gè)元素是由src1和src2中對(duì)應(yīng)的元素按位進(jìn)行異或運(yùn)算所得到的。在函數(shù)cvXorS()中,是與常量value進(jìn)行按位異或運(yùn)算。再次說(shuō)明,如果參數(shù)mask非空,則只計(jì)算與mask中非0值相對(duì)應(yīng)的dst元素。
計(jì)算支持所有的數(shù)據(jù)類型,但src1和src2在函數(shù)cvXor()中必須是相同的數(shù)據(jù)類型。如果數(shù)組的元素是浮點(diǎn)類型的,那么使用浮點(diǎn)數(shù)的二進(jìn)制表示。
cvZero
void cvZero( CvArr* arr );
這個(gè)函數(shù)會(huì)將數(shù)組中的所有通道的所有元素的值都設(shè)置為0。
繪圖
我們經(jīng)常需要繪制圖像或者在已有的圖像上方繪制一些圖形。為此,OpenCV提供了一系列的函數(shù)幫助我們繪制直線、方形和圓形等。
直線
cvLine()是繪圖函數(shù)中最簡(jiǎn)單的,只需用Bresenham算法[Bresenham65]畫一條線 :
void? cvLine(
? CvArr*?? ? array,
? CvPoint? ? pt1,
? CvPoint? ? pt2,
? CvScalar ? color,
?int????? ?? thickness??? = 1,
?int????? ?? connectivity = 8
);
cvLine()函數(shù)中的第一個(gè)屬性是CvArr*。在這里,它一般為一個(gè)圖像類型的指針I(yè)plImage*。隨后兩個(gè)CvPoint是一種簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),它只包括整型變量x和y。我們可以用CvPoint(intx, int?y)函數(shù)快速地構(gòu)造一個(gè)CvPoint類型的變量,這樣可以方便地把兩個(gè)整型變量值賦給CvPoint數(shù)據(jù)結(jié)構(gòu)。
下一個(gè)屬性是CvScalar類型的顏色變量。CvScalar也是一種數(shù)據(jù)結(jié)構(gòu),定義如下所示:
typdef struct {
? double val[4];
} CvScalar;
可以看出,這種結(jié)構(gòu)只是四個(gè)雙精度浮點(diǎn)型變量的集合。在這里,前三個(gè)分別代表紅,綠,藍(lán)通道;沒(méi)有用到第四個(gè)(它只在適當(dāng)?shù)臅r(shí)候用于alpha通道)。一個(gè)常用的便捷宏指令是CV_RGB(r,g,?b),該指令采用三個(gè)數(shù)字作為參數(shù)并將其封裝到CvScalar。
接下來(lái)的兩個(gè)屬性是可選的。thickness是線的粗細(xì)(像素),connectivity被設(shè)為反走樣模式,默認(rèn)值為“8連通”,這種是較為平滑不會(huì)走樣的線型。也可以設(shè)置為“4連通”,這樣的話,斜線會(huì)產(chǎn)生重疊以致看上去過(guò)于粗重,不過(guò)畫起來(lái)速度要快?????????????????????????????得多。
cvRectangle()和cvLine()幾乎同樣便捷。cvRectangle()用于畫矩形。除了沒(méi)有connectivity參數(shù),它和cvLine()的其他參數(shù)都是一樣的。因?yàn)橛纱水a(chǎn)生的矩形總是平行與X和Y軸。利用cvRectangle(),我們只需給出兩個(gè)對(duì)頂點(diǎn),OpenCV便于畫出一個(gè)矩形。
void? cvRectangle(
? CvArr*?? ? array,
? CvPoint? ? pt1,
? CvPoint? ? pt2,
? CvScalar ? color,
?int????? ?? thickness = 1
);
圓形和橢圓
畫圓同樣簡(jiǎn)單,其參數(shù)與前相同。
void? cvCircle (
? CvArr*?? ? array,
? CvPoint? ? center,
?int????? ?? radius,
? CvScalar?? color,
?int????? ?? thickness??? = 1,
?int????? ?? connectivity = 8
);
對(duì)圓形和矩陣等很多封閉圖形來(lái)說(shuō),thickness參數(shù)也可以設(shè)置為CV_FILL,其值是-1;其結(jié)果是使用與邊一樣的顏色填充圓內(nèi)部。
橢圓函數(shù)比cvCircle()略微復(fù)雜一些:
void cvEllipse(
? CvArr*?? ? img,
? CvPoint? ? center,
? CvSize?? ? axes,
? double?? ? angle,
? double?? ?start_angle,
? double?? ?end_angle,
? CvScalar ? color,
?int????? ?? thickness = 1,
?int????? ?? line_type = 8
);?????????????????????????????????????????????????????????????????【79】
這里,主要的新成員是axes屬性,其類型為CvSize。CvSize函數(shù)與CvPoint和CvScalar非常相似;這是一個(gè)僅包含寬度和高度的簡(jiǎn)單結(jié)構(gòu)。同CvPoint和CvScalar一樣,CvSize也有一個(gè)簡(jiǎn)單的構(gòu)造函數(shù)cvSize(int height, intwidth),在需要的時(shí)候返回一個(gè)CvSize數(shù)據(jù)。在這種情況下,height和width參數(shù)分別代表橢圓的長(zhǎng)短半軸長(zhǎng)。
angle是指偏離主軸的角度,從X軸算起,逆時(shí)針?lè)较驗(yàn)檎?。同?#xff0c;start_angle和end_angle表示弧線開始和結(jié)束位置的角度。因此,一個(gè)完整的橢圓必須分別將這兩個(gè)值分別設(shè)為0°和360°。
使用外接矩形是描述橢圓繪制的另一種方法:
void cvEllipseBox(
? CvArr*?? ? img,
? CvBox2D? ? box,
? CvScalar ? color,
?int????? ?? thickness = 1,
?int????? ?? line_type = 8,
?int????? ?? shift???? =0
);
這里用到OpenCV的另一個(gè)結(jié)構(gòu)CvBox2D:
typdef struct {
? CvPoint2D32f ? center;
? CvSize2D32f? ? size;
?float??????? ??? angle;
} CvBox2D;
CvPoint2D32f是CvPoint的浮點(diǎn)形式,同時(shí)CvSizet2D32f也是CvSize的浮點(diǎn)形式。這些,連同傾斜角度,可以有效地描述橢圓的外接矩形。
多邊形
最后,我們有一系列繪制多邊形的函數(shù)。
void cvFillPoly(
? CvArr*??? ??? img,
? CvPoint** ?? pts,
? int*????????? npts,
? int?????????? contours,
? CvScalar? ??? color,
? int?????????? line_type = 8
);
void cvFillConvexPoly(
? CvArr*?? ???? img,
? CvPoint* ??? pts,
? int?????????? npts,
? CvScalar ??? color,
? int?????????? line_type = 8
);
void cvPolyLine(
? CvArr*??? ??? img,
? CvPoint** ?? pts,
? int*????????? npts,
? int?????????? contours,
? int?????????? is_closed,
? CvScalar? ??? color,
? int?????????? thickness = 1,
?int?????? ???? line_type = 8
);? ?????? ??? ??? ?????? ??? ??? ?????? ??? ??? ??? 【79~80】
上述三種方法依據(jù)同一思路又略有不同,其主要區(qū)別是如何描述點(diǎn)。
在cvFillPoly()中,點(diǎn)是由CvPoint數(shù)組提供的。它允許cvFillPoly()在一次調(diào)用中繪制多個(gè)多邊形。同樣地,npts是由記數(shù)點(diǎn)構(gòu)成的數(shù)組,與多邊形對(duì)應(yīng)。如果把變量is_closed設(shè)為true,那么下一個(gè)多邊形的第一個(gè)線段就會(huì)從上一多邊形最后一點(diǎn)開始。cvFillPoly()很穩(wěn)定,可以處理自相交多邊形,有孔的多邊形等復(fù)雜問(wèn)題。然而不幸的是,函數(shù)運(yùn)行起來(lái)相對(duì)緩慢。
cvFillConvexPoly()和cvFillPoly()類似。不同的是,它一次只能畫一個(gè)多邊形,而且只能畫凸多邊形。好處是,cvFillConvexPoly()運(yùn)行得更快。
第三個(gè)cvPolyLine(),其參數(shù)與cvFillPoly()相同,但因?yàn)橹恍璁嫵龆噙呅蔚倪?#xff0c;不需處理相交情況。因此,這種函數(shù)運(yùn)行速度遠(yuǎn)遠(yuǎn)超過(guò)cvFillPoly()。
字體和文字
最后一種形式的繪圖是繪制文字。當(dāng)然,文字創(chuàng)建了一套自己的復(fù)雜格式,但是,在這類事情上,OpenCV一如既往地更關(guān)心提供一個(gè)簡(jiǎn)單的“一招解決問(wèn)題”的方案,這個(gè)方案只適用于一些簡(jiǎn)單應(yīng)用,而不適用于一個(gè)穩(wěn)定的和完整的應(yīng)用(這將降低由其他庫(kù)提供的功能)。
OpenCV有一個(gè)主要的函數(shù),叫cvPutText()。這個(gè)函數(shù)可以在圖像上輸出一些文本。參數(shù)text所指向的文本將打印到圖像上,參數(shù)origin指定文本框左下角位置,參數(shù)color指定文本顏色。
void cvPutText(
?CvArr*??????? ??? img,
? const char*???? text,
?CvPoint?????? ??? origin,
? const CvFont* ? font,
?CvScalar????? ? color
);? ?????? ??? ??? ?????? ??? ??? ?????? ??? ??? ??? 【80~81】
總有一些瑣事使我們的工作比預(yù)期復(fù)雜,此時(shí)是CvFont指針表現(xiàn)的機(jī)會(huì)了。
概括地說(shuō),獲取CvFont*指針的方式就是調(diào)用函數(shù)cvInitFont()。該函數(shù)采用一組參數(shù)配置一些用于屏幕輸出的基本個(gè)特定字體。如果熟悉其他環(huán)境中的GUI編程,勢(shì)必會(huì)覺(jué)得cvInitFont似曾相識(shí),但只需更少的參數(shù)。
為了建立一個(gè)可以傳值給cvPutText()的CvFont,首先必須聲明一個(gè)CvFont變量,然后把它傳遞給cvInitFont()。
void cvInitFont(
? CvFont* ?? font,
? int??????? font_face,
? double? ??hscale,
? double? ??vscale,
? double? ?? shear????= 0,
? int??????? thickness = 1,
? int??????? line_type = 8
);
觀察本函數(shù)與其他相似函數(shù)的不同。正如工作在OpenCV環(huán)境下的cvCreateImage()。調(diào)用cvInitFont()時(shí),初始化一個(gè)已經(jīng)準(zhǔn)備好的CvFont結(jié)構(gòu)(這意味著你創(chuàng)建了一個(gè)變量,并傳給cvInitFont()函數(shù)一個(gè)指向新建的變量指針),而不是像cvCreateImage()那樣創(chuàng)建一個(gè)結(jié)構(gòu)并返回指針。
font_face參數(shù)列在表3-15中(效果在圖3-6中畫出),它可與CV_FONT_ITALIC組合(通過(guò)布爾或操作)。
表3-15:可用字體(全部可選變量)
標(biāo)志名稱???????????????????????????? 描述 | |
CV_FONT_HERSHEY_SIMPLEX | 正常尺寸sanserif字體 |
CV_FONT_HERSHEY_PLAIN | 小尺寸sanserif字體 |
CV_FONT_HERSHEY_DUPLEX | 正常尺寸sanserif, 比 |
CV_FONT_HERSHEY_COMPLEX | 正常尺寸serif, 比 |
???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ? 續(xù)表
標(biāo)志名稱?????????????????????????????? 描述 | |
CV_FONT_HERSHEY_TRIPLEX | 正常尺寸serif, 比CV_FONT_ |
CV_FONT_HERSHEY_COMPLEX_SMALL | 小尺寸的 |
CV_FONT_HERSHEY_SCRIPT_SIMPLEX | 手寫風(fēng)格 |
CV_FONT_HERSHEY_SCRIPT_COMPLEX | 比CV_FONT_HERSHEY_SCRIPT_ |
【81】
圖3-6:表3-15中的8個(gè)字體,繪制時(shí)設(shè)置hscale= vscale = 1.0,且每行的垂直間距為30像素
hscale和vscale只能設(shè)為1.0或0.5。字體渲染時(shí)選擇全高或半高(寬度同比縮放),繪制效果與指定字體的基本定義有關(guān)。
參數(shù)shear創(chuàng)建斜體字,如果設(shè)置為0.0 ,字體不傾斜。當(dāng)設(shè)置為1.0 時(shí),字符傾斜范圍接近45度。
參數(shù)thickness與Line_type的定義與其他繪圖函數(shù)相同。
數(shù)據(jù)存儲(chǔ)
OpenCV提供了一種機(jī)制來(lái)序列化(serialize)和去序列化(de-serialize)其各種數(shù)據(jù)類型,可以從磁盤中按YAML或XML格式讀/寫。在第4章中,我們將專門介紹存儲(chǔ)和調(diào)用常見(jiàn)的對(duì)象IplImages的函數(shù)(cvSaveImage()和cvLoadImage())。此外,第4章將討論讀/寫視頻的特有函數(shù):可以從文件或者攝影機(jī)中讀取數(shù)據(jù)的函數(shù)cvGrabFrame()以及寫操作函數(shù)cvCreateVideoWriter()和cvWriteFrame()。本小節(jié)將側(cè)重于一般對(duì)象的永久存儲(chǔ):讀/寫矩陣、OpenCV結(jié)構(gòu)、配置與日志文件。
首先,我們從有效且簡(jiǎn)便的OpenCV矩陣的保存和讀取功能函數(shù)開始。函數(shù)是cvSave()和cvLoad()。例3-15展示了如何保存和讀取一個(gè)5×5的單位矩陣(對(duì)角線上是1,其余地方都是0)。
例3-15:存儲(chǔ)和讀取CvMat
CvMatA = cvMat( 5, 5, CV_32F,the_matrix_data );
cvSave("my_matrix.xml", &A );
.. .
//to load it then in some other program use …
CvMat*A1 = (CvMat*) cvLoad( "my_matrix.xml" );
CxCore參考手冊(cè)中有整節(jié)內(nèi)容都在討論數(shù)據(jù)存儲(chǔ)。首先要知道,在OpenCV中,一般的數(shù)據(jù)存儲(chǔ)要先創(chuàng)建一個(gè)CvFileStorage結(jié)構(gòu)(如例3-16)所示,該結(jié)構(gòu)將內(nèi)存對(duì)象存儲(chǔ)在一個(gè)樹形結(jié)構(gòu)中。然后通過(guò)使用CV_STORAGE_READ參數(shù)的cvOpenFileStorage()從磁盤讀取數(shù)據(jù),創(chuàng)建填充該結(jié)構(gòu),也可以通過(guò)使用CV_STORAGE_WRITE的cvOpenFileStorage()創(chuàng)建并打開CvFileStorage寫數(shù)據(jù),而后使用適當(dāng)?shù)臄?shù)據(jù)存儲(chǔ)函數(shù)來(lái)填充它。在磁盤上,數(shù)據(jù)的存儲(chǔ)格式為XML或者YAML。
例3-16:CvFileStorage結(jié)構(gòu),數(shù)據(jù)通過(guò)CxCore數(shù)據(jù)存儲(chǔ)函數(shù)訪問(wèn)
typedefstruct CvFileStorage
{
???...????? // hidden fields
}CvFileStorage;
CvFileStorage樹內(nèi)部的數(shù)據(jù)是一個(gè)層次化的數(shù)據(jù)集合,包括標(biāo)量、CxCore對(duì)象(矩陣、序列和圖)以及用戶定義的對(duì)象。
假如有一個(gè)配置文件或日志文件。配置文件告訴我們視頻有多少幀(10),畫面大小(320×240)并且將應(yīng)用一個(gè)3×3的色彩轉(zhuǎn)換矩陣。例3-17展示了如何從磁盤中調(diào)出cfg.xml文件。
例3-17:往磁盤上寫一個(gè)配置文件cfg.xml
CvFileStorage*fs = cvOpenFileStorage(
?"cfg.xml",
?0,
?CV_STORAGE_WRITE
);
cvWriteInt(fs, "frame_count", 10 );
cvStartWriteStruct(fs, "frame_size", CV_NODE_SEQ );
cvWriteInt(fs, 0, 320 );
cvWriteInt(fs, 0, 200 );
cvEndWriteStruct(fs);
cvWrite(fs, "color_cvt_matrix", cmatrix );
cvReleaseFileStorage(&fs );
請(qǐng)留意這個(gè)例子中的一些關(guān)鍵函數(shù)。我們可以定義一個(gè)整型變量通過(guò)cvWritelnt()向結(jié)構(gòu)中寫數(shù)據(jù)。我們也可以使用cvStartWriteStruct()來(lái)創(chuàng)建任意一個(gè)可以任選一個(gè)名稱(如果無(wú)名稱請(qǐng)輸入0或NULL)的結(jié)構(gòu)。這個(gè)結(jié)構(gòu)有兩個(gè)未命名的整型變量,使用cvEndWriteStruct()結(jié)束編寫結(jié)構(gòu)。如果有更多的結(jié)構(gòu)體,我們用相似的方法來(lái)解決;這種結(jié)構(gòu)可以進(jìn)行任意深度的嵌套。最后,我們使用cvWrite()編寫處色彩轉(zhuǎn)換矩陣。將這個(gè)相對(duì)復(fù)雜的矩陣程序與例3-15中簡(jiǎn)單的cvSave()程序進(jìn)行對(duì)比。便會(huì)發(fā)現(xiàn)cvSave()是cvWrite()在只保存一個(gè)矩陣時(shí)的快捷方式。當(dāng)寫完數(shù)據(jù)后,使用cvReleaseFileStorage()釋放CvFileStorage句柄。例3-18顯示了XML格式的輸出內(nèi)容。
例3-18:磁盤中的cfg.xml文件
<?xmlversion="1.0"?>
<opencv_storage>
<frame_count>10</frame_count>
<frame_size>320200</frame_size>
<color_cvt_matrixtype_id="opencv-matrix">
?<rows>3</rows> <cols>3</cols>
?<dt>f</dt>
?<data>...</data></color_cvt_matrix>
</opencv_storage>
我們將會(huì)在例3-19中將這個(gè)配置文件讀入。
例3-19:磁盤中的cfg.xml文件
CvFileStorage*fs = cvOpenFileStorage(
?"cfg.xml",
?0,
?CV_STORAGE_READ
);
intframe_count = cvReadIntByName(
?fs,
?0,
?"frame_count",
?5 /* default value */
);
CvSeq*s = cvGetFileNodeByName(fs,0,"frame_size")->data.seq;
intframe_width = cvReadInt(
?(CvFileNode*)cvGetSeqElem(s,0)
);
intframe_height = cvReadInt(
?(CvFileNode*)cvGetSeqElem(s,1)
);
CvMat*color_cvt_matrix = (CvMat*) cvReadByName(
?fs,
?0,
?"color_cvt_matrix"
);
cvReleaseFileStorage(&fs );
在閱讀時(shí),我們像例3-19中那樣用cvOpenFileStorage()打開XML配置文件。然后用cvReadlntByName()來(lái)讀取frame_count,如果有沒(méi)有讀到的數(shù),則賦一個(gè)默認(rèn)值。在這個(gè)例子中默認(rèn)的值是5。然后使用cvGetFileNodeByName()得到結(jié)構(gòu)體frame_size。在這里我們用cvReadlnt()讀兩個(gè)無(wú)名稱的整型數(shù)據(jù)。隨后使用cvReadByName()讀出我們已經(jīng)定義的色彩轉(zhuǎn)換矩陣。將本例與例3-15中的cvLoad()進(jìn)行對(duì)比。如果我們只有一個(gè)矩陣要讀取,那么可以使用cvLoad(),但是如果矩陣是內(nèi)嵌在一個(gè)較大的結(jié)構(gòu)中,必須使用cvRead()。最后,釋放CvFileStorage結(jié)構(gòu)。
數(shù)據(jù)函數(shù)存儲(chǔ)與CvFileStorage結(jié)構(gòu)相關(guān)的表單列在表3-16中。想了解更多細(xì)節(jié),請(qǐng)查看CxCore手冊(cè)。
表3-16:數(shù)據(jù)存儲(chǔ)函數(shù)
函數(shù)名稱 | 描述 |
打開并釋放 | ? |
cvOpenFileStorage | 為讀/寫打開存儲(chǔ)文件 |
cvReleaseFileStorage | 釋放存儲(chǔ)的數(shù)據(jù) |
寫入 | ? |
cvStartWriteStruct | 開始寫入新的數(shù)據(jù)結(jié)構(gòu) |
cvEndWriteStruct | 結(jié)束寫入數(shù)據(jù)結(jié)構(gòu) |
cvWriteInt | 寫入整數(shù)型 |
cvWriteReal | 寫入浮點(diǎn)型 |
cvWriteString | 寫入字符串 |
cvWriteComment | 寫一個(gè)XML或YAML的注釋字串 |
cvWrite | 寫一個(gè)對(duì)象,例如CvMat |
cvWriteRawData | 寫入多個(gè)數(shù)值 |
cvWriteFileNode | 將文件節(jié)點(diǎn)寫入另一個(gè)文件存儲(chǔ)器 |
讀取 | ? |
cvGetRootFileNode | 獲取存儲(chǔ)器最頂層的節(jié)點(diǎn) |
cvGetFileNodeByName | 在映圖或存儲(chǔ)器中找到相應(yīng)節(jié)點(diǎn) |
cvGetHashedKey | 為名稱返回一個(gè)惟一的指針 |
cvGetFileNode | 在映圖或文件存儲(chǔ)器中找到節(jié)點(diǎn) |
cvGetFileNodeName | 返回文件的節(jié)點(diǎn)名 |
cvReadInt | 讀取一個(gè)無(wú)名稱的整數(shù)型 |
cvReadIntByName | 讀取一個(gè)有名稱的整數(shù)型 |
cvReadReal | 讀取一個(gè)無(wú)名稱的浮點(diǎn)型 |
???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? 續(xù)表
函數(shù) | 描述 |
cvReadRealByName | 讀取一個(gè)有名稱的浮點(diǎn)型 |
cvReadString | 從文件節(jié)點(diǎn)中尋找字符串 |
cvReadStringByName | 找到一個(gè)有名稱的文件節(jié)點(diǎn)并返回它 |
cvRead | 將對(duì)象解碼并返回它的指針 |
cvReadByName | 找到對(duì)象并解碼 |
cvReadRawData | 讀取多個(gè)數(shù)值 |
cvStartReadRawData | 初始化文件節(jié)點(diǎn)序列的讀取 |
cvReadRawDataSlice | 讀取文件節(jié)點(diǎn)的內(nèi)容 |
集成性能基元
Intel公司有一個(gè)產(chǎn)品叫集成性能基元(IntegratedPerformance Primitives,IPP)庫(kù)。這個(gè)庫(kù)實(shí)際上是一個(gè)有著高性能內(nèi)核的工具箱,它主要用于多媒體處理以及其他計(jì)算密集型應(yīng)用,可發(fā)掘處理器架構(gòu)的計(jì)算能力。(其他廠商的處理器也有類似的架構(gòu),只不過(guò)規(guī)模較小。)
就像第一章所探討的,無(wú)論從軟件層面還是公司內(nèi)組織層面OpenCV都與IPP有著緊密的聯(lián)系。最終,OpenCV被設(shè)計(jì)成能夠自動(dòng)識(shí)別IPP庫(kù),自動(dòng)將性能較低的代碼切換為IPP同功能的高性能代碼。IPP庫(kù)允許OpenCV依靠它獲得性能提升,IPP依靠單指令多數(shù)據(jù)(SIMD)指令以及多核架構(gòu)提升性能。
學(xué)會(huì)這些基礎(chǔ)知識(shí),我們就可以執(zhí)行各種各樣的基本任務(wù)。在本書隨后的內(nèi)容中,我們會(huì)發(fā)現(xiàn)OpenCV具有許多高級(jí)功能,幾乎所有這些功能都可切換到IPP運(yùn)行。圖像處理經(jīng)常需要對(duì)大量數(shù)據(jù)做同樣的重復(fù)操作,許多是可并行處理的。因此如果任何利用并行處理方式(MMX,SSE,SSE2等)的代碼獲得了巨大性能提升,您不必感到驚訝。
驗(yàn)證安裝
用來(lái)檢查IPP庫(kù)是否已經(jīng)正常安裝并且檢驗(yàn)運(yùn)行是否正常的方法是使用函數(shù)cvGetModulelnfo(),如例3-20所示。這個(gè)函數(shù)將檢查當(dāng)前OpenCV的版本和所有附加模塊。
例3-20:使用cvGetModulelnfo()檢查IPP
char*libraries;
char*modules;
cvGetModuleInfo(0, &libraries, &modules );
printf("Libraries:%s/nModules: %s/n", libraries, modules );
例3-20中的代碼將打印出描述已經(jīng)安裝的庫(kù)和模塊的文本。結(jié)果如下所示:
Libraries cxcore: 1.0.0
Modules: ippcv20.dll, ippi20.dll,ipps20.dll, ippvm20.dll
此處輸出的模塊列表就是OpenCV從IPP庫(kù)中調(diào)用的模塊的信息。實(shí)際上這些模塊是更底層的CPU庫(kù)函數(shù)的封裝。它的運(yùn)行原理將遠(yuǎn)遠(yuǎn)超出這本書的范圍,但是如果在modules字符串中看到了IPP庫(kù),那么你會(huì)感到很自信,因?yàn)樗械墓ぷ鞫荚诎茨A(yù)期的進(jìn)行。當(dāng)然你可以運(yùn)用這個(gè)信息來(lái)確認(rèn)IPP在您的系統(tǒng)中能正常運(yùn)行。你也許會(huì)用它來(lái)檢查IPP是否已經(jīng)安裝,并根據(jù)IPP存在與否來(lái)自動(dòng)調(diào)整代碼。
小結(jié)
本章介紹了我們經(jīng)常遇到的一些基本數(shù)據(jù)結(jié)構(gòu)。具體說(shuō)來(lái),我們了解了OpenCV矩陣結(jié)構(gòu)和最重要的OpenCV圖像結(jié)構(gòu)IplImage。經(jīng)過(guò)對(duì)兩者的仔細(xì)研究,我們得出一個(gè)結(jié)論:矩陣結(jié)構(gòu)和圖像結(jié)構(gòu)非常相似,如果某函數(shù)可使用CvMat,那也可使用IplImage,反之亦然。
練習(xí)
在下面的練習(xí)中,有些函數(shù)細(xì)節(jié)在本書中未介紹,可能需要參考與OpenCV一起安裝的或者網(wǎng)上OpenCV Wiki中的CxCore手冊(cè)。
1.?????找到并打開…/opencv/cxcore/include/cxtypes.h.通讀并找到可進(jìn)行如下操作的????????????????????函數(shù)。
a.???選取一個(gè)負(fù)的浮點(diǎn)數(shù),取它的絕對(duì)值,四舍五入后,取它的極值。
b.???產(chǎn)生一些隨機(jī)數(shù)。
c.???創(chuàng)建一個(gè)浮點(diǎn)型CvPoint2D32f,并轉(zhuǎn)換成一個(gè)整數(shù)型CvPoint。
d. ? 將一個(gè)CvPoint轉(zhuǎn)換成一個(gè)CvPoint2D32f。
2.?????下面這個(gè)練習(xí)是幫助掌握矩陣類型。創(chuàng)造一個(gè)三通道二維矩陣,字節(jié)類型,大小為100×100,并設(shè)置所有數(shù)值為0。
a.???在矩陣中使用voidcvCircle(CvArr* img, CvPoint center, intradius, CvScalar color, intthickness=1, int line_type=8, int shift=0)畫一個(gè)圓。
b.???使用第2章所學(xué)的方法來(lái)顯示這幅圖像。
3.?????創(chuàng)建一個(gè)擁有三個(gè)通道的二維字節(jié)類型矩陣,大小為100×100,并將所有值賦為0。通過(guò)函數(shù)cvPtr2D將指針指向中間的通道(“綠色”)。以(20,5)與(40,20)為頂點(diǎn)間畫一個(gè)綠色的長(zhǎng)方形。
4.?????創(chuàng)建一個(gè)大小為100×100的三通道RGB圖像。將它的元素全部置0。使用指針?biāo)惴ㄒ?20,5)與(40,20)為項(xiàng)點(diǎn)繪制一個(gè)綠色平面。
5.?????練習(xí)使用感興趣區(qū)域(ROI)。創(chuàng)建一個(gè)210×210的單通道圖像并將其歸0。在圖像中使用ROI和cvSet()建立一個(gè)增長(zhǎng)如金字塔狀的數(shù)組。也就是:外部邊界為0,下一個(gè)內(nèi)部邊界應(yīng)該為20,再下一個(gè)內(nèi)部邊界為40依此類推,直到最后內(nèi)部值為200;所有的邊界應(yīng)該為10個(gè)像素的寬度。最后顯示這個(gè)???????????圖形。
6.?????為一個(gè)圖像創(chuàng)建多個(gè)圖像頭。讀取一個(gè)大小至少為100×100的圖像。另創(chuàng)建兩個(gè)圖像頭并設(shè)置它們的origion,depth,nChannels和widthStep屬性同之前讀取的圖像一樣。在新的圖像頭中,設(shè)置寬度為20,高度為30。最后,將imageData指針?lè)謩e指向像素(5,10)和(50,60)像素位置。傳遞這兩個(gè)新的圖像頭給cvNot()。最后顯示最初讀取的圖像,在那個(gè)大圖像中應(yīng)該有兩個(gè)矩形,矩形內(nèi)的值是原始值的求反值。
7.?????使用cvCmp()創(chuàng)建一個(gè)掩碼。加載一個(gè)真實(shí)的圖像。使用cvSplit()將圖像分割成紅,綠,藍(lán)三個(gè)單通道圖像。
a.???找到并顯示綠圖。
b.???克隆這個(gè)綠圖兩次(分別命名為clone1和clone2)。
c.???求出這個(gè)綠色平面的最大值和最小值。
d.???將clone1的所有元素賦值為thresh=(unsignedchar)((最大值-最小值)/2.0)。
e.???將clone2所有元素賦值為0,然后調(diào)用函數(shù)cvCmp(green_image, clone1, clone2, CV_CMP_GE)?,F(xiàn)在clone2將是一個(gè)標(biāo)識(shí)綠圖中值超過(guò)thresh的掩碼圖像。
f.???最后,使用cvSubS(green_image,thresh/2,green_image, clone2)函數(shù)并顯示結(jié)果。
8.?????創(chuàng)建一個(gè)結(jié)構(gòu),結(jié)構(gòu)中包含一個(gè)整數(shù),一個(gè)CvPoint和一個(gè)CvRect;稱結(jié)構(gòu)為"my_struct"。
a.???寫兩個(gè)函數(shù):voidwrite_my_struct(CvFileStorage * fs, const char * name, my_struct *ms) 和void read_my_struct (CvFileStorage* fs, CvFileNode* ms_node, my_struct* ms)。用它們讀/寫my_struct。
b.???創(chuàng)建一個(gè)元素為my_struct結(jié)構(gòu)體且長(zhǎng)度為10的數(shù)組,并將數(shù)組寫入磁盤和從磁盤讀入內(nèi)存。
?
http://blog.csdn.net/gubenpeiyuan/article/details/8776542