4、数据集准备:从原始图片到YOLO能吃的数据
做目标检测,模型选得再好,训练代码写得再溜,如果数据这关没过,一切都是白搭。我见过太多人花大把时间调参,结果发现是标注格式错了——那种挫败感,啧。
这一章,咱们就把数据准备这条流水线彻底打通。从标注工具怎么用,到COCO和YOLO格式到底差在哪,再到数据增强怎么玩才有效。你想想看,模型看到的每一张图,都是从这里开始的。
4.1 LabelImg标注工具——别嫌它土,够用就行
LabelImg这工具,界面说不上好看,但胜在稳定。我用了快五年,从没出过标注错位的问题。相比之下,有些在线标注平台花里胡哨的,导出格式反而各种坑。
安装与启动
# 方式一:pip安装(推荐)
pip install labelImg
labelImg
# 方式二:源码运行
git clone https://github.com/tzutalin/labelImg
cd labelImg
python labelImg.py
我的习惯:用pip安装,然后在桌面建个快捷方式。标注是个重复劳动,启动越方便越好。
标注流程(三步走)
- 打开图片目录——左侧自动显示图片列表,支持翻页
- 画框——按住鼠标拖拽,框住目标物体
- 打标签——输入类别名称,或者从预定义列表选
快捷键一定要记住:W 画框,A/D 翻页,Ctrl+S 保存。我刚开始标注时不知道快捷键,一个个点按钮,300张图标了一整天——后来用快捷键,半天搞定。
曾经踩过的坑:标注完直接关软件,没保存。结果XML文件是空的,白干两小时。记住:每标完一张图就Ctrl+S,养成肌肉记忆。
4.2 COCO数据集格式——业界标杆,必须看懂
COCO格式说白了就是一个大JSON文件,里面装了三块核心信息。你不需要背下来,但得知道去哪查、怎么改。
| 字段 | 作用 | 说明 |
|---|---|---|
images |
图片信息列表 | 每张图有id、宽高、文件名 |
annotations |
标注信息列表 | 每个框有image_id、bbox[x,y,w,h]、category_id |
categories |
类别字典 | id从1开始,name是类别名 |
{
"images": [{"id": 1, "file_name": "001.jpg", "width": 640, "height": 480}],
"annotations": [{
"id": 1, "image_id": 1,
"bbox": [100, 150, 200, 300], // x, y, w, h
"category_id": 1
}],
"categories": [{"id": 1, "name": "person"}]
}
关键点:COCO的bbox是[x, y, width, height],左上角坐标+宽高。很多人转YOLO时在这里翻车——YOLO要的是中心点坐标+宽高,而且必须归一化。
4.3 YOLO格式转换——最容翻车的环节
YOLO的标注格式极其简单:每个图片对应一个TXT文件,每行一个目标。但越简单的东西,越容易在细节上出错。
# YOLO格式:class_id center_x center_y width height
# 所有坐标都是归一化的(除以图片宽高)
0 0.5 0.5 0.3 0.4
1 0.2 0.8 0.1 0.15
COCO转YOLO的核心代码
import json
import os
def coco_to_yolo(coco_json_path, output_dir):
with open(coco_json_path, 'r') as f:
coco = json.load(f)
# 建立image_id到文件名的映射
img_map = {img['id']: img for img in coco['images']}
for ann in coco['annotations']:
img_info = img_map[ann['image_id']]
img_w, img_h = img_info['width'], img_info['height']
# COCO: [x, y, w, h] → YOLO: [cx, cy, w, h] 归一化
x, y, w, h = ann['bbox']
cx = (x + w / 2) / img_w
cy = (y + h / 2) / img_h
nw = w / img_w
nh = h / img_h
# 写入对应TXT文件
txt_name = img_info['file_name'].replace('.jpg', '.txt')
with open(os.path.join(output_dir, txt_name), 'a') as f:
f.write(f"{ann['category_id']} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}\n")
我曾经犯过的错:类别ID没减1。COCO的category_id从1开始,但YOLO要求从0开始。结果训练出来的模型,检测到人输出的是类别1,但实际类别0才是人——整个模型全废,排查了两天才发现。
4.4 数据增强策略——别让模型死记硬背
数据增强不是为了把1张图变成10张图,而是为了让模型学会「不变性」。说白了,就是让模型知道:不管物体在左边还是右边,不管光线亮还是暗,它都是同一个东西。
我常用的增强组合
| 增强方式 | 适用场景 | 注意点 |
|---|---|---|
| 水平翻转 | 几乎所有场景 | 文字、对称物体慎用 |
| 随机裁剪 | 小目标检测 | 别裁掉目标主体 |
| 色彩抖动 | 光照变化大的场景 | HSV范围别太大 |
| 马赛克增强 | YOLOv4+常用 | 小batch时效果明显 |
用OpenCV实现基础增强
import cv2
import numpy as np
def augment_image(image, bboxes):
"""bboxes: [[cx, cy, w, h], ...] 归一化坐标"""
h, w = image.shape[:2]
# 1. 水平翻转(50%概率)
if np.random.rand() > 0.5:
image = cv2.flip(image, 1)
# 坐标变换:cx = 1 - cx
bboxes[:, 0] = 1 - bboxes[:, 0]
# 2. 亮度调整
value = np.random.randint(-30, 30)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
hsv[:, :, 2] = np.clip(hsv[:, :, 2].astype(int) + value, 0, 255).astype(np.uint8)
image = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
return image, bboxes
我的经验:增强不是越多越好。我见过有人把亮度、对比度、模糊、噪声全加上,结果模型在验证集上反而掉点。一般选3-4种,每种概率0.3-0.5就够了。另外,标注框一定要跟着图一起变换,这个最容易忘。
4.5 数据集目录结构——规范能救命
项目做多了你就会发现,目录结构乱的人,最后自己都找不到数据在哪。我有一套固定的组织方式,用了好几年,分享给你:
dataset/
├── images/
│ ├── train/ # 训练图片
│ ├── val/ # 验证图片
│ └── test/ # 测试图片
├── labels/
│ ├── train/ # 训练标注(TXT)
│ ├── val/ # 验证标注
│ └── test/ # 测试标注
├── data.yaml # 类别配置文件
└── classes.txt # 类别列表
# data.yaml 内容
train: ./images/train
val: ./images/val
nc: 2 # 类别数量
names: ['person', 'car']
一句话总结:标注要准,格式要对,增强要稳,目录要清。这四点做到位,模型训练就成功了一半。