COCO 어노테이션을 YOLO 형식으로 변환하는 방법
Ultralytics YOLO 모델을 훈련하려면 YOLO 형식의 어노테이션이 필요하지만, 많은 인기 어노테이션 도구들은 COCO JSON 형식으로 내보냅니다. 이 가이드에서는 COCO 어노테이션을 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부터 시작하는 클래스 ID를 요구합니다.
| 특성 | COCO JSON | YOLO TXT |
|---|---|---|
| 구조 | 모든 이미지에 대한 단일 JSON 파일 | 이미지당 하나의 .txt 파일 |
| Bbox 형식 | 픽셀 단위 [x_min, y_min, width, height] | 정규화된 (0-1) class x_center y_center width height |
| 클래스 ID | category_id (임의의 숫자로 시작 가능) | 0부터 시작하는 인덱스 |
| 분할 | segmentation 필드의 폴리곤 배열 | 클래스 ID 이후의 폴리곤 좌표 |
| 키포인트 | 픽셀 단위 [x, y, visibility, ...] | 정규화된 [x, y, visibility, ...] |
빠른 시작
COCO 어노테이션을 변환하고 훈련을 시작하는 가장 빠른 방법:
from ultralytics.data.converter import convert_coco
convert_coco(
labels_dir="path/to/annotations/", # JSON 파일이 있는 디렉터리
save_dir="path/to/output/", # 변환된 레이블을 저장할 위치
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 파일은 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,
)3. 디렉터리 구조 정리
변환 후 레이블 파일을 이미지 옆에 배치해야 합니다. YOLO는 images/ 디렉터리를 미러링하는 labels/ 디렉터리를 기대합니다:
import shutil
from pathlib import Path
# 경로 설정
converted_dir = Path("my_dataset/converted/labels")
dataset_dir = Path("my_dataset")
# 각 분할에 대해 이미지 옆으로 레이블 이동
for split in ["train", "val"]:
src = converted_dir / split # convert_coco는 JSON 파일명에서 "instances_" 접두사를 제거합니다
dst = dataset_dir / "labels" / split
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"음수 클래스 ID {cls_id} — JSON의 category_id가 0부터 시작할 수 있습니다"
assert all(0 <= v <= 1 for v in coords), f"좌표가 [0, 1] 범위를 벗어납니다: {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 데이터셋에만 올바릅니다.
해결 방법: 커스텀 데이터셋에는 항상 cls91to80=False를 사용하세요.
훈련 중 레이블을 찾을 수 없음
훈련에서 WARNING: No labels found 또는 0 images, N backgrounds가 표시되면 레이블 파일이 예상된 디렉터리에 없는 것입니다. 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일 수 있습니다.
해결 방법: JSON 어노테이션에서 iscrowd 값을 검사하세요. SAM 마스크를 사용하는 경우 iscrowd: 0으로 설정하도록 JSON을 전처리하세요.
변환된 레이블의 클래스 ID 간격
레이블 파일의 클래스 ID가 비연속적인 경우(예: 0, 1, 2 대신 0, 4, 9), 어노테이션 도구가 비연속적인 category_id 값을 사용합니다.
해결 방법: .txt 파일의 클래스 ID가 dataset.yaml의 names 딕셔너리와 일치하는지 확인하세요. 필요한 경우 ID를 연속 값으로 재매핑하세요.
전체 API 세부 사항 및 매개변수 설명은 convert_coco API 참조를 참조하세요.
FAQ
COCO JSON 어노테이션을 YOLO 형식으로 어떻게 변환하나요?
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 파일을 생성하세요. 전체 워크플로우는 단계별 가이드를 참조하세요.
COCO 변환 후 YOLO 훈련에서 "No labels found"가 표시되는 이유는?
convert_coco()가 images/train/ 옆의 데이터셋 labels/train/에 직접 저장하는 대신 save_dir/labels/ 내의 하위 디렉터리(예: save_dir/labels/train/)에 레이블을 저장하기 때문입니다. YOLO는 레이블이 이미지와 병렬로 위치할 것으로 기대합니다 — 예를 들어, images/train/img.jpg에는 labels/train/img.txt가 필요합니다. 변환된 레이블을 이 구조와 일치하도록 이동하세요. 디렉터리 구조 수정을 참조하세요.
convert_coco()에서 cls91to80은 무엇을 하나요?
cls91to80 매개변수는 COCO category_id 값이 YOLO 클래스 ID로 매핑되는 방식을 제어합니다. True(기본값)인 경우, 비연속 ID(1-90)를 가진 80개 클래스가 있는 표준 COCO 데이터셋을 위해 설계된 조회 테이블을 사용합니다. 커스텀 데이터셋의 경우 항상 cls91to80=False로 설정하세요 — 각 category_id에서 1을 빼서 0부터 시작하는 클래스 ID를 생성합니다.
변환 없이 COCO JSON에서 직접 YOLO를 훈련할 수 있나요?
현재 YOLO 훈련 파이프라인으로는 불가능합니다 — 어노테이션은 이미지당 하나의 파일로 YOLO .txt 형식이어야 합니다. 먼저 convert_coco()를 사용하여 COCO JSON을 변환한 다음 이 가이드를 따라 정리하고 훈련하세요. 지원되는 형식에 대한 자세한 내용은 데이터셋 형식을 참조하세요.
COCO 분할 어노테이션을 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)COCO 키포인트 어노테이션을 YOLO 형식으로 변환하는 방법은?
포즈 추정 훈련을 위해 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로 설정하면 키포인트만 레이블 파일에 기록됩니다 — 분할은 자동으로 무시됩니다.