第三章 数据集准备与标注:工业场景的“第一桶金”
说实话,很多做算法的朋友容易忽略这一步。觉得模型结构才是核心,数据集嘛,网上找找就行。但工业视觉不一样。你想想看,产线上来的产品千奇百怪,网上那些公开数据集根本覆盖不了。我刚开始做工业项目时,就吃过这个亏——用公开数据集训得好好的模型,一上产线,漏检率直接飙到30%。
所以这一章,咱们就聊聊工业数据集怎么来、怎么标、怎么增强、怎么转格式。这些都是实打实的“脏活累活”,但做好了,后面能省你80%的调试时间。
3.1 工业数据集来源:别指望天上掉馅饼
工业场景的数据,说白了就三个来源:自己拍、合作方给、公开数据集凑。但每个来源都有坑。
| 来源 | 优点 | 缺点 | 我的建议 |
|---|---|---|---|
| 现场采集 | 最真实,覆盖产线实际光照、角度、背景 | 成本高,需要搭建采集工位,耗时 | 主力来源,至少占70% |
| 合作方提供 | 省事,可能包含历史缺陷样本 | 格式混乱,标注质量参差不齐 | 必须做数据清洗和标注复核 |
| 公开数据集 | 免费,量大,适合预训练 | 场景差异大,直接迁移效果差 | 只用来做预训练或数据增强的补充 |
工业场景还有个特殊问题:正负样本极度不均衡。正常产品占99%,缺陷产品只有1%。怎么办?我的做法是:
- 正常产品拍1000张就够了,覆盖不同光照和角度
- 缺陷产品有多少拍多少,不够就做数据增强
- 实在缺缺陷样本?去产线蹲点,或者人工制造缺陷(比如划痕、脏污)
3.2 标注工具:LabelImg vs Labelme,别选错了
这两个工具是工业视觉里最常用的。但很多人搞混——以为它们差不多。其实差别大了去了。
| 特性 | LabelImg | Labelme |
|---|---|---|
| 标注形状 | 矩形框(Bounding Box) | 多边形、矩形、圆形、点、线 |
| 输出格式 | PASCAL VOC(XML)、YOLO(TXT) | JSON(自定义格式) |
| 适用场景 | 目标检测(矩形框就够了) | 语义分割、实例分割、关键点检测 |
| 上手难度 | 简单,开箱即用 | 稍复杂,需要理解JSON结构 |
我个人习惯: 如果只是做目标检测,比如检测螺丝有没有拧紧、表面有没有划痕,用LabelImg就够了。但如果你要做更精细的缺陷分割,比如要精确到划痕的轮廓,那就得上Labelme。
3.3 数据增强:Mosaic和MixUp,让数据“变”出来
工业场景数据少,怎么办?数据增强就是“无中生有”的魔法。但别乱用——有些增强反而会降低模型性能。
我重点讲两个在YOLO系列里效果特别好的增强方法:Mosaic 和 MixUp。
Mosaic:四张图拼一张
Mosaic是YOLOv4里提出来的。简单说,就是把4张训练图片随机裁剪、缩放、拼接成一张新图。这样做的好处很明显:
- 一张图里能看到多个场景,模型泛化能力更强
- 小目标被放大的概率增加,对小目标检测友好
- 相当于变相增加了batch size,训练更稳定
MixUp:两张图“叠”在一起
MixUp更狠——直接把两张图按一定比例叠加。比如0.7的图A + 0.3的图B,标签也按同样比例混合。这招对防止过拟合特别有效。
下面这张图展示了Mosaic和MixUp的核心流程差异:
3.4 数据集格式转换:VOC → COCO → YOLO,别被格式绕晕
工业视觉里,数据集格式是个大坑。不同框架、不同模型,要求的格式都不一样。你想想看,好不容易标完数据,结果发现格式不对,还得再转一遍。
最常见的三种格式:
- VOC格式:每个图片对应一个XML文件,里面存着对象名、坐标(xmin, ymin, xmax, ymax)
- COCO格式:所有标注信息存在一个JSON文件里,用字典结构组织
- YOLO格式:每个图片对应一个TXT文件,每行是“类别 x_center y_center width height”(归一化坐标)
我整理了一个转换对照表,方便你理解:
| 格式 | 存储方式 | 坐标表示 | 典型框架 |
|---|---|---|---|
| VOC | 每图一个XML | 坐标 (xmin, ymin, xmax, ymax) | Faster R-CNN, SSD |
| COCO | 单JSON文件 | 坐标 (x, y, w, h) | Detectron2, MMDetection |
| YOLO | 每图一个TXT | 归一化坐标 (cx, cy, w, h) | YOLOv5, YOLOv8, YOLOX |
labelme2voc.py(Labelme自带的)和 voc2yolo.py(GitHub上开源的)。如果你用YOLOv5,它自带一个 utils/datasets.py,里面就有格式转换函数。别自己手写解析逻辑,容易出bug。
下面是一个从VOC转YOLO的Python代码片段,我项目中经常用:
import xml.etree.ElementTree as ET
import os
def voc_to_yolo(xml_file, class_names, output_dir):
tree = ET.parse(xml_file)
root = tree.getroot()
# 获取图片尺寸
width = int(root.find('size/width').text)
height = int(root.find('size/height').text)
# 获取图片文件名(不含扩展名)
image_name = os.path.splitext(root.find('filename').text)[0]
yolo_lines = []
for obj in root.findall('object'):
class_name = obj.find('name').text
if class_name not in class_names:
continue
class_id = class_names.index(class_name)
# 读取VOC坐标
xmin = int(obj.find('bndbox/xmin').text)
ymin = int(obj.find('bndbox/ymin').text)
xmax = int(obj.find('bndbox/xmax').text)
ymax = int(obj.find('bndbox/ymax').text)
# 转换为YOLO归一化坐标
x_center = (xmin + xmax) / 2.0 / width
y_center = (ymin + ymax) / 2.0 / height
w = (xmax - xmin) / width
h = (ymax - ymin) / height
yolo_lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}")
# 写入TXT文件
output_path = os.path.join(output_dir, f"{image_name}.txt")
with open(output_path, 'w') as f:
f.write('\n'.join(yolo_lines))
return output_path
3.5 本章小结:数据准备是“磨刀不误砍柴工”
数据准备这件事,看起来琐碎,但决定了模型的天花板。我见过太多项目,算法调了半天,最后发现是数据标注有错,或者格式不对。所以我的建议是:
- 数据来源:现场采集为主,外部数据为辅
- 标注工具:目标检测用LabelImg,分割用Labelme
- 数据增强:Mosaic必开,MixUp看情况
- 格式转换:用现成工具,别自己造轮子