Как преобразовать аннотации 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 JSON | YOLO 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=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:
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.yaml4. Создай 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:
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 в формат.txtYOLO. Для пользовательских наборов данных задай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задает способ сопоставления значений COCOcategory_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, в файлы меток будут записаны только ключевые точки — сегменты будут молча проигнорированы.
Комментарии
Задай вопрос, поделись результатами или помоги кому-нибудь.