使用OpenCV和Python进行Hough圆检测

10

我想使用OpenCV的(霍夫)圆检测来检测圆形。我在黑色背景上创建了一个实心圆,试图调整参数,使用模糊等方法,但我无法让它找到任何东西。

有什么想法、建议等都可以,谢谢!

我的当前代码大致如下:

import cv2
import numpy as np

"""
params = dict(dp=1,
              minDist=1,
              circles=None,
              param1=300,
              param2=290,
              minRadius=1,
              maxRadius=100)
"""

img = np.ones((200,250,3), dtype=np.uint8)
for i in range(50, 80, 1):
    for j in range(40, 70, 1):
        img[i][j]*=200

cv2.circle(img, (120,120), 20, (100,200,80), -1)


gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
canny = cv2.Canny(gray, 200, 300)

cv2.imshow('shjkgdh', canny)
gray = cv2.medianBlur(gray, 5)
circles = cv2.HoughCircles(gray, cv2.cv.CV_HOUGH_GRADIENT, 1, 20,
              param1=100,
              param2=30,
              minRadius=0,
              maxRadius=0)

print circles
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
    cv2.circle(img,(i[0],i[1]),i[2],(0,255,0),2)
    cv2.circle(img,(i[0],i[1]),2,(0,0,255),3)

cv2.imshow('circles', img)
k = cv2.waitKey(0)
if k == 27:
    cv2.destroyAllWindows()

好的,由于某种原因,它突然可以使用相当多的参数了。我昨天检查了所有的东西,但是没有任何东西能够工作,但现在它可以了。这对我来说没有太多意义,但是... - Shin
霍夫圆变换在实践中有点棘手。 - a-Jays
3个回答

21

您的代码运行得非常好,问题在于您的HoughCircles阈值参数。

让我们尝试从OpenCV文档中了解您正在使用的参数:

param1-第一个特定于方法的参数。在CV_HOUGH_GRADIENT的情况下, 它是传递给Canny()边缘检测器的两个参数中的较高阈值 (较低的一项是两倍小)。

param2-第二个特定于方法的参数。在CV_HOUGH_GRADIENT的情况下, 它是用于在检测阶段圆心的累加器阈值。它越小,就会检测到更多的假圆。 相应于较大累加器值的圆将首先返回。

因此,正如您所看到的,HoughCircles函数在内部调用Canny边缘检测器,这意味着您可以在函数中使用灰度图像,而不是它们的轮廓。

现在将param1减少到30,将param2减少到15,并查看接下来的代码中的结果:

import cv2
import numpy as np

img = np.ones((200,250,3), dtype=np.uint8)
for i in range(50, 80, 1):
    for j in range(40, 70, 1):
        img[i][j]*=200

cv2.circle(img, (120,120), 20, (100,200,80), -1)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

circles = cv2.HoughCircles(gray, cv2.cv.CV_HOUGH_GRADIENT, 1, 20,
              param1=30,
              param2=15,
              minRadius=0,
              maxRadius=0)

print circles
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
    cv2.circle(img,(i[0],i[1]),i[2],(0,255,0),2)
    cv2.circle(img,(i[0],i[1]),2,(0,0,255),3)

cv2.imshow('circles', img)

k = cv2.waitKey(0)
if k == 27:
    cv2.destroyAllWindows()

霍夫圆变换


谢谢 :) 我展示了Canny算法的结果,以便更好地了解HoughCircles算法实际处理的内容。Canny算法的结果总是看起来很干净且相同,这可能导致了我最大的困惑。不知何故,即使我尝试了许多不同的参数,我仍然设定了不适合的参数。由于Canny图像在使用和不使用工作参数时似乎是相同的,因此我仍然不确定为什么某些参数有效而其他参数无效。 - Shin
maxRadius=0是如何工作的?这肯定会限制任何圆形的出现吧? - ComputerScientist
如果您阅读HoughCircle的文档,您会发现maxRadiusminRadius的默认值为0。因此,对于半径大小没有应用任何阈值。故而,获取图像中的所有圆。 - Eliezer Bernart

12
如果你使用HoughCircles不能得到明显圆的像素完美解决方案,那么你可能没有正确使用它。你的错误在于试图自行手动调整超参数,这样是不会起作用的。让电脑为你自动调整参数:
import numpy as np
import argparse
import cv2
import signal

from functools import wraps
import errno
import os
import copy

ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())

image = cv2.imread(args["image"])
orig_image = np.copy(image)
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

cv2.imshow("gray", gray)
cv2.waitKey(0)

circles = None

minimum_circle_size = 100      #this is the range of possible circle in pixels you want to find
maximum_circle_size = 150     #maximum possible circle size you're willing to find in pixels

guess_dp = 1.0

number_of_circles_expected = 1          #we expect to find just one circle
breakout = False

max_guess_accumulator_array_threshold = 100     #minimum of 1, no maximum, (max 300?) the quantity of votes 
                                                #needed to qualify for a circle to be found.
circleLog = []

guess_accumulator_array_threshold = max_guess_accumulator_array_threshold

while guess_accumulator_array_threshold > 1 and breakout == False:
    #start out with smallest resolution possible, to find the most precise circle, then creep bigger if none found
    guess_dp = 1.0
    print("resetting guess_dp:" + str(guess_dp))
    while guess_dp < 9 and breakout == False:
        guess_radius = maximum_circle_size
        print("setting guess_radius: " + str(guess_radius))
        print(circles is None)
        while True:

            #HoughCircles algorithm isn't strong enough to stand on its own if you don't
            #know EXACTLY what radius the circle in the image is, (accurate to within 3 pixels) 
            #If you don't know radius, you need lots of guess and check and lots of post-processing 
            #verification.  Luckily HoughCircles is pretty quick so we can brute force.

            print("guessing radius: " + str(guess_radius) + 
                    " and dp: " + str(guess_dp) + " vote threshold: " + 
                    str(guess_accumulator_array_threshold))

            circles = cv2.HoughCircles(gray, 
                cv2.cv.CV_HOUGH_GRADIENT, 
                dp=guess_dp,               #resolution of accumulator array.
                minDist=100,                #number of pixels center of circles should be from each other, hardcode
                param1=50,
                param2=guess_accumulator_array_threshold,
                minRadius=(guess_radius-3),    #HoughCircles will look for circles at minimum this size
                maxRadius=(guess_radius+3)     #HoughCircles will look for circles at maximum this size
                )

            if circles is not None:
                if len(circles[0]) == number_of_circles_expected:
                    print("len of circles: " + str(len(circles)))
                    circleLog.append(copy.copy(circles))
                    print("k1")
                break
                circles = None
            guess_radius -= 5 
            if guess_radius < 40:
                break;

        guess_dp += 1.5

    guess_accumulator_array_threshold -= 2

#Return the circleLog with the highest accumulator threshold

# ensure at least some circles were found
for cir in circleLog:
    # convert the (x, y) coordinates and radius of the circles to integers
    output = np.copy(orig_image)

    if (len(cir) > 1):
        print("FAIL before")
        exit()

    print(cir[0, :])

    cir = np.round(cir[0, :]).astype("int")

    for (x, y, r) in cir:
        cv2.circle(output, (x, y), r, (0, 0, 255), 2)
        cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)

    cv2.imshow("output", np.hstack([orig_image, output]))
    cv2.waitKey(0)

上述代码将这个:Original

转换成这个:

HoughCircles

想要了解这个正在做什么的更多信息,请参阅:https://dev59.com/m5jga4cB1Zd3GeqPJmQn#46500223


很酷的代码,运行相当不错!只有一些注释。似乎minimum_circle_size根本没有被使用。我会将“if guess_radius < 40”更改为“if guess_radius < minimum_circle_size”。另外,“cv2.cv.CV_HOUGH_GRADIENT”现在是“cv2.HOUGH_GRADIENT”。 - Dmitry Kamenetsky
1
那段代码是匆忙编写的,所以存在漏洞。该代码是一种布朗运动尝试,“找出我想要的圆的半径”,因为houghcircles返回了许多结果。因此,如果知道所需圆的确切半径,houghcircles会产生完美的结果。Houghcircles内部投票系统的问题在于它会在背景中的缺陷中找到无效的圆。 - Eric Leschinski

0
我已经为这个算法编写了一个GUI,您可以更改参数以查看它的工作原理。您可以从Github上查看它:
Github链接:https://github.com/pwwiur/hough-counter

安装:

将其克隆到您的目录中:

git clone https://github.com/pwwiur/hough-counter.git

安装要求:

pip install numpy opencv-python matplotlib pillow

运行:

python main.py

演示

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接