从X-AnyLabeling到YOLO:一站式JSON标签转换实战指南(附Python脚本)
1. 为什么需要JSON到YOLO的标签转换
当你用X-AnyLabeling标注完几百张行人姿态图片后,发现YOLO模型根本不认这些JSON文件,这时候你就需要格式转换了。这就像你写了一封情书,对方却只收电报——不是内容不对,只是格式不匹配。
我去年处理过一个商场监控项目,客户给的就是X-AnyLabeling标注的JSON文件。直接打开看会发现,它记录的是每个标注框的绝对坐标和类别名称。但YOLO需要的是相对坐标和数字编号,就像把"站立"变成数字"0",把坐标从"像素值"变成"0到1之间的小数"。
最常见的三类问题:
- 坐标计算错误:有人直接用左上角坐标当中心点,导致目标框漂移
- 类别映射遗漏:漏掉"弯腰"这类特殊姿态,训练时直接报错
- 批量处理崩溃:脚本没做异常处理,遇到空文件就中断
2. 解剖X-AnyLabeling的JSON结构
先看一个真实案例的JSON片段:
{ "version": "0.1.0", "flags": {}, "shapes": [ { "label": "sitting", "points": [[120, 240], [300, 240], [300, 360], [120, 360]], "shape_type": "polygon" } ], "imagePath": "mall_001.jpg" }关键字段解读:
shapes数组包含所有标注对象- 每个对象的
points是四边形顶点坐标(注意顺序!) label直接使用英文类别名
特别注意:X-AnyLabeling的坐标是[[x1,y1],[x2,y2],[x3,y3],[x4,y4]]的四边形表示,而YOLO需要的是矩形中心点(x,y)和宽高(w,h)。这就涉及到两个关键计算:
- 从四边形到外接矩形的转换
- 绝对坐标到相对坐标的归一化
3. 手把手编写转换脚本
3.1 基础版本:单文件转换
先看核心计算逻辑的Python实现:
def bbox_to_yolo(points, img_w, img_h): """将四边形坐标转换为YOLO格式""" 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) # 计算中心点和宽高 x_center = (x_min + x_max) / 2 / img_w y_center = (y_min + y_max) / 2 / img_h width = (x_max - x_min) / img_w height = (y_max - y_min) / img_h return x_center, y_center, width, height3.2 增强版本:批量处理与异常处理
实际项目中我推荐这样改进:
import traceback def process_batch(json_dir, output_dir): for filename in os.listdir(json_dir): if not filename.endswith('.json'): continue try: with open(os.path.join(json_dir, filename)) as f: data = json.load(f) # 获取图片实际尺寸(更准确的做法) img_path = os.path.join(json_dir, data['imagePath']) with Image.open(img_path) as img: img_w, img_h = img.size # 处理每个标注 with open(f"{output_dir}/{filename.replace('.json','.txt')}", 'w') as out_f: for shape in data['shapes']: # 添加类别验证 if shape['label'] not in CLASS_MAPPING: raise ValueError(f"未知类别: {shape['label']}") # 坐标转换 x, y, w, h = bbox_to_yolo(shape['points'], img_w, img_h) # 写入文件 out_f.write(f"{CLASS_MAPPING[shape['label']]} {x:.6f} {y:.6f} {w:.6f} {h:.6f}\n") except Exception as e: print(f"处理文件 {filename} 出错: {str(e)}") traceback.print_exc()4. 避坑指南与性能优化
4.1 常见报错解决方案
- 坐标超出[0,1]范围:检查图片尺寸是否获取正确,我曾遇到过EXIF旋转导致宽高颠倒的情况
- 类别映射缺失:建议在脚本开头定义完整的CLASS_MAPPING字典
- 空文件处理:添加
if not data['shapes']: continue跳过无标注文件
4.2 高级技巧
- 多进程加速:用
multiprocessing.Pool处理上万文件时,速度能提升5-8倍
from multiprocessing import Pool def worker(args): json_file, output_dir = args # 处理单个文件... if __name__ == '__main__': files = [(f, output_dir) for f in os.listdir(json_dir)] with Pool(processes=4) as pool: pool.map(worker, files)- 可视化校验:用OpenCV绘制转换后的标注框,确保没出错
import cv2 def visualize_yolo(img_path, txt_path): img = cv2.imread(img_path) h, w = img.shape[:2] with open(txt_path) as f: for line in f: cls, x, y, w, h = map(float, line.split()) # 转换回绝对坐标 x1 = int((x - w/2) * w) y1 = int((y - h/2) * h) x2 = int((x + w/2) * w) y2 = int((y + h/2) * h) cv2.rectangle(img, (x1,y1), (x2,y2), (0,255,0), 2) cv2.imshow('check', img) cv2.waitKey(0)5. 完整脚本与使用示例
最终版的脚本应该包含以下功能:
- 自动创建输出目录
- 支持相对/绝对路径
- 详细的日志记录
- 进度显示
使用方法:
python convert.py \ --json-dir ./annotations \ --output-dir ./yolo_labels \ --class-map '{"standing":0, "sitting":1}' \ --workers 4建议在转换完成后,随机抽查5%的文件进行可视化校验。这个习惯帮我避免过三次重大数据错误,特别是在标注团队中途修改过标注规范的情况下。
