Ultralytics YOLO27:
Get Started

Cómo convertir anotaciones COCO al formato YOLO#

Para entrenar modelos Ultralytics YOLO se necesitan anotaciones en formato YOLO, pero muchas herramientas populares de anotación las exportan en formato COCO JSON. Esta guía te muestra cómo convertir tus anotaciones COCO al formato YOLO y empezar a entrenar modelos de detección de objetos, segmentación de instancias y estimación de pose.

¿Prefieres omitir la conversión?

Para entrenar directamente con COCO JSON sin generar archivos .txt, consulta Entrena YOLO con COCO JSON sin conversión.

¿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. La conversión es necesaria porque:

  • Los modelos YOLO necesitan archivos de etiquetas .txt, uno por imagen, que contengan class x_center y_center width height con coordenadas normalizadas.
  • COCO JSON utiliza coordenadas en píxeles en formato [x_min, y_min, width, height] y un único archivo JSON para todas las imágenes.
  • Los ID de clase son distintos: COCO utiliza valores arbitrarios de category_id, mientras que YOLO requiere ID de clase indexados desde cero.
CaracterísticaCOCO JSONYOLO TXT
EstructuraUn único archivo JSON para todas las imágenesUn archivo .txt por imagen
Formato de BBox[x_min, y_min, width, height] en píxelesclass x_center y_center width height normalizado (0-1)
ID de clasecategory_id (puede empezar por cualquier número)Indexado desde cero (empieza por 0)
SegmentaciónMatrices de polígonos en el campo segmentationCoordenadas del polígono 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="my_dataset/annotations/",  # directorio que contiene los archivos JSON
    save_dir="my_dataset/converted/",  # dónde guardar las etiquetas convertidas
    cls91to80=False,  # establece False para conjuntos de datos personalizados (consulta la advertencia siguiente)
)

Después de la conversión, organiza la estructura de directorios, crea un dataset.yaml y empieza a entrenar. Consulta a continuación la guía paso a paso completa.

Conjuntos de datos personalizados: usa siempre `cls91to80=False`

El valor predeterminado de cls91to80=True está diseñado solo para el conjunto de datos COCO estándar, con 80 clases de objetos, que asigna 91 ID de categoría no contiguos a 80 ID de clase contiguos. Para cualquier conjunto de datos personalizado, debes establecer cls91to80=False; de lo contrario, los ID de clase se asignarán incorrectamente sin avisar y el modelo aprenderá clases equivocadas.

Guía de conversión paso a paso#

1. Prepara el conjunto de datos COCO#

Un conjunto de datos 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.json

Cada 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" }
    ]
}

2. Convierte las anotaciones#

Usa la función convert_coco() para convertir las anotaciones COCO JSON al formato YOLO .txt:

Convertir COCO a formato YOLO
from ultralytics.data.converter import convert_coco

convert_coco(
    labels_dir="my_dataset/annotations/",
    save_dir="my_dataset/converted/",
    cls91to80=False,
)

convert_coco() escribe un archivo .txt por cada imagen anotada en un subdirectorio labels/ cuyo nombre coincide con el de cada archivo JSON, pero sin el prefijo instances_ (así, instances_train.json genera labels/train/). Se omiten las imágenes sin anotaciones y no se les asigna ningún archivo de etiquetas, por lo que el árbol labels/ puede no reflejar todas las imágenes:

my_dataset/converted/
├── images/      # created but left empty
└── labels/
    ├── train/   # from instances_train.json
    │   ├── img_001.txt
    │   └── ...
    └── val/     # from instances_val.json
        └── ...
Al volver a ejecutarlo, se crea una nueva carpeta de salida

convert_coco() nunca sobrescribe un save_dir existente: si my_dataset/converted/ ya existe, al volver a ejecutarlo se escribe en my_dataset/converted-2/. Elimina la salida anterior (o cambia save_dir) antes de volver a ejecutarlo; de lo contrario, los siguientes pasos leerán etiquetas obsoletas.

3. Organiza la estructura de directorios#

Después de la conversión, los archivos de etiquetas deben colocarse junto a las imágenes. YOLO espera un directorio labels/ que refleje el directorio images/:

import shutil
from pathlib import Path

converted_dir = Path("my_dataset/converted/labels")
dataset_dir = Path("my_dataset")

# convert_coco asigna a cada subdirectorio el nombre de su archivo JSON (sin el prefijo "instances_"),
# así que recorre los subdirectorios reales en lugar de dar por hecho que se llaman "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))

La estructura del conjunto de datos final debería tener este aspecto:

my_dataset/
├── images/
│   ├── train/
│   │   ├── img_001.jpg
│   │   └── ...
│   └── val/
│       └── ...
├── labels/
│   ├── train/
│   │   ├── img_001.txt
│   │   └── ...
│   └── val/
│       └── ...
└── dataset.yaml

4. Crea dataset.yaml#

Crea un archivo de configuración dataset.yaml que asigne tus categorías COCO a nombres de clase YOLO. Este archivo indica a YOLO dónde están tus datos y qué clases debe detectar:

import json
from pathlib import Path

import yaml

# Lee las categorías del JSON de COCO
with open("my_dataset/annotations/instances_train.json") as f:
    coco = json.load(f)

# Crea nombres de clase que coincidan con la salida de convert_coco (category_id - 1)
categories = sorted(coco["categories"], key=lambda x: x["id"])
names = {cat["id"] - 1: cat["name"] for cat in categories}
# NOTA: convert_coco asigna los ID de clase como category_id - 1, por lo que category_id debe
# empezar por 1. Si tus categorías empiezan por 0, suma 1 primero a cada ID.

# Crea 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: vest

Para obtener más información sobre el formato YAML de los conjuntos de datos, consulta la guía de configuración de conjuntos de datos.

5. Entrena tu modelo YOLO#

Cuando tengas listo el conjunto de datos convertido, entrena un modelo YOLO:

Entrenar con datos COCO convertidos
from ultralytics import YOLO

model = YOLO("yolo26n.pt")  # cargar un modelo preentrenado
results = model.train(data="my_dataset/dataset.yaml", epochs=100, imgsz=640)

Para consultar consejos y prácticas recomendadas de entrenamiento, visita la guía de entrenamiento de modelos.

6. Verifica la conversión#

Antes de entrenar, revisa algunas muestras de archivos de etiquetas para confirmar que los ID 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}"
Consejo

Si aparecen ID de clase negativos, es probable que el JSON de COCO use category_id empezando por 0. Suma 1 a todos los valores category_id del JSON antes de ejecutar convert_coco(), ya que asigna los ID de clase como category_id - 1.

Solución de problemas habituales#

ID de clase incorrectos después de la conversión#

Si el modelo se entrena, pero detecta clases de objetos incorrectas, probablemente estás usando cls91to80=True (valor predeterminado) con un conjunto de datos personalizado. Esto asigna tus valores category_id mediante la tabla de correspondencias de COCO de 91 a 80, que solo es válida para el conjunto de datos COCO estándar. Un category_id sin equivalente en COCO-80 no se asigna a nada y provoca TypeError: must be real number, not NoneType durante la conversión, en lugar de generar etiquetas incorrectas.

Solución: usa siempre cls91to80=False para conjuntos de datos personalizados.

No se encuentran etiquetas durante el entrenamiento#

Si el análisis de etiquetas indica 0 images, N backgrounds y el entrenamiento se interrumpe después con ValueError: train: No labels found in .../labels/train.cache, los archivos de etiquetas no están en el directorio esperado. convert_coco() guarda las etiquetas en un directorio de salida independiente (por ejemplo, save_dir/labels/train/), pero YOLO espera que labels/ esté en paralelo a images/ dentro del directorio del conjunto de datos.

Solución: mueve los archivos de etiquetas para que coincidan con la estructura de directorios esperada. Asegúrate de que labels/train/ esté al mismo nivel que images/train/.

KeyError durante la conversión#

Si al ejecutar convert_coco() aparece KeyError: 'bbox' o un error similar, es probable que labels_dir contenga archivos JSON que no sean de instancias (por ejemplo, captions_train2017.json) y que tengan una estructura de anotaciones diferente.

Solución: coloca únicamente archivos JSON de anotaciones de instancias (por ejemplo, instances_train2017.json) en labels_dir.

Archivos de etiquetas vacíos después de la conversión#

Si la conversión termina, pero los archivos .txt están vacíos o faltan, es posible que todas las anotaciones tengan iscrowd: 1 (algo habitual en las máscaras generadas con SAM) o que las cajas delimitadoras tengan anchura o altura cero. Ejecutar con use_keypoints=True una exportación que solo contenga detecciones produce el mismo resultado, porque se omiten por completo las anotaciones sin el campo keypoints.

Solución: inspecciona los valores iscrowd de las anotaciones JSON. Si usas máscaras de SAM, procesa previamente el JSON para establecer iscrowd: 0. Si has pasado use_keypoints=True, confirma que tus anotaciones incluyan realmente keypoints.

Polígonos con forma de caja a partir de anotaciones de máscaras#

Si use_segments=True registra annotations without a usable polygon, algunas anotaciones no tienen un valor segmentation o este no es una lista con al menos tres pares de coordenadas. Las causas habituales son las exportaciones que solo contienen detecciones, que dejan el campo vacío o sin definir, y la codificación de longitud variable de COCO ({"counts": ..., "size": ...}), que utilizan exportadores de máscaras de bits como SAM; las listas planas de coordenadas sin una lista de polígonos contenedora, los contornos de uno o dos puntos y otros valores mal formados se gestionan del mismo modo. Cada anotación conserva los polígonos válidos y, si no queda ninguno, recurre a una fila de segmento con la forma de su caja delimitadora. Así, las etiquetas siguen siendo válidas, pero esas filas no contienen detalles de la máscara.

Solución: vuelve a exportar las anotaciones con segmentaciones poligonales, decodifica las máscaras RLE en polígonos antes de ejecutar convert_coco() o corrige cualquier valor segmentation mal formado.

Saltos en los ID de clase de las etiquetas convertidas#

Si los ID de clase de los archivos de etiquetas no son contiguos (por ejemplo, 0, 4, 9 en lugar de 0, 1, 2), la herramienta de anotación utiliza valores category_id no contiguos.

Solución: comprueba que los ID de clase de tus archivos .txt coincidan con el diccionario names de dataset.yaml. Si es necesario, reasigna los ID a valores contiguos.

Para ver todos los detalles de la API y las descripciones de los parámetros, consulta la referencia de la API de convert_coco.

Preguntas frecuentes#

  • Usa la función convert_coco() de Ultralytics para convertir anotaciones COCO JSON al formato YOLO .txt. Establece cls91to80=False para los conjuntos de datos personalizados:

    from ultralytics.data.converter import convert_coco
    
    convert_coco(labels_dir="path/to/annotations/", save_dir="output/", cls91to80=False)

    Después de la conversión, reorganiza los archivos de etiquetas para que labels/ refleje el directorio images/ y, a continuación, crea un archivo dataset.yaml. Consulta la guía paso a paso para ver el flujo de trabajo completo.

  • Esto ocurre porque convert_coco() guarda las etiquetas en un subdirectorio de save_dir/labels/ (por ejemplo, save_dir/labels/train/) en lugar de guardarlas directamente en labels/train/ del conjunto de datos, junto a images/train/. YOLO espera que las etiquetas estén en paralelo a las imágenes; por ejemplo, images/train/img.jpg necesita labels/train/img.txt. Mueve las etiquetas convertidas para que coincidan con esta estructura. Consulta cómo corregir la estructura de directorios.

  • El parámetro cls91to80 controla cómo se asignan los valores COCO category_id a los ID de clase de YOLO. Cuando True (valor predeterminado), aplica la tabla de correspondencias coco91_to_coco80_class(), diseñada para el conjunto de datos COCO estándar, que tiene 80 clases con ID no contiguos (1-90). Para los conjuntos de datos personalizados, establece siempre cls91to80=False: simplemente resta 1 a cada category_id para crear ID de clase indexados desde cero.

  • No, no sin código personalizado. El flujo de entrenamiento predeterminado espera etiquetas YOLO .txt, con un archivo por imagen. Por tanto, ejecuta convert_coco() y sigue esta guía paso a paso, o crea una subclase del conjunto de datos para analizar COCO JSON sobre la marcha; consulta Entrenar YOLO con COCO JSON sin conversión. Para obtener más información sobre los formatos compatibles, consulta los formatos de conjuntos de datos.

  • Sí. Usa use_segments=True al llamar a convert_coco() para incluir máscaras de segmentación poligonales en las etiquetas YOLO convertidas. Así se generan archivos de etiquetas compatibles con los 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)
  • Usa use_keypoints=True para convertir anotaciones COCO de puntos clave y entrenar modelos de estimación de pose:

    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 use_segments y use_keypoints tienen ambos el valor True, solo se escribirán los puntos clave en los archivos de etiquetas y se omitirán los segmentos sin avisar.

Comentarios