Cómo convertir anotaciones COCO al formato YOLO#
El entrenamiento de modelos de Ultralytics YOLO requiere anotaciones en formato YOLO, pero muchas herramientas de anotación populares exportan en formato COCO JSON en su lugar. 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 poses.
Para entrenar directamente en COCO JSON sin generar archivos de .txt, consulta Train YOLO on COCO JSON Without Conversion.
¿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 de
.txtcon un archivo por imagen, que contenganclass x_center y_center width heighten coordenadas normalizadas. - COCO JSON usa 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 difieren: COCO utiliza valores arbitrarios de
category_id, mientras que YOLO requiere ID de clase indexados desde cero.
| Característica | COCO JSON | YOLO TXT |
|---|---|---|
| Estructura | Un único archivo JSON para todas las imágenes | Un archivo de .txt por imagen |
| Formato Bbox | [x_min, y_min, width, height] en píxeles | class x_center y_center width height normalizado (0-1) |
| ID de clase | category_id (puede empezar desde cualquier número) | Indexado desde cero (empieza en 0) |
| Segmentación | Matrices de polígonos en el campo de segmentation | Coordenadas de polígono después del ID de clase |
| Puntos clave (Keypoints) | [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 tus directorios, crea un dataset.yaml y empieza a entrenar. Consulta la guía paso a paso completa a continuación.
El valor predeterminado de cls91to80=True está diseñado únicamente para el conjunto de datos COCO estándar con 80 clases de objetos, el cual mapea 91 ID de categorías no contiguas a 80 ID de clases contiguas. Para cualquier conjunto de datos personalizado, debes configurar cls91to80=False; de lo contrario, tus ID de clase se mapearán de forma incorrecta silenciosamente y tu modelo aprenderá clases erróneas.
Guía de conversión paso a paso#
1. 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" }
]
}2. Convierte las anotaciones#
Usa la función convert_coco() para convertir tus anotaciones COCO JSON al formato YOLO de .txt:
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 de .txt por imagen anotada en un subdirectorio de labels/ nombrado según cada archivo JSON, con el prefijo de instances_ eliminado (por lo que instances_train.json produce labels/train/). Las imágenes sin anotaciones se omiten y no obtienen ningún archivo de etiquetas, por lo que el árbol de labels/ puede no reflejar todas las imágenes:
my_dataset/converted/
└── labels/
├── train/ # from instances_train.json
│ ├── img_001.txt
│ └── ...
└── val/ # from instances_val.json
└── ...convert_coco() nunca sobrescribe un save_dir existente: si my_dataset/converted/ ya existe, una nueva ejecución escribe en my_dataset/converted-2/ en su lugar. Elimina la salida anterior (o cambia save_dir) antes de volver a ejecutar, o los siguientes pasos leerán etiquetas obsoletas.
3. Organiza la estructura del directorio#
Después de la conversión, los archivos de etiquetas deben colocarse junto a tus imágenes. YOLO espera un directorio de labels/ que refleje el directorio de 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 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.yaml4. Crea dataset.yaml#
Crea un archivo de configuración de dataset.yaml que mapee tus categorías de COCO a los nombres de clase de YOLO. Este archivo le 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: vestPara obtener más detalles sobre el formato YAML del conjunto de datos, consulta la guía de configuración de conjuntos de datos.
5. 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 obtener consejos de entrenamiento y mejores prácticas, consulta la guía de entrenamiento de modelos.
6. Verifica tu conversión#
Antes de entrenar, verifica manualmente algunos 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}"Si ves ID de clase negativos, es probable que tu COCO JSON use category_id empezando desde 0. Suma 1 a todos los valores de category_id en tu JSON antes de ejecutar convert_coco(), ya que mapea los ID de clase como category_id - 1.
Solución de problemas comunes#
ID 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 conjunto de datos personalizado. Esto mapea tus valores de category_id a través de la tabla de búsqueda 91 a 80 de COCO, lo cual solo es correcto para el conjunto de datos COCO estándar.
Solución: Usa siempre cls91to80=False para conjuntos de datos 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 independiente (p. ej., save_dir/labels/train/), pero YOLO espera que labels/ esté en paralelo con images/ dentro del directorio de tu 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/ sea 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) y que tienen una estructura de anotación diferente.
Solución: Coloca únicamente archivos JSON de anotaciones de instancias (p. ej., instances_train2017.json) en el labels_dir.
Archivos de etiquetas vacíos tras la conversión#
Si la conversión se completa pero los archivos de .txt están vacíos o faltan, es posible que todas las anotaciones tengan iscrowd: 1 (algo común con las máscaras generadas por SAM), o que los cuadros delimitadores tengan un ancho o alto de cero.
Solución: Inspecciona las anotaciones JSON en busca de valores de iscrowd. Si usas máscaras de SAM, preprocesa el JSON para establecer iscrowd: 0.
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 llevan ningún valor de segmentation, o tienen uno que no es una lista de al menos tres pares de coordenadas. Las causas habituales son las exportaciones exclusivas de detección, que dejan el campo ausente o vacío, y la codificación de longitud de ejecución de COCO ({"counts": ..., "size": ...}), que escriben los exportadores de máscaras de bits como SAM; una lista de coordenadas plana sin una lista de polígonos contenedores, contornos de uno y dos puntos y otros valores mal formados se manejan de la misma manera. Una anotación conserva los polígonos que queden y recurre a una fila de segmento con la forma de su cuadro delimitador cuando no queda ninguno, por lo que las etiquetas se mantienen válidas pero esas filas no llevan detalles de máscara.
Solución: Vuelve a exportar las anotaciones con segmentaciones de polígonos, decodifica las máscaras RLE a polígonos antes de ejecutar convert_coco(), o corrige cualquier valor de segmentation mal formado.
Vacíos en los ID de clase en las etiquetas convertidas#
Si los ID 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 de category_id no contiguos.
Solución: Verifica que los ID de clase en tus archivos de .txt coincidan con el diccionario de names en dataset.yaml. Remapea los ID a valores contiguos si es necesario.
Para obtener todos los detalles de la API y las descripciones de los parámetros, consulta la referencia de la API de convert_coco.
FAQ#
Usa la función
convert_coco()de Ultralytics para convertir las anotaciones COCO JSON al formato YOLO de.txt. Configuracls91to80=Falsepara 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 tus archivos de etiquetas para que
labels/refleje el directorio deimages/, y luego crea un archivodataset.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 dentro desave_dir/labels/(p. ej.,save_dir/labels/train/) en lugar de hacerlo directamente en ellabels/train/de tu conjunto de datos junto aimages/train/. YOLO espera que las etiquetas se sitúen de forma paralela a las imágenes; por ejemplo,images/train/img.jpgnecesitalabels/train/img.txt. Mueve tus etiquetas convertidas para que coincidan con esta estructura. Consulta cómo corregir la estructura de directorios.El parámetro
cls91to80controla cómo se mapean los valores decategory_idde COCO a los ID de clase de YOLO. Cuando se establece enTrue(predeterminado), aplica la tabla de búsquedacoco91_to_coco80_class()diseñada para el conjunto de datos COCO estándar, el cual tiene 80 clases con ID no contiguos (1-90). Para conjuntos de datos personalizados, configura siemprecls91to80=False; esto simplemente resta 1 a cadacategory_idpara crear ID de clase indexados desde cero.No con el flujo de trabajo de entrenamiento de YOLO actual: las anotaciones deben estar en formato YOLO de
.txtcon un archivo por imagen. Usaconvert_coco()para convertir tu COCO JSON primero y, a continuación, sigue esta guía para organizar y entrenar. Para obtener más información sobre los formatos compatibles, consulta los formatos de conjuntos de datos.Sí, usa
use_segments=Trueal llamar aconvert_coco()para incluir máscaras de segmentación de polígonos en las etiquetas YOLO convertidas. Esto produce 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)Usa
use_keypoints=Truepara convertir anotaciones de puntos clave de COCO para el 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_segmentscomouse_keypointsse configuran enTrue, solo se escribirán los puntos clave en los archivos de etiquetas; los segmentos se ignorarán silenciosamente.