Ultralytics YOLO27:
Get Started

Как преобразовать аннотации 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)
ID классовcategory_id (может начинаться с любого числа)Нумерация с нуля (начинается с 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 по умолчанию предназначено только для стандартного набора данных COCO с 80 классами объектов: оно сопоставляет 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 в формат .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 называет каждый подкаталог по имени соответствующего 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

# Прочитай категории из файла JSON COCO
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, сначала прибавь 1 к каждому ID.

# Создай 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 классов, вероятно, в JSON COCO используются category_id, начинающиеся с 0. Перед запуском convert_coco() прибавь 1 ко всем значениям category_id в JSON, поскольку функция сопоставляет ID классов как category_id - 1.

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

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

Если модель обучается, но обнаруживает неверные классы объектов, скорее всего, для пользовательского набора данных используется 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.

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

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

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

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

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

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

Комментарии