告别手动转换!用Python脚本一键搞定Labelme到YOLO关键点检测的标签格式
告别手动转换用Python脚本一键搞定Labelme到YOLO关键点检测的标签格式在计算机视觉领域关键点检测任务正变得越来越普遍从人脸特征点识别到工业零件定位都需要精确的关键点标注。然而许多开发者在使用Labelme完成标注后面临着一个共同的痛点如何高效地将这些标注转换为YOLO格式手动转换不仅耗时耗力还容易出错特别是当标注数据量庞大时这个问题尤为突出。本文将介绍一个开箱即用的Python解决方案帮助开发者一键完成从Labelme到YOLO关键点检测格式的转换。这个脚本不仅支持批量处理还能根据项目需求灵活调整关键点顺序和类别定义大大提升工作效率。无论你是正在学习计算机视觉的学生还是需要处理大量标注数据的工程师这个工具都能为你节省宝贵的时间。1. 理解Labelme和YOLO关键点格式差异Labelme和YOLO是两种广泛使用的标注工具和模型框架它们在关键点标注格式上有着显著差异。理解这些差异是进行格式转换的基础。Labelme生成的JSON文件通常包含以下关键信息{ version: 5.1.1, flags: {}, shapes: [ { label: face, points: [[100, 120], [150, 180]], shape_type: rectangle }, { label: left_eye, points: [[125, 140]], shape_type: point } ], imagePath: example.jpg, imageWidth: 640, imageHeight: 480 }而YOLO关键点检测格式则更为紧凑它将所有信息压缩在一行文本中0 0.19531 0.31250 0.07812 0.12500 0.19531 0.29167 2 0.23438 0.29167 2这行数据的含义是第一个数字0表示物体类别接下来的四个数字0.19531 0.31250 0.07812 0.12500表示边界框的中心坐标和宽高归一化值之后每三个数字为一组表示关键点的x坐标、y坐标和可见性标志2表示可见1表示遮挡0表示不可见2. 转换脚本的核心实现我们的Python脚本需要完成以下几个核心任务读取Labelme生成的JSON文件解析其中的边界框和关键点信息转换为YOLO格式并保存为TXT文件以下是脚本的主要结构import os import json # 配置部分 - 需要根据项目修改 bbox_class {face: 0} # 类别名称到ID的映射 keypoint_class [left_eye, right_eye, nose] # 关键点顺序定义 folder_path path/to/labelme/json/files # JSON文件所在目录 def convert_labelme_to_yolo(json_path, img_width, img_height): # 实现转换逻辑的核心函数 pass # 批量处理目录中的所有JSON文件 for file in os.listdir(folder_path): if file.endswith(.json): json_path os.path.join(folder_path, file) # 执行转换...2.1 边界框的转换处理边界框转换的关键步骤包括从Labelme的矩形标注中提取四个角点坐标计算中心点坐标和宽高将绝对坐标转换为归一化坐标0-1之间# 提取边界框坐标 points each_ann[points] x_coords [p[0] for p in points] y_coords [p[1] for p in points] # 计算边界框参数 x_min, x_max min(x_coords), max(x_coords) y_min, y_max min(y_coords), max(y_coords) width x_max - x_min height y_max - y_min center_x (x_min x_max) / 2 center_y (y_min y_max) / 2 # 归一化处理 center_x / img_width center_y / img_height width / img_width height / img_height2.2 关键点的匹配与转换关键点处理更为复杂需要考虑以下几点关键点与边界框的从属关系只保留框内的关键点按照预定义的关键点顺序输出处理缺失的关键点用0填充# 收集属于当前边界框的所有关键点 bbox_keypoints {} for shape in labelme_data[shapes]: if shape[shape_type] point: x, y shape[points][0] if x_min x x_max and y_min y y_max: bbox_keypoints[shape[label]] [x, y] # 按照预定义顺序输出关键点 for kp_name in keypoint_class: if kp_name in bbox_keypoints: x, y bbox_keypoints[kp_name] x_norm x / img_width y_norm y / img_height yolo_str f{x_norm:.5f} {y_norm:.5f} 2 # 2表示可见 else: yolo_str 0 0 0 # 缺失的关键点3. 脚本的定制化配置为了使脚本适应不同的项目需求我们设计了几个可配置的部分3.1 类别定义修改在脚本开头可以修改bbox_class字典来定义自己的类别bbox_class { person: 0, car: 1, dog: 2 }3.2 关键点顺序调整关键点的输出顺序由keypoint_class列表决定可以根据需要重新排列# 人脸关键点示例 keypoint_class [ nose, left_eye, right_eye, left_ear, right_ear ]3.3 输入输出路径设置通过修改folder_path变量可以指定JSON文件的输入目录folder_path /path/to/your/labelme/json/files4. 高级功能与错误处理4.1 批量处理与进度显示当处理大量文件时添加进度显示很有帮助total_files len([f for f in os.listdir(folder_path) if f.endswith(.json)]) processed 0 for file in os.listdir(folder_path): if file.endswith(.json): # 转换处理... processed 1 print(f进度: {processed}/{total_files} ({processed/total_files:.1%}))4.2 常见错误处理在实际使用中可能会遇到以下问题JSON文件格式不正确图片尺寸信息缺失关键点标注不规范我们可以添加相应的错误处理代码try: with open(json_path, r, encodingutf-8) as f: labelme_data json.load(f) # 检查必要的字段 if imageWidth not in labelme_data or imageHeight not in labelme_data: raise ValueError(Missing image dimensions in JSON) except json.JSONDecodeError: print(f错误: {json_path} 不是有效的JSON文件) continue except Exception as e: print(f处理 {json_path} 时出错: {str(e)}) continue4.3 可视化的验证方法为了确保转换结果正确可以添加一个简单的可视化验证函数import cv2 import numpy as np def visualize_yolo_label(image_path, label_path, keypoint_names): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() class_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:5]) # 绘制边界框 x1 int((cx - bw/2) * w) y1 int((cy - bh/2) * h) x2 int((cx bw/2) * w) y2 int((cy bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0,255,0), 2) # 绘制关键点 for i, kp_name in enumerate(keypoint_names): idx 5 i*3 x, y, vis map(float, parts[idx:idx3]) if vis 0: # 只绘制可见的关键点 px int(x * w) py int(y * h) cv2.circle(img, (px, py), 3, (0,0,255), -1) cv2.putText(img, kp_name, (px5, py), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255,255,255), 1) cv2.imshow(Verification, img) cv2.waitKey(0) cv2.destroyAllWindows()5. 性能优化与扩展5.1 多进程加速处理对于大规模数据集可以使用Python的multiprocessing模块加速处理from multiprocessing import Pool def process_file(json_file): if json_file.endswith(.json): json_path os.path.join(folder_path, json_file) # 转换处理... return f处理完成: {json_file} return None if __name__ __main__: json_files os.listdir(folder_path) with Pool(processes4) as pool: # 使用4个进程 results pool.map(process_file, json_files) for result in results: if result: print(result)5.2 支持多种标注类型扩展脚本以支持多边形、线段等其他标注类型for shape in labelme_data[shapes]: if shape[shape_type] polygon: # 处理多边形标注 pass elif shape[shape_type] line: # 处理线段标注 pass5.3 与训练流程集成将转换脚本集成到YOLO训练流程中实现从标注到训练的一站式处理def prepare_yolo_dataset(labelme_dir, yolo_dir): # 创建YOLO目录结构 os.makedirs(os.path.join(yolo_dir, labels), exist_okTrue) os.makedirs(os.path.join(yolo_dir, images), exist_okTrue) # 转换所有Labelme标注 for json_file in os.listdir(labelme_dir): if json_file.endswith(.json): # 执行转换... # 复制对应的图像文件 img_file json_file.replace(.json, .jpg) shutil.copy( os.path.join(labelme_dir, img_file), os.path.join(yolo_dir, images, img_file) )