Cómo convertir anotaciones COCO al formato YOLO
Entrenar modelos Ultralytics YOLO requiere anotaciones en formato YOLO, pero muchas herramientas de anotación populares exportan en formato COCO JSON. Esta guía te muestra cómo convertir tus anotaciones COCO al formato YOLO y comenzar a entrenar modelos de detección de objetos, segmentación de instancias y estimación de poses.
¿Por qué convertir de COCO a YOLO?
El formato COCO JSON guarda todas las anotaciones en un único archivo, mientras que YOLO utiliza un archivo de texto por imagen con coordenadas normalizadas. Es necesario realizar la conversión porque:
- Los modelos YOLO requieren archivos de etiquetas
.txtcon un archivo por imagen, que contengaclass x_center y_center width heighten coordenadas normalizadas. - COCO JSON utiliza coordenadas de píxeles en formato
[x_min, y_min, width, height]con un único archivo JSON para todas las imágenes. - Los IDs de clase difieren: COCO utiliza valores
category_idarbitrarios, mientras que YOLO requiere IDs de clase indexados desde cero.
| Característica | COCO JSON | YOLO TXT |
|---|---|---|
| Estructura | Un único archivo JSON para todas las imágenes | Un archivo .txt por imagen |
| Formato de Bbox | [x_min, y_min, width, height] en píxeles | class x_center y_center width height normalizado (0-1) |
| IDs de clase | category_id (puede empezar desde cualquier número) | Indexado desde cero (empieza en 0) |
| Segmentación | Arrays de polígonos en el campo segmentation | Coordenadas de polígonos después del ID de clase |
| Puntos clave | [x, y, visibility, ...] en píxeles | [x, y, visibility, ...] normalizado |
Inicio rápido
La forma más rápida de convertir anotaciones COCO y empezar a entrenar:
from ultralytics.data.converter import convert_coco
convert_coco(
labels_dir="path/to/annotations/", # directory containing your JSON files
save_dir="path/to/output/", # where to save converted labels
cls91to80=False, # IMPORTANT: set False for custom datasets
)Después de la conversión, organiza la estructura de tus directorios, crea un dataset.yaml y comienza el entrenamiento. Consulta la guía paso a paso completa a continuación.
El valor predeterminado cls91to80=True está diseñado solo para el dataset COCO estándar con 80 clases de objetos, que asigna 91 IDs de categoría no contiguos a 80 IDs de clase contiguos. Para cualquier dataset personalizado, debes establecer cls91to80=False; de lo contrario, tus IDs de clase se asignarán incorrectamente de forma silenciosa y tu modelo aprenderá clases erróneas.
Guía de conversión paso a paso
Prepara tu dataset COCO
Un dataset típico en formato COCO exportado desde herramientas de anotación tiene la siguiente estructura:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ ├── img_002.jpg
│ │ └── ...
│ └── val/
│ ├── img_100.jpg
│ └── ...
└── annotations/
├── instances_train.json
└── instances_val.jsonCada archivo JSON sigue la especificación del formato de datos COCO con tres campos obligatorios: images, annotations y 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" }
]
}Convierte las anotaciones
Usa la función convert_coco() para convertir tus anotaciones COCO JSON al formato YOLO .txt:
from ultralytics.data.converter import convert_coco
convert_coco(
labels_dir="my_dataset/annotations/",
save_dir="my_dataset/converted/",
cls91to80=False,
)Organiza la estructura del directorio
Tras la conversión, los archivos de etiquetas deben colocarse junto a tus imágenes. YOLO espera un directorio labels/ que refleje el directorio images/:
import shutil
from pathlib import Path
# Paths
converted_dir = Path("my_dataset/converted/labels")
dataset_dir = Path("my_dataset")
# Move labels next to images for each split
for split in ["train", "val"]:
src = converted_dir / split # convert_coco strips "instances_" prefix from JSON filename
dst = dataset_dir / "labels" / split
dst.mkdir(parents=True, exist_ok=True)
for f in src.glob("*.txt"):
shutil.move(str(f), str(dst / f.name))Tu estructura de dataset final debería verse así:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ └── ...
│ └── val/
│ └── ...
├── labels/
│ ├── train/
│ │ ├── img_001.txt
│ │ └── ...
│ └── val/
│ └── ...
└── dataset.yamlCrea dataset.yaml
Crea un archivo de configuración dataset.yaml que mapee tus categorías COCO a nombres de clase YOLO. Este archivo le indica a YOLO dónde están tus datos y qué clases detectar:
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)El archivo YAML resultante:
path: /absolute/path/to/my_dataset
train: images/train
val: images/val
names:
0: helmet
1: vestPara más detalles sobre el formato YAML del dataset, consulta la guía de configuración del dataset.
Entrena tu modelo YOLO
Con tu dataset convertido listo, entrena un modelo YOLO:
from ultralytics import YOLO
model = YOLO("yolo26n.pt") # load a pretrained model
results = model.train(data="my_dataset/dataset.yaml", epochs=100, imgsz=640)Para consejos de entrenamiento y mejores prácticas, consulta la guía de entrenamiento de modelos.
Verifica tu conversión
Antes de entrenar, revisa algunos archivos de etiquetas para confirmar que los IDs de clase y las coordenadas sean correctos:
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}"Si ves IDs de clase negativos, es probable que tu COCO JSON utilice category_id empezando desde 0. Suma 1 a todos los valores category_id en tu JSON antes de ejecutar convert_coco(), ya que mapea los IDs de clase como category_id - 1.
Solución de problemas comunes
IDs de clase incorrectos tras la conversión
Si tu modelo se entrena pero detecta clases de objetos incorrectas, es probable que estés usando cls91to80=True (predeterminado) en un dataset personalizado. Esto mapea tus valores category_id a través de la tabla de búsqueda 91-a-80 de COCO, la cual solo es correcta para el dataset COCO estándar.
Solución: Usa siempre cls91to80=False para datasets personalizados.
No se encontraron etiquetas durante el entrenamiento
Si el entrenamiento muestra WARNING: No labels found o 0 images, N backgrounds, tus archivos de etiquetas no están en el directorio esperado. convert_coco() guarda las etiquetas en un directorio de salida separado (p. ej., save_dir/labels/train/), pero YOLO espera labels/ paralelo a images/ dentro del directorio de tu dataset.
Solución: Mueve los archivos de etiquetas para que coincidan con la estructura de directorios esperada. Asegúrate de que labels/train/ sea un hermano de images/train/.
KeyError durante la conversión
Si obtienes KeyError: 'bbox' o errores similares al ejecutar convert_coco(), es probable que tu labels_dir contenga archivos JSON que no son de instancias (p. ej., captions_train2017.json), los cuales tienen una estructura de anotación diferente.
Solución: Solo coloca archivos JSON de anotaciones de instancias (p. ej., instances_train2017.json) en labels_dir.
Archivos de etiquetas vacíos tras la conversión
Si la conversión se completa pero los archivos .txt están vacíos o faltan, es posible que todas las anotaciones tengan iscrowd: 1 (común con máscaras generadas por SAM), o que los bounding boxes tengan cero de ancho o alto.
Solución: Inspecciona tus anotaciones JSON en busca de valores iscrowd. Si utilizas máscaras SAM, preprocesa el JSON para establecer iscrowd: 0.
Brechas en los IDs de clase en etiquetas convertidas
Si los IDs de clase en los archivos de etiquetas no son contiguos (p. ej., 0, 4, 9 en lugar de 0, 1, 2), tu herramienta de anotación utiliza valores category_id no contiguos.
Solución: Verifica que los IDs de clase en tus archivos .txt coincidan con el diccionario names en dataset.yaml. Reasigna los IDs a valores contiguos si es necesario.
Para detalles completos de la API y descripciones de parámetros, consulta la referencia de la API convert_coco.
Preguntas frecuentes
¿Cómo convierto anotaciones COCO JSON al formato YOLO?
Usa la función convert_coco() de Ultralytics para convertir anotaciones COCO JSON al formato YOLO .txt. Establece cls91to80=False para datasets personalizados:
from ultralytics.data.converter import convert_coco
convert_coco(labels_dir="path/to/annotations/", save_dir="output/", cls91to80=False)Tras la conversión, reorganiza tus archivos de etiquetas para que labels/ refleje el directorio images/, luego crea un archivo dataset.yaml. Consulta la guía paso a paso para el flujo de trabajo completo.
¿Por qué el entrenamiento de YOLO muestra "No labels found" después de la conversión COCO?
Esto sucede porque convert_coco() guarda las etiquetas en un subdirectorio dentro de save_dir/labels/ (p. ej., save_dir/labels/train/) en lugar de hacerlo directamente en labels/train/ de tu dataset junto a images/train/. YOLO espera que las etiquetas estén en paralelo a las imágenes; por ejemplo, images/train/img.jpg requiere labels/train/img.txt. Mueve tus etiquetas convertidas para que coincidan con esta estructura. Consulta cómo corregir la estructura de directorios.
¿Qué hace cls91to80 en convert_coco()?
El parámetro cls91to80 controla cómo se mapean los valores category_id de COCO a los IDs de clase YOLO. Cuando es True (predeterminado), usa una tabla de búsqueda diseñada para el dataset COCO estándar, que tiene 80 clases con IDs no contiguos (1-90). Para datasets personalizados, usa siempre cls91to80=False; esto simplemente resta 1 de cada category_id para crear IDs de clase indexados desde cero.
¿Puedo entrenar YOLO directamente con COCO JSON sin convertir?
No con el pipeline de entrenamiento actual de YOLO: las anotaciones deben estar en formato .txt de YOLO con un archivo por imagen. Usa convert_coco() para convertir primero tu COCO JSON, luego sigue esta guía para organizar y entrenar. Para más información sobre formatos soportados, consulta formatos de dataset.
¿Puedo convertir anotaciones de segmentación COCO al formato YOLO?
Sí, utiliza use_segments=True al llamar a convert_coco() para incluir máscaras de segmentación de polígonos en las etiquetas YOLO convertidas. Esto genera archivos de etiquetas compatibles con modelos de segmentación YOLO:
from ultralytics.data.converter import convert_coco
convert_coco(labels_dir="annotations/", save_dir="output/", use_segments=True, cls91to80=False)¿Cómo convierto anotaciones de puntos clave COCO al formato YOLO?
Usa use_keypoints=True para convertir anotaciones de puntos clave COCO para entrenamiento de estimación de poses:
from ultralytics.data.converter import convert_coco
convert_coco(labels_dir="annotations/", save_dir="output/", use_keypoints=True, cls91to80=False)Ten en cuenta que si tanto use_segments como use_keypoints se establecen en True, solo se escribirán los puntos clave en los archivos de etiquetas; los segmentos se ignorarán silenciosamente.