咱们已经获得了感兴趣的轮廓,下一步就是要对轮廓进行选择,有一些轮廓是须要——有一些是不须要的,是噪音。经过判断一个轮廓是否为圆,在不少状况下能够帮助咱们来作这相当重要的一步。
简单的状况,好比下图的啤酒瓶缺口检测:
因为瓶口是有缺陷的,形成最大外轮廓不闭合——这显然和“圆”差距很远,那反过来讲,那些差距比较小的轮廓可能就是没有缺陷的。
再来看比较复杂的状况,咱们来“数钢管”。下图是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