python怎么统计个数_如何用python来统计MacPro的圆圈个数?

不是CV方向的,抛砖引玉来一个简单的基于规则的方法吧。

最简单的方法是用霍夫变换实现圆形目标的识别,而且也能用python3和opencv-python库很轻松地实现。先上结果:计数时顺便用MatplotLib库将识别到的所有圆圈标出

一、霍夫变换的原理

霍夫变换的原理并不复杂,下面以检测直线为例来简单介绍下(如果不感兴趣可以直接跳过):

上图有5个排列没啥规律的点,如果我们把它当成现实生活中图片的极简化情况(假设这5个点是黑色像素点),如果要从图片中识别一条最有可能的直线

equation?tex=y%3Dkx%2Bb ,要怎么找呢?一种很自然的思路是看哪一条直线经过了最多的黑色像素点。

霍夫变换的思想是将直线

equation?tex=y%3Dkx%2Bb 变换为

equation?tex=b%3D-xk%2By 的形式,

equation?tex=k

equation?tex=b 分别变成自变量和因变量,而

equation?tex=x

equation?tex=y 反而成了参数。我们把

equation?tex=k

equation?tex=b 展成的二维空间称为参数空间。根据霍夫变换的性质,一个在原空间的点

equation?tex=%28x%2Cy%29 在参数空间中,作为参数描述了一条直线:参数空间中A(1, 0.5)的图像

下面我们将原图片中5个点分别变换到参数空间,可得下图:

可以看到,5条直线除了C和D平行以外,其他两两相交(有些交点因为太远在上图无法看到),且A、B、C三条直线交于一点。这个交点代表什么呢?代表了存在相同的参数

equation?tex=k

equation?tex=b ,也就是在原空间中A、B、C三点共线!求出交点坐标,也得到了那条直线的方程。

因此,霍夫变换将复杂的多点共线问题转化为简单的多线共点问题,我们只需求出参数空间中有最多条直线穿过的那个交点,就能识别原图中最明显的那条直线。而原空间中斜率不存在的情况(参数空间中直线平行)可以通过转换为极坐标解决。

霍夫变换识别圆同理,但变复杂了一些。我们都知道圆的方程

equation?tex=%28x-a%29%5E2%2B%28y-b%29%5E2%3Dr%5E2 有三个参数,如果

equation?tex=r 确定,参数空间是二维,原空间的点在参数空间会对应一个圆

equation?tex=%28a-x%29%5E2%2B%28b-y%29%5E2%3Dr%5E2 ;当

equation?tex=r 不确定时,参数空间变成三维,原空间的点则会变成一个个圆锥

二、OpenCV的相关算法和API

OpenCV对该算法进行了优化,称为霍夫梯度法,可以分为估计

equation?tex=%28a%2Cb%29 和估计

equation?tex=r 两个步骤

估计

equation?tex=%28a%2Cb%29 (圆心):用Canny算法进行边缘检测,得到边界二值图;

用Sobel算子计算原图的梯度;

遍历边缘二值图中的非0点,沿着梯度方向和反方向画线段(梯度方向为圆弧的法线方向,即半径方向),线段的起点和长度由参数允许的半径区间决定。将线段经过的点在累加器中记数;

对累加器中的点从大到小排序,记数越大越有可能成为圆心,优先估计半径。

估计

equation?tex=r (半径):计算所有边界图中的非0点离圆心的距离,并从小到大排序;

从最小半径

equation?tex=r 开始,距离相差在一个小量范围内的点,都认为是同一个圆,记数属于该半径

equation?tex=r 的非0点数,记为

equation?tex=n

尝试放大半径,同样记数该半径上的点数;

判断两个半径孰优孰劣的依据——点的线密度(

equation?tex=%5Cfrac%7Bn%7D%7Br%7D ),密度越高,半径的可信度越大;

重复以上步骤,直至半径超过参数允许的范围,从而得到最优半径。

说到这里,opencv-python库的相关API就好懂多了:

cv2.HoughCircles(

image,

method,

dp,

minDist,

circles=None,

param1=None,

param2=None,

minRadius=None,

maxRadius=None

)

重要参数说明:image:8位单通道图像(不能是彩色图像)。

method:检测方法,这里只能是cv2.HOUGH_GRADIENT。

dp:用来检测圆心的累加器图像的分辨率与输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。如果dp=1,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半的宽度和高度。

min_dist:霍夫变换检测到的圆的圆心之间的最小距离,即算法能明显区分的两个不同圆之间的最小距离。

param1:Canny算法的高阈值,低阈值设为其的一半。

param2:累加器的阈值,它越小的话,就可以检测到更多残缺的圆,而它越大的话,能通过检测的圆就更加接近完整的圆。

minRadius和maxRadius:给定半径的上下界。

三、具体实现

既然是基于规则的方法,我们需要人工定义各个参数。首先我们找到一张Mac Pro的正面图片,粗略估计下图片中每个圆圈的半径范围:

根据测量结果(45px左右),可以把半径范围设为20到25px之间。半径范围严格一点,就能规避更多我们不需要的圆。

经过一番试验,我们可以调出所有参数。用到OpenCV的代码节选如下:

img = cv2.imread('mp.jpg')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度图像

circle1 = cv2.HoughCircles(

gray,

cv2.HOUGH_GRADIENT,

1,

5,

param1=100,

param2=30,

minRadius=20,

maxRadius=25

)

circles = np.uint16(np.around(circle1[0, :, :])) # 提取为二维并四舍五入

circle_count = 0

for i in circles[:]:

circle_count += 1

答案是158,这毕竟是花了30分钟写出来的代码跑的珍贵成果。花30秒肉眼数一下,没错!(但好像有哪里不对)

完整代码我放进了GitHub,欢迎参考。zamhown/my-zhihu​github.comv2-45fb7d23a32cc6612a53924fac1bfe22_ipico.jpg

参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值