Cách chuyển đổi chú thích COCO sang định dạng YOLO#
Việc huấn luyện các model Ultralytics YOLO yêu cầu chú thích ở định dạng YOLO, nhưng nhiều công cụ chú thích phổ biến lại xuất dữ liệu ở định dạng COCO JSON. Hướng dẫn này trình bày cách chuyển đổi chú thích COCO sang định dạng YOLO và bắt đầu huấn luyện các model phát hiện đối tượng, phân đoạn instance và ước tính tư thế.
Để huấn luyện trực tiếp trên COCO JSON mà không tạo các file .txt, hãy xem Huấn luyện YOLO trên COCO JSON không cần chuyển đổi.
Tại sao cần chuyển đổi từ COCO sang YOLO?#
Định dạng COCO JSON lưu trữ tất cả chú thích trong một file duy nhất, trong khi YOLO sử dụng một file văn bản cho mỗi ảnh với các 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 file nhãn
.txtvới một file cho mỗi ảnh, chứaclass x_center y_center width heighttrong các tọa độ đã chuẩn hóa. - COCO JSON sử dụng tọa độ pixel ở định dạng
[x_min, y_min, width, height]với một file JSON duy nhất cho tất cả ảnh. - ID lớp khác nhau — COCO sử dụng các giá trị
category_idtùy ý, trong khi YOLO yêu cầu ID lớp được đánh chỉ mục từ 0.
| Tính năng | COCO JSON | YOLO TXT |
|---|---|---|
| Cấu trúc | Một file JSON duy nhất cho tất cả ảnh | Một file .txt cho mỗi hình ảnh |
| Định dạng Bbox | [x_min, y_min, width, height] theo pixel | class x_center y_center width height đã chuẩn hóa (0-1) |
| ID lớp | category_id (có thể bắt đầu từ bất kỳ số nào) | Đánh chỉ mục từ 0 (bắt đầu từ 0) |
| Phân đoạn | Mảng polygon trong trường segmentation | Tọa độ polygon sau ID lớp |
| Keypoint | [x, y, visibility, ...] theo 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, sắp xếp cấu trúc thư mục, tạo dataset.yaml và bắt đầu huấn luyện. Xem hướng dẫn từng bước đầy đủ bên dưới.
Giá trị mặc định của cls91to80=True được thiết kế chỉ cho dataset COCO tiêu chuẩn với 80 lớp đối tượng, ánh xạ 91 ID danh mục không liên tiếp thành 80 ID lớp liên tiếp. Với mọi dataset tùy chỉnh, bạn phải đặt cls91to80=False — nếu không, ID lớp sẽ bị ánh xạ sai một cách âm thầm và model sẽ học sai lớp.
Hướng dẫn chuyển đổi từng bước#
1. Chuẩn bị dataset COCO#
Một dataset định dạng COCO điển hình được xuất từ các công cụ chú thích có cấu trúc như 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 chú thích COCO JSON sang định dạng .txt của YOLO:
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 chú thích vào một thư mục con labels/ được đặt tên theo từng file JSON, sau khi loại bỏ tiền tố instances_ (vì vậy instances_train.json tạo ra labels/train/). Các ảnh không có chú thích 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 đầy đủ mọi ảnh:
my_dataset/converted/
├── images/ # created but left empty
└── labels/
├── train/ # from instances_train.json
│ ├── img_001.txt
│ └── ...
└── val/ # from instances_val.json
└── ...convert_coco() không bao giờ ghi đè save_dir hiện có: nếu my_dataset/converted/ đã tồn tại, lần chạy lại sẽ ghi vào my_dataset/converted-2/. Hãy xóa đầ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. Sắp xếp cấu trúc thư mục#
Sau khi chuyển đổi, các file nhãn cần được đặt cùng với ảnh. YOLO yêu cầu một thư mục labels/ phản ánh cấu trúc của 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ẽ như sau:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ └── ...
│ └── val/
│ └── ...
├── labels/
│ ├── train/
│ │ ├── img_001.txt
│ │ └── ...
│ └── val/
│ └── ...
└── dataset.yaml4. Tạo dataset.yaml#
Tạo file cấu hình dataset.yaml ánh xạ các danh mục COCO của bạn sang tên lớp YOLO. File này cho YOLO biết vị trí dữ liệu và các lớp 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)File YAML kết quả:
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 model YOLO#
Khi dataset đã chuyển đổi sẵn sàng, hãy huấn luyện một model 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 và phương pháp hay nhất khi huấn luyện, hãy xem hướng dẫn huấn luyện model.
6. Xác minh quá trình chuyển đổi#
Trước khi huấn luyện, hãy kiểm tra ngẫu nhiên một vài file nhãn để xác nhận ID lớp 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 thấy ID lớp âm, COCO JSON của bạn có thể đang sử dụng category_id bắt đầu từ 0. Hãy cộng 1 vào tất cả giá trị category_id trong JSON trước khi chạy convert_coco(), vì nó ánh xạ ID lớp theo category_id - 1.
Khắc phục các sự cố thường gặp#
ID lớp sai sau khi chuyển đổi#
Nếu model huấn luyện nhưng phát hiện sai lớp đố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. Cách này ánh xạ các giá trị category_id của bạn qua bảng tra cứu COCO 91-to-80, chỉ đúng với dataset COCO tiêu chuẩn. Một category_id không có lớp tương ứng trong COCO-80 sẽ không được ánh xạ và gây ra TypeError: must be real number, not NoneType trong quá trình chuyển đổi thay vì tạo ra nhãn sai.
Giải pháp: Luôn sử dụng cls91to80=False cho 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 quét nhãn báo cáo 0 images, N backgrounds rồi quá trình huấn luyện dừng với ValueError: train: No labels found in .../labels/train.cache, các file nhãn của bạn không nằm trong thư mục được mong đợi. convert_coco() lưu nhãn vào một thư mục đầu ra riêng (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.
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 labels/train/ là thư mục cùng cấp với images/train/.
KeyError trong quá trình chuyển đổi#
Nếu nhận được KeyError: 'bbox' hoặc lỗi tương tự khi chạy convert_coco(), labels_dir của bạn có thể chứa các file JSON không phải annotation instance (ví dụ: captions_train2017.json) có cấu trúc chú thích khác.
Giải pháp: Chỉ đặt các file JSON chú thích instance (ví dụ: instances_train2017.json) trong labels_dir.
File 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, có thể tất cả chú thích đều có iscrowd: 1 (thường gặp với các mask được tạo bởi SAM), hoặc bounding box có chiều rộng hoặc chiều cao bằng 0. Chạy với use_keypoints=True trên dữ liệu chỉ xuất detection cũng cho kết quả tương tự, vì các chú thích không có trường keypoints sẽ bị bỏ qua hoàn toàn.
Giải pháp: Kiểm tra các chú thích JSON để tìm các giá trị iscrowd. Nếu sử dụng mask SAM, hãy tiền xử lý JSON để đặt iscrowd: 0. Nếu đã truyền use_keypoints=True, hãy xác nhận rằng chú thích thực sự chứa keypoints.
Polygon dạng hộp từ chú thích mask#
Nếu use_segments=True ghi log annotations without a usable polygon, một số chú thích không có giá trị segmentation, hoặc giá trị này không phải là danh sách gồm ít nhất ba cặp tọa độ. Các nguyên nhân thường gặp là dữ liệu chỉ xuất detection, khiến trường này bị thiếu hoặc rỗng, và mã hóa run-length của COCO ({"counts": ..., "size": ...}), được các trình xuất bitmask như SAM ghi ra; danh sách tọa độ phẳng không có danh sách polygon bao ngoài, đường bao chỉ có một hoặc hai điểm và các giá trị sai định dạng khác cũng được xử lý tương tự. Một annotation giữ lại các polygon còn tồn tại và chuyển sang một dòng segment có hình dạng như bounding box khi không còn polygon nào, vì vậy các nhãn vẫn hợp lệ nhưng những dòng đó không chứa thông tin chi tiết về mask.
Giải pháp: Xuất lại chú thích với segmentation dạng polygon, giải mã mask RLE thành polygon trước khi chạy convert_coco(), hoặc sửa các giá trị segmentation sai định dạng.
Khoảng trống ID lớp trong các nhãn đã chuyển đổi#
Nếu ID lớp trong các file nhãn không liên tiếp (ví dụ: 0, 4, 9 thay vì 0, 1, 2), công cụ chú thích của bạn đang sử dụng các giá trị category_id không liên tiếp.
Giải pháp: Xác minh ID lớp trong các file .txt khớp với dictionary names trong dataset.yaml. Nếu cần, hãy ánh xạ lại ID thành các giá trị liên tiếp.
Để biết đầy đủ chi tiết về API và mô tả tham số, hãy xem tài liệu tham chiếu API convert_coco.
FAQ#
Sử dụng hàm
convert_coco()từ Ultralytics để chuyển đổi chú thích COCO JSON sang định dạng.txtcủa YOLO. Đặtcls91to80=Falsecho 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 sắp xếp lại các file nhãn để
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.Điều này xảy ra vì
convert_coco()lưu nhãn vào một thư mục con bên trongsave_dir/labels/(ví dụ:save_dir/labels/train/) thay vì trực tiếp vàolabels/train/của dataset, nằm cùng cấp vớiimages/train/. YOLO yêu cầu nhãn nằm song song với ảnh — ví dụ:images/train/img.jpgcần cólabels/train/img.txt. Hãy di chuyển các nhãn đã chuyển đổi để khớp với cấu trúc này. Xem cách khắc phục cấu trúc thư mục.Tham số
cls91to80kiểm soát cách ánh xạ các giá trịcategory_idcủa COCO sang ID lớp YOLO. KhiTrue(mặc định), tham số này áp dụng bảng tra cứucoco91_to_coco80_class()được thiết kế cho dataset COCO tiêu chuẩn, gồm 80 lớp với các ID không liên tiếp (1-90). Với dataset tùy chỉnh, luôn đặtcls91to80=False— tham số này chỉ cần trừ 1 khỏi mỗicategory_idđể tạo ID lớp được đánh chỉ mục từ 0.Không nếu không viết code tùy chỉnh. Pipeline huấn luyện mặc định yêu cầu các nhãn
.txtcủa YOLO với một file cho mỗi ảnh, vì vậy bạn có thể chạyconvert_coco()và làm theo hướng dẫn từng bước này, hoặc tạo subclass cho dataset để phân tích COCO JSON ngay trong quá trình chạy — xem Huấn luyện YOLO trên COCO JSON không cần chuyển đổi. Để biết thêm về các định dạng được hỗ trợ, hãy xem các định dạng dataset.Có, sử dụng
use_segments=Truekhi gọiconvert_coco()để đưa mask segmentation dạng polygon 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 model segmentation 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 chú thích keypoint COCO cho quá trình huấn luyện ước tính tư thế: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, chỉ keypoint được ghi vào các file nhãn — các segment sẽ bị bỏ qua mà không có thông báo.