4. 数据集准备与标注:工业场景下的“食材”与“厨艺”

做目标检测,尤其是工业场景,我常说一句话:“模型是锅,数据是米”。锅好不好固然重要,但米要是夹生、有沙,那这锅饭基本就废了。这一章,咱们就聊聊怎么搞到好米,怎么淘米,怎么下锅。

核心观点:工业场景的数据集,90% 的功夫在“清洗”和“标注规范”上,而不是模型结构。你想想看,一个标注错位的框,能让你的 mAP 掉好几个点。

4.1 工业数据集从哪来?别只盯着公开数据集

很多新手喜欢直接下载 COCO、VOC 来练手。但工业场景,说白了,公开数据集基本没用。你见过哪个公开数据集里有“PCB 板上的微小划痕”或者“汽车焊点飞溅”的图片?

我个人习惯把工业数据来源分成三类:

来源类型 典型场景 获取难度 我的建议
现场采集 产线摄像头实时抓拍、巡检机器人拍照 高(需协调产线) 这是最靠谱的,但注意光照、角度多样性
历史归档 质检部门保存的缺陷图片、维修记录照片 中(需IT部门配合) 数据量大,但往往只有“坏样本”,缺少“好样本”
合成/增强 3D渲染、GAN生成、数据增强 低(自己动手) 适合补充长尾缺陷,但别完全依赖,容易过拟合

我的经验:我曾经帮一个汽车零部件厂做检测,他们只有 200 张缺陷图。我建议他们用“现场采集 + 数据增强”的方式,把产线摄像头架了三天,拍了 5000 张正常件,然后对那 200 张缺陷图做旋转、亮度变化、加噪,最终凑了 2000 张缺陷样本。效果比单纯用那 200 张图好了不止一倍。

4.2 标注工具:LabelImg vs LabelStudio

工具这东西,顺手最重要。我主要用两个:LabelImg 和 LabelStudio。它们各有千秋,别纠结,选一个用熟就行。

4.2.1 LabelImg:轻量、快速、离线

LabelImg 是我最早接触的标注工具。它就是个纯 Python 写的 Qt 应用,安装简单,打开就能用。

  • 优点:启动快,不依赖网络,支持 PascalVOC 和 YOLO 格式直接导出。
  • 缺点:功能单一,不支持多人协作,标注大图时有点卡。
  • 适用场景:个人项目、小团队、快速验证。

安装方式很简单:

# 推荐用 pip 安装
pip install labelImg

# 或者直接下载 exe(Windows 用户)
# 然后命令行运行
labelImg

打开后,界面很直观。左边是工具栏,中间是图片,右边是标签列表。快捷键记几个常用的:W 画框,A 上一张,D 下一张,Ctrl+S 保存。

注意:LabelImg 默认保存为 PascalVOC 格式(XML 文件)。如果你要用 YOLO 格式,记得在菜单栏 View -> Auto Save 旁边,把 PascalVOC 切换成 YOLO。不然你导出的数据格式不对,训练时会报错。

4.2.2 LabelStudio:协作、多模态、功能强大

LabelStudio 是另一个方向。它是个 Web 应用,支持多人同时标注,还能标文本、音频、甚至时间序列。对于工业场景,我主要用它来做团队协作和复杂标注。

  • 优点:支持多人协作、项目管理、数据版本控制、API 接口。
  • 缺点:需要部署服务端,启动慢,资源占用高。
  • 适用场景:团队项目、需要多人审核、数据量大。

部署也很简单:

# 使用 Docker 一键部署
docker run -it -p 8080:8080 -v $(pwd)/my_data:/label-studio/data heartexlabs/label-studio:latest

我个人习惯:小项目用 LabelImg,大项目用 LabelStudio。别在工具上浪费太多时间,核心是把数据标好。

4.3 数据格式转换:XML 与 YOLO 的“翻译官”

标注完数据,你会发现一个问题:LabelImg 默认存的是 XML(PascalVOC 格式),但 YOLO 训练需要的是 TXT(YOLO 格式)。这就好比你有中文的说明书,但机器只认英文。所以,我们需要一个“翻译官”。

两种格式的核心区别在于坐标表示方式:

格式 坐标表示 文件内容示例
PascalVOC (XML) 坐标 (xmin, ymin, xmax, ymax) <xmin>100</xmin> <ymin>150</ymin> <xmax>200</xmax> <ymax>250</ymax>
YOLO (TXT) 归一化坐标 (class, x_center, y_center, width, height) 0 0.5 0.5 0.2 0.3

转换逻辑其实很简单,就是做个归一化:

# 假设图片宽为 W,高为 H
# XML 中的坐标:xmin, ymin, xmax, ymax

# 计算中心点和宽高
x_center = (xmin + xmax) / 2.0 / W
y_center = (ymin + ymax) / 2.0 / H
width = (xmax - xmin) / W
height = (ymax - ymin) / H

# 写入 TXT 文件
# 格式:class_id x_center y_center width height
# 注意:所有值都是 0~1 之间的浮点数

避坑指南:我曾经在转换时,直接把 xmin 和 ymin 当成了中心点,结果训练出来的模型,检测框全偏了。后来花了整整一天排查,才发现是坐标转换写错了。所以,转换完一定要可视化检查,别偷懒。

为了方便,我写了一个简单的转换脚本,你们可以直接用:

import os
import xml.etree.ElementTree as ET

def convert_voc_to_yolo(xml_file, output_dir, classes):
    tree = ET.parse(xml_file)
    root = tree.getroot()
    
    # 获取图片尺寸
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)
    
    # 获取文件名(不含扩展名)
    filename = os.path.splitext(os.path.basename(xml_file))[0]
    
    # 打开 YOLO 格式文件
    with open(os.path.join(output_dir, filename + '.txt'), 'w') as f:
        for obj in root.iter('object'):
            class_name = obj.find('name').text
            if class_name not in classes:
                continue
            class_id = classes.index(class_name)
            
            bbox = obj.find('bndbox')
            xmin = int(bbox.find('xmin').text)
            ymin = int(bbox.find('ymin').text)
            xmax = int(bbox.find('xmax').text)
            ymax = int(bbox.find('ymax').text)
            
            # 归一化
            x_center = (xmin + xmax) / 2.0 / w
            y_center = (ymin + ymax) / 2.0 / h
            width = (xmax - xmin) / w
            height = (ymax - ymin) / h
            
            f.write(f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n")

# 使用示例
classes = ['defect', 'scratch', 'dent']  # 你的类别列表
xml_folder = 'path/to/xml'
output_folder = 'path/to/yolo_labels'
os.makedirs(output_folder, exist_ok=True)

for xml_file in os.listdir(xml_folder):
    if xml_file.endswith('.xml'):
        convert_voc_to_yolo(os.path.join(xml_folder, xml_file), output_folder, classes)

4.4 本章知识体系:一张图看懂

说了这么多,我画了张图,把整个流程串起来。你一看就明白了。

数据集准备与标注流程 数据集准备与标注核心流程 数据来源 现场采集 历史归档 合成增强 标注工具 LabelImg LabelStudio 格式转换 XML (VOC) TXT (YOLO) 最终输出 images/ (图片) labels/ (TXT标注) classes.txt (类别文件) 💡 关键提醒 1. 数据质量 > 数据数量 2. 标注一致性比标注速度更重要 3. 转换后务必可视化检查

我的习惯:每次转换完,我都会写一个简单的可视化脚本,把 YOLO 的框画回原图上,看一眼。如果框的位置不对,或者类别标错了,马上就能发现。别等到训练完才发现,那代价就大了。