如何优化并自动化椭圆检测代码的参数调整流程?
问题描述
现有一段可较好检测类椭圆结构的代码:针对输入图像1,设置自适应阈值C值为6、形态学闭运算核尺寸为(11,11)时可得到理想分割结果;但针对输入图像2、3,需手动调整C值及核尺寸才能检测到全部5个椭圆。所有待检测输入图像均固定包含5个椭圆,希望编写算法实现参数的自动调整,无需手动修改。请问该如何实现,是否有更优方案?
附现有代码:
import cv2 import numpy as np import skimage.exposure # read the input img = cv2.imread('RedJan20.png') # convert to grayscale gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # blur blur = cv2.GaussianBlur(gray, (0,0), sigmaX=99, sigmaY=99) # do division normalization normal = cv2.divide(gray, blur, scale=255) # stretch to full dynamic range stretch = skimage.exposure.rescale_intensity(normal, in_range='image', out_range=(0,255)).astype(np.uint8) # adaptive threshold thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 25, 6) # apply morphology close kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11)) morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) # get external contours contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = contours[0] if len(contours) == 2 else contours[1] # filter out small contours and fit ellipse # draw contour on copy of image and also draw ellipse result1 = img.copy() result2 = img.copy() i = 1 count = 0 for cntr in contours: area = cv2.contourArea(cntr) #if area > 10000: if area > 5000: ellipse = cv2.fitEllipse(cntr) (xc,yc),(d1,d2),angle = ellipse print('ellipse #:', i) print('center:', xc,yc) print('diameters:', d1,d2) print('angle:', angle) print('') cv2.drawContours(result1, [cntr], 0, (0,0,255), 1) cv2.ellipse(result2, (int(xc),int(yc)), (int(d1/2),int(d2/2)), angle, 0, 360, (0,0,255), 1) i += 1 count += 1 print(count) # show results cv2.imshow('gray', gray) cv2.imshow('blur', blur) cv2.imshow('normalized', normal) cv2.imshow('stretched', stretch) cv2.imshow('thresh', thresh) cv2.imshow('morph', morph) cv2.imshow('contours', result1) cv2.imshow('ellipses', result2) cv2.waitKey(0)
自动参数调整实现方案
核心思路是遍历候选参数组合,以检测到的有效椭圆数量(目标为5个)作为判断标准,找到符合要求的参数:
1. 定义参数搜索范围
基于已知有效参数设定合理区间,覆盖不同图像的亮暗、轮廓大小差异:
- 自适应阈值C值:
[-10, 15](步长1) - 形态学核尺寸:
[(5,5), (7,7), ..., (15,15)](步长2,保持奇数)
2. 封装检测逻辑为可复用函数
将阈值处理、形态学操作、椭圆计数逻辑封装,方便批量参数测试:
3. 遍历参数筛选最优解
按顺序遍历所有参数组合,找到能检测到5个椭圆的参数后立即停止搜索,提升效率。
修改后的代码示例
import cv2 import numpy as np import skimage.exposure def detect_ellipses(gray_img, c_value, kernel_size): """封装椭圆检测逻辑,返回有效椭圆数量及中间结果""" # 自适应阈值处理 thresh = cv2.adaptiveThreshold(gray_img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 25, c_value) # 形态学闭运算 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, kernel_size) morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) # 提取外部轮廓 contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = contours[0] if len(contours) == 2 else contours[1] # 统计有效椭圆数量(轮廓面积达标且点数足够拟合椭圆) count = 0 for cntr in contours: if cv2.contourArea(cntr) > 5000 and len(cntr) >= 5: count += 1 return count, thresh, morph, contours # 图像预处理 img = cv2.imread('RedJan20.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (0,0), sigmaX=99, sigmaY=99) normal = cv2.divide(gray, blur, scale=255) stretch = skimage.exposure.rescale_intensity(normal, in_range='image', out_range=(0,255)).astype(np.uint8) # 参数候选集 c_candidates = range(-10, 16) kernel_candidates = [(k, k) for k in range(5, 16, 2)] # 寻找最优参数 best_c = 6 best_kernel = (11,11) found_flag = False for c in c_candidates: for kernel_size in kernel_candidates: ellipse_count, _, _, _ = detect_ellipses(stretch, c, kernel_size) if ellipse_count == 5: best_c = c best_kernel = kernel_size found_flag = True break if found_flag: break # 使用最优参数完成检测与可视化 ellipse_count, thresh, morph, contours = detect_ellipses(stretch, best_c, best_kernel) result1 = img.copy() result2 = img.copy() i = 1 for cntr in contours: area = cv2.contourArea(cntr) if area > 5000 and len(cntr) >=5: ellipse = cv2.fitEllipse(cntr) (xc,yc),(d1,d2),angle = ellipse print(f'ellipse #: {i}') print(f'center: {xc:.2f}, {yc:.2f}') print(f'diameters: {d1:.2f}, {d2:.2f}') print(f'angle: {angle:.2f}\n') cv2.drawContours(result1, [cntr], 0, (0,0,255), 1) cv2.ellipse(result2, (int(xc),int(yc)), (int(d1/2),int(d2/2)), angle, 0, 360, (0,0,255), 1) i += 1 print(f"检测到椭圆数量: {ellipse_count}") print(f"最优参数: C={best_c}, 核尺寸={best_kernel}") # 显示结果 cv2.imshow('stretched', stretch) cv2.imshow('thresh', thresh) cv2.imshow('morph', morph) cv2.imshow('contours', result1) cv2.imshow('ellipses', result2) cv2.waitKey(0) cv2.destroyAllWindows()
更优方案建议
1. 优化预处理逻辑
原代码中用gray做阈值处理,但已经生成了对比度更均匀的stretch,直接使用stretch做阈值能提升不同图像的适配性。
2. 替换自适应阈值为Otsu自动阈值
若椭圆与背景灰度差异明显,Otsu阈值可自动计算分割阈值,无需调整C值:
_, thresh = cv2.threshold(stretch, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
3. 使用专用椭圆检测算法
skimage的hough_ellipse专门针对椭圆检测,无需依赖轮廓筛选,适配性更强:
from skimage.transform import hough_ellipse from skimage.draw import ellipse_perimeter # 图像预处理 img = cv2.imread('RedJan20.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (0,0), sigmaX=99, sigmaY=99) normal = cv2.divide(gray, blur, scale=255) stretch = skimage.exposure.rescale_intensity(normal, in_range='image', out_range=(0,255)).astype(np.uint8) edges = cv2.Canny(stretch, 50, 200) # 霍夫椭圆检测 ellipses = hough_ellipse(edges, accuracy=20, threshold=250, min_size=100, max_size=500) ellipses = sorted(ellipses, key=lambda x: x[0], reverse=True) result = img.copy() count = 0 for e in ellipses: if count >=5: break yc, xc, a, b, orientation = e[1:] cy, cx = ellipse_perimeter(int(yc), int(xc), int(a), int(b), orientation) result[cy, cx] = (0, 0, 255) count +=1 print(f"检测到椭圆数量: {count}") cv2.imshow('hough_ellipses', result) cv2.waitKey(0)
内容的提问来源于stack exchange,提问作者John C Lau
相关产品推荐
相关产品推荐

