YOLO Vision 2026:

Как преобразовать аннотации COCO в формат YOLO#

Для обучения моделей Ultralytics YOLO нужны аннотации в формате YOLO, однако многие популярные инструменты аннотирования экспортируют данные в формате COCO JSON. В этом руководстве показано, как преобразовать аннотации COCO в формат YOLO и начать обучение моделей для обнаружения объектов, сегментации экземпляров и оценки позы.

Хочешь пропустить преобразование?

Чтобы обучаться непосредственно на COCO JSON без создания файлов .txt, см. Обучение YOLO на COCO JSON без преобразования.

Зачем преобразовывать COCO в YOLO?#

Формат COCO JSON хранит все аннотации в одном файле, тогда как YOLO использует отдельный текстовый файл для каждого изображения с нормализованными координатами. Преобразование необходимо, потому что:

  • Моделям YOLO нужны файлы меток .txt — по одному файлу на изображение, содержащему class x_center y_center width height в нормализованных координатах.
  • COCO JSON использует координаты в пикселях в формате [x_min, y_min, width, height] с одним JSON-файлом для всех изображений.
  • Идентификаторы классов различаются — COCO использует произвольные значения category_id, тогда как YOLO требует идентификаторы классов с индексацией от нуля.
ХарактеристикаCOCO JSONYOLO TXT
СтруктураОдин JSON-файл для всех изображенийОдин файл .txt на изображение
Формат bbox[x_min, y_min, width, height] в пикселяхclass x_center y_center width height в нормализованном виде (0–1)
Идентификаторы классовcategory_id (может начинаться с любого числа)Индексация от нуля (начинается с 0)
СегментацияМассивы полигонов в поле segmentationКоординаты полигонов после идентификатора класса
Ключевые точки[x, y, visibility, ...] в пикселях[x, y, visibility, ...] в нормализованном виде

Быстрый старт#

Самый быстрый способ преобразовать аннотации COCO и начать обучение:

from ultralytics.data.converter import convert_coco

convert_coco(
    labels_dir="my_dataset/annotations/",  # directory containing your JSON files
    save_dir="my_dataset/converted/",  # where to save converted labels
    cls91to80=False,  # set False for custom datasets (see warning below)
)

После преобразования организуй структуру каталогов, создай dataset.yaml и начни обучение. Полное пошаговое руководство приведено ниже.

Пользовательские датасеты: всегда используй `cls91to80=False`

Значение по умолчанию cls91to80=True предназначено только для стандартного датасета COCO с 80 классами объектов, где 91 непоследовательный идентификатор категории преобразуется в 80 последовательных идентификаторов классов. Для любого пользовательского датасета необходимо задать cls91to80=False — иначе идентификаторы классов будут незаметно преобразованы неправильно, и модель будет обучаться на неверных классах.

Пошаговое руководство по преобразованию#

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 в формат .txt YOLO:

Преобразование 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 для каждого аннотированного изображения в подкаталог labels/, названный по имени каждого JSON-файла, с удалённым префиксом instances_ (то есть 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 names each subdirectory after its JSON file (minus the "instances_" prefix),
# so iterate the actual subdirectories instead of assuming "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

# Read categories from your COCO JSON
with open("my_dataset/annotations/instances_train.json") as f:
    coco = json.load(f)

# Build class names matching convert_coco output (category_id - 1)
categories = sorted(coco["categories"], key=lambda x: x["id"])
names = {cat["id"] - 1: cat["name"] for cat in categories}
# NOTE: convert_coco maps class IDs as category_id - 1, so category_id must
# start from 1. If your categories start from 0, add 1 to each ID first.

# Create 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")  # load a pretrained model
results = model.train(data="my_dataset/dataset.yaml", epochs=100, imgsz=640)

Советы и рекомендации по обучению см. в руководстве по обучению моделей.

6. Проверь преобразование#

Перед обучением выборочно проверь несколько файлов меток, чтобы убедиться в правильности идентификаторов классов и координат:

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}"
Совет

Если ты видишь отрицательные идентификаторы классов, вероятно, в COCO JSON используются category_id, начинающиеся с 0. Перед запуском convert_coco() добавь 1 ко всем значениям category_id в JSON, поскольку эта функция сопоставляет идентификаторы классов как category_id - 1.

Устранение распространённых проблем#

Неверные идентификаторы классов после преобразования#

Если модель обучается, но обнаруживает объекты неверных классов, вероятно, ты используешь cls91to80=True (значение по умолчанию) для пользовательского датасета. Это преобразует значения category_id через таблицу соответствий COCO 91-to-80, которая корректна только для стандартного датасета COCO. Значение category_id, которому не соответствует класс COCO-80, не сопоставляется ни с чем и вызывает 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) с другой структурой аннотаций.

Решение: размещай в labels_dir только JSON-файлы аннотаций экземпляров (например, instances_train2017.json).

Пустые файлы меток после преобразования#

Если преобразование завершается, но файлы .txt пусты или отсутствуют, все аннотации могут иметь iscrowd: 1 (что часто бывает у масок, созданных с помощью SAM), либо ограничивающие рамки имеют нулевую ширину или высоту. Запуск с параметром use_keypoints=True для экспорта только данных обнаружения даёт тот же результат, поскольку аннотации без поля keypoints полностью пропускаются.

Решение: проверь значения iscrowd в JSON-аннотациях. Если используются маски SAM, предварительно обработай JSON и задай iscrowd: 0. Если ты передал use_keypoints=True, убедись, что аннотации действительно содержат keypoints.

Полигоны в форме рамок для аннотаций масок#

Если use_segments=True записывает annotations without a usable polygon, некоторые аннотации не содержат значения segmentation или содержат значение, не являющееся списком как минимум из трёх пар координат. Обычно это происходит с экспортом только данных обнаружения, где поле отсутствует или пусто, а также с кодированием COCO с использованием длин серий ({"counts": ..., "size": ...}), которое записывают экспортеры битовых масок, например SAM; плоский список координат без внешнего списка полигонов, контуры из одной или двух точек и другие некорректные значения обрабатываются так же. Для аннотации сохраняются все оставшиеся полигоны, а если их нет, используется строка сегментации в форме ограничивающей рамки, поэтому метки остаются корректными, но эти строки не содержат сведений о маске.

Решение: повторно экспортируй аннотации с полигональными сегментациями, декодируй маски RLE в полигоны перед запуском convert_coco() или исправь некорректные значения segmentation.

Пропуски идентификаторов классов в преобразованных метках#

Если идентификаторы классов в файлах меток непоследовательны (например, 0, 4, 9 вместо 0, 1, 2), инструмент аннотирования использует непоследовательные значения category_id.

Решение: проверь, что идентификаторы классов в файлах .txt соответствуют словарю names в dataset.yaml. При необходимости переназначь идентификаторы в последовательные значения.

Полные сведения об API и описание параметров см. в справочнике API convert_coco.

Часто задаваемые вопросы#

  • Используй функцию convert_coco() из Ultralytics, чтобы преобразовать аннотации COCO JSON в формат .txt YOLO. Для пользовательских датасетов задай 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 определяет, как значения category_id COCO сопоставляются с идентификаторами классов YOLO. Если True (значение по умолчанию), применяется таблица соответствий coco91_to_coco80_class(), предназначенная для стандартного датасета COCO, в котором 80 классов с непоследовательными идентификаторами (1–90). Для пользовательских датасетов всегда задавай cls91to80=False — это просто вычитает 1 из каждого category_id, чтобы создать идентификаторы классов с индексацией от нуля.

  • Не без пользовательского кода. Стандартный конвейер обучения ожидает метки YOLO .txt — по одному файлу на изображение, поэтому либо запусти convert_coco() и следуй этому пошаговому руководству, либо унаследуй класс датасета и разбирай COCO JSON на лету — см. Обучение YOLO на COCO JSON без преобразования. Подробнее о поддерживаемых форматах см. в разделе форматы датасетов.

  • Да, используй use_segments=True при вызове convert_coco(), чтобы включить полигональные маски сегментации в преобразованные метки 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, в файлы меток будут записаны только ключевые точки — сегменты будут незаметно проигнорированы.

Комментарии