YOLO Vision 2026:

COCO 어노테이션을 YOLO 형식으로 변환하는 방법#

Ultralytics YOLO 모델을 학습하려면 YOLO 형식의 어노테이션이 필요하지만, 많은 인기 어노테이션 도구들은 대신 COCO JSON 형식으로 내보내기를 수행합니다. 이 가이드에서는 COCO 어노테이션을 YOLO 형식으로 변환하고 객체 검출, 인스턴스 세그멘테이션, 포즈 추정 모델 학습을 시작하는 방법을 설명합니다.

변환 과정을 건너뛰고 싶으신가요?

.txt 파일을 생성하지 않고 COCO JSON으로 직접 학습하려면 변환 없이 COCO JSON으로 YOLO 학습하기를 참조하세요.

COCO에서 YOLO로 변환해야 하는 이유는 무엇인가요?#

COCO JSON 형식은 모든 어노테이션을 단일 파일에 저장하는 반면, YOLO는 정규화된 좌표를 가진 이미지당 하나의 텍스트 파일을 사용합니다. 변환이 필요한 이유는 다음과 같습니다:

  • YOLO 모델에는 이미지당 하나의 파일이 필요한 .txt 라벨 파일이 필요하며, 정규화된 좌표의 class x_center y_center width height(이)가 포함되어야 합니다.
  • COCO JSON은 픽셀 좌표를 사용하며, 모든 이미지에 대해 단일 JSON 파일을 [x_min, y_min, width, height] 형식으로 사용합니다.
  • 클래스 ID가 다릅니다 — COCO는 임의의 category_id 값을 사용하는 반면, YOLO는 0부터 시작하는 인덱스(zero-indexed) 클래스 ID가 필요합니다.
기능COCO JSONYOLO TXT
구조모든 이미지에 대해 단일 JSON 파일이미지당 하나의 .txt 파일
Bbox 형식픽셀 단위의 [x_min, y_min, width, height]정규화된(0-1) class x_center y_center width height
클래스 IDcategory_id (임의의 번호에서 시작 가능)0부터 시작하는 인덱스 (0에서 시작)
분할(Segmentation)segmentation 필드의 다각형(Polygon) 배열클래스 ID 뒤에 오는 다각형 좌표
키포인트(Keypoints)픽셀 단위의 [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=False`를 사용하십시오

cls91to80=True 기본값은 80개의 객체 클래스를 가진 표준 COCO dataset 전용으로 설계되었으며, 이는 연속되지 않은 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 파일은 세 개의 필수 필드(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 형식으로 변환합니다:

COCO를 YOLO 형식으로 변환
from ultralytics.data.converter import convert_coco

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

convert_coco()instances_ 접두사가 제거된 각 JSON 파일 이름을 딴 labels/ 하위 디렉터리에 주석이 달린 이미지당 하나의 .txt 파일을 작성합니다(예: instances_train.jsonlabels/train/를 생성함). 주석이 없는 이미지는 건너뛰고 레이블 파일이 생성되지 않으므로 labels/ 트리가 모든 이미지를 반영하지 않을 수 있습니다.

my_dataset/converted/
└── 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.yaml

4. 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 모델을 학습시키십시오:

변환된 COCO 데이터로 학습
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이 0부터 시작하는 category_id을 사용하고 있을 가능성이 높습니다. convert_coco() 실행은 클래스 ID를 category_id - 1으로 매핑하므로, 실행 전에 JSON의 모든 category_id 값에 1을 더하십시오.

일반적인 문제 해결#

변환 후 잘못된 클래스 ID#

모델이 학습은 되지만 잘못된 객체 클래스를 검출하는 경우, 커스텀 데이터셋에서 cls91to80=True (기본값)을 사용하고 있을 가능성이 높습니다. 이 경우 category_id 값이 COCO 91-to-80 조회 테이블을 통해 매핑되며, 이는 표준 COCO dataset에만 올바르게 작동합니다.

해결 방법: 커스텀 데이터셋에는 항상 cls91to80=False을 사용하세요.

학습 중 레이블을 찾을 수 없음#

학습 중에 WARNING: No labels found 또는 0 images, N backgrounds이 표시되면 레이블 파일이 예상된 디렉터리에 없는 것입니다. convert_coco()는 레이블을 별도의 출력 디렉터리(예: save_dir/labels/train/)에 저장하지만, YOLO는 데이터셋 디렉터리 내부에서 images/와 평행한 labels/를 예상합니다.

해결 방법: 예상되는 디렉토리 구조에 맞게 라벨 파일을 이동하세요. labels/train/images/train/의 형제(sibling) 디렉토리인지 확인하세요.

변환 중 KeyError 발생#

convert_coco()을 실행할 때 KeyError: 'bbox' 또는 유사한 오류가 발생하는 경우, labels_dir에 주석 구조가 다른 비인스턴스 JSON 파일(예: captions_train2017.json)이 포함되어 있을 가능성이 높습니다.

해결 방법: 인스턴스 어노테이션 JSON 파일(예: instances_train2017.json)만 labels_dir에 배치하세요.

변환 후 빈 레이블 파일#

변환이 완료되었는데 .txt 파일이 비어 있거나 누락된 경우, 모든 어노테이션에 iscrowd: 1이 있거나(SAM 생성 마스크에서 흔히 발생함), 바운딩 박스의 너비 또는 높이가 0일 수 있습니다.

해결 방법: JSON 어노테이션의 iscrowd 값을 검사하세요. SAM 마스크를 사용하는 경우, JSON을 전처리하여 iscrowd: 0을 설정하세요.

마스크 어노테이션에서 생성된 상자 형태의 폴리곤#

use_segments=Trueannotations without a usable polygon을(를) 기록하는 경우, 일부 어노테이션에는 segmentation 값이 없거나 최소 세 개의 좌표 쌍으로 구성된 리스트가 아닙니다. 일반적인 원인은 필드가 누락되거나 비어 있는 탐지 전용 내보내기와, SAM 같은 비트마스크 내보내기 도구가 작성하는 COCO 런 길이 인코딩({"counts": ..., "size": ...})입니다. 둘러싸는 폴리곤 리스트가 없는 평면 좌표 리스트, 1점 및 2점 외곽선, 기타 잘못된 형식의 값도 동일한 방식으로 처리됩니다. 어노테이션은 남아 있는 폴리곤을 유지하며, 폴리곤이 전혀 없는 경우 바운딩 박스 형태의 세그먼트 행으로 대체되므로 레이블은 유효하게 유지되지만 해당 행에는 마스크 세부 정보가 포함되지 않습니다.

해결 방법: 폴리곤 세그멘테이션과 함께 어노테이션을 다시 내보내거나, convert_coco()을 실행하기 전에 RLE 마스크를 폴리곤으로 디코딩하거나, 잘못된 형식의 segmentation 값을 수정합니다.

변환된 레이블의 클래스 ID 공백#

라벨 파일의 클래스 ID가 연속되지 않은 경우(예: 0, 1, 2 대신 0, 4, 9), 어노테이션 도구가 연속되지 않은 category_id 값을 사용하고 있는 것입니다.

해결 방법: .txt 파일의 클래스 ID가 dataset.yamlnames 사전과 일치하는지 확인합니다. 필요한 경우 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()이 레이블을 데이터셋의 labels/train/ 내부 images/train/와 나란히 직접 저장하는 대신 save_dir/labels/ 내부의 하위 디렉터리(예: save_dir/labels/train/)에 저장하기 때문에 발생합니다. YOLO는 레이블이 이미지와 평행하게 위치할 것으로 예상합니다. 예를 들어 images/train/img.jpg에는 labels/train/img.txt이 필요합니다. 변환된 레이블을 이동하여 이 구조에 맞추십시오. 디렉터리 구조 수정하기를 참조하십시오.

  • cls91to80 매개변수는 COCO category_id 값이 YOLO 클래스 ID에 매핑되는 방식을 제어합니다. True(기본값)인 경우, 연속되지 않은 ID(1-90)를 가진 80개의 클래스가 있는 표준 COCO 데이터셋용으로 설계된 coco91_to_coco80_class() 조회 테이블이 적용됩니다. 사용자 지정 데이터셋의 경우 항상 cls91to80=False을 설정하십시오. 이는 각 category_id에서 1을 빼서 0부터 시작하는 클래스 ID를 생성합니다.

  • 현재 YOLO 학습 파이프라인으로는 불가능합니다. 어노테이션은 이미지당 하나의 파일을 가진 YOLO .txt 형식이어야 합니다. 먼저 convert_coco()을 사용하여 COCO JSON을 변환한 후, 이 가이드에 따라 정리하고 학습을 진행하세요. 지원되는 형식에 대한 자세한 내용은 데이터셋 형식을 참조하세요.

  • 그렇습니다. 변환된 YOLO 레이블에 다각형 세분화 마스크를 포함하려면 convert_coco()을 호출할 때 use_segments=True을 사용하십시오. 이렇게 하면 YOLO 세분화 모델과 호환되는 레이블 파일이 생성됩니다:

    from ultralytics.data.converter import convert_coco
    
    convert_coco(labels_dir="annotations/", save_dir="output/", use_segments=True, cls91to80=False)
  • 자세 추정 학습을 위해 COCO 키포인트 주석을 변환하려면 use_keypoints=True을 사용하십시오:

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

    use_segmentsuse_keypoints이 모두 True(으)로 설정된 경우, 키포인트만 라벨 파일에 기록되고 세그먼트는 조용히 무시됩니다.

댓글