Come convertire le annotazioni COCO nel formato YOLO#
L'addestramento dei modelli Ultralytics YOLO richiede annotazioni nel formato YOLO, ma molti strumenti di annotazione diffusi esportano invece nel formato COCO JSON. Questa guida mostra come convertire le annotazioni COCO nel formato YOLO e iniziare ad addestrare modelli di rilevamento degli oggetti, segmentazione delle 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 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 coordinate in pixel nel formato
[x_min, y_min, width, height], con un singolo file JSON per tutte le immagini. - Gli ID delle classi sono diversi — COCO utilizza valori
category_idarbitrari, mentre YOLO richiede ID delle classi indicizzati a partire da zero.
| Caratteristica | COCO JSON | YOLO TXT |
|---|---|---|
| Struttura | Un 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 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, ...] normalizzati |
Avvio rapido#
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 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 completa passo passo qui sotto.
Il valore predefinito cls91to80=True è progettato solo per il dataset COCO standard con 80 classi di oggetti, che mappa 91 ID di categoria non contigui in 80 ID di classe contigui. Per qualsiasi dataset personalizzato, devi impostare cls91to80=False; in caso contrario, gli ID delle classi verranno mappati in modo errato senza alcun avviso e il modello apprenderà classi sbagliate.
Guida alla conversione passo passo#
1. Prepara il dataset COCO#
Un dataset tipico in formato COCO esportato dagli strumenti di annotazione presenta 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 annotazioni COCO JSON nel formato .txt di YOLO:
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, rimuovendo il prefisso instances_ (quindi instances_train.json produce 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 invece in my_dataset/converted-2/. Elimina l'output precedente (o modifica save_dir) prima di eseguire nuovamente la procedura, altrimenti i passaggi successivi leggeranno etichette obsolete.
3. Organizza la struttura delle directory#
Dopo la conversione, i file delle etichette devono essere posizionati accanto 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 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 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. Crea dataset.yaml#
Crea un file di configurazione dataset.yaml che associ le categorie COCO ai 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
# 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 ulteriori dettagli sul formato YAML del dataset, consulta la guida alla configurazione del dataset.
5. Addestra il modello YOLO#
Con il 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 e best practice sull'addestramento, consulta la guida all'addestramento dei modelli.
6. Verifica 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 visualizzi ID di classe negativi, probabilmente il tuo COCO JSON utilizza category_id a partire da 0. Aggiungi 1 a tutti i valori category_id nel JSON prima di eseguire convert_coco(), poiché quest'ultimo mappa gli ID delle classi come category_id - 1.
Risoluzione dei problemi comuni#
ID delle classi errati dopo la conversione#
Se il modello si addestra 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 da 91 a 80 di COCO, corretta solo per il dataset COCO standard. Un category_id senza un elemento corrispondente in COCO-80 non viene mappato e genera TypeError: must be real number, not NoneType durante la conversione, invece di produrre etichette errate.
Soluzione: usa sempre cls91to80=False per i dataset personalizzati.
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 esempio save_dir/labels/train/), ma YOLO si aspetta labels/ allo stesso livello di images/ all'interno della directory del dataset.
Soluzione: sposta i file delle etichette in modo da rispettare la struttura delle directory prevista. Assicurati che labels/train/ sia una directory parallela a images/train/.
KeyError durante la conversione#
Se ricevi KeyError: 'bbox' o errori simili eseguendo convert_coco(), probabilmente labels_dir contiene file JSON non relativi alle istanze (ad esempio captions_train2017.json) con una struttura delle annotazioni diversa.
Soluzione: inserisci nella directory labels_dir solo file JSON di annotazione delle istanze (ad esempio instances_train2017.json).
File delle etichette vuoti dopo la conversione#
Se la conversione termina ma i file .txt sono vuoti o mancanti, tutte le annotazioni potrebbero avere iscrowd: 1 (situazione comune con le maschere generate da SAM), oppure le bounding box potrebbero avere larghezza o altezza pari a zero. L'esecuzione con use_keypoints=True su un'esportazione contenente solo dati di rilevamento produce lo stesso risultato, perché le annotazioni senza un campo keypoints vengono ignorate completamente.
Soluzione: esamina le annotazioni JSON alla ricerca di valori iscrowd. Se usi maschere SAM, preelabora il JSON per impostare iscrowd: 0. Se hai passato use_keypoints=True, verifica che le annotazioni contengano effettivamente keypoints.
Poligoni a forma di riquadro dalle annotazioni delle maschere#
Se use_segments=True registra annotations without a usable polygon, alcune annotazioni non contengono alcun valore segmentation, oppure ne contengono uno che non è una lista di almeno tre coppie di coordinate. Le cause più comuni sono le esportazioni contenenti solo dati di rilevamento, che lasciano il campo mancante o vuoto, e la codifica run-length di COCO ({"counts": ..., "size": ...}), scritta dagli esportatori di bitmask come SAM; anche una lista piatta di coordinate senza una lista di poligoni contenitore, contorni costituiti da uno o due punti e altri valori malformati vengono gestiti allo stesso modo. Un'annotazione conserva i poligoni rimasti e, se non ne rimane alcuno, ricorre a una riga di segmento con la forma della relativa bounding box, così le etichette restano valide ma tali 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.
Intervalli negli ID delle classi nelle etichette convertite#
Se gli ID delle classi nei file delle etichette non sono contigui (ad esempio 0, 4, 9 invece di 0, 1, 2), il tuo strumento di annotazione utilizza 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 i dettagli completi sull'API e la descrizione dei parametri, consulta il riferimento API di convert_coco.
FAQ#
Usa la funzione
convert_coco()di Ultralytics per convertire le annotazioni COCO JSON nel formato.txtdi YOLO. 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 file delle etichette in modo che
labels/rispecchi 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 dentrosave_dir/labels/(ad esempiosave_dir/labels/train/) anziché direttamente inlabels/train/del dataset, accanto aimages/train/. YOLO si aspetta che le etichette siano parallele alle immagini: ad esempio,images/train/img.jpgdeve contenerelabels/train/img.txt. Sposta le etichette convertite per rispettare questa struttura. Consulta Correggere la struttura delle directory.Il parametro
cls91to80controlla il modo in cui i valori COCOcategory_idvengono mappati negli ID delle classi YOLO. QuandoTrue(predefinito), applica la tabella di corrispondenzacoco91_to_coco80_class()progettata per il dataset COCO standard, che ha 80 classi con ID non contigui (1-90). Per i dataset personalizzati, imposta semprecls91to80=False; questo sottrae semplicemente 1 da ognicategory_idper creare ID delle classi indicizzati a partire da zero.Non senza codice personalizzato. La pipeline di addestramento predefinita si aspetta etichette YOLO
.txtcon un file per immagine; quindi puoi eseguireconvert_coco()e seguire questa guida passo passo, oppure creare una sottoclasse del dataset per analizzare COCO JSON al volo — 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 le maschere di segmentazione poligonali nelle etichette YOLO convertite. In questo modo vengono prodotti 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 dei keypoint COCO per l'addestramento alla stima della posa:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_keypoints=True, cls91to80=False)Tieni presente che, se sia
use_segmentssiause_keypointssono impostati suTrue, nei file delle etichette verranno scritti solo i keypoint; i segmenti verranno ignorati senza alcun avviso.