如何判斷輪廓是否為圓(算法更新)


    我們已經得到了感興趣的輪廓,下一步就是要對輪廓進行選擇,有一些輪廓是需要——有一些是不需要的,是噪音。通過判斷一個輪廓是否為圓,在很多情況下可以幫助我們來做這至關重要的一步。
    簡單的情況,比如下圖的啤酒瓶缺口檢測:
     由於瓶口是有缺陷的,造成最大外輪廓不閉合——這顯然和“圓”差距很遠,那反過來說,那些差距比較小的輪廓可能就是沒有缺陷的。
    再來看比較復雜的情況,我們來“數鋼管”。下圖是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;
}








免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM