在OpenCV里实现霍夫圆检测2
霍夫圆标准算法如下:
但是上面的算法实在太慢了,因为计算量特别惊人,因此基于梯度的霍夫圆检测就是对标准霍夫圆检测的改进。前面也讨论过使用尺规作图时,可以有三点不共线垂直平分线的方法,其实垂值平分线给我们一个提示,就是只要找到法线,就可以找到圆心,有了圆心的位置再去找半径就容易多了。这样比前面蛮力计算要快很多。如果我们再把垂直平分线往外面推,如下图所示:
这时两点的连线就变成了圆上一点的切线了,而这时垂直平分线就变成圆上一点的法线。如下图所示:
从这里得到一个事实,就是同一个圆的所有法线都是交于圆心上,那么就是说只要我们在圆周上找到法线,然后再找到交点,此点就圆心了。接着下来的问题就是怎么样找到法线?前面学习过边缘识别时使用Sobel算子来计算梯度,那么就可以采用这个方法来算出每一点的梯度,其实这个梯度方向就是法线向量的角度。接着下来根据法线的方向,就有事情可做了。假如我们要计算圆的半径有一个最大最小的范围,那么在圆周上一点,可以一点一点地向法线方向两端画射线,相当于创建一个二维计数器,在法线两个方向上每移动一个像素点,就在这个二维计数器上增加一,相当于投票一票。等把圆周上所有的点都遍历之后,就可以在二维计数器找到计数最大的位置,然后从大到小来排序,就把这些点当作可能圆心点坐标。
接然下来就是对半径进行计数投票了,有了圆心就可以计算圆心到每个圆周上点的半径,如果半径一样的计数器增加一,最后就可以认为半径相等最多像素点的坐标点为圆心点。如下图:
在上图里,半径相等有两个点A、B,而C点的半径只有一个点,这样就可以认为A、B共圆,并且半径是OA=OB。因此通过上面两步的算法就可以确认圆的圆心和半径,那么对于圆的识别就算完成了。
在OpenCV里的大体算法如下:
I、估计圆心
1、把原图做一次Canny边缘检测,得到边缘检测的二值图
2、对原始图像执行一次Sobel算子,计算出所有像素的邻域梯度值
3、初始化圆心空间N(a,b)计数器,令所有的N(a,b)=0
4、遍历Canny边缘二值图中的所有非零像素点,沿着梯度方向画线,将线段经过的所有累加器中的点(a,b)的N(a,b)+=1
5、统计排序N(a,b) 计数器,得到可能的圆心
II、估计半径(针对某一个圆心a,b)
1、计算Canny图中所有非0点距离圆心的距离
2、距离从小到大排序,根据阈值,选取合适的可能半径(比如3和3.5都被划为半径值3中)
3、初始化半径空间r,N(r)=0
4、遍历Canny图中的非0点,对于点所满足的半径r,N(r)+=1
5、统计得到可能的半径值
有了上面的知识,来看一下OpenCV里的HoughCircles函数定义:
image:输入图像
circles:检测的圆形,(a,b,r)的参数集合
method:检测使用的方法,目前OpenCV只提供了CV_HOUGH_GRADIENT方法,即霍夫梯度法
dp:图像像素分辨率与参数空间分辨率的比值(官方文档上写的是图像分辨率与累加器分辨率的比值,它把参数空间认为是一个累加器,毕竟里面存储的都是经过的像素点的数量),dp=1,则参数空间与图像像素空间(分辨率)一样大,dp=2,参数空间的分辨率只有像素空间的一半大
minDist:两个圆心之间的最小距离。这个距离设置过小,会导致本来属于一个圆上的点被分散成几个小圆,过大则导致部分小圆检测不出来
param1:CV_HOUGH_GRADIENT过程中执行Canny边缘检测的阈值
param2:参数空间阈值(即至少多少点经过该参数表示的圆),最少投票数
minRadius:半径最小值
maxRadius:半径最大值
用下面的例子来演示这个函数的使用:
#python 3.7.4,opencv4.1
#蔡军生 https://blog.csdn.net/caimouse/article/details/51749579
#
import cv2
import numpy as np
from scipy import signal
import math
#图片的路径
imgname = "rmb2.png"
#读取图片
image = cv2.imread(imgname, cv2.IMREAD_COLOR)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#图片的高度和宽度
h,w = image.shape[:2]
print('imagesize={}-{}'.format(w,h))
img_blur = cv2.medianBlur(gray, 5)
circles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, 1, 100,
param1=200, param2=60, minRadius=10, maxRadius=200)
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
#
cv2.circle(image, (i[0], i[1]), i[2], (0, 255, 0), 2)
#
cv2.circle(image, (i[0], i[1]), 2, (0, 0, 255), 3)
#
cv2.imshow("Image",image)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果输出如下:
https://blog.csdn.net/caimouse/article/details/51749579
上一篇: 在OpenCV里实现霍夫圆检测1
下一篇: OpenCv--霍夫圆检测