Cách chuyển đổi chú thích COCO sang định dạng YOLO#
Huấn luyện các model Ultralytics YOLO yêu cầu các annotation ở định dạng YOLO, nhưng nhiều công cụ annotation phổ biến lại xuất dữ liệu ở định dạng COCO JSON. Hướng dẫn này chỉ cho bạn cách chuyển đổi các annotation COCO sang định dạng YOLO và bắt đầu huấn luyện các model object detection, instance segmentation và pose estimation.
Để huấn luyện trực tiếp trên COCO JSON mà không cần tạo các file .txt, hãy xem phần Train YOLO on COCO JSON Without Conversion.
Tại sao cần chuyển đổi từ COCO sang YOLO?#
Định dạng COCO JSON lưu trữ toàn bộ các annotation trong một file duy nhất, trong khi YOLO sử dụng mỗi file văn bản cho một ảnh với tọa độ đã chuẩn hóa. Việc chuyển đổi là cần thiết vì:
- Các model YOLO yêu cầu các file nhãn
.txtvới mỗi file tương ứng với một ảnh, chứaclass x_center y_center width heightở định dạng tọa độ đã chuẩn hóa. - COCO JSON sử dụng tọa độ pixel theo định dạng
[x_min, y_min, width, height]với một file JSON duy nhất cho tất cả các ảnh. - ID của class khác nhau — COCO sử dụng các giá trị
category_idtùy ý, trong khi YOLO yêu cầu ID class bắt đầu từ 0.
| Tính năng | COCO JSON | YOLO TXT |
|---|---|---|
| Cấu trúc | Một tệp JSON cho tất cả các ảnh | Một file .txt cho mỗi ảnh |
| Định dạng Bbox | [x_min, y_min, width, height] theo đơn vị pixel | class x_center y_center width height đã chuẩn hóa (0-1) |
| Class ID | category_id (có thể bắt đầu từ bất kỳ số nào) | Chỉ số bắt đầu từ 0 |
| Segmentation | Mảng đa giác trong trường segmentation | Tọa độ đa giác sau Class ID |
| Keypoints | [x, y, visibility, ...] theo đơn vị pixel | [x, y, visibility, ...] đã chuẩn hóa |
Bắt đầu nhanh#
Cách nhanh nhất để chuyển đổi chú thích COCO và bắt đầu huấn luyện:
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)
)Sau khi chuyển đổi, hãy tổ chức cấu trúc thư mục của bạn, tạo file dataset.yaml và bắt đầu huấn luyện. Xem hướng dẫn chi tiết từng bước bên dưới.
Giá trị mặc định của cls91to80=True được thiết kế chỉ dành cho dataset COCO chuẩn với 80 class đối tượng, ánh xạ 91 ID danh mục không liên tục thành 80 ID class liên tục. Đối với bất kỳ dataset tùy chỉnh nào, bạn phải thiết lập cls91to80=False — nếu không, ID class của bạn sẽ bị ánh xạ sai một cách âm thầm và model của bạn sẽ học sai các class.
Hướng dẫn chuyển đổi từng bước#
1. Chuẩn bị tập dữ liệu COCO của bạn#
Một tập dữ liệu định dạng COCO điển hình được xuất từ các công cụ annotation có cấu trúc sau:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ ├── img_002.jpg
│ │ └── ...
│ └── val/
│ ├── img_100.jpg
│ └── ...
└── annotations/
├── instances_train.json
└── instances_val.jsonMỗi file JSON tuân theo đặc tả định dạng dữ liệu COCO với ba trường bắt buộc — images, annotations và 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. Chuyển đổi chú thích#
Sử dụng hàm convert_coco() để chuyển đổi các annotation COCO JSON của bạn sang định dạng 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() ghi một file .txt cho mỗi ảnh được gán nhãn vào một thư mục con labels/ được đặt tên theo từng file JSON, với tiền tố instances_ bị loại bỏ (do đó instances_train.json tạo ra labels/train/). Các ảnh không có annotation sẽ bị bỏ qua và không có file nhãn, do đó cây thư mục labels/ có thể không phản ánh chính xác mọi ảnh:
my_dataset/converted/
└── labels/
├── train/ # from instances_train.json
│ ├── img_001.txt
│ └── ...
└── val/ # from instances_val.json
└── ...convert_coco() không bao giờ ghi đè lên file save_dir hiện có: nếu my_dataset/converted/ đã tồn tại, một lần chạy lại sẽ ghi vào my_dataset/converted-2/ thay thế. Hãy xóa kết quả đầu ra trước đó (hoặc thay đổi save_dir) trước khi chạy lại, nếu không các bước tiếp theo sẽ đọc các nhãn cũ.
3. Tổ chức cấu trúc thư mục#
Sau khi chuyển đổi, các file nhãn cần được đặt cùng cấp với các ảnh của bạn. YOLO mong đợi một thư mục labels/ phản ánh thư mục 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))Cấu trúc dataset cuối cùng của bạn sẽ có dạng:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ └── ...
│ └── val/
│ └── ...
├── labels/
│ ├── train/
│ │ ├── img_001.txt
│ │ └── ...
│ └── val/
│ └── ...
└── dataset.yaml4. Tạo dataset.yaml#
Tạo một file cấu hình dataset.yaml để ánh xạ các danh mục COCO của bạn với tên class YOLO. File này cho YOLO biết dữ liệu của bạn nằm ở đâu và các class nào cần phát hiện:
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)Tệp YAML thu được:
path: /absolute/path/to/my_dataset
train: images/train
val: images/val
names:
0: helmet
1: vestĐể biết thêm chi tiết về định dạng YAML của dataset, hãy xem hướng dẫn cấu hình dataset.
5. Huấn luyện mô hình YOLO của bạn#
Với tập dữ liệu đã chuyển đổi sẵn sàng, hãy huấn luyện mô hình 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)Để biết các mẹo huấn luyện và best practice, hãy xem hướng dẫn huấn luyện model.
6. Xác minh quá trình chuyển đổi của bạn#
Trước khi huấn luyện, hãy kiểm tra ngẫu nhiên một vài tệp nhãn để xác nhận Class ID và tọa độ là chính xác:
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}"Nếu bạn thấy ID class âm, rất có thể COCO JSON của bạn đang sử dụng category_id bắt đầu từ 0. Hãy cộng 1 vào tất cả các giá trị category_id trong file JSON của bạn trước khi chạy convert_coco(), vì hàm này ánh xạ ID class dưới dạng category_id - 1.
Khắc phục các sự cố thường gặp#
Sai Class ID sau khi chuyển đổi#
Nếu model của bạn huấn luyện nhưng phát hiện sai các class đối tượng, có thể bạn đang sử dụng cls91to80=True (mặc định) trên một dataset tùy chỉnh. Thao tác này sẽ ánh xạ các giá trị category_id của bạn thông qua bảng tra cứu 91-sang-80 của COCO, điều này chỉ chính xác đối với dataset COCO chuẩn.
Giải pháp: Luôn sử dụng cls91to80=False cho các dataset tùy chỉnh.
Không tìm thấy nhãn trong quá trình huấn luyện#
Nếu quá trình huấn luyện hiển thị WARNING: No labels found hoặc 0 images, N backgrounds, các file nhãn của bạn không nằm trong thư mục dự kiến. convert_coco() lưu các nhãn vào một thư mục đầu ra riêng biệt (ví dụ: save_dir/labels/train/), nhưng YOLO mong đợi labels/ nằm song song với images/ bên trong thư mục dataset của bạn.
Giải pháp: Di chuyển các file nhãn để khớp với cấu trúc thư mục dự kiến. Đảm bảo rằng labels/train/ là thư mục anh em cùng cấp với images/train/.
KeyError trong quá trình chuyển đổi#
Nếu bạn gặp lỗi KeyError: 'bbox' hoặc các lỗi tương tự khi chạy convert_coco(), rất có thể labels_dir của bạn chứa các file JSON không phải dạng instance (ví dụ: captions_train2017.json) có cấu trúc annotation khác.
Giải pháp: Chỉ đặt các file JSON annotation dạng instance (ví dụ: instances_train2017.json) vào labels_dir.
Các tệp nhãn trống sau khi chuyển đổi#
Nếu quá trình chuyển đổi hoàn tất nhưng các file .txt trống hoặc bị thiếu, tất cả các annotation có thể có iscrowd: 1 (thường gặp với các mask do SAM tạo ra), hoặc các bounding box có chiều rộng hoặc chiều cao bằng không.
Giải pháp: Kiểm tra các annotation JSON của bạn để tìm các giá trị iscrowd. Nếu sử dụng mask của SAM, hãy xử lý trước file JSON để thiết lập iscrowd: 0.
Đa giác có hình hộp từ các annotation mask#
Nếu use_segments=True ghi log annotations without a usable polygon, một số annotation không chứa giá trị segmentation, hoặc giá trị không phải là một danh sách có ít nhất ba cặp tọa độ. Nguyên nhân thông thường là các bản xuất chỉ gồm phát hiện (detection-only), để trống trường này, và mã hóa độ dài chạy COCO ({"counts": ..., "size": ...}) được viết bởi các công cụ xuất bitmask như SAM; danh sách tọa độ phẳng không có danh sách đa giác bao quanh, đường viền một và hai điểm, cùng các giá trị bị lỗi khác đều được xử lý tương tự. Annotation sẽ giữ lại bất kỳ đa giác nào còn sót lại, và quay về dạng một hàng segment có hình dạng giống bounding box của nó khi không có đa giác nào, nhờ đó các nhãn vẫn hợp lệ nhưng các hàng đó sẽ không chứa chi tiết mask.
Giải pháp: Xuất lại các annotation kèm theo phân đoạn đa giác (polygon segmentations), giải mã các mask RLE thành đa giác trước khi chạy convert_coco(), hoặc sửa lại bất kỳ giá trị segmentation nào bị lỗi.
Khoảng trống Class ID trong các nhãn đã chuyển đổi#
Nếu ID class trong các file nhãn không liên tục (ví dụ: 0, 4, 9 thay vì 0, 1, 2), công cụ annotation của bạn đang sử dụng các giá trị category_id không liên tục.
Giải pháp: Xác minh rằng các ID class trong các file .txt của bạn khớp với từ điển names trong dataset.yaml. Ánh xạ lại các ID thành các giá trị liên tục nếu cần.
Để biết chi tiết đầy đủ về API và mô tả tham số, hãy xem tài liệu tham khảo API convert_coco.
Câu hỏi thường gặp#
Sử dụng hàm
convert_coco()từ Ultralytics để chuyển đổi các annotation COCO JSON sang định dạng YOLO.txt. Thiết lậpcls91to80=Falsecho các dataset tùy chỉnh:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="path/to/annotations/", save_dir="output/", cls91to80=False)Sau khi chuyển đổi, hãy tổ chức lại các file nhãn của bạn sao cho
labels/phản ánh thư mụcimages/, sau đó tạo filedataset.yaml. Xem hướng dẫn từng bước để biết quy trình hoàn chỉnh.Hiện tượng này xảy ra vì
convert_coco()lưu các nhãn vào một thư mục con bên trongsave_dir/labels/(ví dụ:save_dir/labels/train/) thay vì lưu trực tiếp vào thư mụclabels/train/của dataset nằm cạnhimages/train/. YOLO mong đợi các nhãn nằm song song với các ảnh — ví dụ:images/train/img.jpgcầnlabels/train/img.txt. Hãy di chuyển các nhãn đã chuyển đổi của bạn để khớp với cấu trúc này. Xem phần khắc phục cấu trúc thư mục.Tham số
cls91to80điều khiển cách các giá trịcategory_idcủa COCO được ánh xạ sang ID class của YOLO. Khi ở giá trịTrue(mặc định), nó áp dụng bảng tra cứucoco91_to_coco80_class()được thiết kế cho dataset COCO chuẩn, có 80 class với các ID không liên tục (1-90). Đối với các dataset tùy chỉnh, hãy luôn thiết lậpcls91to80=False— thao tác này chỉ đơn giản là trừ đi 1 từ mỗi giá trịcategory_idđể tạo ra các ID class bắt đầu từ 0.Không thể thực hiện điều này với pipeline huấn luyện YOLO hiện tại — các annotation phải ở định dạng YOLO
.txtvới mỗi file cho một ảnh. Hãy sử dụngconvert_coco()để chuyển đổi file COCO JSON của bạn trước, sau đó làm theo hướng dẫn này để tổ chức và huấn luyện. Để tìm hiểu thêm về các định dạng được hỗ trợ, hãy xem định dạng dataset.Có, hãy sử dụng
use_segments=Truekhi gọiconvert_coco()để đưa các mask phân đoạn đa giác vào các nhãn YOLO đã chuyển đổi. Thao tác này tạo ra các file nhãn tương thích với các model segmentation của YOLO:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_segments=True, cls91to80=False)Sử dụng
use_keypoints=Trueđể chuyển đổi các annotation keypoint COCO cho việc huấn luyện pose estimation:from ultralytics.data.converter import convert_coco convert_coco(labels_dir="annotations/", save_dir="output/", use_keypoints=True, cls91to80=False)Lưu ý rằng nếu cả
use_segmentsvàuse_keypointsđều được đặt thànhTrue, thì chỉ có các keypoint được ghi vào các file nhãn — các segment sẽ bị bỏ qua một cách âm thầm.