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。


python_opencv椭圆检测与定位 opencv 圆检测_ci



例子用一个opencv的霍夫变换检测圆:




找到灰度图后进行双边滤波,利用houghcircle检测圆,再利用circle将圆显示出来。


python_opencv椭圆检测与定位 opencv 圆检测_边缘检测_02



python_opencv椭圆检测与定位 opencv 圆检测_ci_03

圆形的表达式为(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)的轨迹,相应轨迹的交点就是要找到的直线或圆或想要找到的形状。


python_opencv椭圆检测与定位 opencv 圆检测_ci_04

python_opencv椭圆检测与定位 opencv 圆检测_边缘检测_05

python_opencv椭圆检测与定位 opencv 圆检测_边缘检测_06

python_opencv椭圆检测与定位 opencv 圆检测_灰度图_07

 霍夫圆检测


加载一幅图像并对其模糊化以降噪对模糊化后的图像执行霍夫圆变换 .在窗体中显示检测到的圆.


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()  #销毁所有窗口

python_opencv椭圆检测与定位 opencv 圆检测_ci_08

相关知识补充:

(一)HoughCircles方法


1.加载一幅图像2.执行高斯模糊以降低噪声:GaussianBlur3.转成灰度图: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