小言_互联网的博客

在OpenCV里实现霍夫圆检测1

547人阅读  评论(0)

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

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

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

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

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

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

从这里可以看到半径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()

结果输出如下:

输入图片

高斯平滑后图片

Canny边缘识别

检测到圆,并标记出来

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


转载:https://blog.csdn.net/caimouse/article/details/102363788
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场