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

使用OpenCV实现图像盲文点检测的问题排查与优化

板材表面盲文点位检测优化方案

现存问题

当前实现了两套盲文点位检测方案,均存在明显漏检、误检,无法达到生产使用要求:

  • 方案1:自适应阈值+连通域检测,反复调整参数仍无法过滤所有非盲文噪点,同时存在真点漏检
  • 方案2:模板匹配+非极大值抑制,检测效果完全依赖匹配阈值,参数敏感度高,准确率不稳定
    此前尝试的圆检测方案也存在相同的参数适配问题。

方案1实现代码

import matplotlib.pyplot as plt
 
img1 = cv.imread(r"traitement\prod.png")

plt.figure(figsize=(40,40))
plt.subplot(3,1,1)

gray_img = cv.cvtColor(img1, cv.COLOR_BGR2GRAY)

test = cv.adaptiveThreshold(gray_img, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY_INV, 11, 6)

_, _, boxes, _ = cv.connectedComponentsWithStats(test)

boxes = boxes[1:]
filtered_boxes = []
for x,y,w,h,pixels in boxes:
    if pixels < 1000 and h < 35 and w < 35 and h > 14 and w > 14 and x > 15 and y > 15:
        filtered_boxes.append((x,y,w,h))

for x,y,w,h in filtered_boxes:
    W = int(w)/2
    H = int(h)/2
    cv.circle(img1,(x+int(W),y+int(H)),2,(0,255,0),20) 

cv.imwrite("gray.png",gray_img)
cv.imwrite("test.png",test)

plt.imshow(test)

plt.subplot(3,1,2)
plt.imshow(img1)

方案2实现代码

import cv2 as cv
import numpy as np
from imutils.object_detection import non_max_suppression
import matplotlib.pyplot as plt

  
img = cv.imread('traitement/prod.png')
temp_gray = cv.imread('dot.png',0)
  
W, H = temp.shape[:2]
thresh = 0.6

img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)  
match = cv.matchTemplate(image=img_gray, templ=temp_gray, method=cv.TM_CCOEFF_NORMED)

(y_points, x_points) = np.where(match >= thresh)
  
boxes = list()
  

for (x, y) in zip(x_points, y_points):
    boxes.append((x, y, x + W, y + H))
  
boxes = non_max_suppression(np.array(boxes))
  
for (x1, y1, x2, y2) in boxes:
    cv.circle(img,(x1+int(W/2),y1+int(H/2)),2,(255,0,0),15) 
  
plt.figure(figsize=(40,40))
plt.subplot(3,1,1)
plt.imshow(img)

当前检测效果

  • 自适应阈值连通域方案效果:
    自适应阈值检测效果
  • 模板匹配方案效果:
    模板匹配检测效果

核心问题原因

两套方案效果差的根源不是参数没调好,是前置逻辑缺失、过滤维度太单一:

  1. 没有做光照和背景纹理预处理:自适应阈值对板材本身的凹凸纹理、局部光照变化非常敏感,二值化后会把大量背景噪点识别为前景,和盲文点混在一起
  2. 过滤规则只靠宽高、面积:很多背景噪点的尺寸和盲文点接近,单靠尺寸过滤无法区分
  3. 模板匹配用单模板+固定阈值:盲文点因为按压深度、拍摄角度存在轻微大小、灰度差异,单模板固定阈值很容易漏过变形的真点,同时误匹配纹理相似的噪点

可落地优化方案

优先选择优化后的连通域方案,泛化性比模板匹配更好,参数敏感度更低。

第一步:前置预处理(解决80%的干扰问题)

不要直接对原图做二值化或模板匹配,先做两步处理:

  • 顶帽运算:用大小略大于盲文点(比如25*25)的椭圆核做形态学顶帽变换,过滤掉板材表面缓慢变化的光照差、大面积背景纹理,只保留尺寸和核接近的凸起点(也就是盲文点),处理后盲文点灰度会被统一增强,背景整体压暗
  • 高斯模糊:用3*3核做高斯模糊,去掉顶帽处理后残留的细碎高频噪点,注意不要用均值模糊,会模糊盲文点边缘影响后续判定

第二步:优化二值化与连通域过滤规则

  • 二值化改用自动全局阈值:顶帽处理后背景灰度已经非常均匀,不需要用自适应阈值,阈值取处理后图像的灰度均值+3倍标准差即可,不需要手动反复调整
  • 连通域过滤新增两个形状维度判定,筛掉90%以上的误检:
    • 宽高比过滤:真实盲文点接近正圆,宽高比范围控制在0.8~1.2,长条、扁形噪点直接剔除
    • 圆度过滤:对每个连通域计算圆度(公式:圆度 = 4π * 连通域面积 / 连通域周长²),圆度大于0.7的才保留,不规则形状的噪点会被全部过滤
  • 可选网格校验(零误检):盲文点位本身是按固定行列间距规则排布的,检测完所有候选点后,通过霍夫直线检测找出点位排布的行列线,只有落在网格交叉点邻域内的点才判定为真点,偏离网格的孤立点直接剔除,可以完全消除零星误检

模板匹配方案备选优化点

如果要继续用模板匹配,做两处修改即可大幅提升准确率:

  • 多尺度模板匹配:把原模板按0.9、1.0、1.1三个比例缩放生成多套模板,分别做匹配后合并所有候选框,再做非极大值抑制,解决盲文点大小偏差带来的漏检
  • 替换固定阈值为局部最大值筛选:不再用全局阈值卡结果,只取每个局部邻域内匹配得分最高的点作为候选,再结合上面提到的形状规则、网格规则做二次校验,大幅降低阈值敏感度

优化后连通域方案核心代码参考

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

img1 = cv.imread(r"traitement\prod.png")
gray_img = cv.cvtColor(img1, cv.COLOR_BGR2GRAY)

# 顶帽运算增强盲文点
kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (25, 25))
tophat = cv.morphologyEx(gray_img, cv.MORPH_TOPHAT, kernel)
# 高斯模糊去噪
blur = cv.GaussianBlur(tophat, (3, 3), 0)
# 自动计算全局阈值
thresh_val = blur.mean() + 3 * blur.std()
_, binary = cv.threshold(blur, thresh_val, 255, cv.THRESH_BINARY)

# 连通域检测
num_labels, labels, stats, centroids = cv.connectedComponentsWithStats(binary, connectivity=8)
filtered_points = []

for i in range(1, num_labels):
    x, y, w, h, area = stats[i]
    # 基础尺寸过滤
    if not (14 < w < 35 and 14 < h < 35 and area < 1000 and x > 15 and y > 15):
        continue
    # 宽高比过滤
    aspect_ratio = w / h
    if not (0.8 < aspect_ratio < 1.2):
        continue
    # 提取连通域真实轮廓计算圆度
    mask = (labels == i).astype(np.uint8)
    contours, _ = cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    if not contours:
        continue
    perimeter = cv.arcLength(contours[0], True)
    if perimeter == 0:
        continue
    circularity = 4 * np.pi * area / (perimeter ** 2)
    if circularity < 0.7:
        continue
    # 保留中心点
    cx, cy = centroids[i]
    filtered_points.append((int(cx), int(cy)))

# 绘制检测结果
for cx, cy in filtered_points:
    cv.circle(img1, (cx, cy), 2, (0, 255, 0), 20)

# 可视化
plt.figure(figsize=(40, 40))
plt.subplot(3, 1, 1)
plt.imshow(binary, cmap='gray')
plt.subplot(3, 1, 2)
plt.imshow(cv.cvtColor(img1, cv.COLOR_BGR2RGB))
plt.show()

代码里的核大小、圆度阈值、宽高比范围可以根据你实际采集的图像分辨率微调,调整幅度很小,不需要反复试参。如果需要做到100%无漏检误检,最后加一步盲文网格规则校验即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 22:33:16