COCOアノテーションをYOLO形式に変換する方法#
Ultralytics YOLOモデルのトレーニングにはYOLO形式のアノテーションが必要ですが、多くの一般的なアノテーションツールは、代わりにCOCO JSON形式でエクスポートします。このガイドでは、COCOアノテーションをYOLO形式に変換し、物体検出、インスタンスセグメンテーション、ポーズ推定モデルのトレーニングを開始する方法を説明します。
.txtファイルを生成せずにCOCO JSONを直接使用してトレーニングする方法については、変換なしでCOCO JSONを使用してYOLOをトレーニングするを参照してください。
COCOからYOLOに変換する理由#
COCO JSON形式ではすべてのアノテーションを1つのファイルに保存しますが、YOLOでは正規化座標を含む画像ごとのテキストファイルを使用します。変換が必要な理由は次のとおりです。
- YOLOモデルには
.txtラベルファイルが必要です。画像ごとに1つのファイルを用意し、正規化座標でclass x_center y_center width heightを含めます。 - COCO JSONではピクセル座標を使用します。
[x_min, y_min, width, height]形式で、すべての画像を1つのJSONファイルに保存します。 - クラスIDが異なります — COCOでは任意の
category_id値を使用しますが、YOLOでは0始まりのクラスIDが必要です。
| 機能 | COCO JSON | YOLO TXT |
|---|---|---|
| 構造 | すべての画像を含む単一の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/", # 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)
)変換後、ディレクトリ構造を整理し、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ファイルはCOCOデータ形式の仕様に従い、3つの必須フィールド(images、annotations、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. アノテーションを変換する#
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では、images/ディレクトリを反映したlabels/ディレクトリが必要です。
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))最終的なデータセット構造は次のようになります。
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
# 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)生成された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") # load a pretrained model
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-to-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)が含まれている可能性があります。
解決策:labels_dirには、インスタンスアノテーションJSONファイル(例:instances_train2017.json)のみを配置してください。
変換後のラベルファイルが空になる#
変換は完了したものの.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リファレンスを参照してください。
FAQ#
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(デフォルト)の場合、標準のCOCOデータセット向けに設計されたcoco91_to_coco80_class()ルックアップテーブルを適用します。このデータセットは、不連続なID(1~90)を持つ80クラスで構成されています。カスタムデータセットでは、常に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に設定した場合、ラベルファイルにはキーポイントのみが書き込まれ、セグメントは警告なしに無視されます。