0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

opencv用序列存储轮廓

C语言专家集中营 来源:工程师曾玲 2019-02-02 17:25 次阅读

查找轮廓

轮廓到底是什么?一个轮廓一般对应一系列的点,也就是图像中的一条曲线.表示的方法可能根据不同情况而有所不同.有多重方法可以表示曲线.在openCV中一般用序列来存储轮廓信息.序列中的每一个元素是曲线中一个点的位置.关于序列表示的轮廓细节将在后面讨论,现在只要简单把轮廓想象为使用CvSeq表示的一系列的点就可以了.

函数cvFindContours()从二值图像中寻找轮廓.cvFindContours()处理的图像可以是从cvCanny()函数得到的有边缘像素的图像,或者是从cvThreshold()及cvAdaptiveThreshold()得到的图像,这时的边缘是正和负区域之间的边界.

opencv用序列存储轮廓

图8-2描述了cvFindContours的函数功能,图像的上半部分是神色背景和白色区域(被从A到E标记)的测试图像.下半部分是使用cvFindCountours()函数后会得到的轮廓的说明.这些轮廓被标记为cX或hx,"c"表示"轮廓(contour)","h"表示"孔(hole)","X表述数字".其中一些轮廓用虚划线表示;表明他们是白色区域的外部边界(例如,非0区域).孔(hole)的外部边界(例如,非0区域)即白色区域的内部边界.在图中是用电线表示外部边界的.OpenCV的cvFindContours()函数可区分内部和外部边界.

包含的概念在很多应用中都非常重要.因此.OpenCV允许得到的轮廓被聚合成一个轮廓树,从而把包含关系编码到树结构中.这个测试图的轮廓树在根节点的轮廓叫c0,孔h00和h01是它的字子节点.这些轮廓中直接包含轮廓称为他们的子节点,以此类推.

现在来看cvFindContours()函数

intcvFindContours(CvArr*image,CvMemStorage*storage,CvSeq**first_contour,

intheader_sizeCV_DEFAULT(sizeof(CvContour)),

intmodeCV_DEFAULT(CV_RETR_LIST),

intmethodCV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),

CvPointoffsetCV_DEFAULT(cvPoint(0,0)));

第一个参数 image是输入图像,图像必须是8位单通道图像,并且应该被转化成二值的(例如,所有非0像素的值都是一个定值).cvFindContours()运行的时候,这个图像会被直接涂改,因此如果是将来还有用的图像,应该复制之后再传给cvFindContours().

storage 是内存存储器,cvFindContours()找到的轮廓记录在此内存里.正如之前所说,这个存储器的空间应该由cvCreateMemStorage()分配.

first_contour 是指向CvSeq*的一个指针firstContour.无需动手,cvFindContours()会自动分配该指针.实际上,只要在这里传一个指针就可以了函数会自动设置.不需要分配和释放(new/delete或者malloc/free).就是这个指针(例如,*firstContour)指向轮廓树的首地址(head).cvFindContours()返回值是,找到的所有轮廓的个数

cvSeq* firstContout = NULL;

cvFindContours(..., &firstContour, ...);

headerSize告诉cvFindContours()更多有关对象分配的信息,它可以被设定为sizeof(CvContour)或者sizeof(CvChain)(当近似方法参数method被设定为CV_ChAIN_CODE时使用后者).最后是mode和method参数,他们分别指定计算方法和如何计算.

mode变量可以被设置为以下四个选项之一: CV_RETR_ExTERNAL, CV_RETR_LIST, CV_RETR_CCOMP或CV_RETR_TREE.mode的值向cvFindeContours()说明需要的轮廓类型,和希望的放回值形式.具体说来,mode的值决定把找到的轮廓如何挂到轮廓树节点变量(h_prev,h_next,v_prev和v_next)上,图8-3展示了四种可能的mode值所得到的结果的拓扑结构.

opencv用序列存储轮廓

每中情况下,结构都可以看成是被"横向"连接(h_next和h_prev)联系和被"纵向"连接(v_next和v_prev)不同的"层次".

CV_RETR_EXTERNAL 只检测出最外的轮廓.图8-2中,只有一个最外轮廓,因此图8-3中第一个轮廓指向最外的序列,除此之外没有别的连接

CV_RETR_LIST 检测所有的轮廓并将他们保存到表(list)中.图8-3描绘了从图8-2样图中得到的表.在这个例子中,有8条轮廓被找到,他们相互之间有h_prev和h_next连接(这里并没有使用v_prev和v_next)

CV_RETR_CCOMP 检出所有的轮廓并将他们组织成双层结构(two-level hierarchy),顶层边界是所有成份的外界边界,第二层边界是空的边界.图8-3中,我们能看到5个外部边界,其中3个包含孔.孔被v_next和v_prev可以只包括一个值,此节点可以只有一个子节点.c0中有两个孔,因为v_next可以值包括一个值,次节点可以只有一个子节点.c0之内的所有孔相互间有h_prev和h_next指针连接.

CV_RETR_TREE 检出所有轮廓并且重新建立网状的轮廓结构.在我们给出的例子中(图8-2和8-3中),这意味着根节点是最外的轮廓c0.c0之下是空h00,在同一层次中与另一个孔h01相连接.同理,每个孔都有子节点(相对应的是c000和c010),这些子节点与父节点被垂直连接起来.这个步骤一直持续到图像最内层的轮廓,这些轮廓会成为树叶节点.

以下的五个值与方法相关(例如轮廓会如何被近似).

CV_CHAIN_CODE 用freeman链码输出轮廓,其他方法输出多边形(顶点的序列)

CV_CHAIN_APPROX_NONE 将链码编码中的所有点转换为点

CV_CHAIN_APPROX_SIMPLE 压缩水平,垂直或斜的部分,只保存最后一个点

CV_CHAIN_APPROX_TC89_L1或CV_CHAIN_APPROX_TC89_KCOS使用Ten-Chin链逼近算法中的一个

CV_LINK_RUNS 与上述算法完全不同的算法,连接所有水平层次的轮廓.此方法只可与Cv_RETR_LIST搭配使用.

使用序列表示轮廓

当调用cvFindContours函数的时候,返回多个序列.序列的类型依赖与调用cvFindContours时 所传递的参数.默认情况下使用CV_RETR_LIST和CV_CHAIN_APPROX_SIMPLE参数.

序列中保存一系列的点,这些点构成轮廓,轮廓是本章的重点.轮廓只是序列所能表示物体的一种.轮廓的点的序列,可以用来表示图像空间中的曲线.这种点的序列很常用,所有需要有专门的函数来帮助我们对他进行处理.下面是一组这样的处理函数.

intcvFindContours(CvArr*image,CvMemStorage*storage,

CvSeq**first_contour,

intheader_sizeCV_DEFAULT(sizeof(CvContour)),

intmodeCV_DEFAULT(CV_RETR_LIST),

intmethodCV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),

CvPointoffsetCV_DEFAULT(cvPoint(0,0)));

CvContourScannercvStartFindContours(CvArr*image,

CvMemStorage*storage,

intheader_sizeCV_DEFAULT(sizeof(CvContour)),

intmodeCV_DEFAULT(CV_RETR_LIST),

intmethodCV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),

CvPointoffsetCV_DEFAULT(cvPoint(0,0)));

CvSeq*cvFindNextContour(CvContourScannerscanner);

voidcvSubstituteContour(CvContourScannerscanner,CvSeq*new_contour);

/*Releasescontourscannerandreturnspointertothefirstoutercontour*/

CvSeq*cvEndFindContours(CvContourScanner*scanner);

/*ApproximatesasingleFreemanchainoratreeofchainstopolygonalcurves*/

CvSeq*cvApproxChains(CvSeq*src_seq,

CvMemStorage*storage,

intmethodCV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),

doubleparameterCV_DEFAULT(0),

intminimal_perimeterCV_DEFAULT(0),

intrecursiveCV_DEFAULT(0));

第一函数是cvFindContours(),在前面已经提到.接着是cvStartFindContiors()函数,它和cvFin的Contours()功能类似.但是cvStartFindContours()每次放回一个轮廓,而不像cvFinContours()那样一次查找所有轮廓然后统一返回.调用cvStartFindContours()函数后,返回一个CvSequenceScanner结构.CvSequenceScanner结构中包含一些状态信息,这些信息不可读.你可以通过在cvSequenceScabber结构上依次调用cvFinNextContour()来查找剩余的轮廓.当全部轮廓都被找完之后,cvFindNextContour()将放回NULL

cvSubstituteContour()函数用于替换scanner指向的轮廓.该函数的一个特性是,如果参数 new_contour为NULL,那么当前的轮廓将被从Scanner指定的树或链表中删除(受影响的序列会作适当更新,来保证不会有指针指向不存在的物体).

函数cvEndFindContour()结束轮廓查找,并且将scanner设置为结束状态.注意,scanner并没有被删除,实际上该函数返回的是指针所指序列的第一个元素.

最后一个函数cvApproxChains()函数.该函数将Freeman链转换为多边形表示(精确转换或者近似拟合).

Freeman链码

一般情况下,通过cvFindCountours获取的轮廓是一系列顶点的序列.另一种不同的表达是设置method参数为CV_CHAIN_CODE,然后生成轮廓.当选者CV_CHAIN_CODE标志的时候,检测的轮廓通过Freemain链码[Freeman67](图8-4)的方式返回.在Freeman链码中,多边形被表示为一系列的位移,每一个位移有8个方向,这8个方向使用整数0到7表示.Freeman链码对于识别一些形状的物体很有帮助.如果得到的是Freeman链码,可以通过以下两个函数读出每个点

voidcvStartReadChainPoints(CvChain*chain,

CvChainPtReader*reader);

CvPointcvReadChainPoint(CvChainPtReader*reader);

opencv用序列存储轮廓

第一个函数用来初始化Freeman链CvChainPtReader结构,第二个函数通过CvChainptReader来读每个点,CvChainPtReader对应当前状态.结构CvChain从CvSeq扩展得来.和CvContourScanner从多个轮廓间迭代一样,CvChainPtReader用于迭代一个使用Freemain链码表示轮廓中的每个点.CvChainPtReader和CvSeqReader的用法类似.如您所期望,当所有点都读完后,返回CvChainPtReader值为NULL.

绘制轮廓

一个经常使用的功能是在屏幕上绘制检测到的轮廓.绘制可以用cvDrawContours函数完成

/*Drawscontouroutlinesorfilledinteriorsontheimage*/

voidcvDrawContours(CvArr*img,CvSeq*contour,

CvScalarexternal_color,CvScalarhole_color,

intmax_level,

intthicknessCV_DEFAULT(1),

intline_typeCV_DEFAULT(8),

CvPointoffsetCV_DEFAULT(cvPoint(0,0)));

第一个参数为要绘制轮廓的图像.第二个参数是要绘制的轮廓,他不像乍看上去那么简单,他是轮廓树的根节点.其他的参数(主要是max_level)将会控制如何绘制轮廓树.下一个参数很容易理解,是绘制轮廓所用的颜色.但是hole_color那?请回忆轮廓的分类,有外轮廓,也有"洞"(图8-2中的虚划线和点线).无论绘制单个轮廓还是轮廓树中的所有轮廓,标记为"洞"的轮廓都会使用hole_color指定的颜色绘制.

通过max_level变量可以告诉cvDrawConturs() 如何处理通过节点树变量连结到一个轮廓上的其他任何轮廓.此变量可以被设置为遍历轮廓的最大深度.因此max_level = 0表示与输入轮廓属于同意等级的所有轮廓(更具体的说,输入轮廓和与其相邻的轮廓被画出),max_level = 1表示与输入轮廓属于同一登记的所有轮廓与其子节点被画出,以此类推.如果项要画的轮廓是由cvFindContous()的CV_RETR_CCOMP或CV_RETR_TREE模式得到的话,max_level的负值也是被支持的.在这种情况下,max_level=-1表示只有输入轮廓被画出,以此类推,max_level = -2 表示输入轮廓与其直系(仅直接相连的)子节点会被画出,以此类推.

参数thickness和line_type就如其字面含义所示.最后,我们可以给绘图程序一个偏移量,这样轮廓可以被画在指定的精确坐标上.当轮廓坐标被转换成质心坐标或其他局部坐标系的时候,这个特性非常有用.如果在图像上的不同感兴趣的区域多次执行cvFindContour(),然后又想将所有结果在原来大图像上显示出来,便宜量offset也很有用.相反,可以先从大图提取出一个轮廓,然后在用offset和填充,在小图像上形成和轮廓对应的蒙板(mask);

轮廓例子

首先创建一个窗口用于显示图像,滑动条(trackbar)用于设置阈值,然后对采二值化后的图像提取轮廓并绘制轮廓.当控制参数的滑动条变换时,图像被更新.

#include"stdafx.h"

#include

#include

IplImage*g_image=NULL;

IplImage*g_gray=NULL;

intg_thresh=100;

CvMemStorage*g_storage=nullptr;

voidon_trackbar(int)

{

if(g_storage==nullptr)

{

g_gray=cvCreateImage(cvGetSize(g_image),8,1);

g_storage=cvCreateMemStorage(0);

}

else

{

cvClearMemStorage(g_storage);

}

CvSeq*contours=NULL;

cvCvtColor(g_image,g_gray,CV_BGR2GRAY);

cvThreshold(g_gray,g_gray,g_thresh,255,CV_THRESH_BINARY);

cvFindContours(g_gray,g_storage,&contours);

cvZero(g_gray);

if(contours)

{

cvDrawContours(g_gray,contours,cvScalarAll(255),cvScalarAll(255),100);

}

cvShowImage("Contours",g_gray);

}

int_tmain(intargc,_TCHAR*argv[])

{

g_image=cvLoadImage("C:\\Users\\chenchao\\Desktop\\细胞图象\\正常的红细胞\\5.bmp");

cvNamedWindow("Contours",1);

cvCreateTrackbar("Threshold","Contours",&g_thresh,300,on_trackbar);

on_trackbar(0);

cvWaitKey(0);

printf("HELLO");

return0;

}

如果全局参数g_storage为NULL的话,则用cvCreateMemSotrage(0)创建一个内存存储器.g_gray被初始化为和g_image同样大小的黑色图像,但是为单通道图像.如果g_storage非空的话,则先用cvClearMemStorage清空内存存储器的中间,这样以便重复利用内存存储器中的资源.然后创建一个CvSeq*指针,该指针用来保存cvFindCountours()检测到的轮廓.

然后g_image被转换为灰度图像,接着用g_thresh为参数进行二值化处理,得到的二值图像保存在g_gray中.cvFindContours从二值图像g_gray查找轮廓,然后将得到的轮廓用cvDrawContours()函数绘制为白色到灰度图像.最终图像在窗口中显示出来,并将在回调函数开始处申请的结构释放.

另一个轮廓的例子

在上例中,我们检测出输入图像的轮廓,然后逐个绘制没格轮廓.从这个例子中,我们可以了解到轮廓检测方法(如代码中是CV_RETR_LIST)以及max_depth(代码中是0)等参数的细节.如果设置的max_depth是一个比较大的值,你可以发现cvFindCountours()返回的轮廓是通过h_next连接被遍历.对于其他一些拓扑结构(CV_RETR_TREE,CV_REER_CCOMP等),你会发现有些轮廓被画过不只一次

例8-3 在输入图像上寻找并绘制轮廓

int_tmain(intargc,_TCHAR*argv[])

{

cvNamedWindow("src");

IplImage*img_8uc1=cvLoadImage("C:\\Users\\chenchao\\Desktop\\细胞图象\\正常的红细胞\\5.bmp",0);

IplImage*img_edge=cvCreateImage(cvGetSize(img_8uc1),8,1);

IplImage*img_8uc3=cvCreateImage(cvGetSize(img_8uc1),8,3);

cvThreshold(img_8uc1,img_edge,128,255,CV_THRESH_BINARY);

CvMemStorage*storage=cvCreateMemStorage(0);

CvSeq*first_contour=nullptr;

intNc=cvFindContours(img_edge,storage,&first_contour,sizeof(CvContour),CV_RETR_LIST);

intn=0;

printf("TotalContoursDetected:%d\n",Nc);

for(CvSeq*c=first_contour;c!=NULL;c=c->h_next)

{

cvCvtColor(img_edge,img_8uc3,CV_GRAY2BGR);

cvDrawContours(img_8uc3,c,cvScalar(0,255,0),cvScalar(0,0,255),0,2,8);

printf("contours#%d\n",n);

cvShowImage("src",img_8uc3);

printf("%delements:\n",c->total);

for(inti=0;itotal;++i)

{

CvPoint*p=CV_GET_SEQ_ELEM(CvPoint,c,i);

printf("(%d,%d)\n",p->x,p->y);

}

cvWaitKey(0);

n++;

}

printf("Finishedallcontours.\n");

cvCvtColor(img_8uc1,img_8uc3,CV_GRAY2BGR);

cvShowImage("src",img_8uc3);

cvWaitKey(0);

cvDestroyWindow("src");

cvReleaseImage(&img_8uc1);

cvReleaseImage(&img_8uc3);

cvReleaseImage(&img_edge);

return0;

}

深入分析轮廓

多边形逼近

当我们绘制一个多边形或者进行形状分析的时候,通常需要使用多边形毕竟一个轮廓,使顶点数目变少.有多种方法可以实现这个功能,OpenCV实现了其中的一种逼近算法.函数cvApproxPoly是该算法的一种实现,可以处理轮廓的序列.

(CvSeq*)cvApproxPoly(constvoid*src_seq,

intheader_size,CvMemStorage*storage,

intmethod,doubleeps,

intrecursiveCV_DEFAULT(0));

我们可以传递一个列表或者数状序列给cvApproxPoly,然后对其表示的轮廓进行处理.函数返回值对应第一个轮廓,同样我们可用通过h_next(以及v_next)来访问返回其他的轮廓.

因为cvApproxPoly在返回结果的时候需要创建新的对象,因此 需要指定一个内存存储器以及头结构大小.(一般为sizeof(CvContour)).

逼急算法目前只可使用CV_POLY_APPROx_DP.另外两个参数为逼近算法参数(目前只用到第一个).eps参数指定逼近的精度.如果想了解这个参数如何起作用的的必须仔细了解具体的算法.最后一个参数指定是否针对全部的轮廓(通过h_next和v_next可达的)进行逼近

如果为0,则表示只处理src_seq指向轮廓.

下面简要介绍一下算法的工作原理.参考图8-5,算法先从轮廓(图b)选择2个最远的点,然后将2个连成一个线段(图c),然后再查找轮廓上到线段距离最远的点,添加到逼近后的心轮廓(图d).算法反复迭代,不断将最远点的添加到结果中.直到所有点的点到多边形的最短距离小于eps参数指定的精度(图f).从这里可以看出,精度和轮廓的周长,或者外包矩形周长的几分之一比较合适.

opencv用序列存储轮廓

曲线逼近的过程和寻找关掉点的过程密切相关。跟曲线上的其他点相比,关键点是那些包含曲线信息比较多的点。关键点在逼近算法以及其他应用中都会涉及。函数cvFindDominantPoints()实现了被称为IPAN*[Chetvreikov99]的算法.

CvSeqcvFindDominantPoints(CvSeq*contour,

CvMemStorage*storage,

intmetod=CV_DOMINANT_IPAN,

doubleparameter1=0,

doubleparameter2=0,

doubleparameter3=0,

doubleparameter4=0);

本质上,IPAN算法通过扫描轮廓上并在曲线内部使用可能顶点构造三角形来实现.对于三角形的大小和张角有特殊要求.在此某一特定的全局阈值和它的相邻的张角小的情况下,具有大张角的点被保留.

函数cvFindDominantPoints()按照惯例使用参数CvSeq* 和CvMemStorage* .并且要求指定一个方法,和cvApproxPoly()相同,目前可供选择的方法只有一个,就是CV_DOMINANT_IPAN.

opencv用序列存储轮廓

接下来四个参数是:最短距离dmin,最长距离dmax,相邻距离dn和最大角度θmax.如图8-6所示,算法首先把所有两边距离rpa和rpb在dmin和dmax之间,θab < θmax的三角形找出来.然后保留对于距离dn(dn的大小不得超过dmax)有最小夹角θab的所有点p.dmin,dmax,dn和θmax典型值可以是7,9,9,150(最后一个参数是以度数为单位的角大小).

特性概括

轮廓处理中经常遇到的另一个任务是计算一些轮廓变化的概括特性.这可能包括长度或者其他一些反映轮廓整体大小的度量.另一个有用的特性是轮廓的轮廓矩(contourmoment),可以用来概括轮廓的总形状特性

长度

函数cvContourPerimeter()作用于一个轮廓并返回其长度.事实上,此函数是一个调用函数cvArcLength()的宏.

CVAPI(double)cvArcLength(constvoid*curve,

CvSlicesliceCV_DEFAULT(CV_WHOLE_SEQ),

intis_closedCV_DEFAULT(-1));

CV_INLINEdoublecvContourPerimeter(constvoid*contour)

{

returncvArcLength(contour,CV_WHOLE_SEQ,1);

}

cvArcLength()的第一参数是轮廓,其形式可以是点的序列(CvContour*或CvSeq*)或任一n×2的点的数组.后边的参数是slice,以及表明是否将轮廓视为闭合的一个布尔类型(例如,是否将轮廓的最后一个点视为和第一个点有连接).slice可以让我们只选择曲线上的点的部分集合.

一个和cvArcLength()有紧密关系的函数是cvContourArea(),如其名称所示,这个函数同于计算轮廓的面积.函数的参数contour和slice和cvArcLength()一样.

CVAPI(double)cvContourArea(constCvArr*contour,

CvSlicesliceCV_DEFAULT(CV_WHOLE_SEQ),

intorientedCV_DEFAULT(0));

边界框

当然长度和面积只是轮廓的简单特性,更复杂一些的特性描述应该是矩形边界框,圆形边界框或椭圆形边界框.有两种方法可以得到矩形边界框,圆形与椭圆形编辑框各只有一种方法.

CVAPI(CvRect)cvBoundingRect(CvArr*points,

intupdateCV_DEFAULT(0));

CVAPI(CvBox2D)cvMinAreaRect2(constCvArr*points,

CvMemStorage*storageCV_DEFAULT(NULL));

最简单的方法是调用函数cvBoundingRect();它将放回一个包围轮廓的CvRect.第一个参数points可以是由点组成的序列,一个轮廓(CvContour*)或者一个n×1双通道的矩阵(CvMat*).为了理解第二个参数update,我们需要想想前面的描述,当时说CvContour并不完全等于CvSeq;CvSeq能实现的CvContour都可以实现,CvContour甚至能做的更多一点.其中一个附加功能就是CvRect成员可以记载轮廓自己的边界框.如果调用函数cvBoundingRect()时参数update设置为0,便可以直接从CvCoutour的成员中获取边界框;如果将uodate设置为1,边界框便会被计算出(CvContour成员的内容也会被更新).

cvBoundingRect()得到的长方形的一个问题是,cvRect只能表现一个四边水平和竖直的长方形.然而函数cvMinAreaRect2()可以返回一个包围轮廓最小的长方形,这个长方形可能是倾斜的;请看图8-7,该函数的参数和cvBoundingRect()的相似.opencv的数据类型CvBox2D就是用来表述这样的长方形状的.

typedefstructCvBox2D

{

CvPoint2D32fcenter;/*Centerofthebox.*/

CvSize2D32fsize;/*Boxwidthandlength.*/

floatangle;/*Anglebetweenthehorizontalaxis */

/*andthefirstside(i.e.length)indegrees*/

}

CvBox2D;

opencv用序列存储轮廓

圆形和椭圆形边界

接着我们来看函数cvMinEnclosingCircle().该函数和矩形边界框的作用基本相同,输入同样很灵活,可以是点的序列,也可以是二维点的数组.

CVAPI(int)cvMinEnclosingCircle(constCvArr*points,

CvPoint2D32f*center,

float*radius);

OpenCV里没有专门用来表示圆的结构,因此需要给函数cvMinEnclosingCircle()传递中心和浮点型半径的两个指针来获取计算结果.

与最小包围圆一样,OpenCV提供一函数来拟合一组点,以获取最佳拟合椭圆

CVAPI(CvBox2D)cvFitEllipse2(constCvArr*points);

cvMinEnclosingCircle()和cvFitEllipse2()的细微差别在于,前者只简单计算完全包围已有轮廓的最小圆,而后者使用拟合函数返回一个与轮廓最相近似的椭圆.这意味着并不是轮廓中所有的点都会被包在cvFitEllipse2()返回的椭圆中.该拟合由最小二乘拟合方法算出.

椭圆的拟合结果由CvBox2D结构体返回,给出的矩形正好完全包围椭圆,如图8-8所示.

opencv用序列存储轮廓

几何

在处理CvBox2D或多边形边界的时候,经常需要进行多边形以及边界框的重叠判断.OpenCV提供了一组方便的小函数用于此类测试.

CVAPI(CvRect)cvMaxRect(constCvRect*rect1,

constCvRect*rect );

CVAPI(void)cvBoxPoints(CvBox2Dbox,CvPoint2D32fpt[4]);

/*Initializessequenceheaderforamatrix(columnorrowvector)ofpoints-awrapperforcvMakeSeqHeaderForArray(itdoesnotinitializeboundingrectangle!!!)*/

CVAPI(CvSeq*)cvPointSeqFromMat(intseq_kind,

constCvArr*mat,

CvContour*contour_header,

CvSeqBlock*block);

/*Checkswhetherthepointisinsidepolygon,outside,onanedge(at avertex).Returnspositive,negativeorzerovalue,

correspondingly.Optionally,measuresasigneddistancebetween

thepointandthenearestpolygonedge(measure_dist=1)*/

CVAPI(double)cvPointPolygonTest(constCvArr*contour,

CvPoint2D32fpt,intmeasure_dist);

第一个函数cvMaxRect()根据输入的2个矩形计算,他们的最小外包矩形.

下一个使用函数cvBoxPoints()用于计算CvBox2D结构表示矩形的4个顶点.当然你也可以自己通过三角函数计算,不过这令人头大,而简单调用一下这个函数则可求出.

第三实用函数cvPointSeqFromMat()从mat中初始化序列.这在你需要使用轮廓相关的函数,但是函数又不支持矩阵参数的时候使用.第一个参数用于指定点序列类型,seq_kind可以为以下类型:点集为0;曲线为CV_SEQ_KIND_CURVE;封闭曲线为CV_SEQ_KIND_CURVE|Cv_SEQ_FLAG_CLOSED.第二个参数是输入的矩阵,该参数是连续的1维向量.矩阵类型必须为cv_32C2或CV_32FC2.

下面的两个参数是指针,指针指向的内容通过该函数填充.contour_header参数对应轮廓结构,一般要事先创建,不过由该函数负责初始化.block参数同样如此,也是由该函数复杂初始化.最后,该函数放回一个类型为CvSeq*的序列指针,指向你输入的序列头*contour_header.返回值跟输入参数相同只是为了使用该函数时更方便,因为这样你就可以将该函数当作某个轮廓函数的参数使用,代码写入同一行.

最后一个平面几个相关的函数是cvPointPolygonTest(),用于测试一个点是否在多边形的内部.如果参数measure_dist非零,函数返回值是点到多边形最近距离.如果measure_dist为0,函数返回+1,-1,0,分别表示在内部,外部,在多边形边上.参数contour可以是序列,也可以是2通道矩阵向量.

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储器
    +关注

    关注

    38

    文章

    7151

    浏览量

    162000
  • OpenCV
    +关注

    关注

    29

    文章

    611

    浏览量

    40786

原文标题:opencv轮廓及点在轮廓内判断

文章出处:【微信号:C_Expert,微信公众号:C语言专家集中营】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    创建包围轮廓的矩形和圆形边界框_《OpenCV3编程入门》书本配套源代码

    OpenCV3编程入门》书本配套源代码:创建包围轮廓的矩形和圆形边界框
    发表于 06-06 15:25 3次下载

    创建包围轮廓的矩形边界初级图像混合_《OpenCV3编程入门》书本配套源代码

    OpenCV3编程入门》书本配套源代码:创建包围轮廓的矩形边界初级图像混合
    发表于 06-06 15:39 1次下载

    创建包围轮廓的圆形边界_《OpenCV3编程入门》书本配套源代码

    OpenCV3编程入门》书本配套源代码:创建包围轮廓的圆形边界
    发表于 06-06 15:39 3次下载

    轮廓查找基础_《OpenCV3编程入门》书本配套源代码

    OpenCV3编程入门》书本配套源代码:轮廓查找基础
    发表于 06-06 15:39 4次下载

    OpenCV3编程入门-源码例程全集-创建包围轮廓的矩形边界

    OpenCV3编程入门-源码例程全集-创建包围轮廓的矩形边界
    发表于 09-17 22:54 1次下载

    OpenCV3编程入门-源码例程全集-创建包围轮廓的矩形和圆形边

    OpenCV3编程入门-源码例程全集-创建包围轮廓的矩形和圆形边界框
    发表于 09-17 22:54 2次下载

    OpenCV3编程入门-源码例程全集-创建包围轮廓的圆形边界

    OpenCV3编程入门-源码例程全集-创建包围轮廓的圆形边界
    发表于 09-17 22:54 0次下载

    OpenCV3编程入门-源码例程全集-查找和绘制图片轮廓

    OpenCV3编程入门-源码例程全集-查找和绘制图片轮廓
    发表于 09-17 22:54 2次下载

    OpenCV3编程入门-源码例程全集-查找并绘制轮廓综合示例

    OpenCV3编程入门-源码例程全集-查找并绘制轮廓综合示例
    发表于 09-17 22:54 6次下载

    轮廓查找基础_OpenCV3编程入门-源码例程

    OpenCV3编程入门-源码例程全集-轮廓查找基础,感兴趣的小伙伴们可以瞧一瞧。
    发表于 09-18 16:55 0次下载

    opencv轮廓提取原理与代码的实现

    在检测物体的轮廓时,我们通常会使用到opencv中的findcontour和drawcontour,比较常用而且效果不错。那么findcontour是基于什么原理来实现轮廓的提取呢?在目标识别中我们
    发表于 12-04 16:29 3.1w次阅读

    如何才能提取二值图像中的最大轮廓OpenCV程序免费下载

      本文档的主要内容详细介绍的是如何才能提取二值图像中的最大轮廓OpenCV程序免费下载。
    发表于 10-10 16:49 3次下载
    如何才能提取二值图像中的最大<b class='flag-5'>轮廓</b><b class='flag-5'>OpenCV</b>程序免费下载

    opencv 轮廓放大_OpenCV开发笔记(六十六):红胖子8分钟带你总结形态学操作-膨胀、腐蚀、开运算、闭运算、梯

    opencv 轮廓放大_OpenCV开发笔记(六十六):红胖子8分钟带你总结形态学操作-膨胀、腐蚀、开运算、闭运算、梯......
    发表于 11-24 14:21 21次下载
    <b class='flag-5'>opencv</b> <b class='flag-5'>轮廓</b>放大_<b class='flag-5'>OpenCV</b>开发笔记(六十六):红胖子8分钟带你总结形态学操作-膨胀、腐蚀、开运算、闭运算、梯

    使用轮廓分数提升时间序列聚类的表现

    我们将使用轮廓分数和一些距离指标来执行时间序列聚类实验,并且进行可视化
    的头像 发表于 10-17 10:35 237次阅读
    使用<b class='flag-5'>轮廓</b>分数提升时间<b class='flag-5'>序列</b>聚类的表现

    OpenCV4之图像的轮廓

    图像的轮廓是指图像中具有相同颜色或灰度值的连续点的曲线。轮廓和边缘是有联系的,边缘是轮廓的基础,轮廓是边缘的连续集合。
    的头像 发表于 01-02 12:24 232次阅读