1. 为什么需要从COCO JSON转换到YOLO TXT
当你第一次接触目标检测任务时,可能会被各种数据格式搞得晕头转向。COCO和YOLO是两种最常见的格式,但它们的设计理念完全不同。COCO JSON就像是一个精心整理的档案柜,把所有图片和标注信息都打包在一个大文件里;而YOLO TXT更像是便利贴,每张图片对应一个简单的文本文件,只记录最关键的检测框信息。
我在实际项目中遇到过这样的场景:好不容易下载了一个COCO格式的数据集,但发现自己的YOLOv5模型无法直接使用。这时候就需要进行格式转换。COCO JSON文件包含了太多训练时用不到的元数据(比如图片拍摄时间、授权信息等),而YOLO只需要知道每个物体是什么类别、在图片的什么位置就够了。
最让人头疼的是坐标系的差异。COCO使用绝对像素坐标,标注的是左上角点坐标和宽高;而YOLO使用相对坐标,需要的是中心点坐标和归一化后的宽高。这就好比一个是说"从客厅东北角开始,往南3米、往西2米",另一个是说"在整个房子的正中间偏右30%、偏下20%的位置"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 深入理解COCO JSON文件结构
2.1 COCO JSON的五大组成部分
打开一个典型的COCO JSON文件,你会看到一个巨大的字典结构。我把它比喻成一个五层楼的建筑:
-
info层:相当于大楼的门牌,记录着数据集的基本信息。这里通常有数据集版本、创建日期等,但对我们的转换工作影响不大。
-
licenses层:版权声明区。虽然法律上很重要,但技术上我们可以暂时忽略。
-
images层:图片档案室。每张图片在这里都有一个身份证,包括:
json复制{ "id": 0, "file_name": "0000001.jpg", "width": 640, "height": 480 }这个宽度和高度信息至关重要,因为后续的坐标转换都需要它。
-
annotations层:真正的金矿所在。每个标注对象都包含:
json复制{ "id": 1, "image_id": 0, "category_id": 2, "bbox": [100, 200, 50, 80], "iscrowd": 0 }这里的bbox就是我们要重点处理的对象。
-
categories层:类别目录。把数字ID映射到具体类别名,比如:
json复制{ "id": 1, "name": "person", "supercategory": "animal" }
2.2 特别注意的边界情况
在实际处理中,有几个坑我踩过多次:
-
iscrowd标记:当这个值为1时,表示多个物体挤在一起难以区分。有些数据集会用这个标记来跳过困难样本,我们需要决定是否保留这些标注。
-
segmentation字段:虽然我们主要关注bbox,但有些标注只有多边形分割信息。这时需要计算外接矩形:
python复制import numpy as np points = np.array(segmentation).reshape(-1,2) x_min, y_min = points.min(axis=0) x_max, y_max = points.max(axis=0) bbox = [x_min, y_min, x_max-x_min, y_max-y_min] -
无效标注:偶尔会遇到宽度或高度为0的bbox,这种标注需要过滤掉。
3. YOLO TXT格式详解
3.1 一行一个对象的精简设计
YOLO的标签文件简单得令人感动。每行代表一个检测对象,格式如下:
code复制<class_id> <x_center> <y_center> <width> <height>
所有坐标值都是相对于图片宽高的比例值,范围在0到1之间。举个例子,如果一个500x400的图片上有个中心在(250,200),宽高为100x80的物体,那么对应的YOLO标注应该是:
code复制0 0.5 0.5 0.2 0.2
3.2 不同YOLO版本的细微差别
需要注意的是,YOLOv5和YOLOv8的官方实现都使用上述格式。但有些老版本或者变种可能会有所不同:
- 类别ID起始值:有些从0开始,有些从1开始
- 坐标顺序:极少数实现会要求先y后x
- 分隔符:大部分用空格,但也有用逗号或制表符的
建议在转换前先用labelImg等工具手动标注几张图片,确认你的模型具体需要什么格式。
4. 完整转换脚本解析
4.1 基础转换流程
下面这个Python脚本是我在实际项目中反复打磨过的版本,比原始文章中的更健壮:
python复制import json
from pathlib import Path
def coco2yolo(coco_json_path, output_dir):
# 创建输出目录
output_dir = Path(output_dir)
outpu
