Como converter anotações COCO para o formato YOLO#
O treino de modelos Ultralytics YOLO requer anotações no formato YOLO, mas muitas ferramentas populares de anotação exportam para o formato COCO JSON. Este guia mostra como converter as suas anotações COCO para o formato YOLO e começar a treinar modelos de deteção de objetos, segmentação de instâncias e estimativa de pose.
Para treinar diretamente com COCO JSON sem gerar ficheiros .txt, consulta Treinar YOLO com COCO JSON sem conversão.
Porquê converter de COCO para YOLO?#
O formato COCO JSON armazena todas as anotações num único ficheiro, enquanto o YOLO utiliza um ficheiro de texto por imagem com coordenadas normalizadas. A conversão é necessária porque:
- Os modelos YOLO requerem ficheiros de etiquetas
.txt, com um ficheiro por imagem, contendoclass x_center y_center width heightem coordenadas normalizadas. - O COCO JSON utiliza coordenadas em píxeis no formato
[x_min, y_min, width, height], com um único ficheiro JSON para todas as imagens. - Os IDs das classes são diferentes — o COCO utiliza valores
category_idarbitrários, enquanto o YOLO requer IDs de classe indexados a partir de zero.
| Recurso | COCO JSON | YOLO TXT |
|---|---|---|
| Estrutura | Um único ficheiro JSON para todas as imagens | Um ficheiro .txt por imagem |
| Formato do bbox | [x_min, y_min, width, height] em píxeis | class x_center y_center width height normalizado (0-1) |
| IDs das classes | category_id (pode começar por qualquer número) | Indexado a partir de zero (começa em 0) |
| Segmentação | Matrizes de polígonos no campo segmentation | Coordenadas dos polígonos após o ID da classe |
| Keypoints | [x, y, visibility, ...] em píxeis | [x, y, visibility, ...] normalizados |
Início rápido#
A forma mais rápida de converter anotações COCO e começar o treino:
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)
)Após a conversão, organiza a estrutura do diretório, cria um dataset.yaml e começa o treino. Consulta abaixo o guia passo a passo completo.
O valor predefinido de cls91to80=True foi concebido apenas para o dataset COCO padrão, com 80 classes de objetos, que mapeia 91 IDs de categoria não contíguos para 80 IDs de classe contíguos. Para qualquer dataset personalizado, tens de definir cls91to80=False — caso contrário, os teus IDs de classe serão mapeados incorretamente sem aviso e o teu modelo aprenderá classes erradas.
Guia de conversão passo a passo#
1. Prepara o teu dataset COCO#
Um dataset típico no formato COCO exportado por ferramentas de anotação tem a seguinte estrutura:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ ├── img_002.jpg
│ │ └── ...
│ └── val/
│ ├── img_100.jpg
│ └── ...
└── annotations/
├── instances_train.json
└── instances_val.jsonCada ficheiro JSON segue a especificação do formato de dados COCO, com três campos obrigatórios — 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. Converte as anotações#
Utiliza a função convert_coco() para converter as tuas anotações COCO JSON para o formato .txt do YOLO:
from ultralytics.data.converter import convert_coco
convert_coco(
labels_dir="my_dataset/annotations/",
save_dir="my_dataset/converted/",
cls91to80=False,
)convert_coco() escreve um ficheiro .txt por cada imagem anotada num subdiretório labels/ com o nome de cada ficheiro JSON, removendo o prefixo instances_ (por isso, instances_train.json produz labels/train/). As imagens sem anotações são ignoradas e não recebem nenhum ficheiro de etiquetas, pelo que a árvore labels/ pode não corresponder a todas as imagens:
my_dataset/converted/
├── images/ # created but left empty
└── labels/
├── train/ # from instances_train.json
│ ├── img_001.txt
│ └── ...
└── val/ # from instances_val.json
└── ...convert_coco() nunca substitui um save_dir existente: se my_dataset/converted/ já existir, uma nova execução escreve em my_dataset/converted-2/. Elimina a saída anterior (ou altera save_dir) antes de executar novamente; caso contrário, os passos seguintes lerão etiquetas desatualizadas.
3. Organiza a estrutura do diretório#
Após a conversão, os ficheiros de etiquetas devem ser colocados juntamente com as imagens. O YOLO espera um diretório labels/ que replique o diretório 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))A tua estrutura final do dataset deve ter o seguinte aspeto:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ └── ...
│ └── val/
│ └── ...
├── labels/
│ ├── train/
│ │ ├── img_001.txt
│ │ └── ...
│ └── val/
│ └── ...
└── dataset.yaml4. Cria o dataset.yaml#
Cria um ficheiro de configuração dataset.yaml que mapeie as categorias COCO para os nomes das classes YOLO. Este ficheiro indica ao YOLO onde estão os teus dados e que classes deve detetar:
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)O ficheiro YAML resultante:
path: /absolute/path/to/my_dataset
train: images/train
val: images/val
names:
0: helmet
1: vestPara obter mais detalhes sobre o formato YAML do dataset, consulta o guia de configuração de datasets.
5. Treina o teu modelo YOLO#
Com o dataset convertido pronto, treina um modelo 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)Para obter dicas de treino e boas práticas, consulta o guia de treino de modelos.
6. Verifica a conversão#
Antes do treino, verifica algumas amostras de ficheiros de etiquetas para confirmar que os IDs das classes e as coordenadas estão corretos:
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 vires IDs de classe negativos, é provável que o teu COCO JSON utilize category_id a começar em 0. Adiciona 1 a todos os valores category_id no teu JSON antes de executar convert_coco(), uma vez que este mapeia os IDs das classes como category_id - 1.
Resolução de problemas comuns#
IDs de classe incorretos após a conversão#
Se o teu modelo for treinado, mas detetar classes de objetos erradas, é provável que estejas a utilizar cls91to80=True (predefinido) num dataset personalizado. Isto mapeia os teus valores category_id através da tabela de consulta COCO de 91 para 80, o que só é correto para o dataset COCO padrão. Um category_id sem correspondente no COCO-80 não é mapeado e gera TypeError: must be real number, not NoneType durante a conversão, em vez de produzir etiquetas incorretas.
Solução: utiliza sempre cls91to80=False para datasets personalizados.
Nenhuma etiqueta encontrada durante o treino#
Se a análise das etiquetas indicar 0 images, N backgrounds e o treino for depois interrompido com ValueError: train: No labels found in .../labels/train.cache, os teus ficheiros de etiquetas não estão no diretório esperado. convert_coco() guarda as etiquetas num diretório de saída separado (por exemplo, save_dir/labels/train/), mas o YOLO espera labels/ em paralelo com images/ dentro do diretório do dataset.
Solução: move os ficheiros de etiquetas para corresponder à estrutura de diretórios esperada. Certifica-te de que labels/train/ é irmão de images/train/.
KeyError durante a conversão#
Se obtiveres KeyError: 'bbox' ou erros semelhantes ao executar convert_coco(), é provável que o teu labels_dir contenha ficheiros JSON que não são de instâncias (por exemplo, captions_train2017.json) e que tenham uma estrutura de anotações diferente.
Solução: coloca apenas ficheiros JSON de anotações de instâncias (por exemplo, instances_train2017.json) no labels_dir.
Ficheiros de etiquetas vazios após a conversão#
Se a conversão terminar, mas os ficheiros .txt estiverem vazios ou em falta, todas as anotações podem ter iscrowd: 1 (algo comum em máscaras geradas pelo SAM), ou as caixas delimitadoras podem ter largura ou altura zero. Executar com use_keypoints=True numa exportação apenas de deteção produz o mesmo resultado, porque as anotações sem um campo keypoints são completamente ignoradas.
Solução: verifica os valores iscrowd nas tuas anotações JSON. Se utilizares máscaras SAM, pré-processa o JSON para definir iscrowd: 0. Se tiveres passado use_keypoints=True, confirma que as tuas anotações contêm realmente keypoints.
Polígonos com forma de caixa provenientes de anotações de máscaras#
Se use_segments=True registar annotations without a usable polygon, algumas anotações não contêm um valor segmentation, ou contêm um valor que não é uma lista com pelo menos três pares de coordenadas. As causas habituais são exportações apenas de deteção, que deixam o campo em falta ou vazio, e a codificação de comprimento variável do COCO ({"counts": ..., "size": ...}), utilizada por exportadores de máscaras binárias como o SAM; uma lista de coordenadas simples sem uma lista de polígonos envolvente, contornos com um ou dois pontos e outros valores malformados são tratados da mesma forma. Uma anotação mantém os polígonos que restarem e recorre a uma linha de segmento com a forma da sua caixa delimitadora quando não resta nenhum, pelo que as etiquetas permanecem válidas, mas essas linhas não contêm detalhes da máscara.
Solução: volta a exportar as anotações com segmentações poligonais, descodifica as máscaras RLE para polígonos antes de executar convert_coco() ou corrige quaisquer valores segmentation malformados.
Intervalos nos IDs de classe das etiquetas convertidas#
Se os IDs das classes nos ficheiros de etiquetas não forem contíguos (por exemplo, 0, 4, 9 em vez de 0, 1, 2), a tua ferramenta de anotação utiliza valores category_id não contíguos.
Solução: verifica se os IDs das classes nos teus ficheiros .txt correspondem ao dicionário names em dataset.yaml. Se necessário, remapeia os IDs para valores contíguos.
Para obter todos os detalhes da API e as descrições dos parâmetros, consulta a referência da API convert_coco.
Perguntas frequentes#
Utiliza a função
convert_coco()do Ultralytics para converter anotações COCO JSON para o formato.txtdo YOLO. Definecls91to80=Falsepara datasets personalizados:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="path/to/annotations/", save_dir="output/", cls91to80=False)Após a conversão, reorganiza os teus ficheiros de etiquetas para que
labels/replique o diretórioimages/e, em seguida, cria um ficheirodataset.yaml. Consulta o guia passo a passo para conhecer o fluxo de trabalho completo.Isto acontece porque
convert_coco()guarda as etiquetas num subdiretório dentro desave_dir/labels/(por exemplo,save_dir/labels/train/), em vez de as guardar diretamente nolabels/train/do teu dataset, juntamente comimages/train/. O YOLO espera que as etiquetas estejam em paralelo com as imagens — por exemplo,images/train/img.jpgprecisa delabels/train/img.txt. Move as etiquetas convertidas para corresponder a esta estrutura. Consulta corrigir a estrutura de diretórios.O parâmetro
cls91to80controla a forma como os valorescategory_iddo COCO são mapeados para os IDs de classe do YOLO. QuandoTrue(predefinido), aplica a tabela de consultacoco91_to_coco80_class(), concebida para o dataset COCO padrão, que tem 80 classes com IDs não contíguos (1-90). Para datasets personalizados, define semprecls91to80=False— isto subtrai simplesmente 1 de cadacategory_idpara criar IDs de classe indexados a partir de zero.Não sem código personalizado. O pipeline de treino predefinido espera etiquetas YOLO
.txt, com um ficheiro por imagem. Por isso, executaconvert_coco()e segue este guia passo a passo, ou cria uma subclasse do dataset para analisar o COCO JSON em tempo real — consulta Treinar YOLO com COCO JSON sem conversão. Para obter mais informações sobre os formatos compatíveis, consulta formatos de datasets.Sim, utiliza
use_segments=Trueao chamarconvert_coco()para incluir máscaras de segmentação poligonais nas etiquetas YOLO convertidas. Isto produz ficheiros de etiquetas compatíveis com modelos de segmentação YOLO:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_segments=True, cls91to80=False)Utiliza
use_keypoints=Truepara converter anotações de keypoints COCO para o treino de estimativa de pose:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_keypoints=True, cls91to80=False)Nota que, se
use_segmentseuse_keypointsestiverem ambos definidos comoTrue, apenas os keypoints serão escritos nos ficheiros de etiquetas — os segmentos serão ignorados silenciosamente.