Cách chuyển đổi chú giải COCO sang định dạng YOLO
Việc huấn luyện model Ultralytics YOLO yêu cầu chú giải ở định dạng YOLO, nhưng nhiều công cụ chú giải 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ú giải COCO sang định dạng YOLO và bắt đầu huấn luyện model phát hiện đối tượng, phân đoạn đối tượng và ước lượng tư thế.
Để huấn luyện trực tiếp trên COCO JSON mà không tạo các tệp .txt, hãy xem Huấn luyện YOLO trên COCO JSON mà không cần chuyển đổi.
Vì sao cần chuyển đổi từ COCO sang YOLO?#
Định dạng COCO JSON lưu tất cả chú giải trong một tệp, trong khi YOLO sử dụng một tệp văn bản cho mỗi ảnh với tọa độ đã chuẩn hóa. Cần chuyển đổi vì:
- Model YOLO yêu cầu các tệp nhãn
.txt, mỗi ảnh một tệp, chứaclass x_center y_center width heightvới 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 tệp 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 bắt đầu từ 0.
| Tính năng | COCO JSON | YOLO TXT |
|---|---|---|
| Cấu trúc | Một tệp JSON duy nhất cho tất cả ảnh | Một file .txt cho mỗi ảnh |
| Định dạng BBox | [x_min, y_min, width, height] theo pixel | class x_center y_center width height được 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ỉ số từ 0 (bắt đầu từ 0) |
| Phân đoạn | Mảng polygon hoặc mask RLE trong trường segmentation | Tọa độ polygon sau ID lớp |
| Keypoint | [x, y, visibility, ...] theo pixel | [x, y, visibility, ...] được chuẩn hóa |
Bắt đầu nhanh#
Cách nhanh nhất để chuyển đổi annotation COCO và bắt đầu huấn luyện:
from ultralytics.data.converter import convert_coco
convert_coco(
labels_dir="my_dataset/annotations/", # thư mục chứa các tệp JSON của bạn
save_dir="my_dataset/converted/", # nơi lưu các nhãn đã chuyển đổi
cls91to80=False, # đặt thành False cho dataset tùy chỉnh (xem cảnh báo bên dưới)
)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 tục thành 80 ID lớp liên tục. Với mọi dataset tùy chỉnh, bạn phải đặt cls91to80=False — nếu không, ID lớp của bạn sẽ bị ánh xạ sai mà không có thông báo, khiến model học sai lớp.
Hướng dẫn chuyển đổi từng bước#
1. Chuẩn bị dataset COCO#
Dataset định dạng COCO điển hình được xuất từ các công cụ gán nhãn 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 tệp 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 annotation#
Sử dụng hàm convert_coco() để chuyển đổi annotation COCO JSON 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 tệp .txt cho mỗi ảnh được gán nhãn vào thư mục con labels/, có tên dựa trên từng tệp JSON sau khi bỏ tiền tố instances_ (do đó instances_train.json tạo ra labels/train/). Các ảnh không có annotation sẽ bị bỏ qua và không có tệp nhãn, vì vậy cây thư mục labels/ có thể không phản chiếu đầ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 đè lên 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 nhãn cũ.
3. Sắp xếp cấu trúc thư mục#
Sau khi chuyển đổi, cần đặt các tệp nhãn cạnh các ảnh. YOLO yêu cầu thư mục labels/ có cấu trúc phản chiếu thư mục images/:
import shutil
from pathlib import Path
converted_dir = Path("my_dataset/converted/labels")
dataset_dir = Path("my_dataset")
# convert_coco đặt tên cho từng thư mục con theo tệp JSON tương ứng (bỏ tiền tố "instances_"),
# vì vậy hãy lặp qua các thư mục con thực tế thay vì giả định tên "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ẽ trông như sau:
my_dataset/
├── images/
│ ├── train/
│ │ ├── img_001.jpg
│ │ └── ...
│ └── val/
│ └── ...
├── labels/
│ ├── train/
│ │ ├── img_001.txt
│ │ └── ...
│ └── val/
│ └── ...
└── dataset.yaml4. Tạo dataset.yaml#
Tạo tệp cấu hình dataset.yaml ánh xạ các danh mục COCO thành tên lớp YOLO. Tệp 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
from ultralytics.utils import YAML
# Đọc danh mục từ tệp JSON COCO
with open("my_dataset/annotations/instances_train.json") as f:
coco = json.load(f)
# Tạo tên lớp khớp với đầu ra của convert_coco (category_id - 1)
categories = sorted(coco["categories"], key=lambda x: x["id"])
names = {cat["id"] - 1: cat["name"] for cat in categories}
# LƯU Ý: convert_coco ánh xạ ID lớp theo category_id - 1, vì vậy category_id phải
# bắt đầu từ 1. Nếu danh mục của bạn bắt đầu từ 0, trước tiên hãy cộng 1 vào từng ID.
# Tạo dataset.yaml
dataset = {
"path": str(Path("my_dataset").resolve()),
"train": "images/train",
"val": "images/val",
"names": names,
}
YAML.save("my_dataset/dataset.yaml", dataset)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 model YOLO#
Sau khi dataset đã chuyển đổi sẵn sàng, hãy huấn luyện model YOLO:
from ultralytics import YOLO
model = YOLO("yolo26n.pt") # tải model pretrained
results = model.train(data="my_dataset/dataset.yaml", epochs=100, imgsz=640)Để biết mẹo và các 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 tệp nhãn để xác nhận ID lớp và tọa độ 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, có thể JSON COCO của bạn 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ì hàm này á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 được nhưng phát hiện sai class đối tượng, có thể bạn đang dùng cls91to80=True (mặc định) trên một custom dataset. Tùy chọn này ánh xạ các giá trị category_id thông qua bảng tra cứu COCO 91-to-80, chỉ chính xác với dataset COCO tiêu chuẩn. category_id không có đối ứ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 dùng cls91to80=False cho dataset tùy chỉnh.
Không tìm thấy nhãn khi huấn luyện#
Nếu quá trình quét nhãn báo 0 images, N backgrounds và sau đó huấn luyện bị hủy với ValueError: train: No labels found in .../labels/train.cache, các tệp 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 tệp nhãn để khớp với cấu trúc thư mục được mong đợi. Đảm bảo labels/train/ nằm cùng cấp với images/train/.
KeyError trong quá trình chuyển đổi#
Nếu gặp KeyError: 'bbox' hoặc lỗi tương tự khi chạy convert_coco(), có thể labels_dir của bạn chứa các tệp JSON không phải annotation instance (ví dụ: captions_train2017.json) có cấu trúc annotation khác.
Giải pháp: Chỉ đặt các tệp JSON annotation instance (ví dụ: instances_train2017.json) trong labels_dir.
Tệp nhãn rỗng sau khi chuyển đổi#
Nếu quá trình chuyển đổi hoàn tất nhưng các tệp .txt trống hoặc không tồn tại, tất cả chú thích có thể có iscrowd: 1 mà convert_coco() bỏ qua (ví dụ, CVAT xuất chú thích mask dưới dạng vùng crowd), 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 bản export chỉ có detection sẽ 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 giá trị iscrowd trong chú thích JSON. Khi mỗi mask được gắn cờ crowd bao quanh một đối tượng duy nhất, như trong các bản export mask của CVAT, hãy đặt iscrowd: 0 để use_segments=True chuyển đổi mask RLE; giữ nguyên cờ cho các vùng crowd thực sự bao phủ nhiều đối tượng. Nếu bạn đã truyền use_keypoints=True, hãy xác nhận rằng chú thích của bạn thực sự có keypoints.
Polygon có hình dạng như box từ annotation mask#
Nếu use_segments=True ghi nhật ký annotations without a usable polygon, một số chú thích không có giá trị segmentation, một mã hóa run-length COCO ({"counts": ..., "size": ...}) có size không khớp với [height, width] của ảnh hoặc có mask rỗng hay chỉ chứa các vùng quá nhỏ hoặc quá mảnh để phác thảo bằng ba điểm, hoặc một giá trị không phải RLE cũng không phải danh sách có ít nhất ba cặp tọa độ. Nguyên nhân thường gặp là bản export chỉ có detection, khiến trường này bị thiếu hoặc để trống; danh sách tọa độ phẳng không có danh sách polygon bao ngoài, đường viền 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ự. Chú thích giữ lại các polygon còn hợp lệ và dùng một hàng segment có hình dạng như bounding box nếu không còn polygon nào, vì vậy nhãn vẫn hợp lệ nhưng các hàng đó không chứa thông tin mask chi tiết.
Các giá trị RLE có counts bị thiếu hoặc không thể giải mã, chẳng hạn như chuỗi bị cắt cụt hoặc các run không cộng lại thành size, sẽ khiến quá trình chuyển đổi dừng và báo lỗi thay vì dùng phương án dự phòng.
Giải pháp: Xuất lại chú thích với segmentation dạng polygon hoặc RLE, hoặc sửa mọi giá trị segmentation sai định dạng.
ID lớp bị khuyết trong nhãn đã chuyển đổi#
Nếu ID lớp trong tệp nhãn không liên tục (ví dụ: 0, 4, 9 thay vì 0, 1, 2), công cụ gán nhãn 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 ID lớp trong các tệp .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 tục.
Để xem đầy đủ chi tiết API và mô tả tham số, hãy tham khảo tài liệu tham chiếu API convert_coco.
Câu hỏi thường gặp#
Sử dụng hàm
convert_coco()của Ultralytics để chuyển đổi annotation COCO JSON sang định dạng YOLO.txt. Đặ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, sắp xếp lại các tệp nhãn để
labels/phản chiếu thư mụcimages/, sau đó tạo tệpdataset.yaml. Xem hướng dẫn từng bước để biết toàn bộ quy trì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ì lưu trực tiếp vàolabels/train/của dataset, cạnhimages/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 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ị COCOcategory_idsang 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, có 80 lớp với ID không liên tục (1-90). Với dataset tùy chỉnh, luôn đặtcls91to80=False— tùy chọn này chỉ trừ 1 khỏi mỗicategory_idđể tạo ID lớp đánh chỉ số từ 0.Không thể nếu không có code tùy chỉnh. Pipeline huấn luyện mặc định yêu cầu nhãn YOLO
.txtvới một tệp cho mỗi ảnh, vì vậy hãy chạyconvert_coco()và làm theo hướng dẫn từng bước, hoặc tạo lớp con 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 mà không cần chuyển đổi. Để biết thêm về các định dạng được hỗ trợ, hãy xem định dạng dataset.Có, hãy dùng
use_segments=Truekhi gọiconvert_coco()để đưa mask segmentation vào nhãn YOLO đã chuyển đổi. Segmentation dạng polygon được chuyển đổi trực tiếp; mask mã hóa run-length (RLE), ở dạng nén hoặc không nén, được giải mã bằngfaster-coco-eval(tự động cài đặt khi tệp JSON có chứa mask) thành các đường bao ngoài của vùng. Một đối tượng được chia thành nhiều polygon hoặc vùng mask sẽ được gộp thành một hàng polygon; vì polygon YOLO không thể biểu diễn lỗ, các lỗ trong mask sẽ được lấp đầy. Các chú thích crowd (iscrowd: 1) sẽ bị bỏ qua. Kết quả là các tệp 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 annotation keypoint COCO phục vụ 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 tệp nhãn — segment sẽ bị bỏ qua mà không có thông báo.
Bình luận
Đặt câu hỏi, chia sẻ kết quả hoặc giúp đỡ người khác.