Ultralytics YOLO27:
Get Started

如何将 COCO 标注转换为 YOLO 格式#

训练 Ultralytics YOLO 模型需要使用 YOLO 格式的标注,但许多常用的标注工具导出的是 COCO JSON 格式。本指南将介绍如何把 COCO 标注转换为 YOLO 格式,并开始训练目标检测、实例分割和姿态估计模型。

想跳过转换?

如需直接使用 COCO JSON 训练而不生成 .txt 文件,请参阅无需转换,直接使用 COCO JSON 训练 YOLO。

为什么要从 COCO 转换为 YOLO?#

COCO JSON 格式将所有标注存储在一个文件中,而 YOLO 使用每张图像对应一个文本文件,并存储归一化坐标。转换是必要的,原因如下:

  • YOLO 模型需要 .txt 标签文件,每张图像对应一个文件,文件中包含以归一化坐标表示的 class x_center y_center width height。
  • COCO JSON 使用像素坐标,采用 [x_min, y_min, width, height] 格式,并将所有图像的信息存储在一个 JSON 文件中。
  • 类别 ID 不同——COCO 使用任意的 category_id 值,而 YOLO 要求类别 ID 从 0 开始编号。
特性COCO JSONYOLO TXT
结构所有图像共用一个 JSON 文件每张图像对应一个 .txt 文件
边界框格式像素坐标中的 [x_min, y_min, width, height]class x_center y_center width height 归一化(0-1)
类别 IDcategory_id(可以从任意数字开始)从 0 开始索引(从 0 起)
分割segmentation 字段中的多边形数组类别 ID 后的多边形坐标
关键点像素坐标中的 [x, y, visibility, ...][x, y, visibility, ...] 已归一化

快速入门#

转换 COCO 标注并开始训练的最快方法:

from ultralytics.data.converter import convert_coco

convert_coco(
    labels_dir="my_dataset/annotations/",  # 包含 JSON 文件的目录
    save_dir="my_dataset/converted/",  # 转换后标签的保存位置
    cls91to80=False,  # 自定义数据集请设为 False(见下方警告)
)

转换完成后,整理目录结构、创建 dataset.yaml,然后开始训练。请参阅下方的分步指南。

自定义数据集:始终使用 `cls91to80=False`

cls91to80=True 默认设置仅适用于包含 80 个目标类别的标准 COCO 数据集,它会将 91 个不连续的类别 ID 映射为 80 个连续的类别 ID。对于任何自定义数据集,你必须设置 cls91to80=False,否则系统会静默地错误映射类别 ID,导致模型学到错误的类别。

分步转换指南#

1. 准备 COCO 数据集#

从标注工具导出的典型 COCO 格式数据集具有以下结构:

my_dataset/
├── images/
│   ├── train/
│   │   ├── img_001.jpg
│   │   ├── img_002.jpg
│   │   └── ...
│   └── val/
│       ├── img_100.jpg
│       └── ...
└── annotations/
    ├── instances_train.json
    └── instances_val.json

每个 JSON 文件都遵循 COCO 数据格式规范,包含三个必需字段——images、annotations 和 categories:

{
    "images": [{ "id": 1, "file_name": "img_001.jpg", "width": 640, "height": 480 }],
    "annotations": [
        {
            "id": 1,
            "image_id": 1,
            "category_id": 1,
            "bbox": [100, 50, 200, 150],
            "area": 30000,
            "iscrowd": 0
        }
    ],
    "categories": [
        { "id": 1, "name": "helmet" },
        { "id": 2, "name": "vest" }
    ]
}

2. 转换标注#

使用 convert_coco() 函数,将 COCO JSON 标注转换为 YOLO .txt 格式:

将 COCO 转换为 YOLO 格式
from ultralytics.data.converter import convert_coco

convert_coco(
    labels_dir="my_dataset/annotations/",
    save_dir="my_dataset/converted/",
    cls91to80=False,
)

convert_coco() 会为每张有标注的图像写入一个 .txt 文件,并将文件存放在以各 JSON 文件命名且移除了 instances_ 前缀的 labels/ 子目录中(因此 instances_train.json 会生成 labels/train/)。没有标注的图像会被跳过,也不会生成标签文件,因此 labels/ 目录树可能不会与所有图像一一对应:

my_dataset/converted/
├── images/      # created but left empty
└── labels/
    ├── train/   # from instances_train.json
    │   ├── img_001.txt
    │   └── ...
    └── val/     # from instances_val.json
        └── ...
重新运行会创建新的输出文件夹

convert_coco() 从不覆盖现有的 save_dir:如果 my_dataset/converted/ 已存在,重新运行时会改为写入 my_dataset/converted-2/。重新运行前,请删除之前的输出(或更改 save_dir),否则后续步骤会读取过期标签。

3. 整理目录结构#

转换完成后,需要将标签文件放在图像旁边。YOLO 要求 labels/ 目录与 images/ 目录结构对应:

import shutil
from pathlib import Path

converted_dir = Path("my_dataset/converted/labels")
dataset_dir = Path("my_dataset")

# convert_coco 会以对应 JSON 文件名(去掉 "instances_" 前缀)命名每个子目录,
# 因此请遍历实际的子目录,而不要假定目录名为 "train"/"val"。
for src in converted_dir.iterdir():
    if not src.is_dir():
        continue
    dst = dataset_dir / "labels" / src.name
    dst.mkdir(parents=True, exist_ok=True)
    for f in src.glob("*.txt"):
        shutil.move(str(f), str(dst / f.name))

最终的数据集结构应如下所示:

my_dataset/
├── images/
│   ├── train/
│   │   ├── img_001.jpg
│   │   └── ...
│   └── val/
│       └── ...
├── labels/
│   ├── train/
│   │   ├── img_001.txt
│   │   └── ...
│   └── val/
│       └── ...
└── dataset.yaml

4. 创建 dataset.yaml#

创建一个 dataset.yaml 配置文件,将 COCO 类别映射到 YOLO 类别名称。此文件会告知 YOLO 数据的位置以及要检测的类别:

import json
from pathlib import Path

import yaml

# 从 COCO JSON 读取类别
with open("my_dataset/annotations/instances_train.json") as f:
    coco = json.load(f)

# 构建与 convert_coco 输出相匹配的类别名称(category_id - 1)
categories = sorted(coco["categories"], key=lambda x: x["id"])
names = {cat["id"] - 1: cat["name"] for cat in categories}
# 注意:convert_coco 会将类别 ID 映射为 category_id - 1,因此 category_id 必须
# 从 1 开始。如果类别从 0 开始,请先将每个 ID 加 1。

# 创建 dataset.yaml
dataset = {
    "path": str(Path("my_dataset").resolve()),
    "train": "images/train",
    "val": "images/val",
    "names": names,
}

with open("my_dataset/dataset.yaml", "w") as f:
    yaml.dump(dataset, f, default_flow_style=False)

生成的 YAML 文件:

path: /absolute/path/to/my_dataset
train: images/train
val: images/val
names:
    0: helmet
    1: vest

有关数据集 YAML 格式的更多详情,请参阅数据集配置指南。

5. 训练 YOLO 模型#

转换后的数据集准备就绪后,训练 YOLO 模型:

使用转换后的 COCO 数据进行训练
from ultralytics import YOLO

model = YOLO("yolo26n.pt")  # 加载预训练模型
results = model.train(data="my_dataset/dataset.yaml", epochs=100, imgsz=640)

有关训练技巧和最佳实践,请参阅模型训练指南。

6. 验证转换结果#

开始训练前,抽查几个标签文件,确认类别 ID 和坐标正确:

from pathlib import Path

label_file = Path("my_dataset/labels/train/img_001.txt")
for line in label_file.read_text().strip().splitlines():
    parts = line.split()
    cls_id = int(parts[0])
    coords = [float(v) for v in parts[1:5]]
    assert cls_id >= 0, f"Negative class ID {cls_id} — category_id in your JSON may start from 0"
    assert all(0 <= v <= 1 for v in coords), f"Coordinates out of [0, 1] range: {coords}"
提示

如果看到负类别 ID,你的 COCO JSON 可能使用了从 0 开始的 category_id。运行 convert_coco() 前,请将 JSON 中所有 category_id 值加 1,因为它会按 category_id - 1 映射类别 ID。

常见问题排查#

转换后类别 ID 错误#

如果模型能够训练,却检测出错误的目标类别,你很可能在自定义数据集上使用了 cls91to80=True(默认值)。这会通过 COCO 91 到 80 的查找表映射你的 category_id 值,而该映射仅适用于标准 COCO 数据集。没有 COCO-80 对应项的 category_id 会被映射为空值,并在转换时引发 TypeError: must be real number, not NoneType,而不是生成错误标签。

解决方案:自定义数据集始终使用 cls91to80=False。

训练时未找到标签#

如果标签扫描报告 0 images, N backgrounds,随后训练因 ValueError: train: No labels found in .../labels/train.cache 而中止,则说明标签文件不在预期目录中。convert_coco() 会将标签保存到单独的输出目录(例如 save_dir/labels/train/),但 YOLO 要求 labels/ 位于数据集目录中,并与 images/ 平行。

解决方案:移动标签文件,使其符合预期的目录结构。确保 labels/train/ 与 images/train/ 位于同一层级。

转换时出现 KeyError#

如果运行 convert_coco() 时遇到 KeyError: 'bbox' 或类似错误,你的 labels_dir 可能包含非实例标注 JSON 文件(例如 captions_train2017.json),这些文件的标注结构不同。

解决方案:只将实例标注 JSON 文件(例如 instances_train2017.json)放入 labels_dir。

转换后标签文件为空#

如果转换已完成,但 .txt 文件为空或缺失,则可能是所有标注都带有 iscrowd: 1(常见于由 SAM 生成的掩码),或者边界框的宽度或高度为零。对仅包含检测标注的导出数据使用 use_keypoints=True 运行也会产生相同结果,因为没有 keypoints 字段的标注会被完全跳过。

解决方案:检查 JSON 标注中的 iscrowd 值。如果使用 SAM 掩码,请预处理 JSON 并设置 iscrowd: 0。如果传入了 use_keypoints=True,请确认标注确实包含 keypoints。

掩码标注生成框状多边形#

如果 use_segments=True 记录了 annotations without a usable polygon,则部分标注没有 segmentation 值,或其值不是至少包含三个坐标对的列表。常见原因包括仅包含检测标注的导出数据(该字段缺失或为空),以及 COCO 游程编码({"counts": ..., "size": ...},位掩码导出工具如 SAM 会写入这种编码);没有外层多边形列表的扁平坐标列表、只有一两个点的轮廓,以及其他格式错误的值也会以相同方式处理。标注会保留所有有效多边形;如果没有有效多边形,则回退为形状与其边界框一致的分割行,因此标签仍然有效,但这些行不包含掩码细节。

解决方案:重新导出带有多边形分割标注的数据,在运行 convert_coco() 前将 RLE 掩码解码为多边形,或修正格式错误的 segmentation 值。

转换后的标签中类别 ID 不连续#

如果标签文件中的类别 ID 不连续(例如 0、4、9,而不是 0、1、2),说明你的标注工具使用了不连续的 category_id 值。

解决方案:确认 .txt 文件中的类别 ID 与 dataset.yaml 中的 names 字典一致。如有需要,请将 ID 重新映射为连续值。

有关完整的 API 详情和参数说明,请参阅 convert_coco API 参考。

常见问题#

  • 使用 Ultralytics 中的 convert_coco() 函数,将 COCO JSON 标注转换为 YOLO .txt 格式。对于自定义数据集,请设置 cls91to80=False:

    from ultralytics.data.converter import convert_coco
    
    convert_coco(labels_dir="path/to/annotations/", save_dir="output/", cls91to80=False)

    转换完成后,重新整理标签文件,使 labels/ 与 images/ 目录结构对应,然后创建 dataset.yaml 文件。完整工作流程请参阅分步指南。

  • 这是因为 convert_coco() 会将标签保存到 save_dir/labels/ 内的子目录(例如 save_dir/labels/train/),而不是直接保存到数据集的 labels/train/ 中,与 images/train/ 放在一起。YOLO 要求标签与图像目录平行——例如,images/train/img.jpg 需要对应 labels/train/img.txt。请移动转换后的标签,使其符合此结构。请参阅修正目录结构。

  • cls91to80 参数控制 COCO category_id 值如何映射到 YOLO 类别 ID。当设置为 True(默认值)时,它会应用为标准 COCO 数据集设计的 coco91_to_coco80_class() 查找表;该数据集有 80 个类别,ID 不连续(1-90)。对于自定义数据集,始终设置 cls91to80=False——它只需将每个 category_id 减 1,即可生成从 0 开始索引的类别 ID。

  • 不能直接使用默认流程,需要编写自定义代码。默认训练流程要求 YOLO .txt 标签,每张图像对应一个文件。因此,你可以运行 convert_coco() 并按照此分步指南操作,也可以对数据集类进行子类化,让它即时解析 COCO JSON——请参阅不转换 COCO JSON,直接训练 YOLO。有关支持格式的更多信息,请参阅数据集格式。

  • 可以,调用 convert_coco() 时使用 use_segments=True,即可在转换后的 YOLO 标签中包含多边形分割掩码。生成的标签文件兼容 YOLO 分割模型:

    from ultralytics.data.converter import convert_coco
    
    convert_coco(labels_dir="annotations/", save_dir="output/", use_segments=True, cls91to80=False)
  • 使用 use_keypoints=True 转换 COCO 关键点标注,以用于姿势估计训练:

    from ultralytics.data.converter import convert_coco
    
    convert_coco(labels_dir="annotations/", save_dir="output/", use_keypoints=True, cls91to80=False)

    请注意,如果 use_segments 和 use_keypoints 都设置为 True,标签文件中只会写入关键点,分割区域会被静默忽略。

评论