霍夫圆变换是将二维图像空间中一个圆转换为该圆半径、圆心横纵坐标(r,a,b)所确定的三维参数空间中一个点的过程。由x-y坐标系转换到a-b坐标系。写成如下形式(a-x)²+(b-y)²=r²。那么x-y坐标系中圆形边界上的一点对应到a-b坐标系中即为一个圆。.那x-y坐标系中一个圆形边界上有很多个点,对应到a-b坐标系中就会有很多个圆。由于原图像中这些点都在同一个圆形上,那么转换后a,b必定也满足a-b坐标系下的所有圆形的方程式。直观表现为这许多点对应的圆都会相交于一个点,那么这个交点就可能是圆心(a, b)。设定一个阈值,霍夫空间内累加和大于该阈值的点就对应于圆心。
HoughCircles函数使用的算法改进的霍夫变换——2-1霍夫变换(21HT)。也就是把霍夫变换分为两个阶段,从而减小了霍夫空间的维数。第一阶段用于检测圆心,第二阶段从圆心推导出圆半径。检测圆心的原理是圆心一定是在圆上的每个点的模向量上,即在垂直于该点并且经过该点的切线的垂直线上,这些圆上的模向量的交点就是圆心。该方法所用的霍夫空间与图像空间的性质相同,因此它仅仅是二维空间。检测圆半径的方法是从圆心到圆周上的任意一点的距离(即半径)是相同,只要确定一个阈值,只要相同距离的数量大于该阈值,我们就认为该距离就是该圆心所对应的圆半径,该方法只需要计算半径直方图,不使用霍夫空间。圆心和圆半径都得到了,那么通过公式1一个圆形就得到了。从上面的分析可以看出,2-1霍夫变换把标准霍夫变换的三维霍夫空间缩小为二维霍夫空间,因此无论在内存的使用上还是在运行效率上,2-1霍夫变换都远远优于标准霍夫变换。但该算法有一个不足之处就是由于圆半径的检测完全取决于圆心的检测,因此如果圆心检测出现偏差,那么圆半径的检测肯定也是错误的。2-1霍夫变换的具体步骤为:
第一阶段:检测圆心
1.1、对输入图像边缘检测;
1.2、计算图形的梯度,并确定圆周线,其中圆周的梯度就是它的法线;
1.3、在二维霍夫空间内,绘出所有图形的梯度直线,某坐标点上累加和的值越大,说明在该点上直线相交的次数越多,也就是越有可能是圆心;
1.4、在霍夫空间的4邻域内进行非最大值抑制;
1.5、设定一个阈值,霍夫空间内累加和大于该阈值的点就对应于圆心。
第二阶段:检测圆半径
2.1、计算某一个圆心到所有圆周线的距离,这些距离中就有该圆心所对应的圆的半径的值,这些半径值当然是相等的,并且这些圆半径的数量要远远大于其他距离值相等的数量;
2.2、设定两个阈值,定义为最大半径和最小半径,保留距离在这两个半径之间的值,这意味着我们检测的圆不能太大,也不能太小;
2.3、对保留下来的距离进行排序;
2.4、找到距离相同的那些值,并计算相同值的数量;
2.5、设定一个阈值,只有相同值的数量大于该阈值,才认为该值是该圆心对应的圆半径;
2.6、对每一个圆心,完成上面的2.1~2.5步骤,得到所有的圆半径。
引用https://blog.csdn.net/zhaocj/article/details/50454847
cv.HoughCircles(image, method, dp, minDist, circles, param1, param2, minRadius, maxRadius)
返回值为shape为(1,x,3),三维,其中x为检测到的圆的个数
import cv2 as cv
import numpy as np
def HoughCircles_demo(image):
gay_img = cv.cvtColor(image, cv.COLOR_BGRA2GRAY)
img = cv.medianBlur(gay_img, 7) # 进行中值模糊,去噪点
#要对图像进行滤波去除噪点并转为灰度图像,滤波方法视图像而定
circles = cv.HoughCircles(img, cv.HOUGH_GRADIENT, 1, 50, param1=100, param2=30, minRadius=0, maxRadius=0)
print(np.shape(circles))
circles = np.uint16(np.around(circles))
print(circles)
for i in circles[0, :]: # 遍历矩阵每一行的数据
cv.circle(image, (i[0], i[1]), i[2], (0, 255, 0), 2)
cv.circle(image, (i[0], i[1]), 2, (0, 0, 255), 3)
cv.imshow('circle',image)
img = cv.imread("yuantu.jpg")
cv.imshow("gay_img", img)
HoughCircles_demo(img)
cv.waitKey(0)
cv.destroyAllWindows()