圆形霍夫变换遗漏圆圈

Circular Hough Transform misses circles

我在 Stack Overflow 上阅读了很多关于 Circular Hough 变换的内容,但我似乎遗漏了一些东西。我编写了一个程序来检测 "Bull's Eye" 目标的圆圈。然而,即使在使用了参数之后,该算法还是相当糟糕——它忽略了大部分圆圈,有一次它找到了一个圆圈,但似乎 "wander off"。我什至尝试应用 "Unsharp Mask" 无济于事。我已经添加了我的代码、我开始使用的图像和输出。我希望有人能给我指出正确的方向。

import cv2
import cv2.cv as cv
import numpy as np
import math
# Load Image
img = cv2.imread('circles1.png',0)
# Apply Unsharp Mask
tmp = cv2.medianBlur(img,5)
img = cv2.addWeighted(img,1.5,tmp,-0.5,0)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)

# Hough Transform
circles = cv2.HoughCircles(img,cv.CV_HOUGH_GRADIENT,1,5,
                            param1=100,param2=100,minRadius=0,maxRadius=0)

circles = np.uint16(np.around(circles))

# Go over circles, eliminating the ones that are not cocentric enough
height, width = img.shape
center = (width/2,height/2)
for i in circles[0,:]:
    # draw the outer circle
    if math.sqrt((center[0]-i[0])**2 + (center[1]-i[1])**2) < 15:
        cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),1)
        # draw the center of the circle
        cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)

cv2.imshow('detected circles',cimg)

cv2.waitKey(0)
cv2.destroyAllWindows()

快速解释:我加载图像,应用Unsharp Mask,使用Hough Transfrom检测圆,然后绘制靠近中心的圆(我发现其他圆是假圆)。

我试过使用参数,这是我得到的最好的。我觉得这是一个足够简单的问题,让我感到困惑。我很感激任何帮助。

我的输入图片:

我的输出图像:

正如我在评论中提到的,您需要 运行 对不同半径范围的 cv2.HoughCircles 进行连续迭代,以确保获得所有圆圈。根据圆形霍夫变换的工作方式,指定具有相当大范围的最小和最大半径将是不准确的,而且速度也会很慢。他们不会在文档中告诉您这一点,但要使循环霍夫变换成功运行,以下两件事必须有效:

maxRadius < 3*minRadius
maxRadius - minRadius < 100

综上所述,盲目地使最小半径非常小而最大半径非常大不会给你很好的结果。因此,您可以做的是从...开始...说...radius=1,然后以 20 为步长迭代到 radius=300。在每个 20 块之间,运行 cv2.HoughCircles 并使用这些轮廓更新您的图像。

这样做只需对您的代码进行很少的修改。顺便说一句,我去掉了不锐化的蒙版,因为我用它得到的效果很差。我还稍微更改了 cv2.HoughCircles 中的几个参数,以根据您的情况使其尽可能地发挥作用:

import cv2
import cv2.cv as cv
import numpy as np
import math
# Load Image
img = cv2.imread('circles1.png',0)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)

# Specify different radii
radii = np.arange(0,310,10)

# For each pair of radii...
for idx in range(len(radii)-1):
    # Get the minimum and maximum radius
    # Note you need to add 1 to each minimum
    # as the maximum of the previous pair covers this new minimum
    minRadius = radii[idx]+1
    maxRadius = radii[idx+1]

    # Hough Transform - Change here
    circles = cv2.HoughCircles(img,cv.CV_HOUGH_GRADIENT,1,5,
                               param1=25,param2=75,minRadius=minRadius,maxRadius=maxRadius)

    # Skip if no circles are detected - Change here
    if circles is None:
        continue

    circles = np.uint16(np.around(circles))

    # Go over circles, eliminating the ones that are not cocentric enough
    height, width = img.shape
    center = (width/2,height/2)
    for i in circles[0,:]:
        # draw the outer circle
        if math.sqrt((center[0]-i[0])**2 + (center[1]-i[1])**2) < 15:
            cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),1)
            # draw the center of the circle
            cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)

cv2.imshow('detected circles',cimg)

cv2.waitKey(0)
cv2.destroyAllWindows()

我得到这个数字:

不幸的是,它并不完美,因为它没有检测到所有的圆圈。您必须尝试使用​​ cv2.HoughCircles 函数,直到获得良好的结果。


但是,我不建议在这里使用 cv2.HoughCircles。我可以建议改用 cv2.findContours 吗?这会找到图像中的所有轮廓。在这种情况下,这些将是黑色圆圈。但是,您需要反转图像,因为 cv2.findContours 假设非零像素是对象像素,因此我们可以从假设 np.uint8 类型的图像中减去 255:

# Make copy of original image
cimg2 = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

# Find contours
contours,_ = cv2.findContours(255 - img, cv2.RETR_LIST, cv.CV_CHAIN_APPROX_NONE)

# Draw all detected contours on image in green with a thickness of 1 pixel
cv2.drawContours(cimg2, contours, -1, color=(0,255,0), thickness=1)

# Show the image
cv2.imshow('detected circles', cimg2)
cv2.waitKey(0)
cv2.destroyAllWindows()

这是我得到的: