4、气泡缺陷标注:气泡特征分析、标注规则与边界框策略

气泡,在光学玻璃缺陷里算是最“老实”的一种。为什么这么说?因为它形状规则、边界清晰,不像划痕那样飘忽不定,也不像麻点那样密密麻麻。但老实归老实,标注起来还是有不少门道的。我刚开始带团队做玻璃检测项目时,就发现新人最容易在气泡标注上翻车——不是漏标了微小气泡,就是把边界框画得歪歪扭扭。

今天咱们就把气泡标注这件事彻底聊透。

4.1 气泡的视觉特征:为什么它这么好认?

气泡说白了就是玻璃内部的一个小空腔。光线穿过时,会在气泡的前后两个界面发生折射和反射,形成非常独特的视觉信号。

特征维度 典型表现 标注影响
形状 圆形或椭圆形,边缘光滑 边界框可以紧贴边缘,不需要预留太多余量
边缘 高对比度、清晰锐利 边缘检测算法容易捕捉,标注时定位准确
内部 透光或暗斑(取决于照明方式) 内部纹理均匀,不需要精细分割
尺寸范围 从微米级到毫米级不等 小气泡容易漏标,需要放大检查

我个人习惯在暗场照明下观察气泡。这时候气泡会呈现明亮的圆点,背景是暗的,对比度极高,标注起来特别舒服。明场下气泡反而像个黑洞,边缘虽然清晰,但内部细节全丢,有时候会跟脏点混淆。

核心判断标准:气泡的边界一定是闭合的、光滑的、连续的。如果看到一个“圆形”缺陷但边缘有毛刺或断裂,那大概率不是气泡,可能是麻点群或者划痕的端部。

4.2 标注规则:边界框到底该怎么画?

这个问题我几乎每次培训都会被问到。说白了,边界框的规则取决于你后续要做什么。如果是做目标检测(比如 YOLO、Faster R-CNN),那规则相对宽松;如果是做分割(比如 Mask R-CNN),那就严格得多。

这里我给出一个经过多个项目验证的通用规则:

  1. 最小外接矩形原则:边界框必须刚好框住气泡,不切边、不多余。上下左右各留 2-3 个像素的余量就够了。
  2. 独立气泡独立框:两个气泡即使靠得很近,也要分别画框。除非它们之间的距离小于较小气泡直径的 1/3,这时候可以合并为一个框。
  3. 重叠气泡的处理:如果气泡有部分重叠,每个气泡仍然独立标注。边界框可以重叠,但标注时要在属性里注明“重叠”。
  4. 边界气泡:气泡如果被图像边缘切掉一部分,只要剩余部分超过原气泡面积的 50%,就要标注。否则跳过。

我的经验:标注小气泡(直径 < 10 像素)时,别太相信自动标注工具。我遇到过好几次,自动框把两个相邻的小气泡识别成一个椭圆,结果模型训练出来全是误检。这种时候,手动标注反而更靠谱。

4.3 边界框策略:水平框 vs 旋转框

气泡是圆形的,按理说用水平矩形框(HBB)就够了。但实际项目中,我建议你根据气泡的分布密度来选择策略。

场景 推荐策略 原因
稀疏分布(每张图 < 10 个气泡) 水平矩形框(HBB) 标注快,后处理简单,模型容易收敛
密集分布(每张图 > 30 个气泡) 旋转矩形框(RBB) 减少框之间的重叠,提高 NMS 效果
气泡呈链状排列 旋转矩形框(RBB) 链状气泡整体呈长条分布,水平框会引入大量背景噪声
高精度检测需求 分割掩码(Segmentation) 边界框无法精确描述圆形轮廓,分割掩码更准确

我记得有一次做手机镜头玻璃的检测项目,客户要求气泡定位精度达到 ±2 像素。用水平框怎么调都差一点,后来换成旋转框,精度直接上去了。你想想看,圆形气泡用水平框框住,四个角全是背景,模型学到的特征里混入了大量无效信息,能不偏差吗?

避坑指南:我曾经在一个项目中,为了省事把所有气泡都用水平框标注。结果训练出来的模型,在气泡密集区域频繁漏检。后来排查发现,是因为水平框之间的 IoU 太高,NMS 把很多真实气泡给抑制掉了。从那以后,我养成了一个习惯:先看数据分布,再定标注策略。

4.4 实战标注案例:从原始图像到标注完成

光说不练假把式。咱们走一遍完整的标注流程,用的是一个实际的玻璃样本图像。

步骤 1:图像预处理

拿到原始图像后,我一般先做两步:

  • 调整对比度:用直方图均衡化把气泡和背景的对比度拉开。
  • 去噪:用高斯滤波(核大小 3x3)去除传感器噪声,但别用太大核,否则小气泡会被抹掉。
import cv2
import numpy as np

# 读取图像
img = cv2.imread('glass_sample_04.tif', cv2.IMREAD_GRAYSCALE)

# 直方图均衡化
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
img_eq = clahe.apply(img)

# 高斯去噪
img_blur = cv2.GaussianBlur(img_eq, (3, 3), 0)

# 保存预处理结果
cv2.imwrite('glass_sample_04_preprocessed.tif', img_blur)

步骤 2:气泡检测与定位

预处理之后,我会用霍夫圆检测来找候选气泡。这一步不是最终标注,而是帮我快速定位,减少手动查找的工作量。

# 霍夫圆检测
circles = cv2.HoughCircles(
    img_blur,
    cv2.HOUGH_GRADIENT,
    dp=1.2,
    minDist=20,
    param1=50,
    param2=30,
    minRadius=3,
    maxRadius=100
)

if circles is not None:
    circles = np.round(circles[0, :]).astype("int")
    for (x, y, r) in circles:
        # 在图像上画出候选圆
        cv2.circle(img_blur, (x, y), r, (0, 255, 0), 2)

步骤 3:人工复核与标注

自动检测的结果只能作为参考。我会逐个人工复核:

  • 确认每个候选圆确实是气泡(不是脏点、不是划痕端部)
  • 调整边界框位置和大小(自动检测的圆心有时会偏 1-2 像素)
  • 补充自动检测漏掉的小气泡(直径 < 5 像素的经常被漏掉)

步骤 4:导出标注数据

标注完成后,导出为 COCO 格式或 YOLO 格式。我个人偏好 COCO 格式,因为它支持分割掩码,后续做模型迭代时更灵活。

{
  "images": [
    {
      "id": 1,
      "file_name": "glass_sample_04.tif",
      "width": 2048,
      "height": 2048
    }
  ],
  "annotations": [
    {
      "id": 1,
      "image_id": 1,
      "category_id": 4,  // 气泡类别 ID
      "bbox": [856, 432, 28, 28],  // x, y, width, height
      "segmentation": [[856, 432, 870, 432, 870, 446, 856, 446]],
      "area": 615,
      "iscrowd": 0
    }
  ],
  "categories": [
    {"id": 4, "name": "bubble"}
  ]
}

关键提醒:标注数据导出后,一定要做一次可视化检查。把标注框画回原图上,肉眼过一遍。我见过太多因为标注工具 bug 导致坐标偏移的案例,这一步能救你命。

4.5 气泡标注知识体系

下面这张图总结了气泡标注的核心流程和关键决策点,你可以把它当作标注时的快速参考。

气泡标注知识体系 原始玻璃图像 特征分析:圆形 / 边缘清晰 / 内部均匀 标注策略选择:稀疏→HBB / 密集→RBB / 高精度→Segmentation 标注规则 最小外接矩形 | 独立气泡独立框 | 重叠气泡注明属性 | 边界气泡面积 > 50% 才标 标注数据导出(COCO / YOLO) 可视化验证 ← 这一步不能省

嗯,气泡标注其实不难,但细节决定成败。你只要把特征认准了、策略选对了、规则执行到位了,这部分基本不会出大问题。我见过太多人栽在“差不多就行”的心态上,结果模型训练出来全是坑。

记住一句话:标注的质量,直接决定了模型的天花板。气泡标注如此,其他缺陷也一样。