1. 程式人生 > >(五)OpenCV提供一個功能強大的UI介面,可以在MFC、Qt、WinForms、Cocoa等平臺下使用

(五)OpenCV提供一個功能強大的UI介面,可以在MFC、Qt、WinForms、Cocoa等平臺下使用

1、OpenCV提供一個功能強大的UI介面,可以在MFC、Qt、WinForms、Cocoa等平臺下使用,甚至不需要其他的平臺。新版本的HighGUI介面包括:

建立並控制視窗,該視窗可以顯示圖片並記錄其內容


為視窗添加了trackbars控制元件,可以方便利用滑鼠進行控制而不是之前版本的只能利用鍵盤

讀寫硬碟和記憶體的圖片

讀取攝像頭的視訊、讀寫視訊檔案

先來介紹UI,包括函式createTrackbar、getTrackbarPos、setTrackbarPos、imshow、namedWindow、destroyWindow、destroyAllWindows、MoveWindow、ResizeWindow、SetMouseCallback、waitKey。這些函式保證了影象的基本處理、tarckbar的控制和滑鼠鍵盤的響應



介紹一下讀寫影象視訊的函式:影象相關的函式有imdecode、imencode、imread、imwrite。讀取視訊相關為VideoCapture類,負責捕捉檔案和攝像頭的視訊,該類內有成員函式VideoCapture、open、isOpened、release、grab、retrieve、read、get、set,寫視訊的類為VideoWriter,類內有成員函式VideoWriter、open、isOpened、write

新版本還為Qt做了新函式,這裡就不介紹了,有興趣的朋友可以自己看一下參考手冊的第四章第三節。

這裡介紹幾個常用的新功能,首先介紹一下新增滑桿控制元件Trackbar。呼叫函式為:


view plain
createTrackbar( TrackbarName, "Linear Blend", &alpha_slider, alpha_slider_max, on_trackbar );  

第一個引數為字串作為標籤,第二個引數為所在視窗的名字,第三個引數為儲存滑桿位置的值地址,其範圍為0~alpha_slider_max(第四個引數),最後一個引數為移動滑桿時呼叫的回撥函式名。
OpenCV2.0版本加強了對視訊處理的支援,不再需要對一組連續的圖片進行處理,可以進行實時的影象採集和記錄以及儲存。視訊的操作基本都被封裝在VideoCapture類中。開啟視訊可以可以通過如下程式碼實現:

view plain
VideoCapture captRefrnc(sourceReference);  
// or  
VideoCapture captUndTst;  
captUndTst.open(sourceCompareWith);  

其中sourceReference和sourceCompareWith為string型,為檔名。還可以通過isOpened函式檢測視訊是否成功開啟。也可以呼叫release函式提前關閉視訊。還可以講VideoCapture放到Mat結構中,因為視訊流是一連串的,可以通過read函式或>>操作符逐幀的讀取,例如:
view plain
Mat frameReference, frameUnderTest;  
captRefrnc >> frameReference;  
captUndTst.open(frameUnderTest);  

read函式只能逐幀的抓取,如果要抓取某一幀,可以成對的呼叫grab函式和retrieve函式。get函式可以獲取視訊相關資訊。set函式可以控制視訊的一些值,比如是指視訊的當前位置或幀數。
可以使用VideoWriter類建立新視訊,其open,isOpened函式呼叫方法類似,write函式或<<運算子向視訊寫入內容,可以使用split函式和merge函式單獨調整RGB通道的值

今日,被一個網友指出,說OpenCV以前提供的讀寫功能採用VFW,效率低下且有些格式支援不好。而 OpenCV 2.0 內建了videoInput Library,可以自動在VFW和DirectShow間切換。videoInput是老外寫的功能強大的開源視訊處理庫。是一個第三方庫,2.0~2.2的版本專門有一個3rdparty對該庫進行支援,而在最新的2.3版本中,已經講videoInput庫整合到highgui中了,想使用它的話,只需要在cmake中設定巨集WITH_VIDEOiNPUT=OFF/ON即可。

以後有新學到的東西都會陸續補充進來。

2、Templates是c++的一個很強大的特徵,可以是資料結構更加安全高效。但也會增加編譯時間和程式碼的長度,當函式被頻繁呼叫的時候便步那麼高效,所以在目前的OpenCV版本不推薦過多的使用templates。矩陣元素可以是如下型別中的一種:


? 8-bit unsigned integer (uchar)

? 8-bit signed integer (schar)
? 16-bit unsigned integer (ushort)
? 16-bit signed integer (short)
? 32-bit signed integer (int)
? 32-bit ?oating-point number (?oat)
? 64-bit ?oating-point number (double)

對於這些資料型別又定義瞭如下的列舉變數:


view plain
enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 };  
view plain
CV_32FC1 == CV_32F;  
CV_32FC2 == CV_32FC(2) == CV_MAKETYPE(CV_32F, 2);  
CV_MAKETYPE(depth, n) == ((x&7)<<3) + (n-1);  

3、用OpenCV做演算法的朋友們肯定為隨機數煩惱過,新版本一直支援隨機數產生器啦,而且還繼續支援之前版本的c格式的函式,不過與時俱進,我這裡介紹C++的RNG類。它可以壓縮一個64位的i整數並可以得到scalar和array的隨機數。目前的版本支援均勻分佈隨機數和Gaussian分佈隨機數。隨機數的產生採用的是Multiply-With-Carry演算法和Ziggurat演算法。

其建構函式的初始化可以傳入一個64位的整型引數作為隨機數產生器的初值。next可以取出下一個隨機數,uniform函式可以返回指定範圍的隨機數,gaussian函式返回一個高斯隨機數,fill則用隨機數填充矩陣。

這裡介紹一個uniform的使用事項,就是比如利用它產生0~1的隨機數的問題,具體程式碼如下:
view plain
RNG rng;  
// always produces 0  
double a = rng.uniform(0, 1);  
// produces double from [0, 1)  
double a1 = rng.uniform((double)0, (double)1);  
// produces float from [0, 1)  
double b = rng.uniform(0.f, 1.f);  
// produces double from [0, 1)  
double c = rng.uniform(0., 1.);  
// may cause compiler error because of ambiguity:  
// RNG::uniform(0, (int)0.999999)? or RNG::uniform((double)0, 0.99999)?  
double d = rng.uniform(0, 0.999999);  
就是不能寫成rng.uniform( 0 , 1),因為輸入為int型引數,會呼叫uniform(int,int),只能產生0。請大家注意使用^_^
還有一些隨機數相關的函式,比如randu可以產生一個均勻分佈的隨機數或者矩陣,randn可以產生一個正態分佈的隨機數,randShuffle可以隨機打亂矩陣元素
再簡單介紹一下c版本的隨機數產生器的相關函式,有cvRNG、cvRandArr、cvRandInt、cvRandReal

4、尋找一幅影象的匹配的模板,可以在一段視訊裡尋找出我們感興趣的東西,比如條形碼的識別就可能需要這樣類似的一個工作提取出條形碼區域(當然這樣的方法並不魯棒)。而OpenCV已經為我們整合好了相關的功能。函式為matchTemplate。

所謂模板匹配就是在一幅影象中尋找和模板影象(patch)最相似的區域。該函式的功能為,在輸入源影象Source image(I)中滑動框,尋找各個位置與模板影象Template image(T)的相似度,並將結果儲存在結果矩陣result matrix(R)中。該矩陣的每一個點的亮度表示與模板T的匹配程度。然後可以通過函式minMaxLoc定位矩陣R中的最大值(該函式也可以確定最小值)。


匹配的方法有:

CV_TM_SQDIFF 平方差匹配法,最好的匹配為0,值越大匹配越差
CV_TM_SQDIFF_NORMED 歸一化平方差匹配法
CV_TM_CCORR 相關匹配法,採用乘法操作,數值越大表明匹配越好
CV_TM_CCORR_NORMED 歸一化相關匹配法
CV_TM_CCOEFF 相關係數匹配法,最好的匹配為1,-1表示最差的匹配
CV_TM_CCOEFF_NORMED 歸一化相關係數匹配法
前面兩種方法為越小的值表示越匹配,後四種方法值越大越匹配。

其實模板匹配的使用和直方圖反向投影calcBackProject函式很像,只是直方圖反向投影對比的是直方圖,而模板匹配對比的是影象的畫素值,相比較而言,直方圖反向投影的匹配魯棒性更好。

總結這個函式,感覺功能不是很強大,應用不是很廣,因為只能在影象中搜索出指定的模板,如果模板是從待搜尋目標中截取出來的,效果會很好,如果模板不是待搜素影象的一部分,效果就差的多了,所以該函式的使用還是有很大的侷限性。

OpenCV支援大量的輪廓、邊緣、邊界的相關函式,相應的函式有moments、HuMoments、findContours、drawContours、approxPolyDP、arcLength、boundingRect、contourArea、convexHull、fitEllipse、fitLine、isContourConvex、minAreaRect、minEnclosingCircle、mathcShapes、pointPolygonTest。還有一些c版本的針對老版本的資料結構的函式比如cvApproxChains、cvConvexityDefects。這裡先介紹一些我用過的函式,以後用到再陸續補充。


5、OpenCV裡支援很多邊緣提取的辦法,可是如何在一幅影象裡得到輪廓區域的引數呢,這就需要用到findContours函式,這個函式的原型為:

view plain
//C++:  
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())  
void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point())  

這裡介紹下該函式的各個引數:
輸入影象image必須為一個2值單通道影象

contours引數為檢測的輪廓陣列,每一個輪廓用一個point型別的vector表示

hiararchy引數和輪廓個數相同,每個輪廓contours[ i ]對應4個hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],分別表示後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號,如果沒有對應項,該值設定為負數。

mode表示輪廓的檢索模式

CV_RETR_EXTERNAL表示只檢測外輪廓

CV_RETR_LIST檢測的輪廓不建立等級關係

CV_RETR_CCOMP建立兩個等級的輪廓,上面的一層為外邊界,裡面的一層為內孔的邊界資訊。如果內孔內還有一個連通物體,這個物體的邊界也在頂層。

CV_RETR_TREE建立一個等級樹結構的輪廓。具體參考contours.c這個demo
method為輪廓的近似辦法
CV_CHAIN_APPROX_NONE儲存所有的輪廓點,相鄰的兩個點的畫素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1

CV_CHAIN_APPROX_SIMPLE壓縮水平方向,垂直方向,對角線方向的元素,只保留該方向的終點座標,例如一個矩形輪廓只需4個點來儲存輪廓資訊
CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似演算法
offset表示代表輪廓點的偏移量,可以設定為任意值。對ROI影象中找出的輪廓,並要在整個影象中進行分析時,這個引數還是很有用的。
具體應用參考sample資料夾下面的squares.cpp這個demo
findContours後會對輸入的2值影象改變,所以如果不想改變該2值影象,需建立新mat來存放,findContours後的輪廓資訊contours可能過於複雜不平滑,可以用approxPolyDP函式對該多邊形曲線做適當近似
contourArea函式可以得到當前輪廓包含區域的大小,方便輪廓的篩選


findContours經常與drawContours配合使用,用來將輪廓繪製出來。其中第一個引數image表示目標影象,第二個引數contours表示輸入的輪廓組,每一組輪廓由點vector構成,第三個引數contourIdx指明畫第幾個輪廓,如果該引數為負值,則畫全部輪廓,第四個引數color為輪廓的顏色,第五個引數thickness為輪廓的線寬,如果為負值或CV_FILLED表示填充輪廓內部,第六個引數lineType為線型,第七個引數為輪廓結構資訊,第八個引數為maxLevel
得到了複雜輪廓往往不適合特徵的檢測,這裡再介紹一個點集凸包絡的提取函式convexHull,輸入引數就可以是contours組中的一個輪廓,返回外凸包絡的點集
還可以得到輪廓的外包絡矩形,使用函式boundingRect,如果想得到旋轉的外包絡矩形,使用函式minAreaRect,返回值為RotatedRect;也可以得到輪廓的外包絡圓,對應的函式為minEnclosingCircle;想得到輪廓的外包絡橢圓,對應的函式為fitEllipse,返回值也是RotatedRect,可以用ellipse函式畫出對應的橢圓
如果想根據多邊形的輪廓資訊得到多邊形的多階矩,可以使用類moments,這個類可以得到多邊形和光柵形狀的3階以內的所有矩,類內有變數m00,m10,m01,m20,m11,m02,m30,m21,m12,m03,比如多邊形的質心為 x = m10 / m00,y = m01 / m00。
如果想獲得一點與多邊形封閉輪廓的資訊,可以呼叫pointPolygonTest函式,這個函式返回值為該點距離輪廓最近邊界的距離,為正值為在輪廓內部,負值為在輪廓外部,0表示在邊界上。