zoukankan      html  css  js  c++  java
  • 如何判断轮廓是否为圆(算法更新)

        我们已经得到了感兴趣的轮廓,下一步就是要对轮廓进行选择,有一些轮廓是需要——有一些是不需要的,是噪音。通过判断一个轮廓是否为圆,在很多情况下可以帮助我们来做这至关重要的一步。
        简单的情况,比如下图的啤酒瓶缺口检测:
        由于瓶口是有缺陷的,造成最大外轮廓不闭合——这显然和“圆”差距很远,那反过来说,那些差距比较小的轮廓可能就是没有缺陷的。
        再来看比较复杂的情况,我们来“数钢管”。下图是connection效果,我们发现了很多轮廓,但是只有一部分更接近圆——这些可能就是我们需要寻找的目标。
        至关重要的一步就是建立数学模型。2017年左右为解决实际问题,我建立了模型一
        基于圆的定义: “平面上到定点的距离等于定长的所有点组成的图形叫做圆.定点称为圆心,定长称为半径.”。那么通过判断当前轮廓到一个定点的距离是否为定长,就可以得到当前轮廓是否更像圆。这个定点就可以采用外接圆圆心。这里的度量方式可以是方差。
      //根据轮廓点和圆心计算方差 参考代码
    float ComputeVariance(std::vector<cv::Point> theContour,Point2f theCenter)
    {
        int a[65535],n;
        float aver,s;
        float sum=0,e=0;
        n = theContour.size();
        for(int i=0;i<n;i++)
        {
            a[i] = GetDistance(theContour[i],theCenter);
            sum+=a[i];
        }
        aver=sum/n;
        for(int i=0;i<n;i++)
            e+=(a[i]-aver)*(a[i]-aver);
        e/=n-1;
        s=sqrt(e);
        return e;
    }
        模型一使用了3年左右,也解决了很多问题,特别对于简单问题来说,效果是很好的。但是它有一个明显的缺点,就是关于这个结果方差的度量,每次都需要不断试验才能够得出一个较好的值。2020年我遇到了前面的“数钢管”问题,出现了新的困难,比如下图:
    当轮廓为长条形的时候(上图红圈),会错误地识别为圆。这种长条型轮廓可以抽象为下图红圈:
        这种情况的方差可能不是很大,虽然直观理解其它,它很不是一个圆。
        经过一段时间思索和寻找,建立模型二:
        基于“圆度”的定义:设平面上一个封闭图形(内部无空洞)的面积为S,它的周长为C,则定义该图形的圆度为:
        
                    4 * PI * S
             Afa = ------------
                      C * C

           正圆的afa为1,轮廓的afa值越接近1,轮廓越解决圆
           //afa参考代码
            double s = cv::contourArea(contours_test[i]);//轮廓面积
            double c = cv::arcLength(contours_test[i], true); //轮廓周长
            float afa = 4 * PI*/ (c*c); //afa计算
            afa = abs(afa - 1);


    比较不同afa阈值情况下,对此轮廓筛选结果

    afa(越小越好)结果
    0.5  
    0.3
    0.4
      
        从结果上可以明显看出,afa方法很好地过滤掉了噪音。
        感谢阅读至此,希望有所帮助。!


    参考资料:

    P.S
    connection效果参考代码,来自GOCVHelper,在Github上可以找到,说明文件:

    //寻找并绘制出彩色联通区域
    vector<VP> connection2(Mat src, Mat& draw) {
        RNG rng(12345);
        draw = Mat::zeros(src.rows, src.cols, CV_8UC3);
        vector<VP>contours;
        findContours(src.clone(), contours, RETR_LIST,CHAIN_APPROX_SIMPLE);
        //由于给大的区域着色会覆盖小的区域,所以首先进行排序操作
        //冒泡排序,由小到大排序
        VP vptmp;
        for (int i = 1; i < contours.size(); i++) {
            for (int j = contours.size() - 1; j >= i; j--) {
                if (contourArea(contours[j]) < contourArea(contours[j - 1]))
                {
                    vptmp = contours[j - 1];
                    contours[j - 1= contours[j];
                    contours[j] = vptmp;
                }
            }
        }
        //打印结果
        for (int i = contours.size() - 1; i >= 0; i--) {
            Scalar  color = Scalar(rng.uniform(0255), rng.uniform(0255), rng.uniform(0255));
            drawContours(draw, contours, i, color, -1);
        }
        return contours;
    }







  • 相关阅读:
    每天一个linux命令(21):chgrp,chown,chmod
    设计模式之单例模式
    每天一个linux命令(20):find命令之exec
    每天一个linux命令(19):find 命令概览
    每天一个linux命令(18):locate 命令
    每天一个linux命令(17):whereis 命令
    【6折抢】戴尔i7新品Latitude高性能商用本
    Spring Cloud Gateway VS Zuul 比较,怎么选择?
    Zookeeper怎么实现分布式锁?
    数据库怎么分库分表,垂直?水平?
  • 原文地址:https://www.cnblogs.com/jsxyhelu/p/12708827.html
Copyright © 2011-2022 走看看