霍夫圆检测能检测出目标图像中存在的圆,但在实际使用中,参数调节存在很大的困难,故在本博文中对霍夫圆检测的原理、参数列表、优化经验进行分析总结。详细的列出了各个参数的调节依据,实现了在复杂背景下的霍夫圆检测。
1. 原理介绍
1.1 基本原理
相关知识:
霍夫圆检测与霍夫变换密切相关,霍夫变换是基于极坐标系(是由半径与夹角所描述的一种坐标系)与笛卡尔坐标系(普通的平面坐标系)的相互转变而实现的。笛卡尔坐标系上的一个点,变换到极坐标系上就变成了一条线;反之亦然。
然而,基于霍夫变换的霍夫圆检测方法计算量极大,不适合实际应用。在opencv的实现中,是使用霍夫梯度算法进行圆检测。
参考链接:https://www.cnblogs.com/bjxqmy/p/12333022.html
1.2 霍夫梯度法的原理
1.把原图做一次 Canny 边缘检测,得到边缘检测的二值图。
2.对原始图像执行一次 Sobel 算子,计算出所有像素的邻域梯度值。
3.初始化圆心空间 N(a,b),令所有的 N(a,b)=0。若要求圆心在图像中,则a,b值的范围分别对应图像的宽高,N(a,b)表示一共有a*b个。
4.遍历 Canny 边缘二值图中的所有非零像素点,沿着梯度方向 (切线的垂直方向,根据Sobel 算子计算出的垂直梯度及水平梯度得来)固定搜索半径范围画线,将线段经过的所有累加器中的点 (a,b) 的 N(a,b)+=1。
5.统计排序 N(a,b),得到可能的圆心(N(a,b) 越大,越有可能是圆心)。
参考链接:https://zhuanlan.zhihu.com/p/427270299
1.3 霍夫梯度法缺点
1.在霍夫梯度法中,使用 Sobel 导数来计算局部梯度,那么随之而来的假设是,其可以视作等同于一条基于几个局部点的切线,并这个不是一个数值稳定的做法。在大多数情况下,这样做会得到正确的结果,但或许会在输出中产生一些噪声。
2.在边缘图像中的整个非0像素集被看做每个中心的候选部分,若没有合理设置梯度方向的搜索半径范围,则会导致计算量巨大。
3.因为霍夫梯度算法通过圆弧切线的垂直线进行圆心累加检测,当存在同心圆时,会累加到同一个圆心,这导致只会选择到累加值最大的圆。
1.4 参数详情
霍夫圆检测的使用代码如下
method=cv2.HOUGH_GRADIENT
circles=cv2.HoughCircles(image, method, dp=1, minDist=10, param1=None, param2=None, minRadius=None, maxRadius=None)
其参数列表详情如下:Circles:
用来存储HoughCircles的结果,类型为list,list中对象格式为x,y,r;image:
输入图像,即源图像,8位单通道图像,如果使用彩色图像,需要先转换成灰度图像;method:
定义检测图像中圆的方法。目前唯一实现的方法是cv2.HOUGH_GRADIENT;dp:
图像像素分辨率与参数空间分辨率的比值(官方文档上写的是图像分辨率与累加器分辨率的比值,它把参数空间认为是一个累加器,毕竟里面存储的都是经过的像素点的数量),dp=1,则参数空间与图像像素空间(分辨率)一样大,dp=2,参数空间的分辨率只有像素空间的一半大;#通过设置dp可以减少计算量minDist:
检测到的圆中心(x,y)坐标之间的最小距离。如果minDist太小,则会保留大部分圆心相近的圆。如果minDist太大,则会将圆心相近的圆进行合并(若两圆心距离 < minDist,则认为是同一个圆)。param1:
canny 边缘检测的高阈值,低阈值被自动置为高阈值的一半,默认为 100。;param2:
累加平面某点是否是圆心的判定阈值。大于该阈值才判断为圆。当值设置的很小是,检测到的圆越多。默认值为 100;minRadius:
半径的最小大小(以像素为单位)默认为 0;maxRadius:
半径的最大大小(以像素为单位)默认为 0。
2. 实际应用
2.1 基本运行代码
以下代码,实现了读取图像进行霍夫圆检测,试图检测出下图车辆中的车轮。但其参数设置有待调整优化。
import cv2
import numpy as np
import time
if __name__ == "__main__":
name="test.jpg"
img = cv2.imread(name)
t1=time.time()
GrayImage=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(GrayImage, cv2.HOUGH_GRADIENT, 1, 40, param1=70, param2=30, minRadius=0,maxRadius=0)
t2=time.time()
print("运行时间:",t2-t1)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
x,y,r=i[0],i[1],i[2]
#draw the outer circle
cv2.circle(img,(x,y),r,(0,255,0),2)
cv2.circle(img,(x,y),2,(0,0,255),3)
cv2.imshow("img",img)
cv2.waitKey()
原始图像内容如下:
2.2 优化方案
在运行以上代码时,因计算量巨大,电脑直接卡死。(原因1:图像缺乏处理优化、原因2:霍夫圆参数不合理、)
2.2.1 图像优化
图像优化是为了减少霍夫圆检测过程中的干扰点,因为3个点就可以构成一个圆。
边缘噪声滤除
1、对图像进行中值|均值滤波优化,减少图像中存在的边缘噪声。
所增加的代码如下,在16s内终于出了结果
#GrayImage= cv2.medianBlur(GrayImage,7)#中值滤波 滤除背景噪声
GrayImage= cv2.blur(GrayImage,(7,7))#均值滤波 滤除背景噪声
计算量削减
2、对图像size进行优化,因为进行霍夫圆检测时,图像size对检测精度影响不大。但对图像进行下采样(缩小后)可以大幅度减少计算量。
所增加的代码如下,在0.76s内出了结果,但也不是很准。若缩放到0.25倍,则可在0.06秒内跑出结果
GrayImage=cv2.resize(GrayImage,None,fx=0.5,fy=0.5)
正圆保持
此步骤非必须。
在某些特殊情况,如钢卷的圆心检测,由于重力作用,钢卷的侧面圆被压成了椭圆,需要对其y方向(垂直方向)进行拉伸。如以下代码,将图像在y方向拉伸了1.2倍,此时钢卷的侧面椭圆被拉伸成了正圆。img=cv2.resize(img,None,fx=1,fy=1.2)
2.2.2 检测参数优化
检索半径优化
通过对上图分析,发现检测到的圆太多了,且初步预估车轮的像素半径范围为20-80(通过多次设置范围,确定车轮像素在20~80之间
)。
此时的霍夫圆检测参数如下:minRadius=20,maxRadius=80
circles = cv2.HoughCircles(GrayImage, cv2.HOUGH_GRADIENT, 1, minDist=40, param1=70, param2=30, minRadius=20,maxRadius=80)
此时检测效果如下,代码仅在0.03秒内跑出结果(比原先快了20倍),因此限定了霍夫圆检索半径。
圆心累加优化
经过上个步骤优化后,仍有实际中不存在的圆被检测出来。这是因为累加圆心(圆弧上的在圆心上的累加值,累加值超过该阈值则被认为是一个圆)设置不合理所导致的。
此时的霍夫圆检测参数如下:param2=50, minRadius=20,maxRadius=80
circles = cv2.HoughCircles(GrayImage, cv2.HOUGH_GRADIENT, 1, minDist=4, param1=70, param2=50, minRadius=20,maxRadius=80)
此时检测效果如下,仅在0.03秒内跑出结果(跟上一步骤相同),因此限定了霍夫圆检索半径
圆心距离优化
通过上述步骤后,发现针对某些圆会检测出多个圆心距离很近的圆。原先的圆心距设置很合理,但为了出优化效果,故将上一步的圆心距离设置的很小
。这是由于检测步骤中的最小圆心距离设置不合理所导致的。
此时的霍夫圆检测参数如下:minDist=20, param2=50, minRadius=20,maxRadius=80
此时检测效果如下,已经能较好的定位到车轮区域。
文章来源:https://www.toymoban.com/news/detail-758165.html
最优完整代码
额外说明:若对检测到的圆效果存在疑惑时,可以自行对原图进行cany求边缘,以校验参数param1设置的是否合理
,既根据cany运算的效果来调整param1的值(其为canny 边缘检测的高阈值),通常该值不需要进行调整,因为该值对效果影响不大。文章来源地址https://www.toymoban.com/news/detail-758165.html
import cv2
import numpy as np
import time
if __name__ == "__main__":
name="test.jpg"
img = cv2.imread(name)
t1=time.time()
img=cv2.resize(img,None,fx=0.5,fy=0.5)
GrayImage=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#GrayImage= cv2.medianBlur(GrayImage,7)#中值滤波 滤除背景噪声
GrayImage= cv2.blur(GrayImage,(7,7))#均值滤波 滤除背景噪声
circles = cv2.HoughCircles(GrayImage, cv2.HOUGH_GRADIENT, 1, minDist=20, param1=70, param2=50, minRadius=20,maxRadius=80)
t2=time.time()
print("运行时间:",t2-t1)#只统计霍夫圆检测时间
#这里进行圆绘制
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
x,y,r=i[0],i[1],i[2]
#draw the outer circle
cv2.circle(img,(x,y),r,(0,255,0),2)
cv2.circle(img,(x,y),2,(0,0,255),3)
cv2.imshow("img1",img)
cv2.waitKey()
到了这里,关于opencv 十一 霍夫圆检测原理及高级使用案例(含优化步骤)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!