4. 数据集标注(下)
上一节我们把标注工具和基础概念捋了一遍。这一节,咱们直接上手干。
我会带着你从 Labelme 的安装开始,一路走到 COCO 格式的细节,最后再聊聊怎么保证你的标注数据不出幺蛾子。嗯,这部分内容,说白了就是决定你模型上限的关键一步。
4.1 Labelme 安装与快速上手
Labelme 是我个人最常用的标注工具之一。它轻量、跨平台,而且输出格式非常灵活。我最早接触它的时候,还是用它来标注语义分割数据,后来发现做目标检测的矩形框标注也完全够用。
4.1.1 安装
安装其实很简单,一行命令搞定。前提是你已经装好了 Python 和 pip。
pip install labelme
如果你是在 conda 环境里干活,我建议先创建一个干净的环境:
conda create -n labelme python=3.8
conda activate labelme
pip install labelme
pip install PyQt5,再装 labelme。
4.1.2 启动与界面
安装完成后,在终端输入:
labelme
界面就弹出来了。你想想看,一个干干净净的窗口,左边是工具栏,右边是图片显示区。常用的操作就几个:
- Open – 打开单张图片
- Open Dir – 打开整个图片文件夹(批量标注时用这个)
- Create Polygons – 创建多边形标注(默认是画多边形,但也能画矩形)
- Save – 保存标注结果,默认生成 JSON 文件
我个人习惯把图片文件夹和标注文件分开存放,这样方便后续脚本处理。
4.1.3 标注矩形框
做目标检测,我们主要用矩形框。Labelme 默认是多边形模式,但你可以通过设置让它直接画矩形:
- 点击菜单栏
File→Change Output Dir,选择你的标注输出文件夹。 - 点击左侧工具栏的
Create Polygons按钮(或者按快捷键Ctrl+N)。 - 在图片上点击第一个点,然后点击对角点,右键结束。这时候会弹出一个对话框让你输入标签名称。
- 输入类别名,比如
person、car,点 OK。
嗯,这里要注意:Labelme 的矩形框本质上是四个点的多边形。所以它保存的 JSON 里,points 字段会有四个坐标点。这在后面转 COCO 格式时需要处理一下。
4.2 COCO 格式详解
COCO 格式,说白了就是一套标准化的 JSON 结构。YOLO 官方虽然推荐用 TXT 格式,但很多开源项目、预训练模型、评估工具都认 COCO。我建议你至少学会看懂它。
一个标准的 COCO JSON 文件,最核心的是三个字段:images、annotations、categories。
| 字段 | 类型 | 说明 |
|---|---|---|
images |
列表 | 每张图片的信息:id、文件名、宽、高 |
annotations |
列表 | 每个标注框的信息:所属图片 id、类别 id、bbox、面积等 |
categories |
列表 | 类别定义:id 和 name 的映射 |
举个例子,一个简单的 COCO JSON 长这样:
{
"images": [
{
"id": 1,
"file_name": "000001.jpg",
"width": 1920,
"height": 1080
}
],
"annotations": [
{
"id": 1,
"image_id": 1,
"category_id": 1,
"bbox": [100, 200, 300, 400],
"area": 120000,
"iscrowd": 0
}
],
"categories": [
{
"id": 1,
"name": "person"
}
]
}
[x, y, width, height],也就是左上角坐标 + 宽高。而 YOLO 的 TXT 格式是 [class_id, x_center, y_center, width, height],并且所有坐标都是归一化的。这两个格式之间的转换,是咱们后面写脚本的必修课。
4.3 多类别标注实战
假设我们要做一个交通场景检测,包含三个类别:person、car、traffic_light。
操作步骤其实很简单:
- 打开 Labelme,加载图片文件夹。
- 对每张图片,依次框出所有目标。
- 每个框都输入对应的类别名。
- 保存后,每张图片会生成一个同名的 JSON 文件。
但这里有个坑,我踩过。Labelme 不会自动帮你记住类别列表。如果你标注了 100 张图,每次都要手动输入 person,很容易打错字。比如把 person 打成 persn,后面脚本一跑就报错。
labels.txt,里面每行一个类别名。然后在 Labelme 的 File → Save Automatically 旁边,有个 Edit → Create Line 之类的选项,其实没有直接导入功能。更靠谱的做法是写个小脚本,批量检查 JSON 文件里的类别名是否都在你的预期列表里。
标注完一批数据后,你的文件夹结构应该是这样的:
dataset/
├── images/
│ ├── 000001.jpg
│ ├── 000002.jpg
│ └── ...
└── labels/
├── 000001.json
├── 000002.json
└── ...
4.4 数据质量检查
标注完了别急着高兴。数据质量不过关,模型训练就是白费功夫。我见过太多人标注完直接开训,结果模型 loss 死活降不下去,最后发现是标注框歪了。
我一般会做以下几项检查:
4.4.1 可视化检查
写一个简单的脚本,把标注框画回图片上,肉眼扫一遍。这一步最笨,但也最有效。
import json
import cv2
def check_annotation(image_path, json_path):
with open(json_path, 'r') as f:
data = json.load(f)
img = cv2.imread(image_path)
for shape in data['shapes']:
points = shape['points']
# 矩形框:取左上和右下
x1, y1 = points[0]
x2, y2 = points[2]
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
cv2.putText(img, shape['label'], (int(x1), int(y1)-5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
cv2.imshow('check', img)
cv2.waitKey(0)
4.4.2 统计检查
我会统计一下每张图片的标注框数量、每个类别的样本数。如果某个类别只有几张图有标注,那这个类别基本学不出来。
| 检查项 | 正常范围 | 异常信号 |
|---|---|---|
| 每张图框数 | 1-20 | 超过50,可能漏标或重复标 |
| 类别分布 | 各类别比例接近 | 某个类别占比 < 5% |
| 框大小 | 占图片面积 1%-50% | 框太小或太大,可能是误标 |
4.4.3 边界检查
检查有没有框超出图片边界。Labelme 允许你画到图片外面去,但训练时这些框会导致坐标归一化后出现负数或大于1的值,直接报错。
4.5 本章知识结构
下面这张图,帮你把这一节的核心流程串起来:
从 Labelme 安装开始,到多类别标注,再到理解 COCO 格式,最后用三种检查手段兜底。这一套流程走下来,你的数据集质量基本就有保障了。