3、数据集构建与标注:LabelImg/Labelme使用、COCO数据集格式详解、自定义数据集制作流程

做目标检测,数据是第一步。说实话,模型结构再花哨,数据不行全白搭。我见过太多同学把时间花在调参上,结果发现是标注格式搞错了,或者数据集质量太差。今天咱们就把数据集构建这件事彻底讲透。

3.1 标注工具的选择:LabelImg vs Labelme

先聊聊工具。市面上标注工具不少,但最常用的就两个:LabelImg 和 Labelme。很多人搞不清它们的区别,我简单说说。

特性 LabelImg Labelme
标注类型 矩形框(目标检测) 多边形、矩形、点、线(分割+检测)
输出格式 PASCAL VOC XML / YOLO TXT JSON(支持COCO格式转换)
适用场景 目标检测(YOLO系列) 实例分割、语义分割、关键点检测
安装难度 极简(pip install) 稍复杂(依赖较多)

我个人习惯:做YOLOv5检测任务,首选LabelImg。它轻量、快,而且直接支持YOLO格式输出。但如果你要做分割任务,比如Mask R-CNN或者YOLOv5-seg,那就得上Labelme了。

核心观点:工具只是手段,别在工具选择上纠结太久。LabelImg搞定90%的检测场景,Labelme搞定剩下的10%。

3.2 LabelImg实战:从安装到标注

安装LabelImg很简单,一行命令搞定:

pip install labelImg

然后终端输入 labelImg 就能启动。嗯,这里要注意——如果你用的是Python 3.10以上版本,可能会遇到PyQt5的兼容问题。我遇到过这种情况,解决方案是用conda创建一个Python 3.8的环境。

标注流程其实就四步:

  1. 打开图片目录:点击"Open Dir"选择你的图片文件夹
  2. 创建类别:点击"Change Save Dir"设置标注文件保存路径,然后用"Create RectBox"画框
  3. 标注:框选目标后,输入类别名称(比如person、car)
  4. 保存:按Ctrl+S,默认保存为PASCAL VOC格式的XML文件

小技巧:标注前先创建一个classes.txt文件,里面写好所有类别。然后在LabelImg里加载这个文件,标注时就能直接下拉选择,不用每次都手打。效率能提升30%以上。

输出格式怎么选?我个人建议:如果你打算用YOLOv5训练,直接保存为YOLO格式。在LabelImg里点击"View"→"Auto Save mode",再设置"YOLO"格式。这样每个图片会生成一个同名的.txt文件,里面格式是:

class_id x_center y_center width height

注意,这里的坐标是归一化后的,取值范围0~1。举个例子:

0 0.5 0.5 0.3 0.4
1 0.2 0.8 0.1 0.15

这表示第一个目标类别ID为0,中心点在(0.5, 0.5),宽0.3高0.4。第二个目标类别ID为1,中心点在(0.2, 0.8),宽0.1高0.15。

避坑指南:我曾经犯过一个低级错误——标注时类别ID从1开始,但YOLOv5要求从0开始。结果训练出来的模型,预测结果全部偏移了一个类别。排查了整整一天才发现是这个问题。所以记住:类别ID从0开始

3.3 COCO数据集格式详解

COCO数据集格式是目前最通用的标注格式之一。YOLOv5官方也提供了COCO格式的支持。说白了,你只要把数据转成COCO格式,就能用YOLOv5的脚本直接训练。

COCO格式的核心是一个JSON文件,结构如下:

{
  "images": [
    {
      "id": 1,
      "file_name": "000001.jpg",
      "width": 640,
      "height": 480
    }
  ],
  "annotations": [
    {
      "id": 1,
      "image_id": 1,
      "category_id": 1,
      "bbox": [100, 150, 200, 300],
      "area": 60000,
      "iscrowd": 0
    }
  ],
  "categories": [
    {
      "id": 1,
      "name": "person"
    }
  ]
}

这里有几个关键字段:

  • images:图片信息列表,每张图片一个对象,包含id、文件名、宽高
  • annotations:标注信息列表,每个目标一个对象,包含所属图片id、类别id、bbox坐标
  • categories:类别列表,id和名称的映射

bbox的格式是 [x, y, width, height],注意这是左上角坐标+宽高,不是中心点坐标。和YOLO格式不一样,转换时要小心。

重点:COCO格式的bbox是绝对坐标,单位是像素。YOLO格式是归一化坐标。两者转换公式:

COCO → YOLO:x_center = (x + w/2) / img_w, y_center = (y + h/2) / img_h, w = w / img_w, h = h / img_h

YOLO → COCO:x = (x_center - w/2) * img_w, y = (y_center - h/2) * img_h, w = w * img_w, h = h * img_h

3.4 自定义数据集制作流程

好了,理论讲完了,咱们来点实际的。假设你要做一个「安全帽检测」的数据集,完整流程是这样的:

第一步:数据采集

别想着自己拍几千张照片,太累了。我一般用三种方式:

  • 网上爬虫(注意版权问题)
  • 公开数据集(比如Google Open Images)
  • 自己拍摄(补充特殊场景)

数量上,我建议至少1000张起步。类别越多,每类需要的样本也越多。

第二步:数据清洗

采集回来的图片,先过一遍。模糊的、重复的、无关的,统统删掉。我习惯写个脚本自动检测模糊图片:

import cv2
import os

def is_blurry(image_path, threshold=100):
    img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    laplacian_var = cv2.Laplacian(img, cv2.CV_64F).var()
    return laplacian_var < threshold

# 遍历图片目录,删除模糊图片
for img_file in os.listdir('raw_images'):
    if is_blurry(f'raw_images/{img_file}'):
        os.remove(f'raw_images/{img_file}')
        print(f'删除模糊图片: {img_file}')

第三步:标注

用LabelImg一张一张标。这里有个经验:标注质量比数量重要。我宁愿要500张高质量标注,也不要2000张粗标的数据。边界框要贴合目标,不要留太多背景,也不要切掉目标主体。

第四步:数据集划分

标注完成后,把数据分成三份:训练集70%、验证集20%、测试集10%。YOLOv5要求的数据目录结构是这样的:

dataset/
├── images/
│   ├── train/
│   ├── val/
│   └── test/
└── labels/
    ├── train/
    ├── val/
    └── test/

每个子目录里放对应的图片和标注文件,文件名要一一对应。比如 images/train/001.jpg 对应 labels/train/001.txt

第五步:创建配置文件

在YOLOv5项目里,创建一个 data.yaml 文件:

train: dataset/images/train
val: dataset/images/val
test: dataset/images/test

nc: 2
names: ['helmet', 'no_helmet']

nc是类别数量,names是类别名称列表。注意顺序要和标注时的类别ID一致。

效率提升技巧:标注完成后,用脚本检查一下数据完整性。我写了个小工具,能自动检查:

  • 图片和标注文件是否一一对应
  • 标注框坐标是否越界(比如x+w > img_w)
  • 类别ID是否在有效范围内

这些检查能帮你避免90%的训练报错。

3.5 格式转换:LabelImg标注转COCO格式

有时候你需要把LabelImg标注的数据转成COCO格式,比如要用MMDetection或者Detectron2。我分享一个转换脚本的核心逻辑:

import json
import os
import xml.etree.ElementTree as ET

def voc_to_coco(voc_dir, output_json):
    coco = {
        "images": [],
        "annotations": [],
        "categories": []
    }
    
    # 先构建类别映射
    categories = {}
    # ... 遍历XML文件,提取信息
    
    # 每张图片生成一个image对象
    # 每个目标生成一个annotation对象
    
    with open(output_json, 'w') as f:
        json.dump(coco, f, indent=4)

这个脚本网上有很多现成的,但建议你自己写一遍。为什么?因为每个项目的目录结构、命名规则都不一样,直接套用别人的脚本往往要改半天。我自己就吃过这个亏。

重要提醒:转换时最容易出错的是坐标格式。COCO的bbox是[x, y, w, h],而VOC的XML里存的是[xmin, ymin, xmax, ymax]。转换公式:w = xmax - xmin, h = ymax - ymin。千万别搞反了。

3.6 数据增强:让有限的数据发挥最大价值

数据不够?别急着去采集。先做数据增强。YOLOv5自带了很多增强策略,比如马赛克增强、随机缩放、色彩抖动等。但我要说的是:增强不是越多越好

我见过有人把一张图片增强出50个版本,结果模型过拟合到增强模式上去了。合理的做法是:

  • 小数据集(<500张):用马赛克增强、随机裁剪、翻转
  • 中等数据集(500-2000张):用色彩抖动、模糊、噪声
  • 大数据集(>2000张):基本不用增强,或者只用轻微的马赛克

嗯,这里有个坑——增强后的数据要检查。我曾经用马赛克增强,结果把两个不同类别的目标拼在一起,标注框重叠了,模型学到的特征完全混乱。后来我加了一个检查逻辑:增强后的标注框面积不能小于原图的1%,也不能大于原图的90%。

好了,数据集构建这部分就讲到这里。记住一句话:数据质量决定模型上限,算法只是逼近这个上限。下一章咱们聊聊YOLOv5的网络结构,看看这个模型到底是怎么工作的。