我曾经也有过和你一样的问题,并找到了解决方法
关键在于要对HoughCircles的操作有足够的直觉,以便您可以构建一个程序,自动调整各种图像中寻找圆形所需的超参数。
核心问题,一些直觉
HoughCircles并不是独立存在的,即使它使用最小和最大半径参数,它也需要运行数百或数千次迭代来自动调整和自动拨号正确的设置。然后,在完成后,您需要进行后处理验证步骤,以确保圆形是您想要的100%。问题在于您尝试手动调整输入参数以使用猜测和检查方法来调整HoughCircles自己。这根本行不通。让计算机为您自动调整这些参数。
何时手动调整HoughCircles可以令人满意?
如果您想手动硬编码参数,您绝对需要的是圆的精确半径,误差范围为一到两个像素。您可以猜测dp分辨率并设置累加器阵列投票阈值,您可能会得到满意结果。但是,如果您不知道半径,则HoughCircles的输出是无用的,因为它要么在图像中找到圆形,要么找不到。假设您通过手动调整找到了可接受的调整,则向其显示稍有不同的图像,您的HoughCircles会出现问题,并在图像中找到200个圆。毫无价值。
还有希望:
希望来自于HoughCircles即使在大型图像上也非常快速。您可以编写一个程序,让HoughCircles自动调整设置。如果您不知道半径且可能很小或很大,则从较大的“最小距离参数”,非常细的dp分辨率和非常高的投票阈值开始迭代。因此,当您开始迭代时,HoughCircles可预测地拒绝查找任何圆形,因为设置过于激进且投票未达到阈值。但是,循环保持迭代并逐渐接近最佳设置,让最佳设置成为标志您完成的闪电棒。您找到的第一个圆将是像素完美的最大和最佳圆,在图像中的位置完美无误。只是您必须运行它5000次。
示例Python代码(抱歉,它不是C ++):
它仍然有些粗糙,但您应该能够清理它,以便在不到一秒的时间内获得令人满意的像素完美结果。
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
maximum_circle_size = 150
guess_dp = 1.0
number_of_circles_expected = 1
breakout = False
max_guess_accumulator_array_threshold = 100
circleLog = []
guess_accumulator_array_threshold = max_guess_accumulator_array_threshold
while guess_accumulator_array_threshold > 1 and breakout == False:
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:
print("guessing radius: " + str(guess_radius) +
" and dp: " + str(guess_dp) + " vote threshold: " +
str(guess_accumulator_array_threshold))
circles = cv2.HoughCircles(gray,
cv2.HOUGH_GRADIENT,
dp=guess_dp,
minDist=100,
param1=50,
param2=guess_accumulator_array_threshold,
minRadius=(guess_radius-3),
maxRadius=(guess_radius+3)
)
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
for cir in circleLog:
output = np.copy(orig_image)
if (len(cir) > 1):
print("FAIL before")
exit()
print(cir[0, :])
cir = np.round(cir[0, :]).astype("int")
if (len(cir) > 1):
print("FAIL after")
exit()
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)
所以,如果你运行它,需要大约5秒钟的时间,但它几乎可以完美地完成任务(自动调谐的进一步手动微调可以使其变得次像素完美):
上述代码将此转换为:
转换后如下所示:
使这个工作可行的关键在于在开始之前拥有多少信息。如果您知道半径的容差范围,例如20个像素,那么这个方法可以完美地解决问题并且您就完成了。但是,如果您不知道,则必须通过精心逼近分辨率和投票阈值来聪明地追踪最大投票半径。如果圆形有奇怪的形状,则需要更高的dp分辨率,并且投票阈值需要探索更低的范围。