Come convertire le annotazioni COCO nel formato YOLO#
Per addestrare i modelli Ultralytics YOLO servono annotazioni in formato YOLO, ma molti strumenti diffusi di annotazione esportano invece nel formato COCO JSON. Questa guida illustra come convertire le annotazioni COCO nel formato YOLO e avviare l'addestramento di modelli di rilevamento di oggetti, segmentazione di istanze e stima della posa.
Per addestrare direttamente su COCO JSON senza generare file .txt, consulta Addestrare 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 usa un file di testo per immagine con coordinate normalizzate. La conversione è necessaria perché:
- I modelli YOLO richiedono file di etichette
.txt, uno per immagine, contenenticlass x_center y_center width heightcon coordinate normalizzate. - COCO JSON usa coordinate in pixel nel formato
[x_min, y_min, width, height]e un unico file JSON per tutte le immagini. - Gli ID delle classi sono diversi — COCO usa valori
category_idarbitrari, mentre YOLO richiede ID delle classi a partire da zero.
| Caratteristica | COCO JSON | YOLO TXT |
|---|---|---|
| Struttura | Un unico 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 delle classi | category_id (può iniziare da qualsiasi numero) | Indicizzato a partire da zero (inizia da 0) |
| Segmentazione | Array di poligoni nel campo segmentation | Coordinate dei poligoni dopo l'ID della classe |
| Keypoint | [x, y, visibility, ...] in pixel | [x, y, visibility, ...] normalizzato |
Guida rapida#
Il modo più rapido per convertire le annotazioni COCO e iniziare l'addestramento:
from ultralytics.data.converter import convert_coco
convert_coco(
labels_dir="my_dataset/annotations/", # directory contenente i file JSON
save_dir="my_dataset/converted/", # dove salvare le etichette convertite
cls91to80=False, # imposta False per i dataset personalizzati (vedi l'avviso qui sotto)
)Dopo la conversione, organizza la struttura delle directory, crea un dataset.yaml e avvia l'addestramento. Consulta la guida dettagliata passo passo qui sotto.
Il valore predefinito di cls91to80=True è progettato solo per il dataset COCO standard, con 80 classi di oggetti, e mappa 91 ID di categoria non contigui su 80 ID di classe contigui. Per qualsiasi dataset personalizzato, devi impostare cls91to80=False; altrimenti, gli ID delle classi verranno mappati in modo errato senza alcun avviso e il modello apprenderà classi sbagliate.
Guida dettagliata alla conversione#
1. Preparare il 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 le specifiche del formato dati COCO e include 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. Convertire le annotazioni#
Usa la funzione convert_coco() per convertire le 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 in base a ciascun file JSON, rimuovendo il prefisso instances_ (quindi instances_train.json genera labels/train/). Le immagini senza annotazioni vengono ignorate e non ricevono alcun file di etichette, quindi la struttura labels/ potrebbe non rispecchiare tutte le immagini:
my_dataset/converted/
├── images/ # created but left empty
└── 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 in my_dataset/converted-2/. Elimina l'output precedente (oppure modifica save_dir) prima di eseguire di nuovo, altrimenti i passaggi successivi leggeranno etichette obsolete.
3. Organizzare la struttura delle directory#
Dopo la conversione, i file delle etichette devono essere collocati insieme alle immagini. YOLO si aspetta una directory labels/ che rispecchi la directory images/:
import shutil
from pathlib import Path
converted_dir = Path("my_dataset/converted/labels")
dataset_dir = Path("my_dataset")
# convert_coco assegna a ogni sottodirectory il nome del relativo file JSON (senza il prefisso "instances_"),
# quindi esegui un ciclo sulle sottodirectory effettive invece di dare per scontato che si chiamino "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 struttura finale del dataset dovrebbe essere simile a questa:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ └── ...
│ └── val/
│ └── ...
├── labels/
│ ├── train/
│ │ ├── img_001.txt
│ │ └── ...
│ └── val/
│ └── ...
└── dataset.yaml4. Creare dataset.yaml#
Crea un file di configurazione dataset.yaml che mappi le categorie COCO sui nomi delle classi YOLO. Questo file indica a YOLO dove si trovano i dati e quali classi rilevare:
import json
from pathlib import Path
import yaml
# Leggi le categorie dal file JSON COCO
with open("my_dataset/annotations/instances_train.json") as f:
coco = json.load(f)
# Crea nomi di classi corrispondenti all'output di 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 mappa gli ID delle classi come category_id - 1, quindi category_id deve
# iniziare da 1. Se le categorie iniziano da 0, aggiungi prima 1 a ogni 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)Il file YAML risultante:
path: /absolute/path/to/my_dataset
train: images/train
val: images/val
names:
0: helmet
1: vestPer ulteriori dettagli sul formato YAML dei dataset, consulta la guida alla configurazione dei dataset.
5. Addestrare il modello YOLO#
Una volta pronto il dataset convertito, addestra un modello YOLO:
from ultralytics import YOLO
model = YOLO("yolo26n.pt") # carica un modello preaddestrato
results = model.train(data="my_dataset/dataset.yaml", epochs=100, imgsz=640)Per suggerimenti e best practice sull'addestramento, consulta la guida all'addestramento dei modelli.
6. Verificare la conversione#
Prima dell'addestramento, controlla alcuni file di etichette per verificare che gli ID delle classi 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 trovi ID di classe negativi, è probabile che il file JSON COCO usi category_id a partire da 0. Aggiungi 1 a tutti i valori category_id nel file JSON prima di eseguire convert_coco(), poiché quest'ultimo mappa gli ID delle classi come category_id - 1.
Risoluzione dei problemi più comuni#
ID delle classi errati dopo la conversione#
Se il modello viene addestrato ma rileva classi di oggetti errate, probabilmente stai usando cls91to80=True (predefinito) su un dataset personalizzato. Questo valore mappa i valori category_id tramite la tabella di corrispondenza COCO da 91 a 80, valida solo per il dataset COCO standard. Un category_id senza una corrispondenza COCO-80 non viene mappato e genera TypeError: must be real number, not NoneType durante la conversione, invece di produrre etichette errate.
Soluzione: per i dataset personalizzati, usa sempre cls91to80=False.
Nessuna etichetta trovata durante l'addestramento#
Se la scansione delle etichette segnala 0 images, N backgrounds e l'addestramento si interrompe con ValueError: train: No labels found in .../labels/train.cache, i file delle 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 che labels/ sia parallela a images/ all'interno della directory del dataset.
Soluzione: sposta i file delle etichette in modo che rispettino la struttura delle directory prevista. Assicurati che labels/train/ sia allo stesso livello di images/train/.
KeyError durante la conversione#
Se ricevi KeyError: 'bbox' o errori simili quando esegui convert_coco(), è probabile che labels_dir contenga file JSON che non riguardano istanze (ad es. captions_train2017.json) e hanno una struttura delle annotazioni diversa.
Soluzione: inserisci in labels_dir solo file JSON con annotazioni di istanza (ad es. instances_train2017.json).
File di etichette vuoti dopo la conversione#
Se la conversione termina ma i file .txt sono vuoti o mancano, tutte le annotazioni potrebbero avere iscrowd: 1 (caso comune per le maschere generate con SAM) oppure le bounding box potrebbero avere larghezza o altezza pari a zero. Eseguire la conversione con use_keypoints=True su un'esportazione che contiene solo rilevamenti produce lo stesso risultato, perché le annotazioni senza il campo keypoints vengono ignorate del tutto.
Soluzione: controlla i valori iscrowd nelle annotazioni JSON. Se usi maschere SAM, preelabora il file JSON impostando iscrowd: 0. Se hai passato use_keypoints=True, verifica che le annotazioni contengano effettivamente keypoints.
Poligoni a forma di riquadro da annotazioni di maschere#
Se use_segments=True registra annotations without a usable polygon, alcune annotazioni non contengono un valore segmentation oppure contengono un valore che non è una lista di almeno tre coppie di coordinate. Le cause più comuni sono le esportazioni che includono solo rilevamenti, in cui il campo è assente o vuoto, e la codifica COCO a lunghezza variabile ({"counts": ..., "size": ...}), usata dagli esportatori di maschere binarie come SAM; anche una lista di coordinate lineare senza una lista di poligoni, contorni con uno o due punti e altri valori malformati vengono gestiti allo stesso modo. Un'annotazione mantiene i poligoni validi e, se non ne rimane nessuno, usa una riga di segmentazione con la forma della relativa bounding box: le etichette restano valide, ma queste righe non contengono dettagli della maschera.
Soluzione: riesporta le annotazioni con segmentazioni poligonali, decodifica le maschere RLE in poligoni prima di eseguire convert_coco() oppure correggi i valori segmentation malformati.
Lacune negli ID delle classi nelle etichette convertite#
Se gli ID delle classi nei file delle etichette non sono contigui (ad es. 0, 4, 9 anziché 0, 1, 2), lo strumento di annotazione usa valori category_id non contigui.
Soluzione: verifica che gli ID delle classi nei file .txt corrispondano al dizionario names in dataset.yaml. Se necessario, rimappa gli ID su valori contigui.
Per tutti i dettagli dell'API e le descrizioni dei parametri, consulta la documentazione di riferimento dell'API convert_coco.
Domande frequenti#
Usa la funzione
convert_coco()di Ultralytics per convertire le annotazioni COCO JSON nel formato YOLO.txt. Per i dataset personalizzati, impostacls91to80=False:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="path/to/annotations/", save_dir="output/", cls91to80=False)Dopo la conversione, riorganizza i file delle etichette in modo che
labels/rispecchi la directoryimages/, quindi crea un filedataset.yaml. Per il flusso di lavoro completo, consulta la guida dettagliata passo passo.Succede perché
convert_coco()salva le etichette in una sottodirectory disave_dir/labels/(ad es.save_dir/labels/train/) invece che direttamente inlabels/train/del dataset, accanto aimages/train/. YOLO si aspetta che le etichette siano parallele alle immagini: ad esempio,images/train/img.jpgdeve corrispondere alabels/train/img.txt. Sposta le etichette convertite in modo da rispettare questa struttura. Consulta la guida per correggere la struttura delle directory.Il parametro
cls91to80controlla la mappatura dei valori COCOcategory_idsugli ID delle classi YOLO. QuandoTrue(valore predefinito), applica la tabella di corrispondenzacoco91_to_coco80_class(), progettata per il dataset COCO standard, che contiene 80 classi con ID non contigui (1-90). Per i dataset personalizzati, imposta semprecls91to80=False: questa opzione sottrae semplicemente 1 a ognicategory_idper creare ID di classe indicizzati a partire da zero.Non senza codice personalizzato. La pipeline di addestramento predefinita si aspetta etichette YOLO
.txt, con un file per immagine; puoi quindi eseguireconvert_coco()e seguire questa guida dettagliata passo passo, oppure creare una sottoclasse del dataset per analizzare al volo il file COCO JSON: consulta Addestrare YOLO su COCO JSON senza conversione. Per ulteriori informazioni sui formati supportati, consulta i formati dei dataset.Sì, usa
use_segments=Truequando chiamiconvert_coco()per includere maschere di segmentazione poligonali nelle etichette YOLO convertite. In questo modo otterrai file di etichette compatibili con i modelli YOLO di segmentazione: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 COCO con keypoint e addestrare i modelli per la stima della posa:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_keypoints=True, cls91to80=False)Se sia
use_segmentssiause_keypointssono impostati suTrue, nei file delle etichette verranno scritti solo i keypoint: i segmenti verranno ignorati senza alcun avviso.