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、分析研究,如果从简单的情况开始,控制变量,往往能够左右逢源。

附件列表

时间: 2024-08-06 10:32:20

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

深度研究分析互联网地下产业链的目的

中国互联网行业有一定的特殊性:有国情决定了用户需求的多样性,而主流的互联网产品却很难满足所有的用户心理诉求:加上最近几年互联网高速发展带来的红利,以及技术成本的不断降低,都催生了中国互联网地下产业链. 地下产业链并不是消极的事物,在一定程度上讲,地下产业链更接近用户需求,更能还原互联网的本貌.在中国的传统行业都有一定的所谓黑市门槛,即属于潜规则范畴或者是不为人知的行业秘密,这种信息只流传在最信任的人脉圈子里,读懂了这些,才能算从根本上了解了这个行业. 对于互联网创新者来说,地下产业链代表着最接地

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

什么是轮廓? 轮廓是一系列相连的点组成的曲线,代表了物体的基本外形. 轮廓与边缘好像挺像的? 是的,确实挺像,那么区别是什么呢?简而言之,轮廓是连续的,而边缘并不全都连续(见下图示例).其实边缘主要是作为图像的特征使用,比如可以用边缘特征可以区分脸和手,而轮廓主要用来分析物体的形态,比如物体的周长和面积等,可以说边缘包括轮廓. 边缘和轮廓的区别(图片来源:http://pic.ex2tron.top/cv2_understand_contours.jpg) 寻找轮廓的操作一般用于二值化图,所以通

openCV之头文件分析

我们利用openCV开源库进行项目开发时,往往要牵涉到头文件的添加问题,而openCV中头文件众多,该如何选择呢?下面对openCV2.4.10的头文件进行一个简单的梳理,以便能够快速的添加对应的头文件. 1.首先看下opencv文件夹中的头文件 其中cv.h中包含的头文件: #include "opencv2/core/core_c.h"#include "opencv2/core/core.hpp"#include "opencv2/imgproc/i

OpenCV学习代码记录—— Snake轮廓

很久之前学习过一段时间的OpenCV,当时没有做什么笔记,但是代码都还在,这里把它贴出来做个记录. 代码放在码云上,地址在这里https://gitee.com/solym/OpenCVTest/tree/master/OpenCVTest #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/legac

ELK - 实用日志分析系统

目前日志分析系统用的越来越广泛,而且最主流的技术即ELK,下面和大家分享一下: --------------------------------------------------------------------------------------- 一:简 介  Elastic Stack 是 原 ELK Stack 在 5.0 版本加入 Beats 套件后的新称呼,近两年飞速崛起,成为开源界机器数据分析和日志处理第一选择. 组成: kibana:开源工具,为 EL 提供友好的 Web 界

研究分析Q-SPT7P0327620C5GF晶振激光频率微调技术结果

金属中存在大量的自由电子,在激光作用下这些自由电子受到光频电磁波的强迫振动而产生次波,这些次波形成了强烈的反射波和较弱的透射波,透射部分将被电子通过轫致辐射过程而吸收,继而转化为电子的平均动能,再通过电子与SSP-T7-F晶振晶格之间的驰豫过程转变为热能.在分析激光对于物质的作用时,可以遵照解析模型来分析.       通过对用不同技术沉积的硅薄膜激光损伤的研究,发现了对于所有沉积技术都存在一个早期的损伤情态.这种32.768K形态与人为缺陷得到的形态的相似性,从而证明了纳米级激光的损伤是由于纳

OpenCV函数cvFindContous提取图像轮廓

Opencv中提供了很多关于图像轮廓处理的函数,这里我用cvFindContours函数来提取轮廓,并用cvDrawContours函数将提取的轮廓画出来.函数cvFindContours的第一个参数就是我们要进行提取轮廓的目标图像,这里要注意,这个图像必须是一个二值图.得到二值图的方法有很多,这里我采用的是cvThreshold函数,通过设置阀值来得到相应的二值图.当然,这里阀值的选择根据不同的图像可以自己适当的调整,这样效果就比较好了!下面是源码: #include<cv.h> #incl

opencv 模板匹配,在图像中寻找物体

使用模板匹配在图像中寻找物体 模板匹配 模板匹配就是用来在大图中找小图,也就是说在一副图像中寻找另外一张模板图像的位置: opencv中用 cv.matchTemplate() 实现模板匹配. 模板匹配的原理其实很简单,就是不断地在原图中移动模板图像去比较,有6种不同的比较方法,详情可参考:TemplateMatchModes 1. 平方差匹配CV_TM_SQDIFF:用两者的平方差来匹配,最好的匹配值为0 2. 归一化平方差匹配CV_TM_SQDIFF_NORMED 3. 相关匹配CV_TM_

keystone 认证深度研究分析

一.Keystone Token深度概述 Keystone作为OpenStack项目基础认证模块,目前支持的token类型分别是uuid.pkiz.pki.fernet. 首先,简要叙述一下这四种类型的原理及其优缺点. uuid 比较简单,采用随机生成的序列(128位,以16进制表示)作为id,并构造token内容,需要持久化后端数据库支撑,比如MySQL数据库存储.优点,实现简单:缺点是持久化查询.每次访问都需要keystone相关服务进行认证. pki(pkiz) 基于cms算法,token