OpenCV_輪廓的查找、表達、繪制、特性及匹配


轉摘網址為:http://www.cnblogs.com/slysky/archive/2011/10/14/2212227.html

雖然Canny之類的邊緣檢測算法可以根據像素間的差異檢測出輪廓邊界的像素,但是它並沒有將輪廓作為一個整體。下一步是要將這些邊緣像素組裝成輪廓。

輪廓是構成任何一個形狀的邊界或外形線。直方圖對比和模板匹配根據色彩及色彩的分布來進行匹配,以下包括:輪廓的查找、表達方式、組織方式、繪制、特性、匹配。

首先回憶下幾個結構體:

首先是圖像本身的結構體:
typedef struct CvMat
{
int type; /* CvMat 標識 (CV_MAT_MAGIC_VAL), 元素類型和標記 */
int step; /* 以字節為單位的行數據長度*/
int* refcount; /* 數據引用計數 */
union
{
uchar* ptr;
short* s;
int* i;
float* fl;
double* db;
} data;
union
{
int rows;
int height;
};
union
{
int cols;
int width;
};
這個結構體是最基礎的矩陣,而圖像本身就是一個復雜的矩陣,所以圖像是對這個結構體的繼承:
typedef struct _IplImage
{
int nSize; /* IplImage大小 */
int ID; /* 版本 (=0)*/
int nChannels; /* 大多數OPENCV函數支持1,2,3 或 4 個通道 */
int alphaChannel; /* 被OpenCV忽略 */
int depth; /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,
IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支持 */
char colorModel[4]; /* 被OpenCV忽略 */
char channelSeq[4]; /* 同上 */
int dataOrder; /* 0 - 交叉存取顏色通道, 1 - 分開的顏色通道.
cvCreateImage只能創建交叉存取圖像 */
int origin; /* 0 - 頂—左結構,1 - 底—左結構 (Windows bitmaps 風格) */
int align; /* 圖像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */
int width; /* 圖像寬像素數 */
int height; /* 圖像高像素數*/
struct _IplROI *roi;/* 圖像感興趣區域. 當該值非空只對該區域進行處理 */
struct _IplImage *maskROI; /* 在 OpenCV中必須置NULL */
void *imageId; /* 同上*/
struct _IplTileInfo *tileInfo; /*同上*/
int imageSize; /* 圖像數據大小(在交叉存取格式下imageSize=image->height*image->widthStep),單位字節*/
char *imageData; /* 指向排列的圖像數據 */
int widthStep; /* 排列的圖像行大小,以字節為單位 */
int BorderMode[4]; /* 邊際結束模式, 被OpenCV忽略 */
int BorderConst[4]; /* 同上 */
char *imageDataOrigin; /* 指針指向一個不同的圖像數據結構(不是必須排列的),是為了糾正圖像內存分配准備的 */
}IplImage;
值得注意的地方:首先是origin這個,當有些圖像復制或者視頻播放時候,由於原點坐標位置未定,很容造成圖片倒置。這時就得用void cvFlip( const CvArr* src, CvArr* dst=NULL, int flip_mode=0)函數或者直接設定origin來改變坐標原點;widthstep就是CvMat的step;
構造方法:IplImage* cvCreateImage( CvSize size, int depth, int channels );
直方圖結構:
typedef struct CvHistogram
{
int type;
CvArr* bins;
float thresh[CV_MAX_DIM][2]; /* 對於標准直方圖,bins的值有左邊界+右邊界=2 */
float** thresh2; /* 對於非標准直方圖 */
CvMatND mat; /* embedded matrix header for array histograms */
}CvHistogram;

因此,由於直方圖的復雜性,得到一個圖片的直方圖的步驟就不是一個函數完成的:
1,分割圖片通道
2,求出bins數量及范圍
3,CvHistogram* cvCreateHist( int dims, int* sizes, int type,float** ranges=NULL, int uniform=1 );
創建直方圖
4,void cvCalcHist( IplImage** image, CvHistogram* hist,int accumulate=0, const CvArr* mask=NULL );
計算直方圖

下面開始輪廓的學習。

查找輪廓
    首先是如何在圖像中找到輪廓,可以利用OpenCV提供的方法cvFindContours()可以很方便的查找輪廓。

cvFindContours()方法從二值圖像中尋找輪廓。因此此方法處理的圖像可以是從cvCanny()函數得到的有邊緣像素的圖像,或者從cvThreshold()及cvAdaptiveThreshold()得到的圖像,這時的邊緣是正和負區域之間的邊界。

既然在查找之前,我們需要將彩色圖像轉換成灰度圖像,然后再將灰度圖像轉換成二值圖像。代碼如下所示:

1  CvSeq *contours = 0;
2 cvCvtColor(src,dst,CV_BGR2GRAY);//將源圖像進行灰度化
3 cvThreshold(dst,dst,f_thresh,255,CV_THRESH_BINARY);//二值化閾值 雖然第一個參數是const,但仍可以更改dst
4 cvFindContours(dst,f_storage,&contours); //查找輪廓
5 cvZero(dst);

 

輪廓的表達方式
    使用上面的代碼可以得到圖像的默認輪廓,但是輪廓在電腦中是如何表達的呢?在OpenCv中提供了兩類表達輪廓的方式:頂點的序列、Freeman鏈碼。

 

首先介紹下內存存儲器的概念,這是OpenCV在創建動態對象時存取內存的技術。

 

CvMemStorage* cvCreateMemStorage( int block_size=0 );//創建默認值大小的內存空間
void cvReleaseMemStorage( CvMemStorage** storage );//釋放內存空間
void cvClearMemStorage( CvMemStorage* storage );//清空內存塊,可以用於重復使用,將內存返還給存儲器,而不是返回給系統
void *cvMemStorageAlloc(CvMemStorage *storage,size_t size);//開辟內存空間

 

序列

 

序列是內存存儲器中可以存儲的一種對象。序列是某種結構的鏈表。序列在內存中被實現為一個雙端隊列,因此序列可以實習快速的隨機訪問,以及快速刪除頂端的元素,但是從中間刪除元素則稍慢些。

 

序列結構:
CvSeq
可動態增長元素序列(OpenCV_1.0已發生改變,詳見cxtypes.h) Growable sequence of elements

 

#define CV_SEQUENCE_FIELDS() /
int flags; /* micsellaneous flags */ /
int header_size; /* size of sequence header */ /
struct CvSeq* h_prev; /* previous sequence */ /
struct CvSeq* h_next; /* next sequence */ /
struct CvSeq* v_prev; /* 2nd previous sequence */ /
struct CvSeq* v_next; /* 2nd next sequence */ /
int total; /* total number of elements */ /
int elem_size;/* size of sequence element in bytes */ /
char* block_max;/* maximal bound of the last block */ /
char* ptr; /* current write pointer */ /
int delta_elems; /* how many elements allocated when the sequence grows (sequence granularity) */ /
CvMemStorage* storage; /* where the seq is stored */ /
CvSeqBlock* free_blocks; /* free blocks list */ /
CvSeqBlock* first; /* pointer to the first sequence block */
typedef struct CvSeq
{
CV_SEQUENCE_FIELDS()
} CvSeq;
相關操作就不重復列出(排序,查找,逆序,拆分,復制,讀取,寫入切片的復制,移除,插入,),可以查找相關文檔。

 

1.頂點的序列
    用多個頂點(或各點間的線段)來表達輪廓。假設要表達一個從(0,0)到(2,2)的矩形,
(1)如果用點來表示,那么依次存儲的可能是:(0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,1);
(2)如果用點間的線段來表達輪廓,那么依次存儲的可能是:(0,0),(2,0),(2,2),(0,2)。
 2.Freeman鏈碼
    Freeman鏈碼需要一個起點,以及從起點出發的一系列位移。每個位移有8個方向,從0~7分別指向從正北開始的8個方向。假設要用Freeman鏈碼表達從(0,0)到(2,2)的矩形,可能的表示方法是:起點(0,0),方向鏈2,2,4,4,6,6,0,0。
輪廓之間的組織方式
    在查找到輪廓之后,不同輪廓是怎么組織的呢?根據不同的選擇,它們可能是:(1)列表;(2)雙層結構;(3)樹型結構。
    從縱向上來看,列表只有一層,雙層結構有一或者兩層,樹型結構可能有一層或者多層。
    如果要遍歷所有的輪廓,可以使用遞歸的方式。

輪廓的繪制
    輪廓的繪制比較簡單,用上面提到的方法取得輪廓的所有點,然后把這些點連接成一個多邊形即可。

輪廓的一個例子為:OpenCV_輪廓例子

上例中檢測出輸入圖像的輪廓,然后逐個繪制每個輪廓。下個例子為:

在輸入圖像上尋找並繪制輪廓

輪廓的特性
    輪廓的特性有很多,下面一一介紹。

1.輪廓的多邊形逼近
    輪廓的多邊形逼近指的是:使用多邊形來近似表示一個輪廓。
    多邊形逼近的目的是為了減少輪廓的頂點數目。
    多邊形逼近的結果依然是一個輪廓,只是這個輪廓相對要粗曠一些。
   可以使用方法cvApproxPoly()

2.輪廓的關鍵點
    輪廓的關鍵點是:輪廓上包含曲線信息比較多的點。關鍵點是輪廓頂點的子集。
    可以使用cvFindDominantPoints函數來獲取輪廓上的關鍵點,該函數返回的結果一個包含 關鍵點在輪廓頂點中索引 的序列。再次強調:是索引,不是具體的點。如果要得到關鍵點的具體坐標,可以用索引到輪廓上去找。
3.輪廓的周長和面積
    輪廓的周長可以用cvContourPerimeter或者cvArcLength函數來獲取。
    輪廓的面積可以用cvContourArea函數來獲取。

4.輪廓的邊界框
    有三種常見的邊界框:矩形、圓形、橢圓。
    (1)矩形:在圖像處理系統中提供了一種叫Rectangle的矩形,不過它只能表達邊垂直或水平的特例;OpenCv中還有一種叫Box的矩形,它跟數學上的矩形一致,只要4個角是直角即可。
    如果要獲取輪廓的Rectangle,可以使用cvBoundingRect函數。
    如果要獲取輪廓的Box,可以使用cvMinAreaRect2函數。
    (2)圓形
    如果要獲取輪廓的圓形邊界框,可以使用cvMinEnclosingCircle函數。
    (3)橢圓
    如果要獲取輪廓的橢圓邊界框,可以使用cvFitEllipse2函數。
5.輪廓的矩

    矩是通過對輪廓上所有點進行積分運算(或者認為是求和運算)而得到的一個粗略特征。

在連續情況下,圖像函數為 f(x,y),那么圖像的p+q階幾何矩(標准矩)定義為:

p ,q = 0,1,2…… 

p+q階中心距定義為:

 p,q = 0,1,2……

 

 

其中代表圖像的重心,

 

,

 

對於離散的數字圖像,采用求和號代替積分:

 

,,p,q = 0,1,2 ……

 

N和M分別是圖像的高度和寬度;

歸一化的中心距定義為:;其中

在公式中,p對應x維度上的矩,q對應y維度上的矩,階數表示對應的部分的指數。該計算是對輪廓界上所有像素(數目為n)進行求和。如果p和q全部為0,那么m00實際上對應輪廓邊界上點的數目。

雖然可以直接計算出輪廓的矩,但是經常會用到歸一化的矩(因此不同大小但是形狀相同的物體會有相同的值)。同樣,簡單的矩依賴於所選坐標系,這意味着物體旋轉后就無法正確匹配。

於是就產生了Hu矩以及其他歸一化矩的函數。

Hu矩是歸一化中心矩的線性組合。之所以這樣做是為了能夠獲取代表圖像某個特征的矩函數。這些矩函數對縮放,旋轉和鏡像映射出了(h1)具有不變性。

Hu矩是從中心矩中計算得到。即七個由歸一化中心矩組合成的矩:  

 其中中心矩和歸一化中心矩的定義為:

 

 

   我們可以使用cvContoursMoments函數、cvMoments函數方便的得到輪廓的矩集,然后再相應的方法或函數獲取各種矩。
    特定的矩:cvGetSpatialMoment函數
    中心矩:cvGetCentralMoment函數
    歸一化中心矩:cvGetNormalizedCentralMoment函數
    Hu矩:cvGetHuMoments函數
6.輪廓的輪廓樹
    輪廓樹用來描述某個特定輪廓的內部特征。注意:輪廓樹跟輪廓是一一對應的關系;輪廓樹不用於描述多個輪廓之間的層次關系。

    輪廓樹的創建過程:

    從一個輪廓創建一個輪廓樹是從底端(葉子節點)到頂端(根節點)的。首先搜索三角形突出或者凹陷的形狀的周邊(輪廓上的每一個點都不是完全和它的相鄰點共線的)每個這樣的三角形被一條線段代替,這條線段通過連接非相鄰點的兩點得到;因此實際上三角形或者被削平或者被填滿。每個這樣的替換都把輪廓的頂點減少,並且給輪廓樹創建一個新節點。如果這樣的一個三角形的兩側有原始邊,那么她就是得到的輪廓樹的葉子;如果一側已是一個三角形,那么它就是那個三角形的父節點。這個過程的迭代最終把物體的外形簡稱一個四邊形,這個四邊形也被剖開;得到的兩個三角形是根節點的兩個子節點。

結果的二分樹最終將原始輪廓的形狀性比編碼。每個節點被它所對應的三角形的信息所注釋。

這樣建立的輪廓樹並不太魯棒,因為輪廓上小的改變也可能會徹底改變結果的樹,同時最初的三角形是任意選取的。為了得到較好的描述需要首先使用函數cvApproxPoly()之后將輪廓排列(運用循環移動)成最初的三角形不怎么收到旋轉影響的狀態。
    可以用函數cvCreateContourTree來構造輪廓樹。

 7.輪廓的凸包和凸缺陷
    輪廓的凸包和凸缺陷用於描述物體的外形。凸包和凸缺陷很容易獲得,不過我目前不知道它們到底怎么使用。
    如果要判斷輪廓是否是凸的,可以用cvCheckContourConvexity函數。
    如果要獲取輪廓的凸包,可以用cvConvexHull2函數,返回的是包含頂點的序列。
    如果要獲取輪廓的凸缺陷,可以用cvConvexityDefects函數。
 8.輪廓的成對幾何直方圖
    成對幾何直方圖(pairwise geometrical histogram PGH)是鏈碼編碼直方圖(chain code histogram CCH)的一個擴展或者延伸。CCH是一種直方圖,用來統計一個輪廓的Freeman鏈碼編碼每一種走法的數字。這種直方圖的一個優良性質為當物體旋轉45度,那么新直方圖是老直方圖的循環平移。這樣就可以不受旋轉影響。

    (1)輪廓保存的是一系列的頂點,輪廓是由一系列線段組成的多邊形。對於看起來光滑的輪廓(例如圓),只是線段條數比較多,線段長度比較短而已。實際上,電腦中顯示的任何曲線都由線段組成。
    (2)每兩條線段之間都有一定的關系,包括它們(或者它們的延長線)之間的夾角,兩條線段的夾角范圍是:(0,180)。
    (3)每兩條線段上的點之間還有距離關系,包括最短(小)距離、最遠(大)距離,以及平均距離。最大距離我用了一個偷懶的計算方法,我把輪廓外界矩形的對角線長度看作了最大距離。
    (4)成對幾何直方圖所用的統計數據包括了夾角和距離。

輪廓的匹配
    如果要比較兩個物體,可供選擇的特征很多。如果要判斷某個人的性別,可以根據他(她)頭發的長短來判斷,這很直觀,在長發男稀有的年代准確率也很高。也可以根據這個人尿尿的射程來判斷,如果射程大於0.50米,則是男性。總之,方法很多,不一而足。
    我們在上文中得到了輪廓的這么多特征,它們也可以用於進行匹配。典型的輪廓匹配方法有:Hu矩匹配、輪廓樹匹配、成對幾何直方圖匹配。
1.Hu矩匹配
    輪廓的Hu矩對包括縮放、旋轉和鏡像映射在內的變化具有不變性。cvMatchShapes函數可以很方便的實現對2個輪廓間的匹配。
2.輪廓樹匹配
    用樹的形式比較兩個輪廓。cvMatchContourTrees函數實現了輪廓樹的對比。
3.成對幾何直方圖匹配
    在得到輪廓的成對幾何直方圖之后,可以使用直方圖對比的方法來進行匹配。


免責聲明!

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



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