OpenCV轮廓、多边形逼近、关键点、周长和面积、边界框、矩、轮廓树、凹凸包、几何直方图、匹配

本文主要是介绍OpenCV轮廓、多边形逼近、关键点、周长和面积、边界框、矩、轮廓树、凹凸包、几何直方图、匹配,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


1.轮廓的多边形逼近 

2.轮廓的关键点 

3.轮廓的周长和面积 

4.轮廓的边界框 

5.轮廓的矩 

6.轮廓的轮廓树  

7.轮廓的凸包和凸缺陷 

8.轮廓的成对几何直方图  

9.轮廓的匹配


   轮廓的特性:

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)成对几何直方图所用的统计数据包括了夹角和距离。

9.轮廓的匹配
    如果要比较两个物体,可供选择的特征很多。如果要判断某个人的性别,可以根据他(她)头发的长短来判断,这很直观,在长发男稀有的年代准确率也很高。也可以根据这个人尿尿的射程来判断,如果射程大于0.50米,则是男性。总之,方法很多,不一而足。
    我们在上文中得到了轮廓的这么多特征,它们也可以用于进行匹配。典型的轮廓匹配方法有:Hu矩匹配、轮廓树匹配、成对几何直方图匹配。
 1>Hu矩匹配
    轮廓的Hu矩对包括缩放、旋转和镜像映射在内的变化具有不变性。cvMatchShapes函数可以很方便的实现对2个轮廓间的匹配。
 2>轮廓树匹配
    用树的形式比较两个轮廓。cvMatchContourTrees函数实现了轮廓树的对比。
 3>成对几何直方图匹配
    在得到轮廓的成对几何直方图之后,可以使用直方图对比的方法来进行匹。

 

轮廓匹配源码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,    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();

几何直方图匹配方:


#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("%.2fn", result);//    
}     
//释放内存    
cvReleaseMemStorage(&storage); }     
//模版匹配法的完整实现    
int gesMatchContoursTemplate2(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:%.2fn", 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:%.2fn", i, tempValue);     }     if(minValue >= 0.3)     {    minIndex = -1;     }     
}     
//打印匹配结果    
printf("the result is %dn", 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 %.2fn", 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:%.2fn", result);    
//释放内存    
cvReleaseHist(&hist);    
}



















这篇关于OpenCV轮廓、多边形逼近、关键点、周长和面积、边界框、矩、轮廓树、凹凸包、几何直方图、匹配的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1083946

相关文章

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

uva 10387 Billiard(简单几何)

题意是一个球从矩形的中点出发,告诉你小球与矩形两条边的碰撞次数与小球回到原点的时间,求小球出发时的角度和小球的速度。 简单的几何问题,小球每与竖边碰撞一次,向右扩展一个相同的矩形;每与横边碰撞一次,向上扩展一个相同的矩形。 可以发现,扩展矩形的路径和在当前矩形中的每一段路径相同,当小球回到出发点时,一条直线的路径刚好经过最后一个扩展矩形的中心点。 最后扩展的路径和横边竖边恰好组成一个直

poj 1113 凸包+简单几何计算

题意: 给N个平面上的点,现在要在离点外L米处建城墙,使得城墙把所有点都包含进去且城墙的长度最短。 解析: 韬哥出的某次训练赛上A出的第一道计算几何,算是大水题吧。 用convexhull算法把凸包求出来,然后加加减减就A了。 计算见下图: 好久没玩画图了啊好开心。 代码: #include <iostream>#include <cstdio>#inclu

uva 1342 欧拉定理(计算几何模板)

题意: 给几个点,把这几个点用直线连起来,求这些直线把平面分成了几个。 解析: 欧拉定理: 顶点数 + 面数 - 边数= 2。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

XTU 1237 计算几何

题面: Magic Triangle Problem Description: Huangriq is a respectful acmer in ACM team of XTU because he brought the best place in regional contest in history of XTU. Huangriq works in a big compa

hdu 3065 AC自动机 匹配串编号以及出现次数

题意: 仍旧是天朝语题。 Input 第一行,一个整数N(1<=N<=1000),表示病毒特征码的个数。 接下来N行,每行表示一个病毒特征码,特征码字符串长度在1—50之间,并且只包含“英文大写字符”。任意两个病毒特征码,不会完全相同。 在这之后一行,表示“万恶之源”网站源码,源码字符串长度在2000000之内。字符串中字符都是ASCII码可见字符(不包括回车)。

二分最大匹配总结

HDU 2444  黑白染色 ,二分图判定 const int maxn = 208 ;vector<int> g[maxn] ;int n ;bool vis[maxn] ;int match[maxn] ;;int color[maxn] ;int setcolor(int u , int c){color[u] = c ;for(vector<int>::iter

poj 3304 几何

题目大意:给出n条线段两个端点的坐标,问所有线段投影到一条直线上,如果这些所有投影至少相交于一点就输出Yes!,否则输出No!。 解题思路:如果存在这样的直线,过投影相交点(或投影相交区域中的点)作直线的垂线,该垂线(也是直线)必定与每条线段相交,问题转化为问是否存在一条直线和所有线段相交。 若存在一条直线与所有线段相交,此时该直线必定经过这些线段的某两个端点,所以枚举任意两个端点即可。

POJ 2318 几何 POJ 2398

给出0 , 1 , 2 ... n 个盒子, 和m个点, 统计每个盒子里面的点的个数。 const double eps = 1e-10 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;}struct Point{double x , y