从Labelme到COCO:一站式解析目标检测与实例分割数据格式转换实战
1. 为什么需要从Labelme转换到COCO格式
当你用Labelme标注完几百张图片后,看着密密麻麻的json文件可能会突然意识到一个问题:这些标注数据怎么才能喂给主流的深度学习模型训练?这时候COCO格式就闪亮登场了。COCO(Common Objects in Context)是目前计算机视觉领域最通用的数据集格式之一,像YOLOv5、Mask R-CNN这些明星模型都直接支持COCO格式的数据加载。
我去年帮一家电商客户做商品检测时就踩过这个坑。他们的标注团队用Labelme标注了上万张商品图片,结果发现训练代码读不懂这些数据。后来我们花了三天时间研究格式转换,期间还因为category_id映射错误导致模型把"手机"识别成"鼠标"。所以今天我就把完整的避坑指南整理出来,让你少走弯路。
COCO格式的核心优势在于它的结构化设计。一个标准的COCO格式json文件包含四个关键部分:
- images:记录所有图片的基本信息(宽度、高度、文件名等)
- annotations:保存每个实例的详细标注(bbox坐标、分割多边形、所属类别等)
- categories:定义所有类别名称和ID的对应关系
- licenses(可选):标注数据的使用许可信息
这种结构特别适合处理一张图片包含多个目标实例的场景。相比之下,Labelme的每个json文件只对应单张图片的标注,训练前需要先整合成COCO这种"图片库+标注池"的形式。
2. 转换前的准备工作
2.1 检查你的Labelme标注质量
在开始转换前,强烈建议先做一轮数据质检。我遇到过最离谱的情况是标注员把全部bbox的坐标都存成了字符串格式,导致转换脚本直接报错。这里分享几个快速检查的小技巧:
# 用jq工具快速查看json结构
sudo apt install jq # Ubuntu安装命令
jq '.' example.json | head -20
# 检查标注文件与图片是否匹配
for f in *.json; do
img=${f%.*}.jpg
if [ ! -f "$img" ]; then
echo "缺失图片: $img"
fi
done
常见问题包括:
- 图片路径错误(绝对路径导致在其他机器无法读取)
- 存在空标注文件(标注时误保存)
- 多边形坐标点数量不足(小于3个点无法构成闭合区域)
- 类别名称不一致(比如"car"和"Car"会被视为不同类别)
2.2 准备类别映射文件
COCO格式要求每个类别必须有唯一的整数ID。建议创建一个labels.txt文件来明确定义这种映射关系。例如:
__ignore__
background
person
car
dog
注意第一行的__ignore__是Labelme的特殊标识,对应的category_id会是-1。从第二行开始,category_id从0开始递增。这个文件要保存好,因为训练模型时也需要完全一致的类别顺序。
3. 两种转换方法实战
3.1 使用Labelme官方工具
Labelme其实自带了转换脚本,藏在源码的examples/instance_segmentation目录下。安装依赖时要注意版本兼容性:
# 推荐使用Python虚拟环境
python -m venv labelme2coco
source labelme2coco/bin/activate
# 安装指定版本依赖
pip install labelme==5.1.1 pycocotools==2.0.4 imgviz==1.7.1
转换命令示例:
python labelme2coco.py \
--input_dir ./labelme_annotations \
--output_dir ./coco_dataset \
--labels labels.txt \
--noviz # 跳过可视化可加快速度
这个脚本会自动做三件事:
- 将所有图片复制到JPEGImages目录
- 生成annotations.json
- 为每张图片生成标注可视化效果图(除非加--noviz参数)
常见报错处理:
- "AttributeError: 'NoneType' object has no attribute 'shape'":通常是图片读取失败,检查json中的imagePath字段
- "KeyError: 'xxx' in class_name_to_id":labels.txt中缺少对应的类别
- "ValueError: invalid literal for int() with base 10":标注坐标中包含非数字字符
3.2 自定义转换脚本
当需要特殊处理时(比如合并多个数据集),可能需要自己写转换逻辑。以下是核心代码解析:
def shape_to_coco(shape, image_id, category_id, annotation_id):
"""将Labelme的单个shape转换为COCO标注格式"""
points = np.array(shape["points"])
segmentation = points.flatten().tolist()
# 计算bbox [x,y,width,height]
x_min, y_min = np.min(points, axis=0)
x_max, y_max = np.max(points, axis=0)
bbox = [x_min, y_min, x_max - x_min, y_max - y_min]
# 计算面积(多边形面积公式)
area = 0.5 * np.abs(np.dot(points[:,0], np.roll(points[:,1],1))
- np.dot(points[:,1], np.roll(points[:,0],1)))
return {
"id": annotation_id,
"image_id": image_id,
"category_id": category_id,
"segmentation": [segmentation],
"area": float(area),
"bbox": bbox,
"iscrowd": 0 # 0表示单个对象,1表示一组对象
}
处理矩形标注时要特别注意:Labelme存储的是对角两点坐标,而COCO需要转为[x,y,width,height]格式。实例分割中的group_id字段也要妥善处理,它用于区分同一类别的不同实例。
4. 转换后的数据验证
4.1 结构检查
用Python快速验证生成的COCO文件:
import json
from pycocotools.coco import COCO
with open("annotations.json") as f:
data = json.load(f)
print(f"图片数量: {len(data['images'])}")
print(f"标注数量: {len(data['annotations'])}")
print(f"类别数量: {len(data['categories'])}")
# 检查标注与图片的对应关系
coco = COCO("annotations.json")
img_ids = coco.getImgIds()
ann_ids = coco.getAnnIds(imgIds=img_ids[:1])
print(f"第一张图片的标注数: {len(ann_ids)}")
4.2 可视化验证
安装labelme后可以直接查看COCO格式标注:
labelme --labels labels.txt --input JPEGImages/ --annotations annotations.json
重点检查:
- 所有标注框是否与物体对齐
- 实例分割边缘是否精确
- 类别标签是否正确
- 是否有漏标的物体
5. 进阶技巧与避坑指南
5.1 处理crowd区域
当标注密集小物体(如人群)时,可能需要设置iscrowd=1。这时bbox应该覆盖整个区域,而segmentation可以是简化的轮廓。在Labelme中可以通过给标注添加iscrowd属性实现:
{
"shape_type": "polygon",
"label": "person",
"points": [[...]],
"group_id": 1,
"iscrowd": 1
}
5.2 多数据集合并
合并多个COCO格式数据集时要注意:
- 统一category_id映射
- 重新编排image_id和annotation_id
- 处理可能重复的文件名
建议使用COCO API的merge函数:
from pycocotools.coco import COCO
coco1 = COCO("dataset1.json")
coco2 = COCO("dataset2.json")
merged = COCO()
merged.dataset = {
"images": coco1.dataset["images"] + coco2.dataset["images"],
"annotations": coco1.dataset["annotations"] + coco2.dataset["annotations"],
"categories": coco1.dataset["categories"] # 假设类别一致
}
5.3 性能优化
当处理上万张图片时,转换过程可能很耗时。几个优化建议:
- 使用多进程处理(Python的multiprocessing模块)
- 先将所有图片调整为统一尺寸,减少I/O压力
- 使用更快的json库(如orjson)替代标准json模块
import orjson
# 比标准json快5-10倍
with open("big.json", "rb") as f:
data = orjson.loads(f.read())
更多推荐


所有评论(0)