求助cvSeqsort 排序轮廓按面积排序

opencv轮廓高级应用(轮廓匹配,几何直方图)
OpenCv轮廓高级应用(轮廓匹配,几何直方图)
最近再次用到了opencv轮廓,在这里结合作者冰山一角的博客()以及自己的体会在此稍加说明。其程序主要参见冰山一角的Blog,遗憾的是代码是OpenCV1.0写的,等有时间再用2.4.2改写一篇。
对于轮廓的相关数据结构表示和几本操作(查找轮廓,画轮廓),可参见前面两片关于轮廓的例程,在这里不多讲。
对于查找轮廓我们一般要对图像Canny检测。但是对于很特殊的场合其实我们还可以直接对二值化的图像进行轮廓的提取,找出的轮廓其实就是Blob(这个可能就是为什么OpenCV高版本里面把blob分析抛弃的原因吧,我猜的话),画上外截矩形就是一个ROI,是不是觉得很有用?下面介绍罗阔的高级应用。
&& 轮廓的特性:
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阶几何矩(标准矩)定义为:
,q = 0,1,2……&
p+q阶中心距定义为:
= 0,1,2……
其中和代表图像的重心,
对于离散的数字图像,采用求和号代替积分:
= 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
CCH)的一个扩展或者延伸。CCH是一种直方图,用来统计一个轮廓的Freeman链码编码每一种走法的数字。这种直方图的一个优良性质为当物体旋转45度,那么新直方图是老直方图的循环平移。这样就可以不受旋转影响。
(1)轮廓保存的是一系列的顶点,轮廓是由一系列线段组成的多边形。对于看起来光滑的轮廓(例如圆),只是线段条数比较多,线段长度比较短而已。实际上,电脑中显示的任何曲线都由线段组成。
(2)每两条线段之间都有一定的关系,包括它们(或者它们的延长线)之间的夹角,两条线段的夹角范围是:(0,180)。
(3)每两条线段上的点之间还有距离关系,包括最短(小)距离、最远(大)距离,以及平均距离。最大距离我用了一个偷懒的计算方法,我把轮廓外界矩形的对角线长度看作了最大距离。
(4)成对几何直方图所用的统计数据包括了夹角和距离。
轮廓的匹配
如果要比较两个物体,可供选择的特征很多。如果要判断某个人的性别,可以根据他(她)头发的长短来判断,这很直观,在长发男稀有的年代准确率也很高。也可以根据这个人尿尿的射程来判断,如果射程大于0.50米,则是男性。总之,方法很多,不一而足。
我们在上文中得到了轮廓的这么多特征,它们也可以用于进行匹配。典型的轮廓匹配方法有:Hu矩匹配、轮廓树匹配、成对几何直方图匹配。
1.Hu矩匹配
轮廓的Hu矩对包括缩放、旋转和镜像映射在内的变化具有不变性。cvMatchShapes函数可以很方便的实现对2个轮廓间的匹配。
2.轮廓树匹配
用树的形式比较两个轮廓。cvMatchContourTrees函数实现了轮廓树的对比。
3.成对几何直方图匹配&&&
在得到轮廓的成对几何直方图之后,可以使用直方图对比的方法来进行匹。
轮廓匹配源码1:
轮廓匹配源码1
&&& 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,
&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,
&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 =
&& int Nc = cvFindContours(
img_edge1,
&first_contour1,
sizeof(CvContour),
CV_RETR_LIST
CvContourTree* tree1 = cvCreateContourTree(
first_contour1,
&& storage1,
&&& 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,
&first_contour2,
sizeof(CvContour),
CV_RETR_LIST
CvContourTree* tree2 = cvCreateContourTree(
first_contour2,
&&& double n =
cvMatchContourTrees(tree1,tree1,CV_CONTOURS_MATCH_I1,200);
printf("%d",n);
&&&&cvWaitKey();
几何直方图匹配方:
#include "gesrec.h"
#include &stdio.h&//////////////////////////////////////////
#define PI 3.14159f
//轮廓面积比较函数static int gesContourCompFunc(const void* _a, const void* _b, void* userdata)
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.
//s2 = b-&rect.height * b-&rect.
if(s1 & s2)
retval = <span STYLE="CoLor: #;
else if(s1 == s2)
retval = <span STYLE="CoLor: #;
retval = -<span STYLE="CoLor: #;
//src:BGR dst:void gesFindContours(IplImage* src, IplImage* dst, CvSeq** templateContour, CvMemStorage* templateStorage, int flag)
int//轮廓数
CvMemStorage* first_
CvMemStorage* all_
CvSeq* first_
CvSeq* all_
CvSeq* cur_
//初始化动态内存
first_sto = cvCreateMemStorage(<span STYLE="CoLor: #);
first_cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), first_sto);
all_sto = cvCreateMemStorage(<span STYLE="CoLor: #);
all_cont = cvCreateSeq(<span STYLE="CoLor: #, sizeof(CvSeq), sizeof(CvSeq), all_sto);
//创建源图像对应的灰度图像
gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, <span STYLE="CoLor: #);
cvCvtColor(src, gray, CV_BGR2GRAY);
//得到图像的外层轮廓
count = cvFindContours(gray, first_sto, &first_cont, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
//如果没有检测到轮廓则返回
if(first_sto == NULL)
//将所有的轮廓都放到first_cont中
for(;first_cont != <span STYLE="CoLor: #;first_cont = first_cont-&h_next)
if(((CvContour* )first_cont)-&rect.height * ((CvContour* )first_cont)-&rect.width &= <span STYLE="CoLor: #5)
cvSeqPush(all_cont, first_cont);
//对轮廓按照面积进行排序
cvSeqSort(all_cont, gesContourCompFunc, <span STYLE="CoLor: #);
//在dst中画出轮廓
cvZero(dst);
for(int i = <span STYLE="CoLor: #;i & min(all_cont-&total, <span STYLE="CoLor: #);i++)///////////////////////次数待改
cur_cont = (CvSeq* )cvGetSeqElem(all_cont, i);
if(flag != <span STYLE="CoLor: # && i == <span STYLE="CoLor: #)
*templateContour = cvCloneSeq(cur_cont, templateStorage);
CvScalar color = CV_RGB(rand()&<span STYLE="CoLor: #5, rand()&<span STYLE="CoLor: #5, rand()&<span STYLE="CoLor: #5);
cvDrawContours(dst, (CvSeq* )cur_cont, color, color, -<span STYLE="CoLor: #, <span STYLE="CoLor: #, <span STYLE="CoLor: #);
//判断原点位置以确定是否需要反转图像
if(src-&origin == <span STYLE="CoLor: #)
cvFlip(dst);
//释放内存
cvReleaseMemStorage(&first_sto);
cvReleaseMemStorage(&all_sto);
cvReleaseImage(&gray);
void gesMatchContoursTemplate(IplImage* src, IplImage* dst, CvSeq** templateContour)
CvMemStorage*
//初始化动态内存
storage = cvCreateMemStorage(<span STYLE="CoLor: #);
contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
//得到轮廓并进行匹配
gesFindContours(src, dst, &contour, storage, <span STYLE="CoLor: #);
if(contour-&total != <span STYLE="CoLor: #)//如果得到的轮廓不为空
double result = cvMatchShapes((CvContour* )contour, (CvContour* )(*templateContour), CV_CONTOURS_MATCH_I3);
printf("%.2f\n", result);/////////////////////////////////////////////
//释放内存
cvReleaseMemStorage(&storage);
//模版匹配法的完整实现int gesMatchContoursTemplate2(IplImage* src, IplImage* dst, CvSeq* templateContour)
CvSeq* cur_
CvMemStorage*
double minValue, tempV
int i, minI
//初始化动态内存
storage = cvCreateMemStorage(<span STYLE="CoLor: #);
contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
//得到轮廓并进行匹配
minIndex = -<span STYLE="CoLor: #;
gesFindContours(src, dst, &contour, storage, <span STYLE="CoLor: #);
if(contour-&total != <span STYLE="CoLor: #)//如果得到的轮廓不为空
if(templateContour-&total != <span STYLE="CoLor: #)
cur_cont = (CvSeq* )cvGetSeqElem(templateContour, <span STYLE="CoLor: #);
minValue = cvMatchShapes((CvContour* )contour, (CvContour* )cur_cont, CV_CONTOURS_MATCH_I3);
minIndex = <span STYLE="CoLor: #;
printf("<span STYLE="CoLor: #:%.2f\n", minValue);
for(i = <span STYLE="CoLor: #;i & templateContour-&i++)
cur_cont = (CvSeq* )cvGetSeqElem(templateContour, i);
tempValue = cvMatchShapes((CvContour* )contour, (CvContour* )cur_cont, CV_CONTOURS_MATCH_I3);
if(tempValue & minValue)
minValue = tempV
minIndex =
printf("%d:%.2f\n", i, tempValue);
if(minValue &= <span STYLE="CoLor: #.3)
minIndex = -<span STYLE="CoLor: #;
//打印匹配结果
printf("the result is %d\n", minIndex);
//释放内存
cvReleaseMemStorage(&storage);
return minI
//找出轮廓最大的5个极大值点void gesFindContourMaxs(CvSeq* contour)
CvS//重心位置
CvM//存储5个极大值的数组
double initMax[] = {-<span STYLE="CoLor: #, -<span STYLE="CoLor: #, -<span STYLE="CoLor: #, -<span STYLE="CoLor: #, -<span STYLE="CoLor: #};//初始极大值设置为-1
double minValue, maxV//<span STYLE="CoLor: #个极大值中的最大值与最小值
CvPoint minL//最小值的位置
double preDistance = <span STYLE="CoLor: #;
bool isCandidate = false;//是否是候选的极大值点
//初始化重心位置
center = cvScalarAll(<span STYLE="CoLor: #);
//初始化极大值矩阵
max = cvMat(<span STYLE="CoLor: #, <span STYLE="CoLor: #, CV_64FC1, initMax);
//首先求出轮廓的重心
for(i = <span STYLE="CoLor: #;i & contour-&i++)
p = (CvPoint* )cvGetSeqElem(contour, i);
center.val[<span STYLE="CoLor: #] += p-&x;
center.val[<span STYLE="CoLor: #] += p-&y;
center.val[<span STYLE="CoLor: #] /= contour-&
center.val[<span STYLE="CoLor: #] /= contour-&
//遍历轮廓,找出所有的极大值点
for(i = <span STYLE="CoLor: #;i & contour-&i++)
p = (CvPoint* )cvGetSeqElem(contour, i);
double distance = sqrt(pow(center.val[<span STYLE="CoLor: #] - p-&x, <span STYLE="CoLor: #) + pow(center.val[<span STYLE="CoLor: #] - p-&y, <span STYLE="CoLor: #));
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;
isCandidate = false;
preDistance =
//打印5个极大值
printf("%.2f %.2f %.2f %.2f %.2f\n", cvmGet(&max, <span STYLE="CoLor: #, <span STYLE="CoLor: #), cvmGet(&max, <span STYLE="CoLor: #, <span STYLE="CoLor: #), cvmGet(&max, <span STYLE="CoLor: #, <span STYLE="CoLor: #), cvmGet(&max, <span STYLE="CoLor: #, <span STYLE="CoLor: #), cvmGet(&max, <span STYLE="CoLor: #, <span STYLE="CoLor: #));
//计算轮廓的pair-wise几何直方图CvHistogram* gesCalcContoursPGH(CvSeq* contour)
CvHistogram*//成对几何直方图
CvContour* tempC
//得到成对几何直方图第二个维度上的范围
tempCont = (CvContour* )
cvBoundingRect(tempCont, <span STYLE="CoLor: #);
int sizes[<span STYLE="CoLor: #] = {<span STYLE="CoLor: #, <span STYLE="CoLor: #0};
float ranges[<span STYLE="CoLor: #][<span STYLE="CoLor: #] = {{<span STYLE="CoLor: #,PI}, {<span STYLE="CoLor: #,<span STYLE="CoLor: #0}};
float** rangesPtr = new float* [<span STYLE="CoLor: #];
rangesPtr[<span STYLE="CoLor: #] = ranges[<span STYLE="CoLor: #];
rangesPtr[<span STYLE="CoLor: #] = ranges[<span STYLE="CoLor: #];
//初始化几何直方图
hist = cvCreateHist(<span STYLE="CoLor: #, sizes, CV_HIST_ARRAY, rangesPtr, <span STYLE="CoLor: #);
//计算轮廓的成对几何直方图
cvCalcPGH(contour, hist);
//对轮廓的pair-wise几何直方图进行匹配void gesMatchContoursPGH(CvSeq* contour, CvHistogram* templateHist)
CvHistogram*
//得到轮廓的成对几何直方图
hist = gesCalcContoursPGH(contour);
//归一化直方图
cvNormalizeHist(templateHist, <span STYLE="CoLor: #);
cvNormalizeHist(hist, <span STYLE="CoLor: #);
//直方图匹配
double result = cvCompareHist(hist, templateHist, CV_COMP_INTERSECT);
printf("result:%.2f\n", result);
//释放内存
cvReleaseHist(&hist);
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。OpenCV_轮廓的查找、表达、绘制、特性及匹配
我的图书馆
OpenCV_轮廓的查找、表达、绘制、特性及匹配
虽然Canny之类的边缘检测算法可以根据像素间的差异检测出轮廓边界的像素,但是它并没有将轮廓作为一个整体。下一步是要将这些边缘像素组装成轮廓。
轮廓是构成任何一个形状的边界或外形线。直方图对比和模板匹配根据色彩及色彩的分布来进行匹配,以下包括:轮廓的查找、表达方式、组织方式、绘制、特性、匹配。
首先回忆下几个结构体:
首先是图像本身的结构体:typedef struct CvMat{ /* CvMat 标识
(CV_MAT_MAGIC_VAL), 元素类型和标记 */ /* 以字节为单位的行数据长度*/int*
/* 数据引用计数 */union{uchar*short*int*float*
double*}union{int
};union{int
};这个结构体是最基础的矩阵,而图像本身就是一个复杂的矩阵,所以图像是对这个结构体的继承:typedef struct
_IplImage{int nS /* IplImage大小 */int ID; /* 版本 (=0)*/int
nC /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */int alphaC /* 被OpenCV忽略
*/ /* 像素的位深度: 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 dataO /* 0 - 交叉存取颜色通道, 1 - 分开的颜色通道.cvCreateImage只能创建交叉存取图像
*/ /* 0 - 顶—左结构,1 - 底—左结构 (Windows bitmaps 风格) */
/* 图像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */ /* 图像宽像素数
*/ /* 图像高像素数*/struct _IplROI */* 图像感兴趣区域. 当该值非空只对该区域进行处理
*/struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */void *imageId; /*
同上*/struct _IplTileInfo *tileI /*同上*/int imageS /*
图像数据大小(在交叉存取格式下imageSize=image-&height*image-&widthStep),单位字节*/char
*imageD /* 指向排列的图像数据 */int widthS /* 排列的图像行大小,以字节为单位 */int
BorderMode[4]; /* 边际结束模式, 被OpenCV忽略 */int BorderConst[4]; /* 同上 */char
*imageDataO /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的
*/}IplI值得注意的地方:首先是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{CvArr*float
thresh[CV_MAX_DIM][2]; /* 对于标准直方图,bins的值有左边界+右边界=2 */float** thresh2; /*
对于非标准直方图 */CvMatND /* embedded matrix header for array histograms
因此,由于直方图的复杂性,得到一个图片的直方图的步骤就不是一个函数完成的: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()得到的图像,这时的边缘是正和负区域之间的边界。
既然在查找之前,我们需要将彩色图像转换成灰度图像,然后再将灰度图像转换成二值图像。代码如下所示:
<span style="COLOR: #
CvSeq *contours = <span style="COLOR: #;<span style="COLOR: #
cvCvtColor(src,dst,CV_BGR2GRAY);//将源图像进行灰度化<span style="COLOR: #
cvThreshold(dst,dst,f_thresh,<span style="COLOR: #5,CV_THRESH_BINARY);//二值化阈值 虽然第一个参数是const,但仍可以更改dst<span style="COLOR: #
cvFindContours(dst,f_storage,&contours); //查找轮廓<span style="COLOR: #
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() / /* micsellaneous flags */
/int header_ /* size of sequence header */ /struct CvSeq* h_
/* previous sequence */ /struct CvSeq* h_ /* next sequence */
/struct CvSeq* v_ /* 2nd previous sequence */ /struct CvSeq*
v_ /* 2nd next sequence */ / /* total number of elements */
/int elem_/* size of sequence element in bytes */ /char*
block_/* maximal bound of the last block */ /char* /* current write
pointer */ /int delta_ /* how many elements allocated when the
sequence grows (sequence granularity) */ /CvMemStorage* /* where
the seq is stored */ /CvSeqBlock* free_ /* free blocks list */
/CvSeqBlock* /* pointer to the first sequence block */typedef
struct CvSeq{CV_SEQUENCE_FIELDS()}
CvS相关操作就不重复列出(排序,查找,逆序,拆分,复制,读取,写入切片的复制,移除,插入,),可以查找相关文档。
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)树型结构。&&&
从纵向上来看,列表只有一层,双层结构有一或者两层,树型结构可能有一层或者多层。&&& 如果要遍历所有的轮廓,可以使用递归的方式。
轮廓的绘制&&&
轮廓的绘制比较简单,用上面提到的方法取得轮廓的所有点,然后把这些点连接成一个多边形即可。
轮廓的一个例子为:
上例中检测出输入图像的轮廓,然后逐个绘制每个轮廓。下个例子为:
在输入图像上寻找并绘制轮廓
具体代码为:
1 #include "stdafx.h" 2 #include &iostream& 3 using namespace 4
6 #ifdef _CH_ 7 #pragma package &opencv& 8 #endif 9 <span style="COLOR: # #include "cv.h"<span style="COLOR: # #include "highgui.h"<span style="COLOR: # <span style="COLOR: # using namespace<span style="COLOR: # <span style="COLOR: # int _tmain(int argc, _TCHAR* argv[])<span style="COLOR: # {<span style="COLOR: #
cvNamedWindow("flower",<span style="COLOR: #);<span style="COLOR: # <span style="COLOR: #
IplImage* img_8uc1 = cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);<span style="COLOR: #
IplImage* img_edge = cvCreateImage(cvGetSize(img_8uc1),<span style="COLOR: #,<span style="COLOR: #);<span style="COLOR: #
IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),<span style="COLOR: #,<span style="COLOR: #);<span style="COLOR: # <span style="COLOR: #
cvThreshold(img_8uc1,img_edge,<span style="COLOR: #8,<span style="COLOR: #5,CV_THRESH_BINARY);<span style="COLOR: # <span style="COLOR: #
CvMemStorage* storage = cvCreateMemStorage();<span style="COLOR: #
CvSeq* first_contour = NULL;<span style="COLOR: # <span style="COLOR: #
int Nc = cvFindContours(<span style="COLOR: #
img_edge,<span style="COLOR: #
storage,<span style="COLOR: #
&first_contour,<span style="COLOR: #
sizeof(CvContour),<span style="COLOR: #
CV_RETR_LIST<span style="COLOR: #
);<span style="COLOR: # <span style="COLOR: #
int n=<span style="COLOR: #;<span style="COLOR: #
printf("Total contours detected %d \n",Nc);<span style="COLOR: # <span style="COLOR: #
for (CvSeq* c=first_c!=NULL;c=c-&h_next)<span style="COLOR: #
{<span style="COLOR: #
cvCvtColor(img_8uc1,img_8uc3,CV_GRAY2BGR);<span style="COLOR: # <span style="COLOR: #
cvDrawContours(<span style="COLOR: #
img_8uc3,<span style="COLOR: #
c,<span style="COLOR: #
CV_RGB(<span style="COLOR: #,<span style="COLOR: #,<span style="COLOR: #5),<span style="COLOR: #
CV_RGB(<span style="COLOR: #,<span style="COLOR: #5,<span style="COLOR: #),<span style="COLOR: #
<span style="COLOR: #,<span style="COLOR: #
<span style="COLOR: #,<span style="COLOR: #
<span style="COLOR: #<span style="COLOR: #
);<span style="COLOR: # <span style="COLOR: #
printf("Contour # %d\n",n);<span style="COLOR: #
cvShowImage("flower",img_8uc3);<span style="COLOR: #
printf("%d elements:\n",c-&total);<span style="COLOR: # <span style="COLOR: #
for (int i=<span style="COLOR: #;i&c-&++i)<span style="COLOR: #
{<span style="COLOR: #
CvPoint* p = CV_GET_SEQ_ELEM(CvPoint,c,i);<span style="COLOR: #
printf("(%d,%d)\n",p-&x,p-&y);<span style="COLOR: #
}<span style="COLOR: #
cvWaitKey(<span style="COLOR: #);<span style="COLOR: #
n++;<span style="COLOR: #
}<span style="COLOR: # <span style="COLOR: #
printf("Finished all contours.\n");<span style="COLOR: # <span style="COLOR: #
cvCvtColor(img_8uc1,img_8uc3,CV_GRAY2BGR);<span style="COLOR: #
cvShowImage("flower",img_8uc3);<span style="COLOR: # <span style="COLOR: #
cvWaitKey(<span style="COLOR: #);<span style="COLOR: # <span style="COLOR: #
cvDestroyWindow("flower");<span style="COLOR: #
cvReleaseImage(&img_8uc3);<span style="COLOR: #
cvReleaseImage(&img_8uc1);<span style="COLOR: #
cvReleaseImage(&img_edge);<span style="COLOR: #
return <span style="COLOR: #;<span style="COLOR: # }
轮廓的特性&&& 轮廓的特性有很多,下面一一介绍。
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阶几何矩(标准矩)定义为:
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.成对几何直方图匹配&&&
在得到轮廓的成对几何直方图之后,可以使用直方图对比的方法来进行匹配。
轮廓匹配源码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,
&first_contour1,
sizeof(CvContour),
CV_RETR_LIST
CvContourTree* tree1 = cvCreateContourTree(
first_contour1,
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,
&first_contour2,
sizeof(CvContour),
CV_RETR_LIST
CvContourTree* tree2 = cvCreateContourTree(
first_contour2,
double n = cvMatchContourTrees(tree1,tree1,CV_CONTOURS_MATCH_I1,200);
printf("%d",n);
cvWaitKey();
下面为成对几何直方图匹配方法
轮廓的匹配源码
#include "gesrec.h"#include &stdio.h&//////////////////////////////////////////#define PI 3.14159f//轮廓面积比较函数static int gesContourCompFunc(const void* _a, const void* _b, void* userdata){
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.
//s2 = b-&rect.height * b-&rect.
if(s1 & s2)
retval = <span style="COLOR: #;
else if(s1 == s2)
retval = <span style="COLOR: #;
retval = -<span style="COLOR: #;
return}//src:BGR dst:void gesFindContours(IplImage* src, IplImage* dst, CvSeq** templateContour, CvMemStorage* templateStorage, int flag){
int//轮廓数
CvMemStorage* first_
CvMemStorage* all_
CvSeq* first_
CvSeq* all_
CvSeq* cur_
//初始化动态内存
first_sto = cvCreateMemStorage(<span style="COLOR: #);
first_cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), first_sto);
all_sto = cvCreateMemStorage(<span style="COLOR: #);
all_cont = cvCreateSeq(<span style="COLOR: #, sizeof(CvSeq), sizeof(CvSeq), all_sto);
//创建源图像对应的灰度图像
gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, <span style="COLOR: #);
cvCvtColor(src, gray, CV_BGR2GRAY);
//得到图像的外层轮廓
count = cvFindContours(gray, first_sto, &first_cont, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
//如果没有检测到轮廓则返回
if(first_sto == NULL)
//将所有的轮廓都放到first_cont中
for(;first_cont != <span style="COLOR: #;first_cont = first_cont-&h_next)
if(((CvContour* )first_cont)-&rect.height * ((CvContour* )first_cont)-&rect.width &= <span style="COLOR: #5)
cvSeqPush(all_cont, first_cont);
//对轮廓按照面积进行排序
cvSeqSort(all_cont, gesContourCompFunc, <span style="COLOR: #);
//在dst中画出轮廓
cvZero(dst);
for(int i = <span style="COLOR: #;i & min(all_cont-&total, <span style="COLOR: #);i++)///////////////////////次数待改
cur_cont = (CvSeq* )cvGetSeqElem(all_cont, i);
if(flag != <span style="COLOR: # && i == <span style="COLOR: #)
*templateContour = cvCloneSeq(cur_cont, templateStorage);
CvScalar color = CV_RGB(rand()&<span style="COLOR: #5, rand()&<span style="COLOR: #5, rand()&<span style="COLOR: #5);
cvDrawContours(dst, (CvSeq* )cur_cont, color, color, -<span style="COLOR: #, <span style="COLOR: #, <span style="COLOR: #);
//判断原点位置以确定是否需要反转图像
if(src-&origin == <span style="COLOR: #)
cvFlip(dst);
//释放内存
cvReleaseMemStorage(&first_sto);
cvReleaseMemStorage(&all_sto);
cvReleaseImage(&gray);}void gesMatchContoursTemplate(IplImage* src, IplImage* dst, CvSeq** templateContour){
CvMemStorage*
//初始化动态内存
storage = cvCreateMemStorage(<span style="COLOR: #);
contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
//得到轮廓并进行匹配
gesFindContours(src, dst, &contour, storage, <span style="COLOR: #);
if(contour-&total != <span style="COLOR: #)//如果得到的轮廓不为空
double result = cvMatchShapes((CvContour* )contour, (CvContour* )(*templateContour), CV_CONTOURS_MATCH_I3);
printf("%.2f\n", result);/////////////////////////////////////////////
//释放内存
cvReleaseMemStorage(&storage);}//模版匹配法的完整实现int gesMatchContoursTemplate2(IplImage* src, IplImage* dst, CvSeq* templateContour){
CvSeq* cur_
CvMemStorage*
double minValue, tempV
int i, minI
//初始化动态内存
storage = cvCreateMemStorage(<span style="COLOR: #);
contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
//得到轮廓并进行匹配
minIndex = -<span style="COLOR: #;
gesFindContours(src, dst, &contour, storage, <span style="COLOR: #);
if(contour-&total != <span style="COLOR: #)//如果得到的轮廓不为空
if(templateContour-&total != <span style="COLOR: #)
cur_cont = (CvSeq* )cvGetSeqElem(templateContour, <span style="COLOR: #);
minValue = cvMatchShapes((CvContour* )contour, (CvContour* )cur_cont, CV_CONTOURS_MATCH_I3);
minIndex = <span style="COLOR: #;
printf("<span style="COLOR: #:%.2f\n", minValue);
for(i = <span style="COLOR: #;i & templateContour-&i++)
cur_cont = (CvSeq* )cvGetSeqElem(templateContour, i);
tempValue = cvMatchShapes((CvContour* )contour, (CvContour* )cur_cont, CV_CONTOURS_MATCH_I3);
if(tempValue & minValue)
minValue = tempV
minIndex =
printf("%d:%.2f\n", i, tempValue);
if(minValue &= <span style="COLOR: #.3)
minIndex = -<span style="COLOR: #;
//打印匹配结果
printf("the result is %d\n", minIndex);
//释放内存
cvReleaseMemStorage(&storage);
return minI}//找出轮廓最大的5个极大值点void gesFindContourMaxs(CvSeq* contour){
CvS//重心位置
CvM//存储5个极大值的数组
double initMax[] = {-<span style="COLOR: #, -<span style="COLOR: #, -<span style="COLOR: #, -<span style="COLOR: #, -<span style="COLOR: #};//初始极大值设置为-1
double minValue, maxV//<span style="COLOR: #个极大值中的最大值与最小值
CvPoint minL//最小值的位置
double preDistance = <span style="COLOR: #;
bool isCandidate = false;//是否是候选的极大值点
//初始化重心位置
center = cvScalarAll(<span style="COLOR: #);
//初始化极大值矩阵
max = cvMat(<span style="COLOR: #, <span style="COLOR: #, CV_64FC1, initMax);
//首先求出轮廓的重心
for(i = <span style="COLOR: #;i & contour-&i++)
p = (CvPoint* )cvGetSeqElem(contour, i);
center.val[<span style="COLOR: #] += p-&x;
center.val[<span style="COLOR: #] += p-&y;
center.val[<span style="COLOR: #] /= contour-&
center.val[<span style="COLOR: #] /= contour-&
//遍历轮廓,找出所有的极大值点
for(i = <span style="COLOR: #;i & contour-&i++)
p = (CvPoint* )cvGetSeqElem(contour, i);
double distance = sqrt(pow(center.val[<span style="COLOR: #] - p-&x, <span style="COLOR: #) + pow(center.val[<span style="COLOR: #] - p-&y, <span style="COLOR: #));
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;
isCandidate = false;
preDistance =
//打印5个极大值
printf("%.2f %.2f %.2f %.2f %.2f\n", cvmGet(&max, <span style="COLOR: #, <span style="COLOR: #), cvmGet(&max, <span style="COLOR: #, <span style="COLOR: #), cvmGet(&max, <span style="COLOR: #, <span style="COLOR: #), cvmGet(&max, <span style="COLOR: #, <span style="COLOR: #), cvmGet(&max, <span style="COLOR: #, <span style="COLOR: #));}//计算轮廓的pair-wise几何直方图CvHistogram* gesCalcContoursPGH(CvSeq* contour){
CvHistogram*//成对几何直方图
CvContour* tempC
//得到成对几何直方图第二个维度上的范围
tempCont = (CvContour* )
cvBoundingRect(tempCont, <span style="COLOR: #);
int sizes[<span style="COLOR: #] = {<span style="COLOR: #, <span style="COLOR: #0};
float ranges[<span style="COLOR: #][<span style="COLOR: #] = {{<span style="COLOR: #,PI}, {<span style="COLOR: #,<span style="COLOR: #0}};
float** rangesPtr = new float* [<span style="COLOR: #];
rangesPtr[<span style="COLOR: #] = ranges[<span style="COLOR: #];
rangesPtr[<span style="COLOR: #] = ranges[<span style="COLOR: #];
//初始化几何直方图
hist = cvCreateHist(<span style="COLOR: #, sizes, CV_HIST_ARRAY, rangesPtr, <span style="COLOR: #);
//计算轮廓的成对几何直方图
cvCalcPGH(contour, hist);
return}//对轮廓的pair-wise几何直方图进行匹配void gesMatchContoursPGH(CvSeq* contour, CvHistogram* templateHist){
CvHistogram*
//得到轮廓的成对几何直方图
hist = gesCalcContoursPGH(contour);
//归一化直方图
cvNormalizeHist(templateHist, <span style="COLOR: #);
cvNormalizeHist(hist, <span style="COLOR: #);
//直方图匹配
double result = cvCompareHist(hist, templateHist, CV_COMP_INTERSECT);
printf("result:%.2f\n", result);
//释放内存
cvReleaseHist(&hist);}
发表评论:
TA的最新馆藏

我要回帖

更多关于 js 对象排序 sort 的文章

 

随机推荐