Ultralytics YOLO27: Запишись в список ожидания.
Начать

Как преобразовать аннотации 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)
СегментацияМассивы полигонов или RLE-маски в поле 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

from ultralytics.utils 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,
}

YAML.save("my_dataset/dataset.yaml", dataset)

Итоговый файл 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, которое пропускает convert_coco() (например, CVAT экспортирует аннотации масок как области толпы), либо ограничивающие рамки имеют нулевую ширину или высоту. Запуск с use_keypoints=True на экспорте только для детекции приводит к тому же результату, поскольку аннотации без поля keypoints полностью пропускаются.

Решение: Проверь JSON-аннотации на наличие значений iscrowd. Если каждая маска с флагом толпы очерчивает один объект, как в экспорте масок CVAT, задай iscrowd: 0, чтобы use_segments=True преобразовал ее RLE-маску; оставь флаг у настоящих областей толпы, охватывающих несколько объектов. Если ты передал use_keypoints=True, убедись, что в аннотациях действительно есть keypoints.

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

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

Значения RLE, у которых отсутствует counts или которые невозможно декодировать, например усеченная строка или серии, в сумме не дающие size, останавливают преобразование с ошибкой, а не приводят к резервному варианту.

Решение: Повторно экспортируй аннотации с сегментациями в виде полигонов или RLE либо исправь некорректные значения 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 без преобразования. Подробнее о поддерживаемых форматах см. в разделе форматы наборов данных.

  • Да, используй use_segments=True при вызове convert_coco(), чтобы включить маски сегментации в преобразованные метки YOLO. Сегментации в виде полигонов преобразуются напрямую, а маски, сжатые или несжатые кодированием длин серий (RLE), декодируются с помощью faster-coco-eval (он устанавливается автоматически, если JSON-файл содержит такие маски) в контуры внешних границ областей. Если объект разделен на несколько полигонов или областей маски, они объединяются в одну строку полигона; поскольку полигоны YOLO не могут содержать отверстия, отверстия в масках заполняются. Аннотации толпы (iscrowd: 1) пропускаются. В результате создаются файлы меток, совместимые с моделями 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, в файлы меток будут записаны только ключевые точки — сегменты будут молча проигнорированы.

Участники
glenn-jocherglenn-jocher5raimbekovmraimbekovm5DNDevulapalli Naga Sri Vaishnavi1pderrengerpderrenger1

Комментарии

Задай вопрос, поделись результатами или помоги кому-нибудь.