COCO转YOLO:从官方工具到自定义脚本的实战指南

📅 2026/7/14 9:06:40
COCO转YOLO:从官方工具到自定义脚本的实战指南
1. COCO与YOLO格式的本质区别第一次接触目标检测任务时我被各种数据集格式搞得晕头转向。COCO和YOLO就像两个说着不同方言的邻居虽然都在描述同一件事但表达方式截然不同。COCO格式采用JSON文件存储所有标注信息就像一个把所有东西都打包进集装箱的仓库管理员而YOLO则喜欢把标注分散到每个图片对应的TXT文件里更像是把物品分门别类放在开放式货架上。具体来看差异点坐标系统COCO使用绝对像素坐标[x_min, y_min, width, height]YOLO则采用归一化的相对坐标[x_center, y_center, width, height]文件结构COCO把所有标注塞进单个JSON如instances_val2017.jsonYOLO则为每张图片生成对应的TXT如000001.txt类别处理COCO通过category_id关联类别YOLO直接用类别索引0,1,2...我曾在处理卫星图像项目时因为没注意这个差异导致模型训练完全失败。后来用下面这个对比表格才理清思路特征COCO格式YOLO格式标注文件单个JSON包含所有标注每个图片对应一个TXT文件坐标值范围绝对像素值0-1之间的归一化值坐标顺序[x_min, y_min, width, height][x_center, y_center, w, h]类别表示通过category_id关联直接使用整数索引文件大小通常较大百MB级分散存储单个文件较小2. 官方转换工具的使用技巧Ultralytics提供的convert_coco工具确实方便但直接使用可能会遇到些坑。我在处理SAR图像数据集时就发现几个需要注意的细节2.1 基础使用方法安装最新版YOLOv8后转换命令其实很简单yolo convert coco ./path/to/your_coco.json但实际使用时建议加上输出路径yolo convert coco ./annotations/instances_train2017.json \ --output ./converted_labels \ --save-images2.2 常见问题解决方案问题1类别ID不连续COCO数据集的类别ID可能是跳跃的如1,2,5,7而YOLO需要连续ID。官方工具会自动处理但建议转换后检查生成的classes.txt。问题2图片路径错误如果遇到Image not found错误可以尝试yolo convert coco ./annotations/instances_train2017.json \ --img-path ./images/train2017问题3特殊字符处理当JSON中包含中文或其他特殊字符时添加编码参数with open(annotations.json, r, encodingutf-8) as f: data json.load(f)2.3 高级功能探索官方工具其实支持更多实用参数yolo convert coco input.json \ --output ./labels \ --split-ratio 0.8 0.1 0.1 \ # 自动划分训练/验证/测试集 --min-area 100 \ # 过滤小目标 --remove-crowd # 忽略crowd标注3. 手写Python转换脚本全解析当需要处理自定义标注时官方工具可能不够灵活。这时就需要自己写转换脚本下面是我在多个项目中总结的可靠方案3.1 核心转换函数def coco_to_yolo(size, box): 将COCO的[x,y,w,h]转换为YOLO的[x_center,y_center,w,h] size: (img_width, img_height) box: [x_min, y_min, width, height] dw 1./size[0] dh 1./size[1] x box[0] box[2]/2.0 y box[1] box[3]/2.0 w box[2] h box[3] x round(x * dw, 6) w round(w * dw, 6) y round(y * dh, 6) h round(h * dh, 6) return (x, y, w, h)3.2 完整转换流程import json import os from tqdm import tqdm def convert_coco_json(json_file, output_dir): os.makedirs(output_dir, exist_okTrue) with open(json_file) as f: data json.load(f) # 创建类别映射 id_map {cat[id]: idx for idx, cat in enumerate(data[categories])} # 预构建图像信息字典 images {img[id]: img for img in data[images]} # 按图片分组标注 annotations_dict {} for ann in data[annotations]: img_id ann[image_id] if img_id not in annotations_dict: annotations_dict[img_id] [] annotations_dict[img_id].append(ann) # 处理每张图片 for img_id, img_info in tqdm(images.items()): filename img_info[file_name] txt_name os.path.splitext(filename)[0] .txt with open(os.path.join(output_dir, txt_name), w) as f_txt: for ann in annotations_dict.get(img_id, []): class_id id_map[ann[category_id]] box coco_to_yolo( (img_info[width], img_info[height]), ann[bbox] ) line f{class_id} {box[0]} {box[1]} {box[2]} {box[3]}\n f_txt.write(line)3.3 性能优化技巧处理大型数据集时我总结了几点优化经验使用生成器避免一次性加载所有标注到内存并行处理用multiprocessing加速增量写入每处理完一张图片立即保存优化后的代码片段from multiprocessing import Pool def process_image(args): img_id, img_info, annotations, id_map args # 处理单张图片的代码... with Pool(processes4) as pool: args [(img_id, img_info, annotations_dict.get(img_id, []), id_map) for img_id, img_info in images.items()] pool.map(process_image, args)4. 自定义标注的处理方案实际项目中我们经常遇到非标准COCO格式这时就需要灵活处理。去年在处理工业缺陷检测项目时就遇到了标注格式不一致的问题。4.1 处理非标准字段# 处理带额外属性的标注 for ann in data[annotations]: if special_attribute in ann: # 将特殊属性转换为YOLO格式的附加字段 attr ann[special_attribute] line f {attr}4.2 多任务标注转换对于同时包含检测和分割的标注def convert_segmentation(size, segmentation): 将COCO的分割多边形转换为YOLO格式 normalized [] for seg in segmentation: for i in range(0, len(seg), 2): x seg[i] / size[0] y seg[i1] / size[1] normalized.extend([x, y]) return normalized4.3 验证转换结果转换后务必验证数据我常用这个可视化函数import cv2 import random def visualize_yolo_label(img_path, label_path, class_names): img cv2.imread(img_path) dh, dw img.shape[:2] with open(label_path) as f: for line in f.readlines(): class_id, x, y, w, h map(float, line.split()) # 转换回绝对坐标 x int(x * dw) y int(y * dh) w int(w * dw) h int(h * dh) # 绘制边界框 color [random.randint(0,255) for _ in range(3)] cv2.rectangle(img, (x-w//2, y-h//2), (xw//2, yh//2), color, 2) cv2.putText(img, class_names[int(class_id)], (x-w//2, y-h//2-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, color, 2) cv2.imshow(Preview, img) cv2.waitKey(0)5. 实际项目中的经验分享在医疗影像项目中我遇到了几个典型问题5.1 处理旋转框标注当需要处理DOTA等数据集的旋转框时def rotated_box_to_yolo(size, rbox): rbox: [x_center, y_center, width, height, angle] 返回YOLO旋转框格式: [class_id, xc, yc, w, h, angle] dw, dh 1./size[0], 1./size[1] xc, yc rbox[0] * dw, rbox[1] * dh w, h rbox[2] * dw, rbox[3] * dh angle rbox[4] # 角度通常不需要归一化 return [xc, yc, w, h, angle]5.2 超大图像处理技巧处理卫星图像时我采用分块处理策略def process_large_image(img_path, label_path, tile_size1024): img cv2.imread(img_path) h, w img.shape[:2] for y in range(0, h, tile_size): for x in range(0, w, tile_size): tile img[y:ytile_size, x:xtile_size] # 转换坐标并保存分块5.3 自动化验证流程建议在转换后运行自动化检查def validate_conversion(original_json, yolo_dir): # 检查图片与标注数量是否匹配 # 验证类别一致性 # 检查坐标值是否在0-1范围内 # 验证关键样本的标注准确性