YOLO Vision 2026 :

Comment convertir des annotations COCO au format YOLO#

L'entraînement de modèles Ultralytics YOLO nécessite des annotations au format YOLO, mais de nombreux outils d'annotation populaires exportent plutôt au format COCO JSON. Ce guide t'explique comment convertir tes annotations COCO au format YOLO et commencer à entraîner des modèles de détection d'objets, de segmentation d'instances et d'estimation de pose.

Tu préfères éviter la conversion ?

Pour t'entraîner directement sur COCO JSON sans générer de fichiers .txt, consulte Train YOLO on COCO JSON Without Conversion.

Pourquoi convertir de COCO vers YOLO ?#

Le format COCO JSON stocke toutes les annotations dans un seul fichier, tandis que YOLO utilise un fichier texte par image avec des coordonnées normalisées. La conversion est nécessaire car :

  • Les modèles YOLO nécessitent des fichiers d'étiquettes .txt avec un fichier par image, contenant class x_center y_center width height en coordonnées normalisées.
  • COCO JSON utilise des coordonnées en pixels au format [x_min, y_min, width, height] avec un seul fichier JSON pour toutes les images.
  • Les identifiants de classe diffèrent — COCO utilise des valeurs arbitraires category_id, tandis que YOLO requiert des identifiants de classe indexés à zéro.
FonctionnalitéCOCO JSONYOLO TXT
StructureFichier JSON unique pour toutes les imagesUn fichier .txt par image
Format de bbox[x_min, y_min, width, height] en pixelsclass x_center y_center width height normalisé (0-1)
ID de classecategory_id (peut commencer à n'importe quel nombre)Indexés à zéro (commencent à 0)
SegmentationTableaux de polygones dans le champ segmentationCoordonnées du polygone après l'ID de classe
Keypoints[x, y, visibility, ...] en pixels[x, y, visibility, ...] normalisé

Démarrage rapide#

La méthode la plus rapide pour convertir des annotations COCO et commencer l'entraînement :

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)
)

Après la conversion, organise ta structure de répertoires, crée un dataset.yaml et commence l'entraînement. Consulte le guide étape par étape complet ci-dessous.

Jeux de données personnalisés : utilise toujours `cls91to80=False`

La valeur par défaut cls91to80=True est conçue uniquement pour le jeu de données COCO standard avec 80 classes d'objets, qui associe 91 ID de catégorie non contigus à 80 ID de classe contigus. Pour tout jeu de données personnalisé, tu dois définir cls91to80=False, sous peine de voir tes ID de classe mappés incorrectement de manière silencieuse et ton modèle apprendre de mauvaises classes.

Guide de conversion étape par étape#

1. Prépare ton jeu de données COCO#

Un jeu de données typique au format COCO exporté depuis des outils d'annotation a la structure suivante :

my_dataset/
├── images/
│   ├── train/
│   │   ├── img_001.jpg
│   │   ├── img_002.jpg
│   │   └── ...
│   └── val/
│       ├── img_100.jpg
│       └── ...
└── annotations/
    ├── instances_train.json
    └── instances_val.json

Chaque fichier JSON suit la spécification du format de données COCO avec trois champs obligatoires — images, annotations et 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. Convertis les annotations#

Utilise la fonction convert_coco() pour convertir tes annotations COCO JSON au format YOLO .txt :

Convertir COCO au format YOLO
from ultralytics.data.converter import convert_coco

convert_coco(
    labels_dir="my_dataset/annotations/",
    save_dir="my_dataset/converted/",
    cls91to80=False,
)

convert_coco() écrit un fichier .txt par image annotée dans un sous-répertoire labels/ nommé d'après chaque fichier JSON, avec le préfixe instances_ supprimé (de sorte que instances_train.json produit labels/train/). Les images sans annotations sont ignorées et n'obtiennent aucun fichier d'étiquette, de sorte que l'arborescence labels/ peut ne pas refléter chaque image :

my_dataset/converted/
└── labels/
    ├── train/   # from instances_train.json
    │   ├── img_001.txt
    │   └── ...
    └── val/     # from instances_val.json
        └── ...
Réexécuter crée un nouveau dossier de sortie

convert_coco() n'écrase jamais un save_dir existant : si my_dataset/converted/ existe déjà, une nouvelle exécution écrit plutôt dans my_dataset/converted-2/. Supprime la sortie précédente (ou modifie save_dir) avant de réexécuter, sinon les étapes suivantes liront des étiquettes obsolètes.

3. Organise la structure de répertoire#

Après la conversion, les fichiers d'étiquettes doivent être placés à côté de tes images. YOLO attend un répertoire labels/ qui reflète le répertoire 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))

Ta structure de jeu de données finale devrait ressembler à ceci :

my_dataset/
├── images/
│   ├── train/
│   │   ├── img_001.jpg
│   │   └── ...
│   └── val/
│       └── ...
├── labels/
│   ├── train/
│   │   ├── img_001.txt
│   │   └── ...
│   └── val/
│       └── ...
└── dataset.yaml

4. Crée dataset.yaml#

Crée un fichier de configuration dataset.yaml qui associe tes catégories COCO aux noms de classe YOLO. Ce fichier indique à YOLO où se trouvent tes données et quelles classes détecter :

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)

Le fichier YAML résultant :

path: /absolute/path/to/my_dataset
train: images/train
val: images/val
names:
    0: helmet
    1: vest

Pour plus de détails sur le format YAML du jeu de données, consulte le guide de configuration des jeux de données.

5. Entraîne ton modèle YOLO#

Avec ton jeu de données converti prêt, entraîne un modèle YOLO :

Entraîner sur des données COCO converties
from ultralytics import YOLO

model = YOLO("yolo26n.pt")  # load a pretrained model
results = model.train(data="my_dataset/dataset.yaml", epochs=100, imgsz=640)

Pour des conseils d'entraînement et les meilleures pratiques, consulte le guide d'entraînement des modèles.

6. Vérifie ta conversion#

Avant l'entraînement, vérifie quelques fichiers d'étiquettes pour confirmer que les ID de classe et les coordonnées sont corrects :

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}"
Astuce

Si tu vois des identifiants de classe négatifs, ton COCO JSON utilise probablement category_id en commençant à 0. Ajoute 1 à toutes les valeurs category_id dans ton JSON avant d'exécuter convert_coco(), car il mappe les identifiants de classe sous la forme category_id - 1.

Dépannage des problèmes courants#

Mauvais ID de classe après conversion#

Si ton modèle s'entraîne mais détecte de mauvaises classes d'objets, tu utilises probablement cls91to80=True (par défaut) sur un jeu de données personnalisé. Cela mappe tes valeurs category_id via la table de correspondance COCO 91 vers 80, ce qui n'est correct que pour le jeu de données COCO standard.

Solution : Utilise toujours cls91to80=False pour les jeux de données personnalisés.

Aucune étiquette trouvée pendant l'entraînement#

Si l'entraînement affiche WARNING: No labels found ou 0 images, N backgrounds, tes fichiers d'étiquettes ne se trouvent pas dans le répertoire attendu. convert_coco() enregistre les étiquettes dans un répertoire de sortie séparé (par exemple, save_dir/labels/train/), mais YOLO attend labels/ en parallèle de images/ dans le répertoire de ton jeu de données.

Solution : Déplace les fichiers d'étiquettes pour correspondre à la structure de répertoires attendue. Assure-toi que labels/train/ est un voisin de images/train/.

KeyError pendant la conversion#

Si tu obtiens KeyError: 'bbox' ou des erreurs similaires lors de l'exécution de convert_coco(), ton labels_dir contient probablement des fichiers JSON qui ne sont pas des instances (par exemple, captions_train2017.json) et qui ont une structure d'annotation différente.

Solution : Place uniquement les fichiers JSON d'annotation d'instance (par exemple, instances_train2017.json) dans le labels_dir.

Fichiers d'étiquettes vides après conversion#

Si la conversion se termine mais que les fichiers .txt sont vides ou manquants, toutes les annotations ont peut-être iscrowd: 1 (ce qui est courant avec les masques générés par SAM), ou les boîtes englobantes ont une largeur ou une hauteur nulle.

Solution : Examine tes annotations JSON pour trouver les valeurs iscrowd. Si tu utilises des masques SAM, prétraite le JSON pour définir iscrowd: 0.

Polygones en forme de boîte à partir d'annotations de masques#

Si use_segments=True journalise annotations without a usable polygon, certaines annotations ne comportent aucune valeur segmentation, ou en ont une qui n'est pas une liste d'au moins trois paires de coordonnées. Les causes habituelles sont les exportations de détection seule, qui laissent le champ manquant ou vide, et l'encodage par plages de longueurs COCO ({"counts": ..., "size": ...}), que les exportateurs de masques binaires tels que SAM écrivent ; une liste de coordonnées plates sans liste de polygones englobants, des contours à un ou deux points, et d'autres valeurs mal formées sont traitées de la même manière. Une annotation conserve les polygones restants et se replie sur une ligne de segment ayant la forme de sa boîte englobante lorsqu'il n'y en a aucun, de sorte que les étiquettes restent valides mais que ces lignes ne comportent aucun détail de masque.

Solution : Réexporte les annotations avec des segmentations polygonales, décode les masques RLE en polygones avant d'exécuter convert_coco(), ou corrige toute valeur segmentation mal formée.

Lacunes dans les ID de classe dans les étiquettes converties#

Si les identifiants de classe dans les fichiers d'étiquettes ne sont pas contigus (par exemple, 0, 4, 9 au lieu de 0, 1, 2), ton outil d'annotation utilise des valeurs category_id non contiguës.

Solution : Vérifie que les identifiants de classe de tes fichiers .txt correspondent au dictionnaire names dans dataset.yaml. Remappe les identifiants vers des valeurs contiguës si nécessaire.

Pour tous les détails de l'API et la description des paramètres, consulte la référence de l'API convert_coco.

FAQ#

  • Utilise la fonction convert_coco() d'Ultralytics pour convertir les annotations COCO JSON au format YOLO .txt. Définis cls91to80=False pour les jeux de données personnalisés :

    from ultralytics.data.converter import convert_coco
    
    convert_coco(labels_dir="path/to/annotations/", save_dir="output/", cls91to80=False)

    Après la conversion, réorganise tes fichiers d'étiquettes de sorte que labels/ reflète le répertoire images/, puis crée un fichier dataset.yaml. Consulte le guide étape par étape pour le flux de travail complet.

  • Cela se produit parce que convert_coco() enregistre les étiquettes dans un sous-répertoire à l'intérieur de save_dir/labels/ (par exemple, save_dir/labels/train/) plutôt que directement dans le labels/train/ de ton jeu de données à côté de images/train/. YOLO s'attend à ce que les étiquettes soient placées en parallèle des images — par exemple, images/train/img.jpg nécessite labels/train/img.txt. Déplace tes étiquettes converties pour qu'elles correspondent à cette structure. Consulte la correction de la structure des répertoires.

  • Le paramètre cls91to80 contrôle la façon dont les valeurs COCO category_id sont mappées aux ID de classe YOLO. Lorsque True (par défaut), il applique la table de correspondance coco91_to_coco80_class() conçue pour le jeu de données COCO standard, qui possède 80 classes avec des ID non contigus (1-90). Pour les jeux de données personnalisés, définis toujours cls91to80=False — cela soustrait simplement 1 de chaque category_id pour créer des ID de classe indexés à zéro.

  • Ce n'est pas possible avec le pipeline d'entraînement YOLO actuel — les annotations doivent être au format YOLO .txt avec un fichier par image. Utilise convert_coco() pour convertir d'abord ton COCO JSON, puis suis ce guide pour organiser et t'entraîner. Pour en savoir plus sur les formats pris en charge, consulte les formats de jeux de données.

  • Oui, utilise use_segments=True lors de l'appel à convert_coco() pour inclure des masques de segmentation polygonaux dans les étiquettes YOLO converties. Cela produit des fichiers d'étiquettes compatibles avec les modèles de segmentation YOLO :

    from ultralytics.data.converter import convert_coco
    
    convert_coco(labels_dir="annotations/", save_dir="output/", use_segments=True, cls91to80=False)
  • Utilise use_keypoints=True pour convertir des annotations de points clés COCO pour l'entraînement à l'estimation de pose :

    from ultralytics.data.converter import convert_coco
    
    convert_coco(labels_dir="annotations/", save_dir="output/", use_keypoints=True, cls91to80=False)

    Note que si use_segments et use_keypoints sont tous deux définis sur True, seuls les points clés seront écrits dans les fichiers d'étiquettes — les segments sont ignorés en silence.

Commentaires