共计 3987 个字符,预计需要花费 10 分钟才能阅读完成。
背景痛点
在目标检测任务中,数据标注和模型训练往往是割裂的两个环节。传统标注工具(如 LabelImg)生成的标注文件格式(如 Pascal VOC)与 YOLO 训练框架所需的格式不一致,导致开发者需要花费大量时间进行格式转换和手动调整。这不仅降低了工作效率,还容易引入人为错误。

技术方案
AnyLabeling 标注数据格式解析
AnyLabeling 支持多种标注格式,默认输出为 JSON 格式。一个典型的 JSON 标注文件如下:
{
"version": "0.1.0",
"flags": {},
"shapes": [
{
"label": "person",
"points": [[100, 200],
[300, 400]
],
"group_id": null,
"shape_type": "rectangle",
"flags": {}}
],
"imagePath": "example.jpg",
"imageData": null,
"imageHeight": 1080,
"imageWidth": 1920
}
格式转换脚本示例
以下 Python 脚本将 AnyLabeling 的 JSON 标注转换为 YOLO 格式:
import json
import os
def convert_anylabeling_to_yolo(json_path, output_dir, class_mapping):
"""
Convert AnyLabeling JSON annotations to YOLO format.
:param json_path: Path to the JSON annotation file
:param output_dir: Directory to save YOLO format annotations
:param class_mapping: Dictionary mapping class names to class IDs
"""
try:
with open(json_path, 'r') as f:
data = json.load(f)
image_width = data['imageWidth']
image_height = data['imageHeight']
txt_filename = os.path.splitext(os.path.basename(json_path))[0] + '.txt'
txt_path = os.path.join(output_dir, txt_filename)
with open(txt_path, 'w') as f:
for shape in data['shapes']:
label = shape['label']
if label not in class_mapping:
continue
class_id = class_mapping[label]
points = shape['points']
# Convert absolute coordinates to relative coordinates
x_center = (points[0][0] + points[1][0]) / 2 / image_width
y_center = (points[0][1] + points[1][1]) / 2 / image_height
width = abs(points[1][0] - points[0][0]) / image_width
height = abs(points[1][1] - points[0][1]) / image_height
f.write(f"{class_id} {x_center} {y_center} {width} {height}\n")
except Exception as e:
print(f"Error converting {json_path}: {str(e)}")
# Example usage
class_mapping = {"person": 0, "car": 1, "dog": 2}
convert_anylabeling_to_yolo("example.json", "yolo_labels", class_mapping)
YOLO 数据集目录结构规范
YOLO 期望的数据集目录结构如下:
dataset/
├── images/
│ ├── train/
│ ├── val/
│ └── test/
└── labels/
├── train/
├── val/
└── test/
关键配置文件调整
data.yaml 示例:
train: ../dataset/images/train
val: ../dataset/images/val
# number of classes
nc: 3
# class names
names: ['person', 'car', 'dog']
实现细节
标注坐标系的转换原理
YOLO 使用相对坐标(0- 1 之间),而 AnyLabeling 输出的是绝对坐标(像素值)。转换公式如下:
- x_center = (x1 + x2) / 2 / image_width
- y_center = (y1 + y2) / 2 / image_height
- width = (x2 – x1) / image_width
- height = (y2 – y1) / image_height
多类别标签的映射策略
建议在项目开始时建立明确的类别映射表,并在整个项目中保持一致。可以使用字典来维护这种映射关系。
数据集自动划分脚本
以下脚本将数据集划分为 train/val/test:
import os
import random
import shutil
def split_dataset(image_dir, label_dir, output_dir, ratios=(0.7, 0.2, 0.1)):
"""
Split dataset into train/val/test sets.
:param image_dir: Directory containing images
:param label_dir: Directory containing labels
:param output_dir: Root directory for output
:param ratios: Tuple of (train_ratio, val_ratio, test_ratio)
"""
# Create output directories
os.makedirs(os.path.join(output_dir, 'images', 'train'), exist_ok=True)
os.makedirs(os.path.join(output_dir, 'images', 'val'), exist_ok=True)
os.makedirs(os.path.join(output_dir, 'images', 'test'), exist_ok=True)
os.makedirs(os.path.join(output_dir, 'labels', 'train'), exist_ok=True)
os.makedirs(os.path.join(output_dir, 'labels', 'val'), exist_ok=True)
os.makedirs(os.path.join(output_dir, 'labels', 'test'), exist_ok=True)
# Get all image files
image_files = [f for f in os.listdir(image_dir) if f.endswith(('.jpg', '.png'))]
random.shuffle(image_files)
# Calculate split indices
total = len(image_files)
train_end = int(total * ratios[0])
val_end = train_end + int(total * ratios[1])
# Split and copy files
for i, filename in enumerate(image_files):
basename = os.path.splitext(filename)[0]
label_file = basename + '.txt'
if i < train_end:
subset = 'train'
elif i < val_end:
subset = 'val'
else:
subset = 'test'
# Copy image
shutil.copy(os.path.join(image_dir, filename),
os.path.join(output_dir, 'images', subset, filename))
# Copy label if exists
if os.path.exists(os.path.join(label_dir, label_file)):
shutil.copy(os.path.join(label_dir, label_file),
os.path.join(output_dir, 'labels', subset, label_file))
# Example usage
split_dataset("raw_images", "raw_labels", "dataset")
避坑指南
标注质量检查要点
- 检查是否有漏标的对象
- 验证标注框是否准确覆盖目标
- 确保类别标签正确无误
- 检查标注框是否超出图像边界
常见格式错误排查
- 文件编码问题:确保使用 UTF- 8 编码
- 路径问题:使用绝对路径或正确的相对路径
- 格式错误:YOLO 标签文件每行应为 ”class_id x_center y_center width height”
- 图像与标签不匹配:检查文件名是否一致
小样本场景下的数据增强建议
- 使用 Albumentations 或 torchvision 进行数据增强
- 考虑使用 Mosaic 增强
- 尝试复制少量样本并进行随机变换
- 使用迁移学习或预训练模型
延伸思考
- 如何处理标注数据中的类别不平衡问题?
- 在实时标注场景下,如何优化标注到训练的流程?
- 对于大规模数据集,如何设计更高效的标注 - 训练迭代流程?
正文完
