YOLO Vision 2026:

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 exportan en formato COCO JSON. Esta guía 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 Entrenar YOLO con COCO JSON sin conversión.

¿Por qué convertir de COCO a YOLO?#

El formato COCO JSON almacena 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 requieren archivos de etiquetas .txt con un archivo por imagen que contenga class x_center y_center width height en 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 ID de clase son diferentes: COCO utiliza valores category_id arbitrarios, 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 de los 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="my_dataset/annotations/",  # directory containing your JSON files
    save_dir="my_dataset/converted/",  # where to save converted labels
    cls91to80=False,  # set False for custom datasets (see warning below)
)

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

Datasets personalizados: usa siempre `cls91to80=False`

El valor predeterminado de cls91to80=True está diseñado solo para el dataset 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 dataset personalizado, debes establecer cls91to80=False; de lo contrario, tus ID de clase se asignarán incorrectamente de forma silenciosa y tu modelo aprenderá clases equivocadas.

Guía de conversión paso a paso#

1. Prepara tu dataset COCO#

Un dataset típico con 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#

Utiliza la función convert_coco() para convertir tus anotaciones COCO JSON al formato .txt de YOLO:

Convertir COCO al 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 se basa en cada archivo JSON, eliminando el prefijo instances_ (por lo que instances_train.json produce labels/train/). Las imágenes sin anotaciones se omiten y no reciben 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
        └── ...
Volver a ejecutar crea una nueva carpeta de salida

convert_coco() nunca sobrescribe un save_dir existente: si my_dataset/converted/ ya existe, una nueva ejecución escribe en my_dataset/converted-2/. Elimina la salida anterior (o cambia save_dir) antes de volver a ejecutar; de lo contrario, los pasos siguientes 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 replique el directorio images/:

import shutil
from pathlib import Path

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

# convert_coco names each subdirectory after its JSON file (minus the "instances_" prefix),
# so iterate the actual subdirectories instead of assuming "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 final del dataset debería ser la siguiente:

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 de 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

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

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

5. Entrena tu modelo YOLO#

Con tu dataset convertido listo, entrena un modelo YOLO:

Entrenar con datos COCO convertidos
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 consultar consejos y prácticas recomendadas de entrenamiento, visita la guía de entrenamiento de modelos.

6. Verifica la conversión#

Antes de entrenar, comprueba algunas muestras de archivos de etiquetas para confirmar que los ID de clase y las coordenadas son 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 ves ID de clase negativos, es probable que tu COCO JSON utilice category_id empezando por 0. Añade 1 a todos los valores category_id de tu 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 tu modelo entrena, pero detecta clases de objetos incorrectas, probablemente estés utilizando cls91to80=True (predeterminado) en un dataset personalizado. Esto asigna tus valores category_id mediante la tabla de consulta de 91 a 80 de COCO, lo que solo es correcto para el dataset COCO estándar. Un category_id sin equivalente en COCO-80 no se asigna a nada y genera TypeError: must be real number, not NoneType durante la conversión, en lugar de producir etiquetas incorrectas.

Solución: utiliza siempre cls91to80=False para datasets personalizados.

No se han encontrado etiquetas durante el entrenamiento#

Si el análisis de etiquetas informa de 0 images, N backgrounds y el entrenamiento se interrumpe después con ValueError: train: No labels found in .../labels/train.cache, tus 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 labels/ en paralelo a images/ dentro del directorio del 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 directorio hermano de images/train/.

KeyError durante la conversión#

Si obtienes KeyError: 'bbox' u otros errores similares al ejecutar convert_coco(), es probable que tu 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 máscaras generadas por SAM), o que las cajas delimitadoras tengan anchura o altura cero. Ejecutar con use_keypoints=True sobre una exportación que solo contiene detecciones produce el mismo resultado, porque las anotaciones sin un campo keypoints se omiten por completo.

Solución: inspecciona los valores iscrowd de tus anotaciones JSON. Si utilizas 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 ningún valor segmentation, o tienen uno que no es una lista de al menos tres pares de coordenadas. Las causas habituales son las exportaciones que solo contienen detecciones, que dejan el campo vacío o ausente, y la codificación por longitudes de COCO ({"counts": ..., "size": ...}), que escriben exportadores de máscaras binarias como SAM; una lista plana de coordenadas sin una lista de polígonos contenedora, contornos de uno o dos puntos y otros valores con formato incorrecto se gestionan del mismo modo. Una anotación conserva los polígonos que queden y, si no queda ninguno, recurre a una fila de segmento con la forma de su caja delimitadora, por lo que las etiquetas siguen siendo válidas, aunque 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 con formato incorrecto.

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), tu herramienta de anotación utiliza valores category_id no contiguos.

Solución: verifica 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 consultar todos los detalles de la API y las descripciones de los parámetros, visita la referencia de la API de convert_coco.

Preguntas frecuentes#

  • Utiliza la función convert_coco() de Ultralytics para convertir las anotaciones COCO JSON al formato .txt de YOLO. 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)

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

  • Esto ocurre porque convert_coco() guarda las etiquetas en un subdirectorio dentro de save_dir/labels/ (por ejemplo, save_dir/labels/train/) en lugar de directamente en el 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 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 category_id de COCO a los ID de clase de YOLO. Cuando True (predeterminado), aplica la tabla de consulta coco91_to_coco80_class(), diseñada para el dataset COCO estándar, que tiene 80 clases con ID no contiguos (1-90). Para datasets personalizados, establece siempre cls91to80=False; esto simplemente resta 1 a cada category_id para crear ID de clase indexados desde cero.

  • No sin código personalizado. La canalización de entrenamiento predeterminada espera etiquetas .txt de YOLO, con un archivo por imagen, así que puedes ejecutar convert_coco() y seguir esta guía paso a paso, o crear una subclase del dataset 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 formatos de datasets.

  • Sí, utiliza use_segments=True al llamar a convert_coco() para incluir máscaras de segmentación poligonales 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)
  • Utiliza use_keypoints=True para convertir las anotaciones de puntos clave COCO para el entrenamiento 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 están establecidos en True, solo se escribirán los puntos clave en los archivos de etiquetas; los segmentos se ignorarán silenciosamente.

Comentarios