COCOアノテーションをYOLO形式に変換する方法#
Ultralytics YOLOモデルの学習にはYOLO形式のアノテーションが必要ですが、多くの一般的なアノテーションツールでは、代わりにCOCO JSON形式でエクスポートされます。このガイドでは、COCOアノテーションをYOLO形式に変換し、物体検出、インスタンスセグメンテーション、ポーズ推定モデルの学習を開始する方法を説明します。
.txtファイルを生成せずにCOCO JSONを直接使って学習するには、変換せずにCOCO JSONでYOLOを学習するを参照してください。
COCOからYOLOに変換する理由#
COCO JSON形式ではすべてのアノテーションが1つのファイルに保存されますが、YOLOでは画像ごとに1つのテキストファイルを使用し、座標は正規化されています。変換が必要な理由は次のとおりです。
- YOLOモデルでは、画像ごとに1つの
.txtラベルファイルが必要です。各ファイルには、正規化座標で表したclass x_center y_center width heightが含まれます。 - COCO JSONではピクセル座標を使用します。すべての画像を1つのJSONファイルにまとめた
[x_min, y_min, width, height]形式です。 - クラスIDが異なります — COCOでは任意の
category_id値が使われますが、YOLOでは0始まりのクラスIDが必要です。
| 機能 | COCO JSON | YOLO TXT |
|---|---|---|
| 構造 | すべての画像を1つのJSONファイルに格納 | 画像ごとに1つの.txtファイル |
| BBox形式 | ピクセル単位の[x_min, y_min, width, height] | class x_center y_center width heightで正規化(0~1) |
| クラスID | category_id(任意の番号から開始できます) | 0始まり(0から開始) |
| セグメンテーション | segmentationフィールド内のポリゴン配列 | クラスIDの後に続くポリゴン座標 |
| キーポイント | ピクセル単位の[x, y, visibility, ...] | [x, y, visibility, ...]で正規化 |
クイックスタート#
COCOアノテーションを変換してトレーニングを開始する最も簡単な方法:
from ultralytics.data.converter import convert_coco
convert_coco(
labels_dir="my_dataset/annotations/", # JSONファイルを含むディレクトリ
save_dir="my_dataset/converted/", # 変換済みラベルの保存先
cls91to80=False, # カスタムデータセットではFalseに設定してください(下記の警告を参照)
)変換後、ディレクトリ構造を整理し、dataset.yamlを作成して、トレーニングを開始します。以下の手順ガイド全体もご覧ください。
cls91to80=Trueのデフォルト設定は、80個のオブジェクトクラスを持つ標準のCOCOデータセット専用に設計されています。この設定では、連続していない91個のカテゴリIDが、連続した80個のクラスIDに対応付けられます。カスタムデータセットでは、必ずcls91to80=Falseを設定してください。設定しない場合、クラスIDが誤って対応付けられ、モデルが誤ったクラスを学習しても警告されません。
変換手順ガイド#
1. COCOデータセットを準備する#
アノテーションツールからエクスポートした一般的なCOCO形式のデータセットは、次のような構造です:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ ├── img_002.jpg
│ │ └── ...
│ └── val/
│ ├── img_100.jpg
│ └── ...
└── annotations/
├── instances_train.json
└── instances_val.json各JSONファイルは、必須の3つのフィールド(images、annotations、categories)を含むCOCOデータ形式の仕様に従います:
{
"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. アノテーションを変換する#
convert_coco()関数を使用して、COCO JSONアノテーションを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()は、アノテーション付き画像ごとに1つの.txtファイルを、各JSONファイル名に基づいたlabels/サブディレクトリに書き込みます。その際、instances_プレフィックスは削除されます(つまり、instances_train.jsonからlabels/train/が生成されます)。アノテーションのない画像はスキップされ、ラベルファイルも作成されないため、labels/ツリーにはすべての画像が含まれるとは限りません:
my_dataset/converted/
├── images/ # created but left empty
└── labels/
├── train/ # from instances_train.json
│ ├── img_001.txt
│ └── ...
└── val/ # from instances_val.json
└── ...convert_coco()は既存のsave_dirを上書きしません。my_dataset/converted/がすでに存在する場合、再実行時の出力先はmy_dataset/converted-2/になります。再実行する前に以前の出力を削除するか、save_dirを変更してください。そうしないと、次の手順で古いラベルが読み込まれます。
3. ディレクトリ構造を整理する#
変換後、ラベルファイルを画像と同じ場所に配置する必要があります。YOLOでは、labels/ディレクトリがimages/ディレクトリと同じ構造になっている必要があります:
import shutil
from pathlib import Path
converted_dir = Path("my_dataset/converted/labels")
dataset_dir = Path("my_dataset")
# convert_cocoは各サブディレクトリにJSONファイル名(「instances_」プレフィックスを除く)を付けます。
# そのため、「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))最終的なデータセット構造は次のようになります:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ └── ...
│ └── val/
│ └── ...
├── labels/
│ ├── train/
│ │ ├── img_001.txt
│ │ └── ...
│ └── val/
│ └── ...
└── dataset.yaml4. dataset.yamlを作成する#
COCOカテゴリをYOLOクラス名に対応付けるdataset.yaml設定ファイルを作成します。このファイルで、YOLOにデータの場所と検出対象のクラスを指定します:
import json
from pathlib import Path
import yaml
# COCO JSONからカテゴリを読み込む
with open("my_dataset/annotations/instances_train.json") as f:
coco = json.load(f)
# convert_cocoの出力に合うクラス名を作成する(category_id - 1)
categories = sorted(coco["categories"], key=lambda x: x["id"])
names = {cat["id"] - 1: cat["name"] for cat in categories}
# 注意: convert_cocoはクラスIDをcategory_id - 1として対応付けるため、category_idは1から
# 開始する必要があります。カテゴリが0から始まる場合は、各IDに1を加えてください。
# 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)生成されるYAMLファイル:
path: /absolute/path/to/my_dataset
train: images/train
val: images/val
names:
0: helmet
1: vestデータセットYAML形式の詳細は、データセット設定ガイドをご覧ください。
5. YOLOモデルをトレーニングする#
変換済みのデータセットを準備したら、YOLOモデルをトレーニングします:
from ultralytics import YOLO
model = YOLO("yolo26n.pt") # 事前トレーニング済みモデルを読み込む
results = model.train(data="my_dataset/dataset.yaml", epochs=100, imgsz=640)トレーニングのヒントとベストプラクティスについては、モデルのトレーニングガイドをご覧ください。
6. 変換結果を確認する#
トレーニングの前に、いくつかのラベルファイルを抜き取りで確認し、クラスIDと座標が正しいことを確かめます:
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}"負のクラスIDが表示される場合、COCO JSONのcategory_idは0から始まっている可能性があります。convert_coco()を実行する前に、JSON内のすべてのcategory_id値に1を加えてください。これは、クラスIDをcategory_id - 1として対応付けるためです。
よくある問題のトラブルシューティング#
変換後のクラスIDが誤っている#
モデルはトレーニングできても誤ったオブジェクトクラスを検出する場合、カスタムデータセットでcls91to80=True(デフォルト)を使用している可能性があります。この設定は、category_idの値をCOCOの91対80のルックアップテーブルで対応付けますが、これは標準のCOCOデータセットでのみ正しく機能します。COCO-80に対応するものがないcategory_idは何にも対応付けられず、誤ったラベルを生成する代わりに、変換中にTypeError: must be real number, not NoneTypeが発生します。
解決策: カスタムデータセットでは、必ずcls91to80=Falseを使用してください。
トレーニング中にラベルが見つからない#
ラベルスキャンで0 images, N backgroundsと報告され、その後トレーニングがValueError: train: No labels found in .../labels/train.cacheで中断する場合、ラベルファイルが想定されたディレクトリにありません。convert_coco()はラベルを別の出力ディレクトリ(例: save_dir/labels/train/)に保存しますが、YOLOではデータセットディレクトリ内でimages/と並列になるようにlabels/を配置する必要があります。
解決策: 想定されるディレクトリ構造に合うようにラベルファイルを移動します。labels/train/がimages/train/と同じ階層にあることを確認してください。
変換中のKeyError#
convert_coco()の実行時にKeyError: 'bbox'などのエラーが発生する場合、labels_dirに、異なるアノテーション構造を持つインスタンス以外のJSONファイル(例: captions_train2017.json)が含まれている可能性があります。
解決策: インスタンスアノテーションJSONファイル(例: instances_train2017.json)のみをlabels_dirに配置してください。
変換後のラベルファイルが空#
変換が完了しても.txtファイルが空または見つからない場合、すべてのアノテーションにiscrowd: 1が含まれている可能性があります(SAMで生成されたマスクでよく見られます)。または、バウンディングボックスの幅か高さが0である可能性があります。use_keypoints=Trueを指定して検出専用のエクスポートを処理した場合も、同じ結果になります。keypointsフィールドのないアノテーションはすべてスキップされるためです。
解決策: JSONアノテーションのiscrowd値を確認してください。SAMマスクを使用する場合は、JSONを前処理してiscrowd: 0を設定します。use_keypoints=Trueを指定した場合は、アノテーションに実際にkeypointsが含まれていることを確認してください。
マスクアノテーションからボックス形状のポリゴンが生成される#
use_segments=Trueがannotations without a usable polygonをログに記録する場合、一部のアノテーションにsegmentationの値がないか、3組以上の座標ペアを含むリストではない値が含まれています。一般的な原因には、フィールドが欠落または空になっている検出専用エクスポートや、SAMなどのビットマスクエクスポーターが出力するCOCOのランレングス符号化({"counts": ..., "size": ...})があります。外側のポリゴンリストがないフラットな座標リスト、1点または2点だけの輪郭、その他の不正な値も同様に処理されます。アノテーションには有効なポリゴンがあればそのポリゴンが保持され、有効なポリゴンがない場合はバウンディングボックスと同じ形状のセグメント行にフォールバックします。そのためラベルは有効なままですが、該当する行にはマスクの詳細が含まれません。
解決策: ポリゴンセグメンテーションを含むアノテーションを再エクスポートするか、convert_coco()の実行前にRLEマスクをポリゴンにデコードするか、不正なsegmentation値を修正してください。
変換済みラベルのクラスIDに欠番がある#
ラベルファイル内のクラスIDが連続していない(例: 0、1、2ではなく0、4、9)場合、アノテーションツールが連続していないcategory_id値を使用しています。
解決策: .txtファイル内のクラスIDが、dataset.yaml内のnames辞書と一致することを確認してください。必要に応じて、IDを連続した値に再割り当てします。
APIの詳細とパラメーターの説明については、convert_coco APIリファレンスをご覧ください。
よくある質問#
Ultralyticsの
convert_coco()関数を使用して、COCO JSONアノテーションをYOLOの.txt形式に変換します。カスタムデータセットではcls91to80=Falseを設定してください:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="path/to/annotations/", save_dir="output/", cls91to80=False)変換後、ラベルファイルを整理し、
labels/がimages/ディレクトリと同じ構造になるようにしてから、dataset.yamlファイルを作成します。ワークフロー全体については、手順ガイドをご覧ください。これは、
convert_coco()がラベルをsave_dir/labels/内のサブディレクトリ(例:save_dir/labels/train/)に保存し、データセットのlabels/train/内でimages/train/と並ぶ場所に直接保存しないためです。YOLOでは、画像と並列にラベルを配置する必要があります。たとえば、images/train/img.jpgにはlabels/train/img.txtが必要です。この構造に合うように変換済みラベルを移動してください。ディレクトリ構造の修正もご覧ください。cls91to80パラメーターは、COCOのcategory_id値をYOLOのクラスIDに対応付ける方法を制御します。True(デフォルト)の場合、80クラスと連続していないID(1~90)を持つ標準のCOCOデータセット向けに設計されたcoco91_to_coco80_class()ルックアップテーブルが適用されます。カスタムデータセットでは、常にcls91to80=Falseを設定してください。これにより、各category_idから1を引き、0始まりのクラスIDを作成します。カスタムコードなしではできません。デフォルトのトレーニングパイプラインは、画像ごとに1つのファイルを持つYOLOの
.txtラベルを想定しています。そのため、convert_coco()を実行してこの手順ガイドに従うか、データセットをサブクラス化してCOCO JSONをその場で解析してください。詳しくは変換せずにCOCO JSONでYOLOをトレーニングするをご覧ください。対応形式については、データセット形式をご覧ください。はい。
convert_coco()の呼び出し時にuse_segments=Trueを使用すると、変換後のYOLOラベルにポリゴンセグメンテーションマスクを含められます。これにより、YOLOセグメンテーションモデルと互換性のあるラベルファイルが生成されます:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_segments=True, cls91to80=False)use_keypoints=Trueを使用して、姿勢推定のトレーニング向けにCOCOのキーポイントアノテーションを変換します:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_keypoints=True, cls91to80=False)use_segmentsとuse_keypointsの両方がTrueに設定されている場合、ラベルファイルにはキーポイントのみが書き込まれ、セグメントは警告なしで無視されます。