YOLO 数据集转换:3种JSON格式解析与Python脚本适配实战 YOLO 数据集转换3种JSON格式解析与Python脚本适配实战在计算机视觉项目中数据格式的兼容性往往是第一个需要攻克的堡垒。当你从不同来源获取标注数据时可能会遇到COCO、Labelme和自定义JSON等多种格式而YOLO训练却要求统一的TXT格式。本文将深入解析这三种主流JSON格式的结构差异并提供可复用的Python转换方案助你打通数据预处理的关键环节。1. 理解YOLO标注格式的核心要求YOLO所需的TXT标注文件遵循严格的规范化标准。每个图像对应一个同名文本文件其中每行表示一个对象的标注信息格式为class_id x_center y_center width height所有坐标值必须是归一化的0到1之间计算方式如下x_center 边界框中心x坐标 / 图像宽度y_center 边界框中心y坐标 / 图像高度width 边界框宽度 / 图像宽度height 边界框高度 / 图像高度关键检查点确认坐标值是否已完成归一化验证class_id是否从0开始连续编号检查图像尺寸与标注是否匹配2. COCO JSON格式解析与转换COCOCommon Objects in Context是当前最通用的目标检测数据集格式其标注文件包含以下核心结构{ images: [{id: 0, width: 640, height: 480, file_name: 001.jpg}], annotations: [ { id: 1, image_id: 0, category_id: 2, bbox: [x,y,width,height], # 左上角坐标宽高 area: 1200, iscrowd: 0 } ], categories: [{id: 0, name: person}, {id: 1, name: car}] }转换时需要特别注意COCO使用绝对像素坐标非归一化bbox格式为[x_top_left, y_top_left, width, height]需要建立category_id到class_id的映射关系完整转换脚本import json from pathlib import Path def coco2yolo(coco_path, output_dir): with open(coco_path) as f: data json.load(f) # 创建类别映射表 cat_map {cat[id]: idx for idx, cat in enumerate(data[categories])} # 按图像ID分组标注 img_anns {} for ann in data[annotations]: img_id ann[image_id] img_anns.setdefault(img_id, []).append(ann) # 处理每张图像 for img in data[images]: img_id img[id] img_w, img_h img[width], img[height] txt_path Path(output_dir) / f{Path(img[file_name]).stem}.txt with open(txt_path, w) as f_txt: for ann in img_anns.get(img_id, []): # 转换bbox格式 x, y, w, h ann[bbox] x_center (x w/2) / img_w y_center (y h/2) / img_h w_norm w / img_w h_norm h / img_h # 写入YOLO格式 class_id cat_map[ann[category_id]] line f{class_id} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}\n f_txt.write(line)3. Labelme JSON格式深度处理Labelme是流行的图像标注工具其生成的JSON文件结构如下{ version: 5.1.1, flags: {}, shapes: [ { label: dog, points: [[x1,y1], [x2,y2]], # 多边形点或矩形对角点 shape_type: rectangle, flags: {} } ], imagePath: image.jpg, imageData: null, imageHeight: 600, imageWidth: 800 }特殊处理要点支持多边形和矩形两种标注方式需要将多边形转换为外接矩形对于非矩形标注点坐标可能是浮点数或整数转换代码实现import json import numpy as np from pathlib import Path def labelme2yolo(labelme_path, output_dir, class_map): with open(labelme_path) as f: data json.load(f) img_h, img_w data[imageHeight], data[imageWidth] txt_path Path(output_dir) / f{Path(data[imagePath]).stem}.txt with open(txt_path, w) as f_txt: for shape in data[shapes]: label shape[label] points np.array(shape[points]) # 获取边界框坐标 if shape[shape_type] rectangle: x_min, y_min points.min(axis0) x_max, y_max points.max(axis0) else: # 多边形转外接矩形 x_min, y_min points.min(axis0) x_max, y_max points.max(axis0) # 计算YOLO格式坐标 width x_max - x_min height y_max - y_min x_center (x_min width/2) / img_w y_center (y_min height/2) / img_h width / img_w height / img_h # 写入文件 class_id class_map[label] line f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n f_txt.write(line)4. 自定义JSON格式的灵活适配自定义JSON格式千变万化我们需要先分析其结构。以下是一个典型示例[ { image: 001.jpg, annotations: [ { label: cat, bbox: { x: 0.25, # 中心x (已归一化) y: 0.35, # 中心y w: 0.1, # 宽度 h: 0.15 # 高度 } } ] } ]适配策略确认坐标是否已归一化检查bbox表示方式中心点宽高 或 角点坐标验证标签命名一致性通用转换函数def custom_json2yolo(json_path, output_dir, class_map): with open(json_path) as f: data json.load(f) for item in data: img_name Path(item[image]).stem txt_path Path(output_dir) / f{img_name}.txt with open(txt_path, w) as f_txt: for ann in item[annotations]: label ann[label] bbox ann[bbox] # 假设已经是YOLO格式 if all(key in bbox for key in [x, y, w, h]): x, y, w, h bbox[x], bbox[y], bbox[w], bbox[h] # 处理角点格式 elif all(key in bbox for key in [x1, y1, x2, y2]): x1, y1, x2, y2 bbox[x1], bbox[y1], bbox[x2], bbox[y2] w x2 - x1 h y2 - y1 x x1 w/2 y y1 h/2 class_id class_map[label] line f{class_id} {x:.6f} {y:.6f} {w:.6f} {h:.6f}\n f_txt.write(line)5. 实战构建通用转换工具链将上述模块整合为可配置的转换流水线import argparse from pathlib import Path def auto_convert(input_path, output_dir, format_type, class_mapNone): 智能转换入口函数 Path(output_dir).mkdir(exist_okTrue) if format_type coco: coco2yolo(input_path, output_dir) elif format_type labelme: if not class_map: raise ValueError(class_map required for Labelme format) labelme2yolo(input_path, output_dir, class_map) elif format_type custom: if not class_map: raise ValueError(class_map required for custom format) custom_json2yolo(input_path, output_dir, class_map) else: raise ValueError(fUnsupported format: {format_type}) if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(input, helpInput JSON file path) parser.add_argument(output_dir, helpOutput directory for TXT files) parser.add_argument(--format, choices[coco, labelme, custom], requiredTrue) parser.add_argument(--class_map, helpJSON file mapping labels to class IDs) args parser.parse_args() class_map json.loads(Path(args.class_map).read_text()) if args.class_map else None auto_convert(args.input, args.output_dir, args.format, class_map)使用示例# 转换COCO格式 python converter.py annotations.json output_coco --format coco # 转换Labelme格式需提供类别映射 python converter.py labelme.json output_labelme --format labelme --class_map classes.json6. 验证与调试技巧转换后务必进行数据验证可视化检查使用OpenCV绘制边界框import cv2 def visualize_yolo(img_path, txt_path, class_names): img cv2.imread(img_path) h, w img.shape[:2] with open(txt_path) as f: for line in f: class_id, x, y, w_box, h_box map(float, line.split()) # 转换回像素坐标 x1 int((x - w_box/2) * w) y1 int((y - h_box/2) * h) x2 int((x w_box/2) * w) y2 int((y h_box/2) * h) # 绘制矩形和标签 cv2.rectangle(img, (x1,y1), (x2,y2), (0,255,0), 2) cv2.putText(img, class_names[int(class_id)], (x1,y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (36,255,12), 2) cv2.imshow(Preview, img) cv2.waitKey(0)统计验证检查类别分布和框尺寸分布import matplotlib.pyplot as plt def analyze_annotations(txt_dir): class_counts {} box_ratios [] for txt_path in Path(txt_dir).glob(*.txt): with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue class_id int(parts[0]) w float(parts[3]) h float(parts[4]) # 统计类别 class_counts[class_id] class_counts.get(class_id, 0) 1 # 计算宽高比 if h 0: box_ratios.append(w/h) # 绘制统计图 plt.figure(figsize(12,5)) plt.subplot(121) plt.bar(class_counts.keys(), class_counts.values()) plt.title(Class Distribution) plt.subplot(122) plt.hist(box_ratios, bins50) plt.title(Width/Height Ratio Distribution) plt.show()7. 高级应用处理复杂场景多任务数据转换同时处理检测和分割标注def coco2yolo_seg(coco_path, output_dir): 转换COCO实例分割标注 with open(coco_path) as f: data json.load(f) cat_map {cat[id]: idx for idx, cat in enumerate(data[categories])} img_anns defaultdict(list) for ann in data[annotations]: img_anns[ann[image_id]].append(ann) for img in data[images]: txt_path Path(output_dir) / f{Path(img[file_name]).stem}.txt with open(txt_path, w) as f_txt: for ann in img_anns.get(img[id], []): if not ann[segmentation]: continue class_id cat_map[ann[category_id]] # 获取所有多边形点COCO允许多个多边形 segments [] for seg in ann[segmentation]: points np.array(seg).reshape(-1,2) # 归一化 points[:,0] / img[width] points[:,1] / img[height] segments.append(points.flatten().tolist()) # YOLO分割格式class_id x1 y1 x2 y2 ... xn yn line f{class_id} .join(map(str, sum(segments, []))) \n f_txt.write(line)性能优化技巧使用多进程处理大型数据集from multiprocessing import Pool def process_file(args): 包装函数供多进程调用 file_path, format_type, output_dir, class_map args try: auto_convert(file_path, output_dir, format_type, class_map) return True except Exception as e: print(fError processing {file_path}: {str(e)}) return False def batch_convert(file_list, format_type, output_dir, class_mapNone, workers4): 批量转换入口 tasks [(f, format_type, output_dir, class_map) for f in file_list] with Pool(workers) as pool: results pool.map(process_file, tasks) print(fSuccessfully processed {sum(results)}/{len(file_list)} files)

相关新闻

最新新闻

Flask + GCN 垃圾评论识别系统:从文本构图到在线部署

Flask + GCN 垃圾评论识别系统:从文本构图到在线部署

在内容平台的实际业务里,垃圾评论治理通常要经历“发现—标注—过滤—追封”四个环节。很多团队一开始用关键词黑白名单,后来换成 TF-IDF 加逻辑回归,再往后开始上深度学习模型。但有一个问题始终存在:广告评论和正常评论的词重叠…

2026/9/7 15:23:42
MAI-Cyber-1-Flash轻量级AI模型在网络安全威胁检测中的实践

MAI-Cyber-1-Flash轻量级AI模型在网络安全威胁检测中的实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/7 15:23:42
从一条爆款到批量生产:整蛊反应类短视频内容链路拆解

从一条爆款到批量生产:整蛊反应类短视频内容链路拆解

这次我们拆的不是某个开源模型,而是一条能持续产生爆款的短视频内容生产链路。标题里这条整蛊视频,核心逻辑是“假装打电话说丈夫感兴趣的事,看他会不会凑上来”,一听到自己感兴趣的东西瞬间精神。它看起来只是一个家庭小剧场&…

2026/9/7 15:23:42
猫抓 cat-catch:免费浏览器资源嗅探扩展,如何快速下载网页视频

猫抓 cat-catch:免费浏览器资源嗅探扩展,如何快速下载网页视频

猫抓 cat-catch:免费浏览器资源嗅探扩展,如何快速下载网页视频 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 想把网页里的…

2026/9/7 15:23:42
CMSIS-FreeRTOS源码审计:任务调度与内存分配的隐藏陷阱

CMSIS-FreeRTOS源码审计:任务调度与内存分配的隐藏陷阱

先说一个我最近真实的经历。手上一个基于 Cortex-M4 的数据采集项目,传感器每隔 10ms 上报一次数据,主控端用 CMSIS-FreeRTOS 建了三个线程:采集线程、处理线程、通信线程。结果一上系统就出幺蛾子:用 osDelay(1) 想让某个任务让出…

2026/9/7 15:23:42
函数从入门到实战:参数传递、闭包与高阶函数用法详解

函数从入门到实战:参数传递、闭包与高阶函数用法详解

函数这东西,一开始接触的时候很容易被当成一个“必须完成的作业题”来写:老师让定义几个函数,按题目要求算个结果,跑通就算交差。但等到真正做项目、写脚本、处理数据,才意识到函数是所有代码里最值得花时间琢磨的东西…

2026/9/7 15:18:41