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

OpenCV圆形射击靶弹孔检测优化方案咨询

霍夫圆检测弹孔效果差是必然的——弹孔根本不是标准正圆,边缘经常带毛边、破损,再加上靶面本身的环形印刷线会产生大量干扰边缘,霍夫圆的梯度投票机制很容易被杂讯带偏,参数调得再死也容易漏检、误判。

现有代码核心问题
  • 直接对全图Canny边缘做霍夫圆检测,靶面印刷环纹、光照不均产生的杂边会产生大量无效投票,尺寸小、边缘不规整的弹孔很容易被杂讯覆盖
  • 弹孔本质是穿透/灼烧形成的暗斑,不是轮廓清晰的亮边圆环,霍夫圆基于边缘梯度投票的机制天然适配性差
  • 弹孔检测半径硬卡7-10像素太死,不同拍摄距离、不同口径的弹孔尺寸稍有波动就会直接漏检
  • 代码存在逻辑bug:调用countHitScore前没判断hits是否为空,没检出弹孔时直接调用.tolist()会直接崩溃
可直接落地的改进方案(传统CV实现,无需训练模型,正视角固定机位场景下准确率足够)

1. 预处理加光照归一化

原代码只用了双边滤波去噪,解决不了光照不均导致的局部弹孔对比度低问题,灰度化后加一步自适应直方图均衡拉平局部亮度:

gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 新增光照归一化
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
gray = clahe.apply(gray)
bilateral = cv.bilateralFilter(gray, 7, 15, 10)

2. 用暗阈值分割代替全图提边缘

弹孔普遍比周围靶纸颜色深,用自适应阈值直接抠出暗区域作为弹孔候选,比全图提边缘的干扰少90%:

# 提取比周围区域暗的连通块
thresh = cv.adaptiveThreshold(
    bilateral, 255,
    cv.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv.THRESH_BINARY_INV, 15, 3
)
# 形态学开运算去掉印刷纹理产生的细碎噪点
kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3,3))
thresh = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=1)

3. 用轮廓特征筛选代替霍夫圆检测弹孔

对阈值分割出的连通域做轮廓检测,按弹孔的几何特征过滤无效块,稳定性远高于霍夫圆:

  • 面积在弹孔对应区间(半径5-15像素对应面积约78-706像素,可根据实际拍摄分辨率调整)
  • 圆度(4*π*面积/(周长²))大于0.6,排除不规则印刷杂点、划痕
  • 外接矩形宽高比在0.7-1.3之间,排除长条状干扰
    对应实现代码替换原有的弹孔霍夫圆检测部分:
contours, _ = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
hits = []
for cnt in contours:
    area = cv.contourArea(cnt)
    # 过滤面积不符合的块
    if area < 70 or area > 800:
        continue
    perimeter = cv.arcLength(cnt, True)
    if perimeter == 0:
        continue
    circularity = 4 * math.pi * area / (perimeter ** 2)
    x,y,w,h = cv.boundingRect(cnt)
    aspect_ratio = w / h
    # 按圆度、宽高比筛选
    if circularity > 0.6 and 0.7 < aspect_ratio < 1.3:
        # 以轮廓质心作为弹孔中心,等面积圆半径作为弹孔半径
        M = cv.moments(cnt)
        cx = int(M['m10'] / M['m00'])
        cy = int(M['m01'] / M['m00'])
        r = int(math.sqrt(area / math.pi))
        hits.append([[cx, cy, r]])
# 转成和原霍夫圆输出一致的数组格式,兼容后续计分逻辑
hits = np.array(hits) if len(hits) >0 else None

4. 修复原有计分逻辑的空指针bug

score = 0
if hits is not None:
    score = countHitScore(hits.tolist(), delta_r, biggest_circle_center)
print(f'The score is: {score}')
方案适配说明
  • 上述方案对弹孔形状的容忍度远高于霍夫圆,只要弹孔和周围靶纸存在明显亮度差就能检出,基本不会把靶环圆弧误判为弹孔
  • 如果后续需要应对斜视角拍摄、弹孔重叠、复杂光照场景,可以标注几百张样本训个YOLO系列的小目标检测模型,准确率能到99%以上,正视角固定机位场景下传统方案已经足够用。

原始检测效果样例1
原始检测效果样例2
原始检测效果样例3

从附的检测结果看,现有方案存在大量漏检、误检,替换上述轮廓检测逻辑后问题基本能解决。

内容的提问来源于stack exchange,提问作者Marian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 05:42:31