您的位置:首页 > 运维架构

opencv的实用研究--分析轮廓并寻找边界点

2016-01-06 20:04 274 查看
opencv的实用研究--分析轮廓并寻找边界点
​轮廓是图像处理中非常常见的。对现实中的图像进行采样、色彩变化、灰度变化之后,能够处理得到的是“轮廓”。它直接地反应你了需要分析对象的边界特征。而对轮廓的分析,实际上也就是对原图像特征的分析。
在Opencv中,已经实现了基础的轮廓算法,但是相比较于比如halcon这样的专业软件,在轮廓处理这块的功能还是比较缺乏的。这里就通过一个具体问题,说明自己的学习研究。不对之处欢迎批评。
P.S这里的轮廓处理相关函数,已经包涵在GOBase中,具体可以到公告中找Github.
一、问题提出
那么如果对于一个简单的图像,比如



已经获得了最大物体的轮廓,比如

//灰度域变化 threshold(gray,gray,0,255,THRESH_BINARY_INV); GaussianBlur(gray,gray,Size(3,3),0,0); //寻找和绘制轮廓 VPbigestContour=FindBigestContour(gray); contours.push_back(bigestContour);

  



由于在opencv里面,轮廓是以

vector<vector<point>>

保存的,那么如何获得这个轮廓的四个顶点了?
尝试直接打印轮廓中第一个点,那么的确是左上角



但是不具有通用性,在一些比较复杂的图片上面效果不行,比如



那么也就是说,必须通过特征分析的方法获得已经获得的轮廓中点的特性,而opencv本身没有提供相关功能。
二、直观的解决

现在,对于“左上”和“右下”的两个点,是比较好分析的。因为在所有的包含在轮廓中的点中,他们一个是x,y同时最小的,一个是x,y同时最大的。
比较复杂的是“左下”和"右上"两个点,因为他们的数值不是非常有特征,比较容易产生混淆。这个时候,如果仅仅是通过x,y值来分析,即使是对于简单图像,也很难得到稳定的结果。

intitopleft=65535; intidownright=0; Pointptopleft; Pointpdownright; Pointpdownleft; for(inti=0;i<bigestContour.size();i++){ //左上 if(bigestContour[i].x+bigestContour[i].y<itopleft){ itopleft=bigestContour[i].x+bigestContour[i].y; ptopleft=bigestContour[i]; } //右下 if(bigestContour[i].x+bigestContour[i].y>idownright){ idownright=bigestContour[i].x+bigestContour[i].y; pdownright=bigestContour[i]; } } intidownleft=65534; //对于左下的点来说,应该是所有y大于左上的点中,x最小的 for(inti=0;i<bigestContour.size();i++){ if(bigestContour[i].y>ptopleft.y){ if(bigestContour[i].x<idownleft){ idownleft=bigestContour[i].x; pdownleft=bigestContour[i]; } } } //绘制 circle(board,ptopleft,10,Scalar(255),5); circle(board,pdownright,10,Scalar(255),5); circle(board,pdownleft,10,Scalar(255),5);

  

三、利用模型来解决

那么,直观的方法是不稳定的。这个时候,我想到在进行图像处理的时候,有所谓“特征点”的说法。比较常见的比如harris/shift/surf。那么我是否能够通过分析轮廓图像,找到轮廓图像特征点的方法找到我需要的边角了?
编码实现:

///在board上寻找角点 /////Detectorparameters intblockSize=2; intapertureSize=3; doublek=0.04; intthresh=1; ///Detectingcorners board.convertTo(board,CV_32F); cornerHarris(board,dst,2,3,0.04); /////Normalizing normalize(dst,dst_norm,0,255,NORM_MINMAX,CV_32FC1,Mat()); convertScaleAbs(dst_norm,dst_norm_scaled); /////Drawingacirclearoundcorners for(intj=0;j<dst_norm.rows;j++){ for(inti=0;i<dst_norm.cols;i++){ if((int)dst_norm.at<float>(j,i)>thresh){ circle(dst_norm_scaled,Point(i,j),5,Scalar(0),2,8,0); circle(src,Point(i,j),5,Scalar(255,0,0),-1,8,0); } } }

  

得到结果



NICE,在图像中已经明显的显示出来了4个边界点,再加上已经有的两个点,得到结果不成问题。
四、问题进一步研究

但是这里其实是用了一个“投机取巧”的方法,那就是使用图像处理的才使用的harris算法来分析轮廓。opencv默认实现的harris速度慢且会内存移除。用在这个简单的例子里面看似可以,但是无法处理现实问题。所以就必须分析原理。
做图像处理有一段时间了,我经常反思回忆,在图像处理中,能够稳定解决问题的,往往依靠的是“先验知识,本质特征”;越是分析逼近图像的本质特征,越能够发现稳定的解决方法。比如对于轮廓的角来说,很容易想到处于边角的点和两边的点肯定具有一定的关系,而这种关系具有特征性。
所以有目的地寻找论文,很快就有了成果:



















对于我的研究来说,这篇论文两个贡献:一个是告知首先要对图像进行高斯模糊,这个是我之前没有想到的。特别是对于现实世界中的轮廓,这种方法效果很好。因为边角经过模糊,那么还是边角,但毛刺经过模糊,能够有效去除。
论文中的算法实现是比较简单的,并且给出了简化算法,直接编码验证:



//遍历轮廓,求出所有支撑角度
inticount=bigestContour.size();
floatfmax=-1;//用于保存局部最大值
intimax=-1;
boolbstart=false;
for(inti=0;i<bigestContour.size();i++){
Point2fpa=(Point2f)bigestContour[(i+icount-7)%icount];
Point2fpb=(Point2f)bigestContour[(i+icount+7)%icount];
Point2fpc=(Point2f)bigestContour[i];
//两支撑点距离
floatfa=getDistance(pa,pb);
floatfb=getDistance(pa,pc)+getDistance(pb,pc);
floatfang=fa/fb;
floatfsharp=1-fang;
if(fsharp>0.05){
bstart=true;
if(fsharp>fmax){
fmax=fsharp;
imax=i;
}
}else{
if(bstart){
circle(board,bigestContour[imax],10,Scalar(255),1);
circle(src,bigestContour[imax],10,Scalar(255,255,255),1);
imax=-1;
fmax=-1;
bstart=false;
}
}
}


  

编码过程中,相比较于原文,有两处优化(原文中应该也提到了,但是没有明说):一是通过取模,使得所有的轮廓点都参与运算;二是通过比较,取出角点的局部最大值。

实现效果,比较理想:



五、小结反思
1、掌握知识,如果不能归纳数学模型,并且编码实现,不叫真正掌握;
2、分析研究,如果从简单的情况开始,控制变量,往往能够左右逢源。

附件列表

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: