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

来源:互联网 发布:淘宝客活动广场靠谱吗 编辑:程序博客网 时间:2024/06/04 19:39

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

 本文转载于http://kouriba.iteye.com/blog/1627557,作者:kouriba

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()得到的图像,这时的边缘是正和负区域之间的边界。

既然在查找之前,我们需要将彩色图像转换成灰度图像,然后再将灰度图像转换成二值图像。代码如下所示:

[cpp] view plaincopyprint?
  1. 1CvSeq*contours=0;
  2. 2cvCvtColor(src,dst,CV_BGR2GRAY);//将源图像进行灰度化
  3. 3cvThreshold(dst,dst,f_thresh,255,CV_THRESH_BINARY);//二值化阈值虽然第一个参数是const,但仍可以更改dst
  4. 4cvFindContours(dst,f_storage,&contours);//查找轮廓
  5. 5cvZero(dst);
Cpp代码 复制代码
  1. 1  CvSeq *contours = 0;   
  2. 2  cvCvtColor(src,dst,CV_BGR2GRAY);//将源图像进行灰度化  
  3. 3  cvThreshold(dst,dst,f_thresh,255,CV_THRESH_BINARY);//二值化阈值 虽然第一个参数是const,但仍可以更改dst  
  4. 4  cvFindContours(dst,f_storage,&contours); //查找轮廓  
  5. 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_轮廓例子

上例中检测出输入图像的轮廓,然后逐个绘制每个轮廓。下个例子为:

在输入图像上寻找并绘制轮廓

具体代码为:

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

轮廓的特性

轮廓的特性有很多,下面一一介绍。

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.成对几何直方图匹配
在得到轮廓的成对几何直方图之后,可以使用直方图对比的方法来进行匹配。

轮廓的匹配源码

[cpp] view plaincopyprint?
  1. /************************Hu矩匹配********************************************/
  2. //IplImage*img_8uc1=cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);
  3. //IplImage*img_edge1=cvCreateImage(cvGetSize(img_8uc1),8,1);
  4. ////IplImage*img_8uc3=cvCreateImage(cvGetSize(img_8uc1),8,3);
  5. //
  6. //cvThreshold(img_8uc1,img_edge1,128,255,CV_THRESH_BINARY);
  7. //
  8. //
  9. //CvMemStorage*storage1=cvCreateMemStorage();
  10. //CvSeq*first_contour1=NULL;
  11. //
  12. //intNc=cvFindContours(
  13. //img_edge1,
  14. //storage1,
  15. //&first_contour1,
  16. //sizeof(CvContour),
  17. //CV_RETR_LIST
  18. //);
  19. //
  20. //IplImage*img_8uc12=cvLoadImage("flower1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
  21. //IplImage*img_edge12=cvCreateImage(cvGetSize(img_8uc12),8,1);
  22. ////IplImage*img_8uc3=cvCreateImage(cvGetSize(img_8uc1),8,3);
  23. //
  24. //cvThreshold(img_8uc12,img_edge12,128,255,CV_THRESH_BINARY);
  25. //
  26. //
  27. //CvMemStorage*storage2=cvCreateMemStorage();
  28. //CvSeq*first_contour2=NULL;
  29. //
  30. //intNc2=cvFindContours(
  31. //img_edge12,
  32. //storage2,
  33. //&first_contour2,
  34. //sizeof(CvContour),
  35. //CV_RETR_LIST
  36. //);
  37. //
  38. //doublen=cvMatchShapes(first_contour1,first_contour2,CV_CONTOURS_MATCH_I1,0);
  39. //
  40. //printf("%d",n);
  41. //
  42. //cvWaitKey();
  43. /***************************轮廓树匹配***********************************************/
  44. //IplImage*img_8uc1=cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);
  45. //IplImage*img_edge1=cvCreateImage(cvGetSize(img_8uc1),8,1);
  46. ////IplImage*img_8uc3=cvCreateImage(cvGetSize(img_8uc1),8,3);
  47. //
  48. //cvThreshold(img_8uc1,img_edge1,128,255,CV_THRESH_BINARY);
  49. //
  50. //
  51. //CvMemStorage*storage1=cvCreateMemStorage();
  52. //CvSeq*first_contour1=NULL;
  53. //
  54. //intNc=cvFindContours(
  55. //img_edge1,
  56. //storage1,
  57. //&first_contour1,
  58. //sizeof(CvContour),
  59. //CV_RETR_LIST
  60. //);
  61. //CvContourTree*tree1=cvCreateContourTree(
  62. //first_contour1,
  63. //storage1,
  64. //200
  65. //);
  66. //
  67. //IplImage*img_8uc12=cvLoadImage("flower1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
  68. //IplImage*img_edge12=cvCreateImage(cvGetSize(img_8uc12),8,1);
  69. ////IplImage*img_8uc3=cvCreateImage(cvGetSize(img_8uc1),8,3);
  70. //
  71. //cvThreshold(img_8uc12,img_edge12,128,255,CV_THRESH_BINARY);
  72. //
  73. //
  74. //CvMemStorage*storage2=cvCreateMemStorage();
  75. //CvSeq*first_contour2=NULL;
  76. //
  77. //intNc2=cvFindContours(
  78. //img_edge12,
  79. //storage2,
  80. //&first_contour2,
  81. //sizeof(CvContour),
  82. //CV_RETR_LIST
  83. //);
  84. //CvContourTree*tree2=cvCreateContourTree(
  85. //first_contour2,
  86. //storage2,
  87. //200
  88. //);
  89. //doublen=cvMatchContourTrees(tree1,tree1,CV_CONTOURS_MATCH_I1,200);
  90. //
  91. //printf("%d",n);
  92. //
  93. //cvWaitKey();
Cpp代码 复制代码
  1. /************************Hu矩匹配********************************************/       
  2. //    IplImage* img_8uc1 = cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);  
  3. //    IplImage* img_edge1 = cvCreateImage(cvGetSize(img_8uc1),8,1);  
  4. ////    IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);  
  5. //   
  6. //    cvThreshold(img_8uc1,img_edge1,128,255,CV_THRESH_BINARY);  
  7. //   
  8. //   
  9. //    CvMemStorage* storage1 = cvCreateMemStorage();  
  10. //    CvSeq* first_contour1 = NULL;  
  11. //   
  12. //    int Nc = cvFindContours(  
  13. //        img_edge1,  
  14. //        storage1,  
  15. //        &first_contour1,  
  16. //        sizeof(CvContour),  
  17. //        CV_RETR_LIST  
  18. //        );   
  19. //   
  20. //    IplImage* img_8uc12 = cvLoadImage("flower1.jpg",CV_LOAD_IMAGE_GRAYSCALE);  
  21. //    IplImage* img_edge12 = cvCreateImage(cvGetSize(img_8uc12),8,1);  
  22. ////    IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);  
  23. //   
  24. //    cvThreshold(img_8uc12,img_edge12,128,255,CV_THRESH_BINARY);  
  25. //   
  26. //   
  27. //    CvMemStorage* storage2 = cvCreateMemStorage();  
  28. //    CvSeq* first_contour2 = NULL;  
  29. //   
  30. //    int Nc2 = cvFindContours(  
  31. //        img_edge12,  
  32. //        storage2,  
  33. //        &first_contour2,  
  34. //        sizeof(CvContour),  
  35. //        CV_RETR_LIST  
  36. //        );   
  37. //   
  38. //    double n = cvMatchShapes(first_contour1,first_contour2,CV_CONTOURS_MATCH_I1,0);  
  39. //   
  40. //    printf("%d",n);  
  41. //   
  42. //    cvWaitKey();  
  43.   
  44. /***************************轮廓树匹配***********************************************/  
  45.     //    IplImage* img_8uc1 = cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);  
  46.     //    IplImage* img_edge1 = cvCreateImage(cvGetSize(img_8uc1),8,1);  
  47.     ////    IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);  
  48.     //   
  49.     //    cvThreshold(img_8uc1,img_edge1,128,255,CV_THRESH_BINARY);  
  50.     //   
  51.     //   
  52.     //    CvMemStorage* storage1 = cvCreateMemStorage();  
  53.     //    CvSeq* first_contour1 = NULL;  
  54.     //   
  55.     //    int Nc = cvFindContours(  
  56.     //        img_edge1,  
  57.     //        storage1,  
  58.     //        &first_contour1,  
  59.     //        sizeof(CvContour),  
  60.     //        CV_RETR_LIST  
  61.     //        );  
  62.   
  63.     //    CvContourTree* tree1 = cvCreateContourTree(  
  64.     //        first_contour1,  
  65.     //        storage1,  
  66.     //        200  
  67.     //        );  
  68.     //   
  69.     //    IplImage* img_8uc12 = cvLoadImage("flower1.jpg",CV_LOAD_IMAGE_GRAYSCALE);  
  70.     //    IplImage* img_edge12 = cvCreateImage(cvGetSize(img_8uc12),8,1);  
  71.     ////    IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);  
  72.     //   
  73.     //    cvThreshold(img_8uc12,img_edge12,128,255,CV_THRESH_BINARY);  
  74.     //   
  75.     //   
  76.     //    CvMemStorage* storage2 = cvCreateMemStorage();  
  77.     //    CvSeq* first_contour2 = NULL;  
  78.     //   
  79.     //    int Nc2 = cvFindContours(  
  80.     //        img_edge12,  
  81.     //        storage2,  
  82.     //        &first_contour2,  
  83.     //        sizeof(CvContour),  
  84.     //        CV_RETR_LIST  
  85.     //        );  
  86.     //    CvContourTree* tree2 = cvCreateContourTree(  
  87.     //        first_contour2,  
  88.     //        storage2,  
  89.     //        200  
  90.     //        );  
  91.     //    double n = cvMatchContourTrees(tree1,tree1,CV_CONTOURS_MATCH_I1,200);  
  92.     //   
  93.     //    printf("%d",n);  
  94.     //   
  95.     //    cvWaitKey();  


下面为成对几何直方图匹配方法

[cpp] view plaincopyprint?
  1. #include"gesrec.h"
  2. #include<stdio.h>//////////////////////////////////////////
  3. #definePI3.14159f
  4. //轮廓面积比较函数
  5. staticintgesContourCompFunc(constvoid*_a,constvoid*_b,void*userdata)
  6. {
  7. intretval;
  8. doubles1,s2;
  9. CvContour*a=(CvContour*)_a;
  10. CvContour*b=(CvContour*)_b;
  11. s1=fabs(cvContourArea(a));
  12. s2=fabs(cvContourArea(b));
  13. //s1=a->rect.height*a->rect.width;
  14. //s2=b->rect.height*b->rect.width;
  15. if(s1<s2)
  16. {
  17. retval=1;
  18. }
  19. elseif(s1==s2)
  20. {
  21. retval=0;
  22. }
  23. else
  24. {
  25. retval=-1;
  26. }
  27. returnretval;
  28. }
  29. //src:BGRdst:
  30. voidgesFindContours(IplImage*src,IplImage*dst,CvSeq**templateContour,CvMemStorage*templateStorage,intflag)
  31. {
  32. intcount;//轮廓数
  33. IplImage*gray;
  34. CvMemStorage*first_sto;
  35. CvMemStorage*all_sto;
  36. CvSeq*first_cont;
  37. CvSeq*all_cont;
  38. CvSeq*cur_cont;
  39. //初始化动态内存
  40. first_sto=cvCreateMemStorage(0);
  41. first_cont=cvCreateSeq(CV_SEQ_ELTYPE_POINT,sizeof(CvSeq),sizeof(CvPoint),first_sto);
  42. all_sto=cvCreateMemStorage(0);
  43. all_cont=cvCreateSeq(0,sizeof(CvSeq),sizeof(CvSeq),all_sto);
  44. //创建源图像对应的灰度图像
  45. gray=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
  46. cvCvtColor(src,gray,CV_BGR2GRAY);
  47. //得到图像的外层轮廓
  48. count=cvFindContours(gray,first_sto,&first_cont,sizeof(CvContour),CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
  49. //如果没有检测到轮廓则返回
  50. if(first_sto==NULL)
  51. {
  52. return;
  53. }
  54. //将所有的轮廓都放到first_cont中
  55. for(;first_cont!=0;first_cont=first_cont->h_next)
  56. {
  57. if(((CvContour*)first_cont)->rect.height*((CvContour*)first_cont)->rect.width>=625)
  58. cvSeqPush(all_cont,first_cont);
  59. }
  60. //对轮廓按照面积进行排序
  61. cvSeqSort(all_cont,gesContourCompFunc,0);
  62. //在dst中画出轮廓
  63. cvZero(dst);
  64. for(inti=0;i<min(all_cont->total,3);i++)///////////////////////次数待改
  65. {
  66. cur_cont=(CvSeq*)cvGetSeqElem(all_cont,i);
  67. if(flag!=0&&i==0)
  68. {
  69. *templateContour=cvCloneSeq(cur_cont,templateStorage);
  70. }
  71. CvScalarcolor=CV_RGB(rand()&255,rand()&255,rand()&255);
  72. cvDrawContours(dst,(CvSeq*)cur_cont,color,color,-1,1,8);
  73. }
  74. //判断原点位置以确定是否需要反转图像
  75. if(src->origin==1)
  76. {
  77. cvFlip(dst);
  78. }
  79. //释放内存
  80. cvReleaseMemStorage(&first_sto);
  81. cvReleaseMemStorage(&all_sto);
  82. cvReleaseImage(&gray);
  83. }
  84. voidgesMatchContoursTemplate(IplImage*src,IplImage*dst,CvSeq**templateContour)
  85. {
  86. CvSeq*contour;
  87. CvMemStorage*storage;
  88. //初始化动态内存
  89. storage=cvCreateMemStorage(0);
  90. contour=cvCreateSeq(CV_SEQ_ELTYPE_POINT,sizeof(CvSeq),sizeof(CvPoint),storage);
  91. //得到轮廓并进行匹配
  92. gesFindContours(src,dst,&contour,storage,1);
  93. if(contour->total!=0)//如果得到的轮廓不为空
  94. {
  95. doubleresult=cvMatchShapes((CvContour*)contour,(CvContour*)(*templateContour),CV_CONTOURS_MATCH_I3);
  96. printf("%.2f\n",result);/////////////////////////////////////////////
  97. }
  98. //释放内存
  99. cvReleaseMemStorage(&storage);
  100. }
  101. //模版匹配法的完整实现
  102. intgesMatchContoursTemplate2(IplImage*src,IplImage*dst,CvSeq*templateContour)
  103. {
  104. CvSeq*contour;
  105. CvSeq*cur_cont;
  106. CvMemStorage*storage;
  107. doubleminValue,tempValue;
  108. inti,minIndex;
  109. //初始化动态内存
  110. storage=cvCreateMemStorage(0);
  111. contour=cvCreateSeq(CV_SEQ_ELTYPE_POINT,sizeof(CvSeq),sizeof(CvPoint),storage);
  112. //得到轮廓并进行匹配
  113. minIndex=-1;
  114. gesFindContours(src,dst,&contour,storage,1);
  115. if(contour->total!=0)//如果得到的轮廓不为空
  116. {
  117. if(templateContour->total!=0)
  118. {
  119. cur_cont=(CvSeq*)cvGetSeqElem(templateContour,0);
  120. minValue=cvMatchShapes((CvContour*)contour,(CvContour*)cur_cont,CV_CONTOURS_MATCH_I3);
  121. minIndex=0;
  122. printf("0:%.2f\n",minValue);
  123. }
  124. for(i=1;i<templateContour->total;i++)
  125. {
  126. cur_cont=(CvSeq*)cvGetSeqElem(templateContour,i);
  127. tempValue=cvMatchShapes((CvContour*)contour,(CvContour*)cur_cont,CV_CONTOURS_MATCH_I3);
  128. if(tempValue<minValue)
  129. {
  130. minValue=tempValue;
  131. minIndex=i;
  132. }
  133. printf("%d:%.2f\n",i,tempValue);
  134. }
  135. if(minValue>=0.3)
  136. {
  137. minIndex=-1;
  138. }
  139. }
  140. //打印匹配结果
  141. printf("theresultis%d\n",minIndex);
  142. //释放内存
  143. cvReleaseMemStorage(&storage);
  144. returnminIndex;
  145. }
  146. //找出轮廓最大的5个极大值点
  147. voidgesFindContourMaxs(CvSeq*contour)
  148. {
  149. inti;
  150. CvScalarcenter;//重心位置
  151. CvPoint*p;
  152. CvMatmax;//存储5个极大值的数组
  153. doubleinitMax[]={-1,-1,-1,-1,-1};//初始极大值设置为-1
  154. doubleminValue,maxValue;//5个极大值中的最大值与最小值
  155. CvPointminLoc;//最小值的位置
  156. doublepreDistance=0;
  157. boolisCandidate=false;//是否是候选的极大值点
  158. //初始化重心位置
  159. center=cvScalarAll(0);
  160. //初始化极大值矩阵
  161. max=cvMat(1,5,CV_64FC1,initMax);
  162. //首先求出轮廓的重心
  163. for(i=0;i<contour->total;i++)
  164. {
  165. p=(CvPoint*)cvGetSeqElem(contour,i);
  166. center.val[0]+=p->x;
  167. center.val[1]+=p->y;
  168. }
  169. center.val[0]/=contour->total;
  170. center.val[1]/=contour->total;
  171. //遍历轮廓,找出所有的极大值点
  172. for(i=0;i<contour->total;i++)
  173. {
  174. p=(CvPoint*)cvGetSeqElem(contour,i);
  175. doubledistance=sqrt(pow(center.val[0]-p->x,2)+pow(center.val[1]-p->y,2));
  176. if(distance>preDistance)
  177. {
  178. isCandidate=true;
  179. }
  180. elseif(distance<preDistance&&isCandidate==true)
  181. {
  182. cvMinMaxLoc(&max,&minValue,&maxValue,&minLoc);
  183. if(distance>minValue)
  184. {
  185. cvmSet(&max,minLoc.y,minLoc.x,distance);
  186. }
  187. isCandidate=false;
  188. }
  189. else
  190. {
  191. isCandidate=false;
  192. }
  193. preDistance=distance;
  194. }
  195. //打印5个极大值
  196. 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));
  197. }
  198. //计算轮廓的pair-wise几何直方图
  199. CvHistogram*gesCalcContoursPGH(CvSeq*contour)
  200. {
  201. CvHistogram*hist;//成对几何直方图
  202. CvContour*tempCont;
  203. //得到成对几何直方图第二个维度上的范围
  204. tempCont=(CvContour*)contour;
  205. cvBoundingRect(tempCont,1);
  206. intsizes[2]={60,200};
  207. floatranges[2][2]={{0,PI},{0,200}};
  208. float**rangesPtr=newfloat*[2];
  209. rangesPtr[0]=ranges[0];
  210. rangesPtr[1]=ranges[1];
  211. //初始化几何直方图
  212. hist=cvCreateHist(2,sizes,CV_HIST_ARRAY,rangesPtr,1);
  213. //计算轮廓的成对几何直方图
  214. cvCalcPGH(contour,hist);
  215. returnhist;
  216. }
  217. //对轮廓的pair-wise几何直方图进行匹配
  218. voidgesMatchContoursPGH(CvSeq*contour,CvHistogram*templateHist)
  219. {
  220. CvHistogram*hist;
  221. //得到轮廓的成对几何直方图
  222. hist=gesCalcContoursPGH(contour);
  223. //归一化直方图
  224. cvNormalizeHist(templateHist,1);
  225. cvNormalizeHist(hist,1);
  226. //直方图匹配
  227. doubleresult=cvCompareHist(hist,templateHist,CV_COMP_INTERSECT);
  228. printf("result:%.2f\n",result);
  229. //释放内存
  230. cvReleaseHist(&hist);
  231. }
0 0
原创粉丝点击