咱們已經獲得了感興趣的輪廓,下一步就是要對輪廓進行選擇,有一些輪廓是須要——有一些是不須要的,是噪音。經過判斷一個輪廓是否爲圓,在不少狀況下能夠幫助咱們來作這相當重要的一步。
簡單的狀況,好比下圖的啤酒瓶缺口檢測:
因爲瓶口是有缺陷的,形成最大外輪廓不閉合——這顯然和「圓」差距很遠,那反過來講,那些差距比較小的輪廓可能就是沒有缺陷的。
再來看比較複雜的狀況,咱們來「數鋼管」。下圖是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
*s
/ (c
*c);
//afa計算
afa
= abs(afa
-
1);
比較不一樣afa閾值狀況下,對此輪廓篩選結果
afa(越小越好) |
結果 |
無 |
|
0.5 |
|
0.3 |
|
0.4 |
|
從結果上能夠明顯看出,afa方法很好地過濾掉了噪音。
感謝閱讀至此,但願有所幫助。!
參考資料:
https://bbs.csdn.net/topics/50480119