欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

在OpenCV里实现霍夫圆检测1

程序员文章站 2022-07-14 11:19:02
...

前面学习了霍夫直线检测,现在把它推广一下用来检测圆,不过它的思路还是用投票来计算交点的方式,但是霍夫空间的坐标系作了改变。让我们来回忆一下初中的几何,如果给出不共线三个点,怎么样画一个圆?如果用尺规作图的方法,如下图:

在OpenCV里实现霍夫圆检测1

这里是采用垂直平分线的方法来找圆,显然速度很快。但是还没有别的法来找圆呢?仔细考虑一下,其实还有别的方法来找圆心的,比如下图:

在OpenCV里实现霍夫圆检测1

在这图里可以发现使用同一个半径不停地画圆,比如以A点、B点、D点画圆,这三个圆相关于C点,那么C点就这三个点共圆的圆心点。从这里会发一个规律,如果以相同的半径在圆周上作圆,所有的圆都会相交于圆心,那么就可以利用这个规律来找圆心了,如下图:

在OpenCV里实现霍夫圆检测1

因此,可以在一个图片上(比如左图),遍历所有黑色的像素,每个像素都作一个给半径r的圆,如果这些圆相交数量越多,那么这么点就是圆周上的圆心,圆的半径为r。这里相交的数量换成霍夫变换的思路就是投票数量。但是还有一个问题,因为给出图像之后怎么知道半径r呢?其实也没有别的好办法,只能使用蛮力,枚举所有半径,从0开始直到图片的像素大小。另外再来看圆的数学公式:

在OpenCV里实现霍夫圆检测1

这个是直角坐标系下的公式,一幅图片里只能给出我们什么已知的条件呢?可以把上面的公式改写一下:

在OpenCV里实现霍夫圆检测1

图片里已知的条件就是x、y、θ,未知有a,b,r这三个,因此需要把图像的二维空间进行升维变换,在三维空间里来处理,用a,b,r三个变量为作坐标轴,如下图所示:

在OpenCV里实现霍夫圆检测1

从这里可以看到半径r是从0开始,不断地向上变大。如果在XY图像里每一个点作不同的半径,变换到参数空间里就成为一个圆锥,这样不同XY里的点,全部变换过来,就变成圆锥进行相交了。如果在某一个半径r上相交的点最多,就是投票最多,那么这个半径r就是要找的半径,这个(a,b)的值就是圆心,这样就完成从图片里寻找半径和圆心的任务。

根据上面的原理,就可以使用代码进行实现:

#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_GRAYSCALE)

#图片的高度和宽度
h,w = image.shape[:2]
print('imagesize={}-{}'.format(w,h))

output = image.copy()

blur_image = cv2.GaussianBlur(image,(3,3),0)
cv2.imshow('Gaussian Blurred Image',blur_image)

edged_image = cv2.Canny(blur_image,75,150)
cv2.imshow('Edged Image', edged_image)

#
height,width = edged_image.shape
radii = 100
acc_array = np.zeros(((height+2*radii,width+2*radii,radii)))

filter3D = np.zeros((30,30,radii))
filter3D[:,:,:]=1
print(acc_array.shape)
#变换为霍夫参数空间
def fill_acc_array(x0,y0,radius):
    for theta in np.linspace(0,360,180):
        a = x0 - radius*math.cos(theta/180.0*math.pi)
        b = y0 - radius*math.sin(theta/180.0*math.pi)

        a = int(round(a))
        b = int(round(b))
        acc_array[b,a,radius]+=1 #投票
#遍历所有边缘元素
edges = np.where(edged_image==255)
for i in range(0,len(edges[0])):

    y=edges[0][i] #行
    x=edges[1][i] #列

    for radius in range(20,55):
        fill_acc_array(x,y,radius)
        
#找到投票多的点
i=0
j=0
while(i<height-30):
    while(j<width-30):
        filter3D=acc_array[i:i+30,j:j+30,:]*filter3D
        max_pt = np.where(filter3D==filter3D.max())

        a = max_pt[0]       
        b = max_pt[1]
        c = max_pt[2]

        b=b+j
        a=a+i

        if(filter3D.max()>90):
            cv2.circle(output,(b,a),c,(0,255,0),2)#标记圆

        j=j+30
        filter3D[:,:,:]=1

    j=0
    i=i+30   

cv2.imshow('Detected circle',output)
#
cv2.imshow("Image",image)

cv2.waitKey(0)
cv2.destroyAllWindows()

结果输出如下:

在OpenCV里实现霍夫圆检测1

输入图片

在OpenCV里实现霍夫圆检测1

高斯平滑后图片

在OpenCV里实现霍夫圆检测1

Canny边缘识别

在OpenCV里实现霍夫圆检测1

检测到圆,并标记出来

https://blog.csdn.net/caimouse/article/details/51749579

相关标签: opencv