YOLO Vision 2026:

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.

Preferisci saltare la conversione?

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 .txt con un file per immagine, contenente class x_center y_center width height in 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 JSONYOLO TXT
StrutturaSingolo file JSON per tutte le immaginiUn file .txt per immagine
Formato Bbox[x_min, y_min, width, height] in pixelclass x_center y_center width height normalizzato (0-1)
ID di classecategory_id (può iniziare da qualsiasi numero)Indicizzati a zero (inizia da 0)
SegmentazioneArray di poligoni nel campo segmentationCoordinate 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.

Dataset personalizzati: usa sempre `cls91to80=False`

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.json

Ogni 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:

Converti COCO in 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() 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
        └── ...
L'esecuzione ripetuta crea una nuova cartella di output

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.yaml

4. 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: vest

Per 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:

Addestra sui dati COCO convertiti
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}"
Suggerimento

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. Imposta cls91to80=False per 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 directory images/, quindi crea un file dataset.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 di save_dir/labels/ (ad es. save_dir/labels/train/) anziché direttamente in labels/train/ del tuo dataset insieme a images/train/. YOLO si aspetta che le etichette siano posizionate parallelamente alle immagini — ad esempio, images/train/img.jpg richiede labels/train/img.txt. Sposta le etichette convertite per farle corrispondere a questa struttura. Vedi correzione della struttura delle directory.

  • Il parametro cls91to80 controlla come i valori di category_id di COCO vengono mappati agli ID di classe YOLO. Quando è impostato su True (predefinito), applica la tabella di ricerca coco91_to_coco80_class() progettata per il dataset COCO standard, che ha 80 classi con ID non continui (1-90). Per i dataset personalizzati, imposta sempre cls91to80=False — questo sottrae semplicemente 1 da ciascun category_id per creare ID di classe indicizzati a zero.

  • Non con l'attuale pipeline di addestramento YOLO: le annotazioni devono essere nel formato YOLO .txt con un file per immagine. Usa convert_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=True quando chiami convert_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=True per 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_segments che use_keypoints sono impostati su True, solo i keypoint verranno scritti nei file di etichette — i segmenti verranno ignorati silenziosamente.

Commenti