
COCO 格式转换实战3 种工具将 LabelMe/VOC 标注转为标准 JSON在计算机视觉领域数据标注格式的统一性直接关系到模型训练的效率与效果。COCOCommon Objects in Context格式因其结构化设计和完善的生态系统已成为目标检测、实例分割等任务的事实标准。然而实际项目中我们常遇到LabelMe、VOC等不同标注格式需要转换为COCO JSON的情况。本文将深入解析三种主流转换方案并提供可落地的代码实现。1. 理解COCO格式的核心结构COCO格式通过单个JSON文件整合所有标注信息其核心由五个部分组成{ info: {...}, // 数据集元信息 licenses: [...], // 许可协议列表 images: [...], // 图像基本信息 annotations: [...], // 物体标注信息 categories: [...] // 物体类别定义 }关键字段说明字段说明images.id图像唯一标识符需与annotations中的image_id对应annotations每个对象实例的标注包含以下关键属性- bbox[x,y,width,height]格式x/y为左上角坐标- area物体区域面积像素- iscrowd0表示单个对象1表示对象群影响分割标注格式categories必须包含id和name字段supercategory为可选父类别注意COCO的bbox格式与VOC的[xmin, ymin, xmax, ymax]不同转换时需进行相应计算。2. 方案一使用pycocotools官方工具pycocotools是COCO官方维护的Python工具包其提供的转换接口最为规范。以下是VOC转COCO的完整流程2.1 安装与环境准备pip install pycocotools2.2 转换脚本核心代码from pycocotools.coco import COCO import xml.etree.ElementTree as ET import json import os def voc_to_coco(voc_dir, output_json): categories [] annotations [] images [] # 构建类别列表示例 categories.append({id: 1, name: person}) ann_id 1 for img_id, xml_file in enumerate(os.listdir(os.path.join(voc_dir, Annotations))): tree ET.parse(os.path.join(voc_dir, Annotations, xml_file)) root tree.getroot() # 添加图像信息 img_name root.find(filename).text img_size root.find(size) images.append({ id: img_id, file_name: img_name, width: int(img_size.find(width).text), height: int(img_size.find(height).text) }) # 处理每个标注对象 for obj in root.iter(object): bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) width xmax - xmin height ymax - ymin annotations.append({ id: ann_id, image_id: img_id, category_id: 1, # 应与categories中的id对应 bbox: [xmin, ymin, width, height], area: width * height, iscrowd: 0 }) ann_id 1 # 生成COCO格式JSON coco_dict { images: images, annotations: annotations, categories: categories } with open(output_json, w) as f: json.dump(coco_dict, f)2.3 方案优劣分析优势官方维护格式兼容性100%保证支持RLE格式的分割标注内置验证工具可检查格式正确性局限不支持LabelMe格式直接转换需要自行处理类别映射关系3. 方案二使用labelme2coco开源脚本对于LabelMe标注数据推荐使用社区维护的labelme2coco工具。该方案特别适合多边形标注的转换3.1 转换流程import json import numpy as np from shapely.geometry import Polygon class LabelmeToCoco: def __init__(self, labelme_json_list, output_json): self.labelme_json_list labelme_json_list self.output_json output_json self.categories self._load_categories() def convert(self): coco_data { images: [], annotations: [], categories: self.categories } for img_id, json_path in enumerate(self.labelme_json_list): with open(json_path) as f: labelme_data json.load(f) # 添加图像信息 coco_data[images].append({ id: img_id, file_name: labelme_data[imagePath], width: labelme_data[imageWidth], height: labelme_data[imageHeight] }) # 处理每个形状标注 for shape in labelme_data[shapes]: points np.array(shape[points]) if shape[shape_type] rectangle: xmin, ymin points.min(axis0) xmax, ymax points.max(axis0) bbox [xmin, ymin, xmax-xmin, ymax-ymin] segmentation [[xmin,ymin, xmax,ymin, xmax,ymax, xmin,ymax]] else: segmentation [points.flatten().tolist()] poly Polygon(points) bbox list(poly.bounds) # [xmin, ymin, xmax, ymax] bbox[2] - bbox[0] # convert to width bbox[3] - bbox[1] # convert to height coco_data[annotations].append({ id: len(coco_data[annotations]), image_id: img_id, category_id: self._get_category_id(shape[label]), segmentation: segmentation, area: float(Polygon(points).area), bbox: bbox, iscrowd: 0 }) with open(self.output_json, w) as f: json.dump(coco_data, f)3.2 关键处理逻辑多边形转换使用Shapely库计算多边形面积和外包矩形矩形处理将LabelMe的矩形转为COCO的[x,y,w,h]格式类别映射通过_get_category_id方法实现标签名称到ID的转换提示对于复杂场景建议先统一LabelMe中的标签命名规范避免转换时出现类别不一致问题。4. 方案三基于BaseDT库的一键转换BaseDT是一个新兴的数据处理工具库其特色是提供统一的格式转换接口4.1 安装与基础使用pip install basedtfrom basedt.converter import LabelConverter # VOC转COCO converter LabelConverter(input_formatvoc, output_formatcoco) converter.convert_folder( input_dirpath/to/voc_dataset, output_fileoutput.json ) # LabelMe转COCO converter LabelConverter(input_formatlabelme, output_formatcoco) converter.convert_folder( input_dirpath/to/labelme_dataset, output_fileoutput.json )4.2 高级功能示例BaseDT支持通过配置文件自定义转换规则# convert_config.yaml category_mapping: pedestrian: person vehicle: car rider: personconverter LabelConverter( input_formatvoc, output_formatcoco, config_fileconvert_config.yaml )5. 转换方案决策树根据项目需求选择最合适的转换方案是否需要处理LabelMe多边形标注 ├── 是 → 选择方案二labelme2coco └── 否 ├── 是否需要官方标准验证 │ ├── 是 → 选择方案一pycocotools │ └── 否 → 选择方案三BaseDT └── 是否需要批量处理多种格式 ├── 是 → 选择方案三BaseDT └── 否 → 根据团队熟悉度选择方案一或三三种方案的关键指标对比指标pycocotoolslabelme2cocoBaseDT支持VOC格式✓✗✓支持LabelMe格式✗✓✓支持分割标注✓✓✓安装复杂度中等低低自定义灵活性低高中验证工具内置需自行验证部分内置6. 常见问题与解决方案问题1转换后bbox坐标异常可能原因VOC的坐标是从1开始计数而COCO从0开始图像尺寸与标注坐标不匹配解决方案# 坐标校正示例 xmin max(0, float(bbox.find(xmin).text) - 1) ymin max(0, float(bbox.find(ymin).text) - 1)问题2类别ID不连续处理方法# 建立类别映射表 category_map {cat: 1, dog: 2} # 名称到ID的映射 if label not in category_map: category_map[label] len(category_map) 1问题3大文件转换内存不足优化策略分批处理标注文件使用ijson库流式解析JSONimport ijson def large_file_converter(input_json): with open(input_json, rb) as f: for prefix, event, value in ijson.parse(f): if prefix images.item: process_image(value)