HoughCircles无法在此图像上检测到圆圈 [英] HoughCircles can't detect circles on this image

查看:592
本文介绍了HoughCircles无法在此图像上检测到圆圈的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试检测图像中包含一个圆点的圆,但是很遗憾,我无法做到这一点.我正在使用opencv HoughTransform,但找不到使这项工作有效的参数.

src = imread("encoded.jpg",1);
    /// Convert it to gray
    cvtColor(src, src_gray, CV_BGR2GRAY);

    vector<Vec3f> circles;

    /// Apply the Hough Transform to find the circles
    HoughCircles(src_gray, circles, CV_HOUGH_GRADIENT, 1, 10,
        100, 30, 1, 30 // change the last two parameters
        // (min_radius & max_radius) to detect larger circles
        );

    /// Draw the circles detected
    for (size_t i = 0; i < circles.size(); i++)
    {
        cout << "Positive" << endl;
        Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
        int radius = cvRound(circles[i][2]);
        // circle center
        circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0);
        // circle outline
        circle(src, center, radius, Scalar(0, 0, 255), 3, 8, 0);
    }

    /// Show your results
    namedWindow("Hough Circle Transform Demo", CV_WINDOW_AUTOSIZE);
    imshow("Hough Circle Transform Demo", src_gray);
    waitKey(0);

我的图片在这里:

为什么HoughCircles无法在此图像中检测到圆圈?它似乎正在处理其他更简单的图像,例如电路板的图像.

解决方案

我遇到了您的确切问题并找到了解决方案

关键在于对HoughCircles的操作有足够的直觉,因此您可以构建一个程序,该程序针对要在其中查找圆的所有各种图像自动调整超参数.

核心问题,一些直觉

HoughCircles并不能独立运行,即使它建议使用最小和最大半径参数,也需要运行数百或数千次迭代才能在正确的设置中自动调整和自动拨号.然后,完成操作后,您需要进行后处理验证步骤,以100%确保圆是您想要的.问题是您正在尝试通过猜测和检查将输入参数手动调整到HoughCircles.那根本行不通.让计算机为您自动调整这些参数.

何时可以手动调整HoughCircles?

如果要手动对参数进行硬编码,则绝对需要的一件事是将圆的精确半径控制在一两个像素以内.您可以猜测dp分辨率并设置累加器阵列的投票阈值,然后可能就可以了.但是,如果您不知道半径,则HoughCircles输出是无用的,因为它可以在任何地方或任何地方都找不到圆.并假设您确实手工找到了可以接受的调整,并向它显示了一个像素相差几个像素的图像,并且您的HoughCircles出现了怪异现象,并在图像中找到了200个圆.不值钱

有希望:

希望源于HoughCircles即使在大图像上也非常快的事实.您可以为HoughCircles编写程序以完美地自动调整设置.如果您不知道半径,并且半径可能很小或很大,那么您将从一个大的最小距离参数",一个非常好的dp分辨率和一个非常高的投票阈值开始.因此,当您开始进行迭代时,HoughCircles可以预测地拒绝找到任何圈子,因为设置过于激进并且投票无法清除阈值.但是,循环会不断迭代并逐步爬升至最佳设置,让最佳设置成为表示您完成操作的避雷针.您找到的第一个圆圈将是图像中像素完美的最大和最佳圆圈,并且HoughCircles会给您留下深刻的印象,它将正确的像素递给您应有的像素完美的圆圈.只是您必须运行五千次.

示例python代码(对不起,它不是C ++):

它的边缘仍然很粗糙,但是您应该可以清理它,以便在一秒钟内获得令人满意的像素效果.

import numpy as np
import argparse
import cv2
import signal

from functools import wraps
import errno
import os
import copy

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())

# load the image, clone it for output, and then convert it to grayscale
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

#hand tune this
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")

    # loop over the (x, y) coordinates and radius of the circles
    if (len(cir) > 1):
        print("FAIL after")
        exit()

    for (x, y, r) in cir:
        # draw the circle in the output image, then draw a rectangle
        # corresponding to the center of the circle
        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)

    # show the output image
    cv2.imshow("output", np.hstack([orig_image, output]))
    cv2.waitKey(0)

因此,如果您执行此操作,则需要花费5秒钟,但它几乎达到了像素完美(自动调谐器的进一步手动调整使其达到亚像素完美):

上面的代码将其转换为:

对此:

实现此目的的秘诀在于,在开始之前,您要掌握多少信息.如果您知道半径达到某个公差(例如20像素),则可以完美完成.但是,如果您不这样做,则必须对如何谨慎地接近解决方案和投票阈值,如何在最大投票半径范围内变得更加聪明.如果圆的形状奇怪,则dp分辨率将需要更高,而投票阈值将需要探索更低的范围.

I am trying to detect circles in my image containing a circle of dots, but unfortunately I am not able to do so. I am using opencv HoughTransform and I can't find parameters that make this work.

src = imread("encoded.jpg",1);
    /// Convert it to gray
    cvtColor(src, src_gray, CV_BGR2GRAY);

    vector<Vec3f> circles;

    /// Apply the Hough Transform to find the circles
    HoughCircles(src_gray, circles, CV_HOUGH_GRADIENT, 1, 10,
        100, 30, 1, 30 // change the last two parameters
        // (min_radius & max_radius) to detect larger circles
        );

    /// Draw the circles detected
    for (size_t i = 0; i < circles.size(); i++)
    {
        cout << "Positive" << endl;
        Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
        int radius = cvRound(circles[i][2]);
        // circle center
        circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0);
        // circle outline
        circle(src, center, radius, Scalar(0, 0, 255), 3, 8, 0);
    }

    /// Show your results
    namedWindow("Hough Circle Transform Demo", CV_WINDOW_AUTOSIZE);
    imshow("Hough Circle Transform Demo", src_gray);
    waitKey(0);

My Image is here:

Why can't HoughCircles detect circles in this image? It seems to be working on other simpler images like that of a circuit board.

解决方案

I had your exact problem and found a solution

The key is in having enough intuition about what HoughCircles is doing so you can build a program that auto-tunes the hyper parameters for all the various images you want to find circles in.

Core problem, some intuition

HoughCircles doesn't stand on its own, even though it suggests it might with the min and max radius parameters, you need to run hundreds or thousands of iterations to auto-tune and auto-dial in the right settings. Then after you're done you need post processing verification step to be 100% sure that the circle is what you wanted. The problem is you're trying to hand-tune the input parameters to HoughCircles yourself by using guess-and-check. That is NOT going to work, at all. Have the computer auto-tune these parameters for you.

When can hand-tuning for HoughCircles be satisfactory?

If you want to hardcode your parameters by hand, the one thing you absolutly need is the EXACT radius of your circle to within one or two pixels. You can guess the dp resolution and set the accumulator array voting threshold and you'll probably be okay. But if you don't know radius, HoughCircles output is useless because either it finds circles everywhere or nowhere. And suppose you do find an acceptable tuning by hand, you show it an image that's a few pixels different, and your HoughCircles freaks out and finds 200 circles in the image. Worthless.

There is hope:

Hope comes from the fact that HoughCircles is very quick even on large images. You can write a program for HoughCircles to auto-tune the settings perfectly. If you don't know the radius and it could be small or large, you start out with a large "minimum distance parameter", a very fine dp resolution, and a very high voting threshold. So as you start out iterating and HoughCircles predictably refuses to find any circles because settings are way too aggressive and votes don't clear threshold. But the loop keeps iterates and creeping up on the optimal settings, letting the optimal setting be the lightning rod that signals you're done. The first circle you find will be the pixel perfect largest and best circle in the image, and you'll be impressed at HoughCircles handing you a pixel-perfect circle right where it ought to be. It's just you had to run it 5 thousand times.

Example python code (sorry its not C++) :

It's still rough around the edges but you should be able to clean it up so you get a gratifying pixel perfect result in under a second.

import numpy as np
import argparse
import cv2
import signal

from functools import wraps
import errno
import os
import copy

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())

# load the image, clone it for output, and then convert it to grayscale
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

#hand tune this
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")

    # loop over the (x, y) coordinates and radius of the circles
    if (len(cir) > 1):
        print("FAIL after")
        exit()

    for (x, y, r) in cir:
        # draw the circle in the output image, then draw a rectangle
        # corresponding to the center of the circle
        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)

    # show the output image
    cv2.imshow("output", np.hstack([orig_image, output]))
    cv2.waitKey(0)

So if you run that, It takes a good 5 seconds, but it gets it nearly pixel perfect (Further hand-tuning of the auto-tuner gets it sub-pixel perfect):

The above code converts this:

To this:

The secret sauce to make this work is in how much information you have before you begin. If you know radius to a certain tolerance like 20 pixels, then this works perfect you're done. But if you don't, you have to be smart about how you creep up on the radius of maximum votes with carefully approaching resolution and voting threshold. If the circles are oddly shaped, the dp resolution will need to be higher, and the voting threshold will need to explore lower ranges.

这篇关于HoughCircles无法在此图像上检测到圆圈的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆