OpenCV HoughCircles超参数自动调优问题求助
问题
我是编程新手,用OpenCV的HoughCircles检测单张图片里的单个圆形,手动调参的代码效果不错:
detected_circles = cv2.HoughCircles(bw_contoured_img8, cv2.HOUGH_GRADIENT, 1, 20, param1 = 255, param2 = 30, minRadius = 25, maxRadius = 80)
但手动调参随机性强、可复现性差,每次都得看结果调整。现在要处理一批相似但有细微平移/旋转、预处理差异的图片,想实现超参数自动调优。参考了一段自动调参代码,运行后检测出的圆形过大,效果远不如手动调参,求解决:
# load the image, clone it for output, and then convert it to grayscale image = bw_contoured_img8 orig_image = np.copy(image) output = image.copy() circles = None min_circle_size = 30 maximum_circle_size = 70 # Maximum possible circle size we'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 tuning max_guess_accumulator_array_threshold = 100 # Minimum of 1, no maximum, 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("ressetting 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(orig_image, cv2.HOUGH_GRADIENT, dp=guess_dp, # Resolution of accumulator array minDist=10, # Number of pixels center of circles should be from each other param1=255, 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 < min_circle_size: 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 output = cv2.circle(output, (x, y), r, (255, 255, 255), 2) output = cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (255, 255, 255), -1) # Show the output image plt.imshow(output)
注:cv2.imshow会导致内核崩溃,所以用plt.imshow显示结果。
解决方案
问题根源
- 半径搜索逻辑偏误:代码从最大半径开始往下搜,找到符合条件的圆就直接终止,优先记录了大半径结果,而非精准匹配的半径。
- 参数区间与手动调参不符:自动调参缩小了半径范围(手动是25-80,自动是30-70),还把
minDist从20改成10,容易导致误检。 - 结果筛选缺失:
circleLog存了所有符合条件的圆,但最后直接遍历覆盖输出,没选最优结果。
修改后的代码
import cv2 import numpy as np import copy import matplotlib.pyplot as plt image = bw_contoured_img8 orig_image = np.copy(image) output = image.copy() circles = None # 对齐手动调参的半径范围 min_circle_size = 25 maximum_circle_size = 80 guess_dp = 1.0 number_of_circles_expected = 1 breakout = False # 参考手动调参的param2,缩小搜索范围减少无效计算 max_param2 = 50 min_param2 = 20 circleLog = [] # 从高到低遍历param2,优先保留投票多的可靠结果 for param2 in range(max_param2, min_param2 - 1, -2): guess_dp = 1.0 while guess_dp <= 3.0 and not breakout: # dp不设过大,避免精度丢失 # 从半径范围中间开始搜索,平衡大小匹配 guess_radius = (min_circle_size + maximum_circle_size) // 2 while True: circles = cv2.HoughCircles(orig_image, cv2.HOUGH_GRADIENT, dp=guess_dp, minDist=20, # 对齐手动调参的minDist,避免密集误检 param1=255, param2=param2, minRadius=min_circle_size, maxRadius=maximum_circle_size ) if circles is not None: if len(circles[0]) == number_of_circles_expected: # 记录param2值,方便后续筛选最优结果 circleLog.append((param2, copy.copy(circles))) break guess_radius -= 2 if guess_radius < min_circle_size: break guess_dp += 0.5 # 选param2最高的结果(投票数最多,检测最可靠) if circleLog: circleLog.sort(reverse=True, key=lambda x: x[0]) best_param2, best_circles = circleLog[0] output = np.copy(orig_image) cir = np.round(best_circles[0, :]).astype("int") for (x, y, r) in cir: output = cv2.circle(output, (x, y), r, (255, 255, 255), 2) output = cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (255, 255, 255), -1) plt.imshow(output) plt.show()
修改要点
- 对齐手动调参核心参数:把
minRadius、maxRadius、minDist改成手动调参的数值,保留已验证的有效区间。 - 优化半径搜索:从半径范围中间开始搜,而非最大半径,避免优先匹配大尺寸;同时取消
guess_radius±3的小范围限制,覆盖完整有效半径区间。 - 结果筛选:记录每个结果的
param2值,最后选param2最高的结果(投票数最多,检测更可靠)。 - 缩小无效搜索范围:
dp限制在1-3之间(过大的dp会降低精度),param2从50降到20(参考手动调参的30),减少不必要的计算。
内容的提问来源于stack exchange,提问作者lpe
相关产品推荐
相关产品推荐

