You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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显示结果。


解决方案

问题根源

  1. 半径搜索逻辑偏误:代码从最大半径开始往下搜,找到符合条件的圆就直接终止,优先记录了大半径结果,而非精准匹配的半径。
  2. 参数区间与手动调参不符:自动调参缩小了半径范围(手动是25-80,自动是30-70),还把minDist从20改成10,容易导致误检。
  3. 结果筛选缺失: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 17:35:14