日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OpenCV_轮廓的查找、表达、绘制、特性及匹配

發(fā)布時(shí)間:2023/12/20 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV_轮廓的查找、表达、绘制、特性及匹配 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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

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

首先回憶下幾個(gè)結(jié)構(gòu)體:

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

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

下面開始輪廓的學(xué)習(xí)。

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

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

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

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

?

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

?

首先介紹下內(nèi)存存儲(chǔ)器的概念,這是OpenCV在創(chuàng)建動(dòng)態(tài)對(duì)象時(shí)存取內(nèi)存的技術(shù)。

?

CvMemStorage* cvCreateMemStorage( int block_size=0 );//創(chuàng)建默認(rèn)值大小的內(nèi)存空間
void cvReleaseMemStorage( CvMemStorage** storage );//釋放內(nèi)存空間
void cvClearMemStorage( CvMemStorage* storage );//清空內(nèi)存塊,可以用于重復(fù)使用,將內(nèi)存返還給存儲(chǔ)器,而不是返回給系統(tǒng)
void *cvMemStorageAlloc(CvMemStorage *storage,size_t size);//開辟內(nèi)存空間

?

序列

?

序列是內(nèi)存存儲(chǔ)器中可以存儲(chǔ)的一種對(duì)象。序列是某種結(jié)構(gòu)的鏈表。序列在內(nèi)存中被實(shí)現(xiàn)為一個(gè)雙端隊(duì)列,因此序列可以實(shí)習(xí)快速的隨機(jī)訪問,以及快速刪除頂端的元素,但是從中間刪除元素則稍慢些。

?

序列結(jié)構(gòu):
CvSeq
可動(dòng)態(tài)增長(zhǎng)元素序列(OpenCV_1.0已發(fā)生改變,詳見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;
相關(guān)操作就不重復(fù)列出(排序,查找,逆序,拆分,復(fù)制,讀取,寫入切片的復(fù)制,移除,插入,),可以查找相關(guān)文檔。

?

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

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

輪廓的一個(gè)例子為:OpenCV_輪廓例子

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

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

具體代碼為:

輪廓例子 1 #include "stdafx.h"
2 #include <iostream>
3 using namespace std;
4
5
6 #ifdef _CH_
7 #pragma package <opencv>
8 #endif
9
10 #include "cv.h"
11 #include "highgui.h"
12
13 using namespace std;
14
15 int _tmain(int argc, _TCHAR* argv[])
16 {
17 cvNamedWindow("flower",1);
18
19 IplImage* img_8uc1 = cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);
20 IplImage* img_edge = cvCreateImage(cvGetSize(img_8uc1),8,1);
21 IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);
22
23 cvThreshold(img_8uc1,img_edge,128,255,CV_THRESH_BINARY);
24
25 CvMemStorage* storage = cvCreateMemStorage();
26 CvSeq* first_contour = NULL;
27
28 int Nc = cvFindContours(
29 img_edge,
30 storage,
31 &first_contour,
32 sizeof(CvContour),
33 CV_RETR_LIST
34 );
35
36 int n=0;
37 printf("Total contours detected %d \n",Nc);
38
39 for (CvSeq* c=first_contour;c!=NULL;c=c->h_next)
40 {
41 cvCvtColor(img_8uc1,img_8uc3,CV_GRAY2BGR);
42
43 cvDrawContours(
44 img_8uc3,
45 c,
46 CV_RGB(0,0,255),
47 CV_RGB(0,255,0),
48 0,
49 2,
50 8
51 );
52
53 printf("Contour # %d\n",n);
54 cvShowImage("flower",img_8uc3);
55 printf("%d elements:\n",c->total);
56
57 for (int i=0;i<c->total;++i)
58 {
59 CvPoint* p = CV_GET_SEQ_ELEM(CvPoint,c,i);
60 printf("(%d,%d)\n",p->x,p->y);
61 }
62 cvWaitKey(0);
63 n++;
64 }
65
66 printf("Finished all contours.\n");
67
68 cvCvtColor(img_8uc1,img_8uc3,CV_GRAY2BGR);
69 cvShowImage("flower",img_8uc3);
70
71 cvWaitKey(0);
72
73 cvDestroyWindow("flower");
74 cvReleaseImage(&img_8uc3);
75 cvReleaseImage(&img_8uc1);
76 cvReleaseImage(&img_edge);
77 return 0;
78 }

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

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

2.輪廓的關(guān)鍵點(diǎn)
??? 輪廓的關(guān)鍵點(diǎn)是:輪廓上包含曲線信息比較多的點(diǎn)。關(guān)鍵點(diǎn)是輪廓頂點(diǎn)的子集。
??? 可以使用cvFindDominantPoints函數(shù)來獲取輪廓上的關(guān)鍵點(diǎn),該函數(shù)返回的結(jié)果一個(gè)包含 關(guān)鍵點(diǎn)在輪廓頂點(diǎn)中索引 的序列。再次強(qiáng)調(diào):是索引,不是具體的點(diǎn)。如果要得到關(guān)鍵點(diǎn)的具體坐標(biāo),可以用索引到輪廓上去找。
3.輪廓的周長(zhǎng)和面積
??? 輪廓的周長(zhǎng)可以用cvContourPerimeter或者cvArcLength函數(shù)來獲取。
??? 輪廓的面積可以用cvContourArea函數(shù)來獲取。

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

????矩是通過對(duì)輪廓上所有點(diǎn)進(jìn)行積分運(yùn)算(或者認(rèn)為是求和運(yùn)算)而得到的一個(gè)粗略特征。

在連續(xù)情況下,圖像函數(shù)為 f(x,y),那么圖像的p+q階幾何矩(標(biāo)準(zhǔn)矩)定義為:

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

p+q階中心距定義為:

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

?

?

其中和代表圖像的重心,

?

,

?

對(duì)于離散的數(shù)字圖像,采用求和號(hào)代替積分:

?

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

?

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

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

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

雖然可以直接計(jì)算出輪廓的矩,但是經(jīng)常會(huì)用到歸一化的矩(因此不同大小但是形狀相同的物體會(huì)有相同的值)。同樣,簡(jiǎn)單的矩依賴于所選坐標(biāo)系,這意味著物體旋轉(zhuǎn)后就無法正確匹配。

于是就產(chǎn)生了Hu矩以及其他歸一化矩的函數(shù)。

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

Hu矩是從中心矩中計(jì)算得到。即七個(gè)由歸一化中心矩組合成的矩:??

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

?

?

?? 我們可以使用cvContoursMoments函數(shù)、cvMoments函數(shù)方便的得到輪廓的矩集,然后再相應(yīng)的方法或函數(shù)獲取各種矩。
??? 特定的矩:cvGetSpatialMoment函數(shù)
??? 中心矩:cvGetCentralMoment函數(shù)
??? 歸一化中心矩:cvGetNormalizedCentralMoment函數(shù)
??? Hu矩:cvGetHuMoments函數(shù)
6.輪廓的輪廓樹
??? 輪廓樹用來描述某個(gè)特定輪廓的內(nèi)部特征。注意:輪廓樹跟輪廓是一一對(duì)應(yīng)的關(guān)系;輪廓樹不用于描述多個(gè)輪廓之間的層次關(guān)系。

??? 輪廓樹的創(chuàng)建過程:

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

結(jié)果的二分樹最終將原始輪廓的形狀性比編碼。每個(gè)節(jié)點(diǎn)被它所對(duì)應(yīng)的三角形的信息所注釋。

這樣建立的輪廓樹并不太魯棒,因?yàn)檩喞闲〉母淖円部赡軙?huì)徹底改變結(jié)果的樹,同時(shí)最初的三角形是任意選取的。為了得到較好的描述需要首先使用函數(shù)cvApproxPoly()之后將輪廓排列(運(yùn)用循環(huán)移動(dòng))成最初的三角形不怎么收到旋轉(zhuǎn)影響的狀態(tài)。
??? 可以用函數(shù)cvCreateContourTree來構(gòu)造輪廓樹。

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

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

輪廓的匹配
??? 如果要比較兩個(gè)物體,可供選擇的特征很多。如果要判斷某個(gè)人的性別,可以根據(jù)他(她)頭發(fā)的長(zhǎng)短來判斷,這很直觀,在長(zhǎng)發(fā)男稀有的年代準(zhǔn)確率也很高。也可以根據(jù)這個(gè)人尿尿的射程來判斷,如果射程大于0.50米,則是男性。總之,方法很多,不一而足。
??? 我們?cè)谏衔闹械玫搅溯喞倪@么多特征,它們也可以用于進(jìn)行匹配。典型的輪廓匹配方法有:Hu矩匹配、輪廓樹匹配、成對(duì)幾何直方圖匹配。
1.Hu矩匹配
??? 輪廓的Hu矩對(duì)包括縮放、旋轉(zhuǎn)和鏡像映射在內(nèi)的變化具有不變性。cvMatchShapes函數(shù)可以很方便的實(shí)現(xiàn)對(duì)2個(gè)輪廓間的匹配。
2.輪廓樹匹配
??? 用樹的形式比較兩個(gè)輪廓。cvMatchContourTrees函數(shù)實(shí)現(xiàn)了輪廓樹的對(duì)比。
3.成對(duì)幾何直方圖匹配
??? 在得到輪廓的成對(duì)幾何直方圖之后,可以使用直方圖對(duì)比的方法來進(jìn)行匹配。

輪廓匹配源碼1 /************************Hu矩匹配********************************************/
// IplImage* img_8uc1 = cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);
// IplImage* img_edge1 = cvCreateImage(cvGetSize(img_8uc1),8,1);
//// IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);
//
// cvThreshold(img_8uc1,img_edge1,128,255,CV_THRESH_BINARY);
//
//
// CvMemStorage* storage1 = cvCreateMemStorage();
// CvSeq* first_contour1 = NULL;
//
// int Nc = cvFindContours(
// img_edge1,
// storage1,
// &first_contour1,
// sizeof(CvContour),
// CV_RETR_LIST
// );
//
// IplImage* img_8uc12 = cvLoadImage("flower1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
// IplImage* img_edge12 = cvCreateImage(cvGetSize(img_8uc12),8,1);
//// IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);
//
// cvThreshold(img_8uc12,img_edge12,128,255,CV_THRESH_BINARY);
//
//
// CvMemStorage* storage2 = cvCreateMemStorage();
// CvSeq* first_contour2 = NULL;
//
// int Nc2 = cvFindContours(
// img_edge12,
// storage2,
// &first_contour2,
// sizeof(CvContour),
// CV_RETR_LIST
// );
//
// double n = cvMatchShapes(first_contour1,first_contour2,CV_CONTOURS_MATCH_I1,0);
//
// printf("%d",n);
//
// cvWaitKey();

/***************************輪廓樹匹配***********************************************/
// IplImage* img_8uc1 = cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);
// IplImage* img_edge1 = cvCreateImage(cvGetSize(img_8uc1),8,1);
//// IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);
//
// cvThreshold(img_8uc1,img_edge1,128,255,CV_THRESH_BINARY);
//
//
// CvMemStorage* storage1 = cvCreateMemStorage();
// CvSeq* first_contour1 = NULL;
//
// int Nc = cvFindContours(
// img_edge1,
// storage1,
// &first_contour1,
// sizeof(CvContour),
// CV_RETR_LIST
// );

// CvContourTree* tree1 = cvCreateContourTree(
// first_contour1,
// storage1,
// 200
// );
//
// IplImage* img_8uc12 = cvLoadImage("flower1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
// IplImage* img_edge12 = cvCreateImage(cvGetSize(img_8uc12),8,1);
//// IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);
//
// cvThreshold(img_8uc12,img_edge12,128,255,CV_THRESH_BINARY);
//
//
// CvMemStorage* storage2 = cvCreateMemStorage();
// CvSeq* first_contour2 = NULL;
//
// int Nc2 = cvFindContours(
// img_edge12,
// storage2,
// &first_contour2,
// sizeof(CvContour),
// CV_RETR_LIST
// );
// CvContourTree* tree2 = cvCreateContourTree(
// first_contour2,
// storage2,
// 200
// );
// double n = cvMatchContourTrees(tree1,tree1,CV_CONTOURS_MATCH_I1,200);
//
// printf("%d",n);
//
// cvWaitKey();

下面為成對(duì)幾何直方圖匹配方法

輪廓的匹配源碼 #include "gesrec.h"
#include <stdio.h>//

#define PI 3.14159f

//輪廓面積比較函數(shù)
static int gesContourCompFunc(const void* _a, const void* _b, void* userdata)
{
int retval;
double s1, s2;
CvContour* a = (CvContour*)_a;
CvContour* b = (CvContour*)_b;

s1 = fabs(cvContourArea(a));
s2 = fabs(cvContourArea(b));
//s1 = a->rect.height * a->rect.width;
//s2 = b->rect.height * b->rect.width;

if(s1 < s2)
{
retval = 1;
}
else if(s1 == s2)
{
retval = 0;
}
else
{
retval = -1;
}

return retval;
}

//src:BGR dst:
void gesFindContours(IplImage* src, IplImage* dst, CvSeq** templateContour, CvMemStorage* templateStorage, int flag)
{
int count;//輪廓數(shù)
IplImage* gray;
CvMemStorage* first_sto;
CvMemStorage* all_sto;
CvSeq* first_cont;
CvSeq* all_cont;
CvSeq* cur_cont;

//初始化動(dòng)態(tài)內(nèi)存
first_sto = cvCreateMemStorage(0);
first_cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), first_sto);
all_sto = cvCreateMemStorage(0);
all_cont = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvSeq), all_sto);

//創(chuàng)建源圖像對(duì)應(yīng)的灰度圖像
gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
cvCvtColor(src, gray, CV_BGR2GRAY);

//得到圖像的外層輪廓
count = cvFindContours(gray, first_sto, &first_cont, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

//如果沒有檢測(cè)到輪廓?jiǎng)t返回
if(first_sto == NULL)
{
return;
}

//將所有的輪廓都放到first_cont中
for(;first_cont != 0;first_cont = first_cont->h_next)
{
if(((CvContour* )first_cont)->rect.height * ((CvContour* )first_cont)->rect.width >= 625)
cvSeqPush(all_cont, first_cont);
}

//對(duì)輪廓按照面積進(jìn)行排序
cvSeqSort(all_cont, gesContourCompFunc, 0);

//在dst中畫出輪廓
cvZero(dst);
for(int i = 0;i < min(all_cont->total, 3);i++)///次數(shù)待改
{
cur_cont = (CvSeq* )cvGetSeqElem(all_cont, i);
if(flag != 0 && i == 0)
{
*templateContour = cvCloneSeq(cur_cont, templateStorage);
}

CvScalar color = CV_RGB(rand()&255, rand()&255, rand()&255);
cvDrawContours(dst, (CvSeq* )cur_cont, color, color, -1, 1, 8);
}

//判斷原點(diǎn)位置以確定是否需要反轉(zhuǎn)圖像
if(src->origin == 1)
{
cvFlip(dst);
}

//釋放內(nèi)存
cvReleaseMemStorage(&first_sto);
cvReleaseMemStorage(&all_sto);
cvReleaseImage(&gray);
}

void gesMatchContoursTemplate(IplImage* src, IplImage* dst, CvSeq** templateContour)
{
CvSeq* contour;
CvMemStorage* storage;

//初始化動(dòng)態(tài)內(nèi)存
storage = cvCreateMemStorage(0);
contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);

//得到輪廓并進(jìn)行匹配
gesFindContours(src, dst, &contour, storage, 1);
if(contour->total != 0)//如果得到的輪廓不為空
{
double result = cvMatchShapes((CvContour* )contour, (CvContour* )(*templateContour), CV_CONTOURS_MATCH_I3);
printf("%.2f\n", result);/
}

//釋放內(nèi)存
cvReleaseMemStorage(&storage);
}

//模版匹配法的完整實(shí)現(xiàn)
int gesMatchContoursTemplate2(IplImage* src, IplImage* dst, CvSeq* templateContour)
{
CvSeq* contour;
CvSeq* cur_cont;
CvMemStorage* storage;
double minValue, tempValue;
int i, minIndex;

//初始化動(dòng)態(tài)內(nèi)存
storage = cvCreateMemStorage(0);
contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);

//得到輪廓并進(jìn)行匹配
minIndex = -1;
gesFindContours(src, dst, &contour, storage, 1);
if(contour->total != 0)//如果得到的輪廓不為空
{
if(templateContour->total != 0)
{
cur_cont = (CvSeq* )cvGetSeqElem(templateContour, 0);
minValue = cvMatchShapes((CvContour* )contour, (CvContour* )cur_cont, CV_CONTOURS_MATCH_I3);
minIndex = 0;
printf("0:%.2f\n", minValue);
}

for(i = 1;i < templateContour->total;i++)
{
cur_cont = (CvSeq* )cvGetSeqElem(templateContour, i);
tempValue = cvMatchShapes((CvContour* )contour, (CvContour* )cur_cont, CV_CONTOURS_MATCH_I3);
if(tempValue < minValue)
{
minValue = tempValue;
minIndex = i;
}
printf("%d:%.2f\n", i, tempValue);
}

if(minValue >= 0.3)
{
minIndex = -1;
}
}

//打印匹配結(jié)果
printf("the result is %d\n", minIndex);

//釋放內(nèi)存
cvReleaseMemStorage(&storage);

return minIndex;
}

//找出輪廓最大的5個(gè)極大值點(diǎn)
void gesFindContourMaxs(CvSeq* contour)
{
int i;
CvScalar center;//重心位置
CvPoint* p;
CvMat max;//存儲(chǔ)5個(gè)極大值的數(shù)組
double initMax[] = {-1, -1, -1, -1, -1};//初始極大值設(shè)置為-1
double minValue, maxValue;//5個(gè)極大值中的最大值與最小值
CvPoint minLoc;//最小值的位置
double preDistance = 0;
bool isCandidate = false;//是否是候選的極大值點(diǎn)

//初始化重心位置
center = cvScalarAll(0);

//初始化極大值矩陣
max = cvMat(1, 5, CV_64FC1, initMax);

//首先求出輪廓的重心
for(i = 0;i < contour->total;i++)
{
p = (CvPoint* )cvGetSeqElem(contour, i);
center.val[0] += p->x;
center.val[1] += p->y;
}
center.val[0] /= contour->total;
center.val[1] /= contour->total;

//遍歷輪廓,找出所有的極大值點(diǎn)
for(i = 0;i < contour->total;i++)
{
p = (CvPoint* )cvGetSeqElem(contour, i);
double distance = sqrt(pow(center.val[0] - p->x, 2) + pow(center.val[1] - p->y, 2));

if(distance > preDistance)
{
isCandidate = true;
}
else if(distance < preDistance && isCandidate == true)
{
cvMinMaxLoc(&max, &minValue, &maxValue, &minLoc);

if(distance > minValue)
{
cvmSet(&max, minLoc.y, minLoc.x, distance);
}
isCandidate = false;
}
else
{
isCandidate = false;
}

preDistance = distance;
}

//打印5個(gè)極大值
printf("%.2f %.2f %.2f %.2f %.2f\n", cvmGet(&max, 0, 0), cvmGet(&max, 0, 1), cvmGet(&max, 0, 2), cvmGet(&max, 0, 3), cvmGet(&max, 0, 4));
}

//計(jì)算輪廓的pair-wise幾何直方圖
CvHistogram* gesCalcContoursPGH(CvSeq* contour)
{
CvHistogram* hist;//成對(duì)幾何直方圖
CvContour* tempCont;

//得到成對(duì)幾何直方圖第二個(gè)維度上的范圍
tempCont = (CvContour* )contour;
cvBoundingRect(tempCont, 1);

int sizes[2] = {60, 200};
float ranges[2][2] = {{0,PI}, {0,200}};
float** rangesPtr = new float* [2];
rangesPtr[0] = ranges[0];
rangesPtr[1] = ranges[1];

//初始化幾何直方圖
hist = cvCreateHist(2, sizes, CV_HIST_ARRAY, rangesPtr, 1);

//計(jì)算輪廓的成對(duì)幾何直方圖
cvCalcPGH(contour, hist);

return hist;
}

//對(duì)輪廓的pair-wise幾何直方圖進(jìn)行匹配
void gesMatchContoursPGH(CvSeq* contour, CvHistogram* templateHist)
{
CvHistogram* hist;

//得到輪廓的成對(duì)幾何直方圖
hist = gesCalcContoursPGH(contour);

//歸一化直方圖
cvNormalizeHist(templateHist, 1);
cvNormalizeHist(hist, 1);

//直方圖匹配
double result = cvCompareHist(hist, templateHist, CV_COMP_INTERSECT);
printf("result:%.2f\n", result);

//釋放內(nèi)存
cvReleaseHist(&hist);
}

?

總結(jié)

以上是生活随笔為你收集整理的OpenCV_轮廓的查找、表达、绘制、特性及匹配的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。