opencv的实用研究--分析轮廓并寻找边界点
​      轮廓是图像处理中非常常见的。对现实中的图像进行采样、色彩变化、灰度变化之后,能够处理得到的是“轮廓”。它直接地反应你了需要分析对象的边界特征。而对轮廓的分析,实际上也就是对原图像特征的分析。
      在Opencv中,已经实现了基础的轮廓算法,但是相比较于比如halcon这样的专业软件,在轮廓处理这块的功能还是比较缺乏的。这里就通过一个具体问题,说明自己的学习研究。不对之处欢迎批评。
       P.S这里的轮廓处理相关函数,已经包涵在GOBase中,具体可以到公告中找Github.
一、问题提出
      那么如果对于一个简单的图像,比如
      已经获得了最大物体的轮廓,比如
  //灰度域变化
threshold(gray,gray,0,255,THRESH_BINARY_INV);
GaussianBlur(gray,gray,Size(3,3),0,0);
//寻找和绘制轮廓
VP bigestContour = FindBigestContour(gray);
contours.push_back(bigestContour);

  

 
    
     由于在opencv里面,轮廓是以

  1.  vector<vector<point>>

保存的,那么如何获得这个轮廓的四个顶点了?

     尝试直接打印轮廓中第一个点,那么的确是左上角
但是不具有通用性,在一些比较复杂的图片上面效果不行,比如
那么也就是说,必须通过特征分析的方法获得已经获得的轮廓中点的特性,而opencv本身没有提供相关功能。
二、直观的解决
      现在,对于“左上”和“右下”的两个点,是比较好分析的。因为在所有的包含在轮廓中的点中,他们一个是x,y同时最小的,一个是x,y同时最大的。
      比较复杂的是“左下”和"右上"两个点,因为他们的数值不是非常有特征,比较容易产生混淆。这个时候,如果仅仅是通过x,y值来分析,即使是对于简单图像,也很难得到稳定的结果。
  1. int itopleft =65535;
    int idownright =0;
    Point ptopleft;
    Point pdownright;
    Point pdownleft;
    for(int i=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];
    }
    }
    int idownleft =65534;
    //对于左下的点来说,应该是所有y大于左上的点中,x最小的
    for(int i=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上寻找角点
///// Detector parameters
int blockSize = 2;
int apertureSize = 3;
double k = 0.04;
int thresh = 1;
/// Detecting corners
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 );
///// Drawing a circle around corners
for( int j = 0; j < dst_norm.rows ; j++ ) {
for( int i = 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速度慢且会内存移除。用在这个简单的例子里面看似可以,但是无法处理现实问题。所以就必须分析原理。
      做图像处理有一段时间了,我经常反思回忆,在图像处理中,能够稳定解决问题的,往往依靠的是“先验知识,本质特征”;越是分析逼近图像的本质特征,越能够发现稳定的解决方法。比如对于轮廓的角来说,很容易想到处于边角的点和两边的点肯定具有一定的关系,而这种关系具有特征性。
      所以有目的地寻找论文,很快就有了成果:
 
 
      对于我的研究来说,这篇论文两个贡献:一个是告知首先要对图像进行高斯模糊,这个是我之前没有想到的。特别是对于现实世界中的轮廓,这种方法效果很好。因为边角经过模糊,那么还是边角,但毛刺经过模糊,能够有效去除。
       论文中的算法实现是比较简单的,并且给出了简化算法,直接编码验证:
  1.  

    //遍历轮廓,求出所有支撑角度
    int icount = bigestContour.size();
    float fmax = -1;//用于保存局部最大值
    int imax = -1;
    bool bstart = false;
    for (int i=0;i<bigestContour.size();i++){
    Point2f pa = (Point2f)bigestContour[(i+icount-7)%icount];
    Point2f pb = (Point2f)bigestContour[(i+icount+7)%icount];
    Point2f pc = (Point2f)bigestContour[i];
    //两支撑点距离
    float fa = getDistance(pa,pb);
    float fb = getDistance(pa,pc)+getDistance(pb,pc);
    float fang = fa/fb;
    float fsharp = 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、分析研究,如果从简单的情况开始,控制变量,往往能够左右逢源。

opencv的实用研究--分析轮廓并寻找边界点的更多相关文章

  1. SQL中的Null深入研究分析

    SQL中的Null深入研究分析 虽然熟练掌握SQL的人对于Null不会有什么疑问,但总结得很全的文章还是很难找,看到一篇英文版的, 感觉还不错. Tony Hoare 在1965年发明了 null 引 ...

  2. TCP异常关闭研究分析

    版权声明:本文由谢代斌原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/108 来源:腾云阁 https://www.qclo ...

  3. 介绍了Apache日志文件每条数据的请意义以及一些实用日志分析命令

    这篇文章主要介绍了apache日志文件每条数据的请意义,以及一些实用日志分析命令,需要的朋友可以参考下(http://wap.0834jl.com) 一.日志分析 如果apache的安装时采用默认的配 ...

  4. 【计算机视觉】OpenCV篇(9) - 轮廓(寻找/绘制轮廓)

    什么是轮廓? 轮廓是一系列相连的点组成的曲线,代表了物体的基本外形. 轮廓与边缘好像挺像的? 是的,确实挺像,那么区别是什么呢?简而言之,轮廓是连续的,而边缘并不全都连续(见下图示例).其实边缘主要是 ...

  5. opencv——形态学深究(分析和应用)

    摘要: 形态学一般指生物学中研究动物和植物结构的一个分支.用数学形态学(也称图像代数)表示以形态为基础对图像进行分析的数学工具. 基本思想是用具有一定形态的结构元素去度量和提取图像中的对应形状以达到对 ...

  6. opencv——PCA(主要成分分析)数学原理推导

    引言: 最近一直在学习主成分分析(PCA),所以想把最近学的一点知识整理一下,如果有不对的还请大家帮忙指正,共同学习. 首先我们知道当数据维度太大时,我们通常需要进行降维处理,降维处理的方式有很多种, ...

  7. OpenCV图像处理与视频分析详解

    1.OpenCV4环境搭建 VS2017新建一个控制台项目 配置包含目录 配置库目录 配置链接器 配置环境变量 重新启动VS2017 2.第一个图像显示程序 main.cpp #include< ...

  8. openCV之头文件分析

    我们利用openCV开源库进行项目开发时,往往要牵涉到头文件的添加问题,而openCV中头文件众多,该如何选择呢?下面对openCV2.4.10的头文件进行一个简单的梳理,以便能够快速的添加对应的头文 ...

  9. opencv2.4.13+python2.7学习笔记--OpenCV中的图像处理--图像轮廓

    阅读对象:无要求. 1.代码 ''' OpenCV中的轮廓 轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度.为了更加准确,要使用二值化图像.在寻找轮廓之前,要进行阈值化 ...

随机推荐

  1. jeasyui datagrid 使用记

    1. 一开始想用$('#dg').datagrid('getChanges')来保存整个table修改的行, 结果发现当前编辑的行不算,要失去焦点才有记录 2. 然后改用       $('#dg') ...

  2. GTA项目 二, JSON接口开放跨域访问

    public class CORSAttribute : ActionFilterAttribute { public string Domains { get; set; } public CORS ...

  3. 6.理解DispatcherServlet

    DispatcherServlet的作用 DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,负责职责的分派, 且与Spring IoC容器无缝 ...

  4. leetcode96 Unique Binary Search Trees

    题目: Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For e ...

  5. Eclipse插件CheckStyle的安装和使用

    转载自:http://www.cnblogs.com/lanxuezaipiao/p/3202169.html CheckStyle是SourceForge下的一个项目,提供了一个帮助JAVA开发人员 ...

  6. android设置系统模式

    android 静音与振动1,设置静音和振动静音和振动都属于来电后的动作.所以在设置静音和振动时都只是设置一些标识,并往数据库写入相应标识. 文件:packages/apps/settings/src ...

  7. mesos概述

    mesos解决的问题 不同的分布式运算框架(spark,hadoop,ES,MPI,Cassandra,etc.)中的不同任务往往需要的资源(内存,CPU,网络IO等)不同,它们运行在同一个集群中,会 ...

  8. 10.Java设计模式 工厂模式,单例模式

    Java 之工厂方法和抽象工厂模式 1. 概念 工厂方法:一抽象产品类派生出多个具体产品类:一抽象工厂类派生出多个具体工厂类:每个具体工厂类只能创建一个具体产品类的实例. 即定义一个创建对象的接口(即 ...

  9. Debian类系统必做——将【你的用户】加入sudoers用户组

    切换到root:su root   修改sudoers nano /etc/sudoers 在root    ALL=(ALL:ALL) ALL下,加入:liz        ALL=(ALL:ALL ...

  10. Asp.net中后台C#数组与前台Javascript数组交互

    摘自:http://blog.csdn.net/a6225301/article/details/20003305 在上一篇<asp.net中javascript与后台c#交互>中实现了前 ...