从COCO到自定义:手把手教你用Python脚本转换和验证关键点标注JSON
从COCO到自定义Python脚本实现关键点标注JSON的转换与验证在计算机视觉领域关键点检测任务如人体姿态估计、面部特征点定位的模型训练离不开高质量标注数据。COCO关键点格式因其标准化程度高、兼容主流框架如MMPose、Detectron2而成为行业通用标准。但实际项目中我们常遇到标注工具输出格式不匹配、关键点可见性标识错误、骨架连接关系缺失等问题——这些问题往往在训练前数据加载阶段才暴露导致不必要的返工。本文将分享一套完整的Python解决方案涵盖以下核心场景解析COCO关键点标注的底层数据结构批量转换Labelme/VGG等常见格式至COCO标准自动化验证关键点数组长度、可见性标识合规性修复骨架连接关系与关键点命名的映射错误1. COCO关键点格式深度解析1.1 标注文件结构解剖一个合规的COCO关键点标注JSON包含四个核心字段{ info: {}, // 数据集元信息 licenses: [], // 版权声明 images: [ // 图像基础信息 { id: 1, // 唯一标识 width: 640, height: 480, file_name: image1.jpg } ], annotations: [ // 关键点标注主体 { image_id: 1, category_id: 1, keypoints: [x1,y1,v1, x2,y2,v2,...], // 关键点坐标可见性 num_keypoints: 17, // 实际标注点数(v0) bbox: [x,y,w,h] // 对象边界框 } ], categories: [ // 关键点类别定义 { id: 1, name: person, keypoints: [nose, left_eye,...], // 关键点名称 skeleton: [[16,14],[14,12],...] // 连接关系 } ] }注关键点可见性标识v的取值规则0未标注xy01标注但不可见如被遮挡2标注且可见1.2 关键验证指标使用以下Python代码快速检查标注完整性def validate_coco_keypoints(anno_path): with open(anno_path) as f: data json.load(f) required_fields [images, annotations, categories] assert all(field in data for field in required_fields), 缺少必要字段 for anno in data[annotations]: kps anno[keypoints] assert len(kps) % 3 0, f关键点数组长度应为3的倍数当前{len(kps)} assert anno[num_keypoints] sum(1 for i in range(0,len(kps),3) if kps[i2]0), num_keypoints计数错误2. 格式转换实战Labelme→COCO2.1 处理Labelme多边形标注Labelme生成的标注文件通常包含多边形坐标需转换为COCO的bbox格式def labelme_to_coco(labelme_dir, output_path): coco_data {images: [], annotations: [], categories: []} # 构建类别映射示例为人体关键点 coco_data[categories] [{ id: 1, name: person, keypoints: [nose, left_eye,...], # 17个关键点 skeleton: [[16,14],[14,12],...] # COCO标准骨架 }] for img_file in os.listdir(labelme_dir): if not img_file.endswith(.json): continue with open(os.path.join(labelme_dir, img_file)) as f: labelme json.load(f) # 转换图像信息 img_id len(coco_data[images]) 1 coco_data[images].append({ id: img_id, width: labelme[imageWidth], height: labelme[imageHeight], file_name: img_file.replace(.json, .jpg) }) # 转换标注信息 for shape in labelme[shapes]: if shape[shape_type] ! point: continue # 计算边界框所有关键点的最小外接矩形 points np.array([point for point in shape[points]]) x_min, y_min points.min(axis0) x_max, y_max points.max(axis0) coco_data[annotations].append({ image_id: img_id, category_id: 1, bbox: [x_min, y_min, x_max-x_min, y_max-y_min], keypoints: [p for pt in points for p in [pt[0], pt[1], 2]], # 默认可见 num_keypoints: len(points), iscrowd: 0 }) with open(output_path, w) as f: json.dump(coco_data, f)2.2 关键点名称映射不同工具的关键点命名可能不同需建立映射表Labelme关键点COCO标准名称索引nosenose0left_eyeleft_eye1.........KEYPOINT_MAP { nose: 0, left_eye: 1, # 其他关键点映射... } def map_keypoints(labelme_points, mapping): coco_kps [0] * (17 * 3) # 初始化COCO格式数组 for name, x, y in labelme_points: idx mapping.get(name, -1) if idx 0: coco_kps[idx*3] x coco_kps[idx*31] y coco_kps[idx*32] 2 # 默认可见 return coco_kps3. 高级校验与修复3.1 骨架连接性验证检查skeleton定义是否与keypoints名称匹配def validate_skeleton(coco_data): cat coco_data[categories][0] kp_names cat[keypoints] for connection in cat[skeleton]: assert all(0 i len(kp_names) for i in connection), \ f无效骨架连接{connection}关键点总数{len(kp_names)} print(骨架验证通过)3.2 可见性标识修复自动修正常见可见性标注错误def fix_visibility(anno): kps anno[keypoints] fixed 0 for i in range(0, len(kps), 3): x, y, v kps[i], kps[i1], kps[i2] if v 0 and (x ! 0 or y ! 0): # 坐标非零但标记为未标注 kps[i2] 2 fixed 1 elif v 0 and x 0 and y 0: # 坐标为零但标记为已标注 kps[i2] 0 fixed 1 if fixed 0: anno[num_keypoints] sum(1 for i in range(0,len(kps),3) if kps[i2]0) return fixed4. 与训练框架的集成4.1 MMPose数据加载适配确保标注文件通过MMPose的TopDownCocoDataset验证from mmpose.datasets import TopDownCocoDataset def test_with_mmpose(anno_path): dataset TopDownCocoDataset( ann_fileanno_path, img_prefixpath/to/images, pipeline[] ) try: dataset.load_annotations() print(✅ 标注文件兼容MMPose) except Exception as e: print(f❌ 加载失败: {str(e)})4.2 Detectron2关键点可视化使用Detectron2快速验证标注正确性from detectron2.data import MetadataCatalog from detectron2.utils.visualizer import Visualizer def visualize_keypoints(coco_data, img_path): metadata MetadataCatalog.get(coco_keypoints) metadata.set(keypoint_namescoco_data[categories][0][keypoints]) img cv2.imread(img_path) visualizer Visualizer(img[:, :, ::-1], metadata) for anno in coco_data[annotations]: vis visualizer.draw_and_connect_keypoints( np.array(anno[keypoints]).reshape(-1,3) ) cv2.imshow(Keypoints, vis.get_image()[:, :, ::-1]) cv2.waitKey(0)在完成多个项目的关键点数据迁移后发现最常见的错误是num_keypoints与实际的可见关键点数不一致骨架连接索引超出关键点数组范围不同标注工具间的关键点命名不匹配建议在转换流程中加入严格的验证步骤可节省后期调试时间。