Come convertire le annotazioni COCO nel formato YOLO#
L'addestramento di modelli Ultralytics YOLO richiede annotazioni in formato YOLO, ma molti popolari strumenti di annotazione esportano invece in formato COCO JSON. Questa guida mostra come convertire le annotazioni COCO in formato YOLO e iniziare l'addestramento di modelli di rilevamento oggetti, segmentazione di istanze e stima della posa.
Per addestrare direttamente su COCO JSON senza generare file .txt, consulta Addestra YOLO su COCO JSON senza conversione.
Perché convertire da COCO a YOLO?#
Il formato COCO JSON memorizza tutte le annotazioni in un unico file, mentre YOLO utilizza un file di testo per immagine con coordinate normalizzate. La conversione è necessaria perché:
- I modelli YOLO richiedono file di etichette
.txtcon un file per immagine, contenenteclass x_center y_center width heightin coordinate normalizzate. - COCO JSON utilizza le coordinate dei pixel nel formato
[x_min, y_min, width, height]con un singolo file JSON per tutte le immagini. - Gli ID delle classi differiscono — COCO utilizza valori arbitrari di
category_id, mentre YOLO richiede ID di classe indicizzati a zero.
| Funzionalità | COCO JSON | YOLO TXT |
|---|---|---|
| Struttura | Singolo file JSON per tutte le immagini | Un file .txt per immagine |
| Formato Bbox | [x_min, y_min, width, height] in pixel | class x_center y_center width height normalizzato (0-1) |
| ID di classe | category_id (può iniziare da qualsiasi numero) | Indicizzati a zero (inizia da 0) |
| Segmentazione | Array di poligoni nel campo segmentation | Coordinate del poligono dopo l'ID di classe |
| Punti chiave (Keypoints) | [x, y, visibility, ...] in pixel | [x, y, visibility, ...] normalizzato |
Avvio rapido#
Il modo più veloce per convertire le annotazioni COCO e iniziare l'addestramento:
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)
)Dopo la conversione, organizza la struttura delle directory, crea un dataset.yaml e avvia l'addestramento. Consulta la guida passo-passo completa di seguito.
Il valore predefinito di cls91to80=True è progettato solo per il dataset COCO standard con 80 classi di oggetti, che mappa 91 ID di categoria non continui in 80 ID di classe continui. Per qualsiasi dataset personalizzato, devi impostare cls91to80=False — altrimenti gli ID delle classi verranno mappati silenziosamente in modo errato e il tuo modello imparerà classi sbagliate.
Guida alla conversione passo-passo#
1. Prepara il tuo dataset COCO#
Un tipico dataset in formato COCO esportato dagli strumenti di annotazione ha la seguente struttura:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ ├── img_002.jpg
│ │ └── ...
│ └── val/
│ ├── img_100.jpg
│ └── ...
└── annotations/
├── instances_train.json
└── instances_val.jsonOgni file JSON segue la specifica del formato dati COCO con tre campi obbligatori: images, annotations e 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. Converti le annotazioni#
Usa la funzione convert_coco() per convertire le tue annotazioni COCO JSON nel formato YOLO .txt:
from ultralytics.data.converter import convert_coco
convert_coco(
labels_dir="my_dataset/annotations/",
save_dir="my_dataset/converted/",
cls91to80=False,
)convert_coco() scrive un file .txt per ogni immagine annotata in una sottodirectory labels/ denominata come ciascun file JSON, con il prefisso instances_ rimosso (quindi instances_train.json produce labels/train/). Le immagini senza annotazioni vengono saltate e non ricevono alcun file di etichetta, quindi l'albero di labels/ potrebbe non rispecchiare ogni immagine:
my_dataset/converted/
└── labels/
├── train/ # from instances_train.json
│ ├── img_001.txt
│ └── ...
└── val/ # from instances_val.json
└── ...convert_coco() non sovrascrive mai un save_dir esistente: se my_dataset/converted/ esiste già, una nuova esecuzione scrive invece su my_dataset/converted-2/. Elimina l'output precedente (o cambia save_dir) prima di rieseguire, altrimenti i passaggi successivi leggeranno etichette obsolete.
3. Organizza la struttura delle directory#
Dopo la conversione, i file di etichette devono essere posizionati accanto alle tue immagini. YOLO si aspetta una directory labels/ che specchi la directory 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 tua struttura del dataset finale dovrebbe apparire così:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ └── ...
│ └── val/
│ └── ...
├── labels/
│ ├── train/
│ │ ├── img_001.txt
│ │ └── ...
│ └── val/
│ └── ...
└── dataset.yaml4. Crea dataset.yaml#
Crea un file di configurazione dataset.yaml che mappa le tue categorie COCO ai nomi delle classi YOLO. Questo file indica a YOLO dove si trovano i tuoi dati e quali classi rilevare:
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)Il file YAML risultante:
path: /absolute/path/to/my_dataset
train: images/train
val: images/val
names:
0: helmet
1: vestPer maggiori dettagli sul formato YAML del dataset, consulta la guida alla configurazione del dataset.
5. Addestra il tuo modello YOLO#
Con il tuo dataset convertito pronto, addestra un modello 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)Per suggerimenti sull'addestramento e best practice, consulta la guida all'addestramento del modello.
6. Verifica la tua conversione#
Prima dell'addestramento, controlla a campione alcuni file di etichette per confermare che gli ID di classe e le coordinate siano corretti:
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}"Se vedi ID di classe negativi, probabilmente il tuo COCO JSON utilizza category_id a partire da 0. Aggiungi 1 a tutti i valori di category_id nel tuo JSON prima di eseguire convert_coco(), poiché mappa gli ID di classe come category_id - 1.
Risoluzione dei problemi comuni#
ID di classe errati dopo la conversione#
Se il tuo modello si addestra ma rileva classi di oggetti errate, probabilmente stai usando cls91to80=True (predefinito) su un dataset personalizzato. Questo mappa i tuoi valori di category_id tramite la tabella di ricerca COCO 91-a-80, che è corretta solo per il dataset COCO standard.
Soluzione: Usa sempre cls91to80=False per i dataset personalizzati.
Nessuna etichetta trovata durante l'addestramento#
Se l'addestramento mostra WARNING: No labels found o 0 images, N backgrounds, i tuoi file di etichette non si trovano nella directory prevista. convert_coco() salva le etichette in una directory di output separata (ad es. save_dir/labels/train/), ma YOLO si aspetta labels/ in parallelo a images/ all'interno della directory del tuo dataset.
Soluzione: Sposta i file di etichette in modo che corrispondano alla struttura di directory prevista. Assicurati che labels/train/ sia un elemento sussidiario di images/train/.
KeyError durante la conversione#
Se ricevi KeyError: 'bbox' o errori simili durante l'esecuzione di convert_coco(), probabilmente il tuo labels_dir contiene file JSON che non sono istanze (ad es. captions_train2017.json) e che hanno una struttura di annotazione diversa.
Soluzione: Inserisci solo file JSON di annotazione di istanze (ad es. instances_train2017.json) in labels_dir.
File di etichette vuoti dopo la conversione#
Se la conversione viene completata ma i file .txt sono vuoti o mancanti, tutte le annotazioni potrebbero avere iscrowd: 1 (comune con le maschere generate da SAM), oppure i bounding box hanno larghezza o altezza pari a zero.
Soluzione: Ispeziona le tue annotazioni JSON per verificare la presenza di valori iscrowd. Se usi le maschere SAM, preelabora il JSON per impostare iscrowd: 0.
Poligoni a forma di box da annotazioni di maschera#
Se use_segments=True registra annotations without a usable polygon, alcune annotazioni non contengono alcun valore segmentation, oppure ne contengono uno che non è un elenco di almeno tre coppie di coordinate. Le cause più comuni sono le esportazioni basate sul solo rilevamento, che lasciano il campo mancante o vuoto, e la codifica a lunghezza di esecuzione COCO ({"counts": ..., "size": ...}), scritta da esportatori di maschere di bit come SAM; un elenco di coordinate piatte senza un elenco di poligoni di inclusione, contorni a uno e due punti e altri valori non formattati vengono gestiti allo stesso modo. Un'annotazione mantiene i poligoni rimanenti e ripiega su una riga di segmento modellata come il suo bounding box quando nessuno è presente, in modo che le etichette rimangano valide ma quelle righe non contengano dettagli sulla maschera.
Soluzione: Esporta nuovamente le annotazioni con segmentazioni di poligoni, decodifica le maschere RLE in poligoni prima di eseguire convert_coco(), o correggi eventuali valori segmentation non formattati correttamente.
Lacune negli ID di classe nelle etichette convertite#
Se gli ID delle classi nei file di etichette non sono continui (ad es. 0, 4, 9 anziché 0, 1, 2), il tuo strumento di annotazione utilizza valori category_id non continui.
Soluzione: Verifica che gli ID delle classi nei tuoi file .txt corrispondano al dizionario names in dataset.yaml. Rimappa gli ID a valori continui se necessario.
Per i dettagli completi sull'API e le descrizioni dei parametri, consulta la riferimento API di convert_coco.
FAQ#
Usa la funzione
convert_coco()di Ultralytics per convertire le annotazioni COCO JSON nel formato YOLO.txt. Impostacls91to80=Falseper i dataset personalizzati:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="path/to/annotations/", save_dir="output/", cls91to80=False)Dopo la conversione, riorganizza i tuoi file di etichette in modo che
labels/specchi la directoryimages/, quindi crea un filedataset.yaml. Consulta la guida passo-passo per il flusso di lavoro completo.Questo accade perché
convert_coco()salva le etichette in una sottodirectory all'interno disave_dir/labels/(ad es.save_dir/labels/train/) anziché direttamente inlabels/train/del tuo dataset insieme aimages/train/. YOLO si aspetta che le etichette siano posizionate parallelamente alle immagini — ad esempio,images/train/img.jpgrichiedelabels/train/img.txt. Sposta le etichette convertite per farle corrispondere a questa struttura. Vedi correzione della struttura delle directory.Il parametro
cls91to80controlla come i valori dicategory_iddi COCO vengono mappati agli ID di classe YOLO. Quando è impostato suTrue(predefinito), applica la tabella di ricercacoco91_to_coco80_class()progettata per il dataset COCO standard, che ha 80 classi con ID non continui (1-90). Per i dataset personalizzati, imposta semprecls91to80=False— questo sottrae semplicemente 1 da ciascuncategory_idper creare ID di classe indicizzati a zero.Non con l'attuale pipeline di addestramento YOLO: le annotazioni devono essere nel formato YOLO
.txtcon un file per immagine. Usaconvert_coco()per convertire prima il tuo COCO JSON, quindi segui questa guida per organizzare e addestrare. Per ulteriori informazioni sui formati supportati, consulta i formati dei dataset.Sì, usa
use_segments=Truequando chiamiconvert_coco()per includere le maschere di segmentazione dei poligoni nelle etichette YOLO convertite. Questo produce file di etichette compatibili con i modelli di segmentazione YOLO:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_segments=True, cls91to80=False)Usa
use_keypoints=Trueper convertire le annotazioni dei keypoint COCO per l'addestramento della stima della posa:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_keypoints=True, cls91to80=False)Nota che se sia
use_segmentscheuse_keypointssono impostati suTrue, solo i keypoint verranno scritti nei file di etichette — i segmenti verranno ignorati silenziosamente.