最近再次用到了opencv轮廓,在这里结合作者冰山一角的博客(http://www.cnblogs.com/slysky/)以及自己的体会在此稍加说明。其程序主要参见冰山一角的Blog,遗憾的是代码是OpenCV1.0写的,等有时间再用2.4.2改写一篇。
1.轮廓的多边形逼近
2.轮廓的关键点
3.轮廓的周长和面积
4.轮廓的边界框
5.轮廓的矩
在连续情况下,图像函数为 f(x,y),那么图像的p+q阶几何矩(标准矩)定义为:
p ,q = 0,1,2……
p+q阶中心距定义为:
其中和代表图像的重心,
,
对于离散的数字图像,采用求和号代替积分:
,,p,q = 0,1,2 ……
N和M分别是图像的高度和宽度;
归一化的中心距定义为:;其中
在公式中,p对应x维度上的矩,q对应y维度上的矩,阶数表示对应的部分的指数。该计算是对轮廓界上所有像素(数目为n)进行求和。如果p和q全部为0,那么m00实际上对应轮廓边界上点的数目。
虽然可以直接计算出轮廓的矩,但是经常会用到归一化的矩(因此不同大小但是形状相同的物体会有相同的值)。同样,简单的矩依赖于所选坐标系,这意味着物体旋转后就无法正确匹配。
于是就产生了Hu矩以及其他归一化矩的函数。
Hu矩是归一化中心矩的线性组合。之所以这样做是为了能够获取代表图像某个特征的矩函数。这些矩函数对缩放,旋转和镜像映射出了(h1)具有不变性。
Hu矩是从中心矩中计算得到。即七个由归一化中心矩组合成的矩:
6.轮廓的轮廓树
结果的二分树最终将原始轮廓的形状性比编码。每个节点被它所对应的三角形的信息所注释。
这样建立的轮廓树并不太鲁棒,因为轮廓上小的改变也可能会彻底改变结果的树,同时最初的三角形是任意选取的。为了得到较好的描述需要首先使用函数cvApproxPoly()之后将轮廓排列(运用循环移动)成最初的三角形不怎么收到旋转影响的状态。
轮廓的匹配
1.Hu矩匹配
2.轮廓树匹配
3.成对几何直方图匹配
轮廓匹配源码1:
轮廓匹配源码1
几何直方图匹配方:
#include "gesrec.h" #include <stdio.h>// #define PI 3.14159f //轮廓面积比较函数 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;//轮廓数 IplImage* gray; CvMemStorage* first_sto; CvMemStorage* all_sto; CvSeq* first_cont; CvSeq* all_cont; CvSeq* cur_cont; //初始化动态内存 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); //创建源图像对应的灰度图像 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); //如果没有检测到轮廓则返回 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); } //对轮廓按照面积进行排序 cvSeqSort(all_cont, gesContourCompFunc, 0); //在dst中画出轮廓 cvZero(dst); for(int i = 0;i < min(all_cont->total, 3);i++)///次数待改 { 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); } //判断原点位置以确定是否需要反转图像 if(src->origin == 1) { cvFlip(dst); } //释放内存 cvReleaseMemStorage(&first_sto); cvReleaseMemStorage(&all_sto); cvReleaseImage(&gray); } void gesMatchContoursTemplate(IplImage* src, IplImage* dst, CvSeq** templateContour) { CvSeq* contour; CvMemStorage* storage; //初始化动态内存 storage = cvCreateMemStorage(0); contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage); //得到轮廓并进行匹配 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);/ } //释放内存 cvReleaseMemStorage(&storage); } //模版匹配法的完整实现 int gesMatchContoursTemplate 2(IplImage* src, IplImage* dst, CvSeq* templateContour) { CvSeq* contour; CvSeq* cur_cont; CvMemStorage* storage; double minValue, tempValue; int i, minIndex; //初始化动态内存 storage = cvCreateMemStorage(0); contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage); //得到轮廓并进行匹配 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; } } //打印匹配结果 printf("the result is %d\n", minIndex); //释放内存 cvReleaseMemStorage(&storage); return minIndex; } //找出轮廓最大的5个极大值点 void gesFindContourMaxs(CvSeq* contour) { int i; CvScalar center;//重心位置 CvPoint* p; CvMat max;//存储5个极大值的数组 double initMax[] = {-1, -1, -1, -1, -1};//初始极大值设置为-1 double minValue, maxValue;//5个极大值中的最大值与最小值 CvPoint minLoc;//最小值的位置 double preDistance = 0; bool isCandidate = false;//是否是候选的极大值点 //初始化重心位置 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; //遍历轮廓,找出所有的极大值点 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个极大值 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)); } //计算轮廓的pair-wise几何直方图 CvHistogram* gesCalcContoursPGH(CvSeq* contour) { CvHistogram* hist;//成对几何直方图 CvContour* tempCont; //得到成对几何直方图第二个维度上的范围 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); //计算轮廓的成对几何直方图 cvCalcPGH(contour, hist); return hist; } //对轮廓的pair-wise几何直方图进行匹配 void gesMatchContoursPGH(CvSeq* contour, CvHistogram* templateHist) { CvHistogram* hist; //得到轮廓的成对几何直方图 hist = gesCalcContoursPGH(contour); //归一化直方图 cvNormalizeHist(templateHist, 1); cvNormalizeHist(hist, 1); //直方图匹配 double result = cvCompareHist(hist, templateHist, CV_COMP_INTERSECT); printf("result:%.2f\n", result); //释放内存 cvReleaseHist(&hist); }