不是CV方向的,抛砖引玉来一个简单的基于规则的方法吧。
最简单的方法是用霍夫变换实现圆形目标的识别,而且也能用python3和opencv-python库很轻松地实现。先上结果:
霍夫变换的原理并不复杂,下面以检测直线为例来简单介绍下(如果不感兴趣可以直接跳过):
上图有5个排列没啥规律的点,如果我们把它当成现实生活中图片的极简化情况(假设这5个点是黑色像素点),如果要从图片中识别一条最有可能的直线 ,要怎么找呢?一种很自然的思路是看哪一条直线经过了最多的黑色像素点。
霍夫变换的思想是将直线 变换为 的形式, 和 分别变成自变量和因变量,而 和 反而成了参数。我们把 和 展成的二维空间称为参数空间。根据霍夫变换的性质,一个在原空间的点 在参数空间中,作为参数描述了一条直线:
下面我们将原图片中5个点分别变换到参数空间,可得下图:
可以看到,5条直线除了C和D平行以外,其他两两相交(有些交点因为太远在上图无法看到),且A、B、C三条直线交于一点。这个交点代表什么呢?代表了存在相同的参数 和 ,也就是在原空间中A、B、C三点共线!求出交点坐标,也得到了那条直线的方程。
因此,霍夫变换将复杂的多点共线问题转化为简单的多线共点问题,我们只需求出参数空间中有最多条直线穿过的那个交点,就能识别原图中最明显的那条直线。而原空间中斜率不存在的情况(参数空间中直线平行)可以通过转换为极坐标解决。
霍夫变换识别圆同理,但变复杂了一些。我们都知道圆的方程 有三个参数,如果 确定,参数空间是二维,原空间的点在参数空间会对应一个圆 ;当 不确定时,参数空间变成三维,原空间的点则会变成一个个圆锥[1],如果直接求交点,计算量会很大。
OpenCV对该算法进行了优化,称为霍夫梯度法,可以分为估计 和估计 两个步骤[2]。
估计 (圆心):
估计 (半径):
说到这里,opencv-python库的相关API就好懂多了:
cv2.HoughCircles( image, method, dp, minDist, circles=None, param1=None, param2=None, minRadius=None, maxRadius=None )
重要参数说明:
既然是基于规则的方法,我们需要人工定义各个参数。首先我们找到一张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,欢迎参考。