news 2026/4/17 13:28:24

实战指南:从COCO标注(.json)到YOLO训练(.txt)的无损格式转换

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
实战指南:从COCO标注(.json)到YOLO训练(.txt)的无损格式转换

1. 为什么需要COCO到YOLO的格式转换

第一次接触目标检测任务时,我就被各种数据格式搞得晕头转向。COCO和YOLO作为两种最流行的标注格式,它们的差异经常让新手踩坑。COCO数据集采用JSON格式存储标注信息,而YOLO则需要TXT文件。这不仅仅是文件格式的差异,更重要的是标注数据的表示方式完全不同。

COCO格式使用(x,y,width,height)来表示边界框,其中x和y是边界框中心的坐标。而YOLO格式需要的是归一化后的(x_center,y_center,width,height),所有值都在0到1之间。这种转换看似简单,但实际操作中会遇到很多细节问题,比如COCO的类别ID可能不连续,需要重新映射;图像尺寸信息需要用来做归一化计算等。

我在处理COCO2017数据集时就遇到过这样的问题:转换后的标注文件在YOLOv5训练时总是报错,花了整整一天才发现是坐标归一化时的小数位数问题。这也是我决定写下这篇详细教程的原因,希望能帮助大家避开这些坑。

2. 准备工作与环境配置

2.1 数据集获取与检查

首先需要下载COCO数据集,官方提供了2017版本的train和val数据集。下载后应该检查文件结构是否完整,通常包含以下几个关键部分:

  • annotations/instances_train2017.json - 训练集的标注文件
  • train2017/ - 训练集图片文件夹
  • val2017/ - 验证集图片文件夹

我建议在开始转换前先用Python的json模块快速检查一下标注文件的内容结构。可以运行以下代码:

import json with open('instances_train2017.json', 'r') as f: data = json.load(f) print(f"数据集包含{len(data['images'])}张图片") print(f"共有{len(data['categories'])}个类别") print(f"标注数量:{len(data['annotations'])}")

2.2 Python环境准备

转换脚本需要一些基本的Python包,建议使用conda创建一个干净的环境:

conda create -n coco2yolo python=3.8 conda activate coco2yolo pip install tqdm

这里特别推荐使用tqdm包,它能为转换过程添加进度条,在处理数万张图片时非常有用。我最初没用进度条,有一次脚本运行了半小时我都不知道是否卡住了,加上进度条后体验好多了。

3. 核心转换代码详解

3.1 坐标转换原理

COCO和YOLO格式的核心差异在于边界框的表示方式。COCO使用绝对坐标,而YOLO使用相对坐标。转换的关键步骤如下:

  1. 从COCO的(x,y,width,height)转换为(x_center,y_center,width,height)
  2. 将所有坐标值归一化到0-1之间
  3. 处理类别ID映射

这里有个容易出错的细节:COCO的(x,y)已经是中心点坐标,但有些教程会错误地再进行一次中心点计算。实际上正确的转换公式应该是:

def convert(size, box): # size是图片的(width, height) dw = 1. / size[0] dh = 1. / size[1] x = box[0] # COCO的x已经是中心点x坐标 y = box[1] # COCO的y已经是中心点y坐标 w = box[2] h = box[3] x = x * dw w = w * dw y = y * dh h = h * dh return (x, y, w, h)

3.2 完整转换脚本

下面是我优化后的完整转换脚本,增加了错误处理和日志记录:

import os import json from tqdm import tqdm import argparse def parse_args(): parser = argparse.ArgumentParser(description='Convert COCO format to YOLO format') parser.add_argument('--json_path', required=True, help='Path to COCO json annotation file') parser.add_argument('--save_path', default='labels', help='Directory to save YOLO format labels') parser.add_argument('--debug', action='store_true', help='Enable debug mode') return parser.parse_args() def convert_bbox(size, box): """Convert COCO bbox format to YOLO format""" dw = 1. / size[0] dh = 1. / size[1] x, y, w, h = box x = x * dw w = w * dw y = y * dh h = h * dh return (round(x, 6), round(y, 6), round(w, 6), round(h, 6)) def main(): args = parse_args() # 创建保存目录 os.makedirs(args.save_path, exist_ok=True) # 加载COCO标注数据 with open(args.json_path, 'r') as f: data = json.load(f) # 创建类别映射 id_map = {cat['id']: idx for idx, cat in enumerate(data['categories'])} # 保存类别文件 with open(os.path.join(args.save_path, 'classes.txt'), 'w') as f: for cat in data['categories']: f.write(f"{cat['name']}\n") # 处理每张图片 for img in tqdm(data['images'], desc='Processing images'): img_id = img['id'] file_name = img['file_name'] img_width = img['width'] img_height = img['height'] # 对应的txt文件名 txt_name = os.path.splitext(file_name)[0] + '.txt' txt_path = os.path.join(args.save_path, txt_name) # 收集该图片的所有标注 annotations = [ann for ann in data['annotations'] if ann['image_id'] == img_id] # 写入YOLO格式标注 with open(txt_path, 'w') as f_txt: for ann in annotations: category_id = id_map[ann['category_id']] bbox = convert_bbox((img_width, img_height), ann['bbox']) line = f"{category_id} {bbox[0]} {bbox[1]} {bbox[2]} {bbox[3]}\n" f_txt.write(line) if args.debug and len(annotations) > 0: print(f"Processed {file_name} with {len(annotations)} annotations") if __name__ == '__main__': main()

这个脚本有几个改进点:

  1. 增加了debug模式,方便排查问题
  2. 使用更安全的文件路径处理
  3. 添加了详细的进度显示
  4. 对浮点数进行了合理的四舍五入

4. 与YOLO训练框架的对接

4.1 准备YOLO配置文件

转换完成后,还需要准备YOLO训练所需的配置文件。以YOLOv5为例,需要创建一个data/coco.yaml文件:

# COCO 2017 dataset http://cocodataset.org # 训练和验证数据的路径 train: ../coco/train2017.txt val: ../coco/val2017.txt # 类别数量 nc: 80 # 类别名称 names: ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush']

4.2 验证转换结果

在开始训练前,强烈建议验证转换后的标注是否正确。可以使用以下代码快速检查:

import cv2 import random def visualize_annotation(image_path, label_path, classes): # 读取图片 img = cv2.imread(image_path) h, w = img.shape[:2] # 读取标注 with open(label_path, 'r') as f: lines = f.readlines() # 绘制每个边界框 for line in lines: cls_id, x_center, y_center, width, height = map(float, line.split()) # 转换回绝对坐标 x_center *= w y_center *= h width *= w height *= h x1 = int(x_center - width / 2) y1 = int(y_center - height / 2) x2 = int(x_center + width / 2) y2 = int(y_center + height / 2) # 随机颜色 color = (random.randint(0,255), random.randint(0,255), random.randint(0,255)) # 绘制矩形和类别 cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, classes[int(cls_id)], (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, color, 2) return img # 示例使用 classes = open('labels/classes.txt').read().splitlines() img = visualize_annotation('train2017/000000000009.jpg', 'labels/000000000009.txt', classes) cv2.imshow('Annotation', img) cv2.waitKey(0) cv2.destroyAllWindows()

这个可视化脚本能帮助你快速确认标注是否正确。我在第一次转换时就发现有些边界框明显偏移了,通过可视化及时发现了问题。

5. 常见问题与解决方案

5.1 类别ID不匹配问题

COCO数据集的类别ID不是连续的(从1到90,但实际只有80类),这会导致直接使用原ID时YOLO报错。解决方案是创建从COCO ID到连续ID的映射:

id_map = {} for i, category in enumerate(data['categories']): id_map[category['id']] = i

5.2 图片尺寸异常处理

有些图片可能没有标注信息,或者在转换过程中可能遇到图片尺寸为0的情况。应该在convert函数中添加检查:

def convert(size, box): if size[0] == 0 or size[1] == 0: return (0, 0, 0, 0) # 其余转换逻辑...

5.3 大规模数据处理的优化

当处理完整的COCO数据集(12万+训练图片)时,内存可能成为瓶颈。可以采用流式处理的方式:

import ijson def process_large_json(json_path): with open(json_path, 'rb') as f: # 使用ijson逐步解析 images = ijson.items(f, 'images.item') for img in images: # 处理每张图片... pass

6. 高级技巧与扩展

6.1 多进程加速转换

对于大型数据集,可以使用Python的multiprocessing加速处理:

from multiprocessing import Pool def process_image(args): img, data, save_path, id_map = args # 处理单张图片的逻辑... if __name__ == '__main__': # ...其他代码... with Pool(processes=4) as pool: # 使用4个进程 args_list = [(img, data, args.save_path, id_map) for img in data['images']] pool.map(process_image, args_list)

6.2 与YOLOv8的兼容性

YOLOv8的格式要求与v5基本相同,但有一些额外的元数据可以添加。可以在转换时加入这些信息:

with open(txt_path, 'w') as f_txt: for ann in annotations: # 添加难度等级等额外信息 difficulty = ann.get('difficulty', 0) line = f"{category_id} {bbox[0]} {bbox[1]} {bbox[2]} {bbox[3]} {difficulty}\n" f_txt.write(line)

6.3 自定义数据集的扩展

这套转换方法不仅适用于COCO,也可以用于其他自定义数据集。关键是理解核心的转换逻辑,然后根据具体数据格式调整。例如,对于旋转框的转换,需要额外处理角度信息。

在实际项目中,我还遇到过需要合并多个数据集的情况。这时可以先分别转换为YOLO格式,然后统一处理类别映射。记住一点:保持数据格式的一致性比追求完美标注更重要,特别是在团队协作中。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 13:27:21

5分钟掌握城通网盘直连地址获取终极方案

5分钟掌握城通网盘直连地址获取终极方案 【免费下载链接】ctfileGet 获取城通网盘一次性直连地址 项目地址: https://gitcode.com/gh_mirrors/ct/ctfileGet 你是否曾因城通网盘的广告等待和复杂流程而烦恼?ctfileGet是一款专业的前端工具,专为解决…

作者头像 李华
网站建设 2026/4/17 13:27:20

WhisperLiveKit vs 商业API实测:完全离线的语音转文字方案能打几分?

WhisperLiveKit与商业语音API的终极对决:隐私与性能如何兼得? 在数字化转型浪潮中,语音转文字技术已成为企业工作流中不可或缺的一环。然而,当涉及敏感会议记录、医疗问诊或法律咨询等场景时,将音频数据上传至第三方云…

作者头像 李华
网站建设 2026/4/17 13:27:19

GetQzonehistory:免费开源工具一键备份QQ空间历史说说完整指南

GetQzonehistory:免费开源工具一键备份QQ空间历史说说完整指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否担心那些记录青春点滴的QQ空间说说不小心丢失&#xff…

作者头像 李华
网站建设 2026/4/17 13:25:12

视频元数据怎么修改?4个小白方法,不用敲代码

前言视频元数据填错真的超烦!上传时标题、作者或者拍摄日期写错,要么平台不显示,要么被判定异常,连搜索都受影响。有时候想改描述、版权信息,翻遍软件都找不到入口,急得抓瞎!其实视频元数据修改…

作者头像 李华