COCO 格式数据集制作实战:从 LabelMe JSON 到 MMDetection 可用的 3 步转换
当我们需要使用 MMDetection、YOLO 等主流目标检测框架进行模型训练时,COCO 格式数据集是最常见的选择之一。本文将详细介绍如何将 LabelMe 标注工具生成的 JSON 格式标注文件,转换为标准的 COCO 格式数据集,并确保其完全兼容 MMDetection 框架。
1. 理解 COCO 数据集格式
COCO(Common Objects in Context)数据集格式是目前计算机视觉领域最流行的标注格式之一。它采用 JSON 文件存储所有标注信息,具有以下核心结构:
{ "info": {...}, "licenses": [...], "images": [ { "id": int, "width": int, "height": int, "file_name": str, "license": int, "flickr_url": str, "coco_url": str, "date_captured": datetime } ], "annotations": [ { "id": int, "image_id": int, "category_id": int, "segmentation": RLE or [polygon], "area": float, "bbox": [x,y,width,height], "iscrowd": 0 or 1 } ], "categories": [ { "id": int, "name": str, "supercategory": str } ] }关键字段说明:
- images:记录所有图像的基本信息
- annotations:存储所有标注框的详细信息
- categories:定义数据集的类别体系
提示:对于自定义数据集,info 和 licenses 字段可以省略,但 images、annotations 和 categories 三个部分是必须的。
2. 从 LabelMe 到 COCO 的转换流程
2.1 准备工作:目录结构与数据整理
首先需要建立符合 COCO 标准的目录结构:
COCO_ROOT/ ├── annotations/ # 存放标注文件 │ ├── instances_train2017.json │ └── instances_val2017.json ├── train2017/ # 训练集图片 └── val2017/ # 验证集图片建议的数据处理流程:
- 将所有原始图片放入
images/文件夹 - 将所有 LabelMe 生成的 JSON 标注文件放入
labels/文件夹 - 确保图片和标注文件一一对应(同名不同扩展名)
2.2 核心转换代码实现
以下是一个完整的 Python 转换脚本,可将 LabelMe 标注转换为 COCO 格式:
import json import os import cv2 from glob import glob from tqdm import tqdm class Labelme2Coco: def __init__(self, labelme_json_dir, output_json_path): self.labelme_json_dir = labelme_json_dir self.output_json_path = output_json_path self.images = [] self.annotations = [] self.categories = [] self.ann_id = 1 # 定义你的类别列表 self.classes = ['cat', 'dog', 'person'] # 替换为你的实际类别 def convert(self): # 创建类别信息 for i, cls in enumerate(self.classes, 1): self.categories.append({ "id": i, "name": cls, "supercategory": "object" }) # 处理所有LabelMe JSON文件 json_files = glob(os.path.join(self.labelme_json_dir, '*.json')) for img_id, json_file in enumerate(tqdm(json_files), 1): with open(json_file, 'r') as f: data = json.load(f) # 添加图片信息 img_path = os.path.join(os.path.dirname(json_file), data['imagePath']) img = cv2.imread(img_path) height, width = img.shape[:2] self.images.append({ "id": img_id, "file_name": data['imagePath'], "width": width, "height": height }) # 处理每个标注 for shape in data['shapes']: label = shape['label'] points = shape['points'] # 计算边界框[x,y,width,height] x_min = min(p[0] for p in points) x_max = max(p[0] for p in points) y_min = min(p[1] for p in points) y_max = max(p[1] for p in points) width = x_max - x_min height = y_max - y_min # 添加标注信息 self.annotations.append({ "id": self.ann_id, "image_id": img_id, "category_id": self.classes.index(label) + 1, "bbox": [x_min, y_min, width, height], "area": width * height, "iscrowd": 0, "segmentation": [[]] # 空分割标注 }) self.ann_id += 1 # 保存为COCO格式 coco_data = { "images": self.images, "annotations": self.annotations, "categories": self.categories } with open(self.output_json_path, 'w') as f: json.dump(coco_data, f, indent=2) # 使用示例 converter = Labelme2Coco( labelme_json_dir='path/to/labelme_jsons', output_json_path='annotations/instances_train2017.json' ) converter.convert()2.3 数据集划分与验证
通常我们需要将数据集划分为训练集和验证集,推荐比例为 7:3 或 8:2。可以使用以下代码实现随机划分:
import random import shutil from sklearn.model_selection import train_test_split def split_dataset(image_dir, label_dir, output_dir, test_size=0.3): # 获取所有图片文件名(不带扩展名) image_files = [f.split('.')[0] for f in os.listdir(image_dir) if f.endswith('.jpg')] # 随机划分 train_files, val_files = train_test_split(image_files, test_size=test_size, random_state=42) # 创建输出目录 os.makedirs(os.path.join(output_dir, 'train2017'), exist_ok=True) os.makedirs(os.path.join(output_dir, 'val2017'), exist_ok=True) # 移动图片文件 for file in train_files: shutil.copy( os.path.join(image_dir, file + '.jpg'), os.path.join(output_dir, 'train2017', file + '.jpg') ) for file in val_files: shutil.copy( os.path.join(image_dir, file + '.jpg'), os.path.join(output_dir, 'val2017', file + '.jpg') ) return train_files, val_files3. 验证与 MMDetection 集成
3.1 验证 COCO 格式正确性
转换完成后,可以使用 COCO API 验证格式是否正确:
from pycocotools.coco import COCO import matplotlib.pyplot as plt # 加载验证 coco = COCO('annotations/instances_train2017.json') # 获取所有类别 cats = coco.loadCats(coco.getCatIds()) print('COCO categories: \n', [cat['name'] for cat in cats]) # 随机显示一张图片及其标注 img_ids = coco.getImgIds() img_id = random.choice(img_ids) img = coco.loadImgs(img_id)[0] ann_ids = coco.getAnnIds(imgIds=img['id']) anns = coco.loadAnns(ann_ids) # 可视化 I = plt.imread(os.path.join('train2017', img['file_name'])) plt.imshow(I) coco.showAnns(anns) plt.show()3.2 配置 MMDetection 数据集
在 MMDetection 中使用转换后的数据集,需要创建或修改配置文件:
# configs/my_config.py dataset_type = 'CocoDataset' data_root = 'path/to/COCO_ROOT/' train_pipeline = [ dict(type='LoadImageFromFile'), dict(type='LoadAnnotations', with_bbox=True), dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), dict(type='RandomFlip', flip_ratio=0.5), dict(type='Normalize', mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375]), dict(type='Pad', size_divisor=32), dict(type='DefaultFormatBundle'), dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']) ] data = dict( samples_per_gpu=2, workers_per_gpu=2, train=dict( type=dataset_type, ann_file=data_root + 'annotations/instances_train2017.json', img_prefix=data_root + 'train2017/', pipeline=train_pipeline), val=dict( type=dataset_type, ann_file=data_root + 'annotations/instances_val2017.json', img_prefix=data_root + 'val2017/', pipeline=test_pipeline), test=dict( type=dataset_type, ann_file=data_root + 'annotations/instances_val2017.json', img_prefix=data_root + 'val2017/', pipeline=test_pipeline))3.3 常见问题排查
在实际转换过程中,可能会遇到以下问题:
坐标系统不一致:
- LabelMe 使用多边形点坐标,而 COCO 使用 [x,y,width,height]
- 确保转换时正确计算边界框
类别 ID 不连续:
- COCO 要求 category_id 从 1 开始连续编号
- 0 通常保留为背景类
图像尺寸不匹配:
- 某些框架要求所有图像尺寸相同
- 可以在数据加载时统一调整尺寸
标注质量检查:
- 使用可视化工具检查转换后的标注是否正确
- 特别注意边界框是否包含完整目标
注意:对于小目标检测任务,建议检查 area 字段是否正确计算,某些框架会基于此过滤过小的目标。
通过以上三个关键步骤,我们就能将 LabelMe 标注的数据集转换为 MMDetection 可直接使用的 COCO 格式。这种转换不仅适用于目标检测任务,经过适当调整后也可用于实例分割等更复杂的视觉任务。