OpenCV—圆检测
圆检测
- 圆的表示方式
两点确定一条直线,不在一条直线上的三点确定一个圆。与使用(r,theta)来表示一条直线相似,使用(a,b,r)来确定一个圆心为(a,b)半径为 r 的圆。
- 如何判断多个点是否在一个圆上
同样经过一个点可以作出无数个圆,假设某个点平面坐标为(xi,yi),使用的参数为(ai,bi,ri)则经过此点的圆的表达式为(xi-ai)^2 +(yi-bi) ^ 2=ri ^ 2。对于点(xj,yj),必定存在(aj,bj,rj)使得 近似计算中 ai=aj,bi= bj, ri = rj,即两个点在同一个圆上;同理如果三个点在同一个圆上,则也必须存在 ai=aj =ak = a, bi=bj=bk = b, ri=rj=rk = r 的情况。
- 如何检测圆
要将圆检测出来,也就是要将上述的(a,b,r)求解出来。
假设r确定,此时点(x,y)又已知,根据(x-a)^2 +(y-b) ^ 2=r ^ 2 ,则(a,b)的轨迹在几何上则变成了以(x,y)为圆心,r为半径的圆;而r不确定时,(a,b,r)的轨迹变成了以(x,y)为顶点的一个圆锥。则(ai,bi,ri),(aj,bj,rj), (ak,bk,rk)的圆为下图中圆锥面的角点A。
例子用一个opencv的霍夫变换检测圆:
https://blog.csdn.net/kh1445291129/article/details/45674417
找到灰度图后进行双边滤波,利用houghcircle检测圆,再利用circle将圆显示出来。
圆形的表达式为(x−xcenter)2+(y−ycenter)2=r2(x−xcenter)2+(y−ycenter)2=r2,一个圆环的确定需要三个参数。那么霍夫变换的累加器必须是三维的,但是这样的计算效率很低。
这里opencv中使用霍夫梯度的方法,这里利用了边界的梯度信息
首先对图像进行canny边缘检测,对边缘中的每一个非0点,通过Sobel算法计算局部梯度。那么计算得到的梯度方向,实际上就是圆切线的法线。三条法线即可确定一个圆心,同理在累加器中对圆心通过的法线进行累加,就得到了圆环的判定。
其他形状
无论是直线还是圆,都是根据本身的几何形状的数学性质,直线根据极坐标的(r,theta)来表示,圆根据(a,b,r)来表示。要检测出直线或者圆其实就是要找到这样的(r,theta)或者(a,b,r)。根据相关的直线或圆的表达式反推出(r,theta)及(a,b,r)的轨迹,相应轨迹的交点就是要找到的直线或圆或想要找到的形状。
霍夫圆检测
加载一幅图像并对其模糊化以降噪 对模糊化后的图像执行霍夫圆变换 . 在窗体中显示检测到的圆.
def detect_circle_demo(image):
# dst = cv.bilateralFilter(image, 0, 150, 5) #高斯双边模糊,不太好调节,霍夫噪声敏感,所以要先消除噪声
# cv.imshow("1",dst)
# dst = cv.pyrMeanShiftFiltering(image,5,100) #均值迁移,EPT边缘保留滤波,霍夫噪声敏感,所以要先消除噪声
# cv.imshow("2", dst)
dst = cv.GaussianBlur(image,(13,15),15) #使用高斯模糊,修改卷积核ksize也可以检测出来
# cv.imshow("3", dst)
gray = cv.cvtColor(dst,cv.COLOR_BGR2GRAY)
circles = cv.HoughCircles(gray,cv.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles)) #around对数据四舍五入,为整数
for i in circles[0,:]:
cv.circle(image,(i[0],i[1]),i[2],(0,0,255),2)
cv.circle(image,(i[0],i[1]),2,(255,0,0),2) #圆心
cv.imshow("detect_circle_demo",image)
src = cv.imread("./c.png") #读取图片
cv.namedWindow("input image",cv.WINDOW_AUTOSIZE) #创建GUI窗口,形式为自适应
cv.imshow("input image",src) #通过名字将图像和窗口联系
detect_circle_demo(src)
cv.waitKey(0) #等待用户操作,里面等待参数是毫秒,我们填写0,代表是永远,等待用户操作
cv.destroyAllWindows() #销毁所有窗口
相关知识补充:
(一)HoughCircles方法
1.加载一幅图像 2.执行高斯模糊以降低噪声:GaussianBlur 3.转成灰度图:cvtColor 4.执行霍夫圆变换:HoughCircles
def HoughCircles(image, method, dp, minDist, circles=None, param1=None, param2=None, minRadius=None, maxRadius=None): # real signature unknown; restored from __doc__
cv.HoughCircles(gray,cv.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)
1.image:输入图像 (灰度图) 2.method:指定检测方法. 现在OpenCV中只有霍夫梯度法 3.dp: 指的是累加器的图像和原图像的比例,如果dp为1,则累加器的图像和原图具有相同的比例,如果dp为2,则累加器图像的长宽均只有原图的一半。` 解释一下: 当dp为1后,那么累加器会在累加器图像[0,w],[0,h]上可能的圆心位置(a,b)进行投票,最后来计算是否达到阈值来判定是否为圆心。 当dp为2后,那么累加器会在累加器图像[0,w/2],[0,h/2]上可能的圆心位置(a,b)进行投票,最后来计算是否达到阈值来判定是否为圆心。 显然,当dp为2后,由于累加器图像的缩小,之前在dp=1上投票的可疑的相邻4个圆心的坐标点,在dp=2的累加器图像上会投票四次(因为长宽只有一半) 与之对应下面的(4,5,6,7,8)都需要调整,例如:minDist 需要扩大一些,param_2需要扩大差不多4倍,半径也需要调小一点 4.minDist :如果圆心之间的距离在这个范围内会被认为是同心圆 5.param_1 = 200: Canny边缘检测的高阈值 6.param_2 = 100: 要检测到一个圆,那么必须检测到多少个点在这个圆的圆周上,实际上就是累加器的阈值 7.min_radius = 0: 能检测到的最小圆半径, 默认为0. 8.max_radius = 0: 能检测到的最大圆半径, 默认为0