Ultralytics YOLO27:
Get Started

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ế.

Muốn bỏ qua bước chuyển đổi?

Để 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ứa class x_center y_center width height vớ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_id tùy ý, trong khi YOLO yêu cầu ID lớp bắt đầu từ 0.
Tính năngCOCO JSONYOLO TXT
Cấu trúcMột tệp JSON duy nhất cho tất cả ảnhMột file .txt cho mỗi ảnh
Định dạng BBox[x_min, y_min, width, height] theo pixelclass x_center y_center width height được chuẩn hóa (0-1)
ID lớpcategory_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ạnMảng polygon trong trường segmentationTọ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.

Dataset tùy chỉnh: luôn sử dụng `cls91to80=False`

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.json

Mỗ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:

Chuyển COCO sang định dạng 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 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
        └── ...
Chạy lại sẽ tạo một thư mục đầu ra mới

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 sẽ trông như sau:

my_dataset/
├── images/
│   ├── train/
│   │   ├── img_001.jpg
│   │   └── ...
│   └── val/
│       └── ...
├── labels/
│   ├── train/
│   │   ├── img_001.txt
│   │   └── ...
│   └── val/
│       └── ...
└── dataset.yaml

4. 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

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,
}

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 model YOLO#

Sau khi dataset đã chuyển đổi sẵn sàng, hãy huấn luyện model YOLO:

Huấn luyện trên dữ liệu COCO đã chuyển đổi
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}"
Mẹo

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 lớp đối tượng, có thể bạn đang dùng cls91to80=True (mặc định) trên 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, vốn 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 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 rỗng hoặc bị thiếu, có thể mọi annotation đều có iscrowd: 1 (thường gặp ở các mask được tạo bằng 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 xuất chỉ dành cho detection cũng cho kết quả tương tự, vì các annotation 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 annotation JSON. Nếu dùng mask SAM, hãy tiền xử lý JSON để đặt iscrowd: 0. Nếu bạn đã truyền use_keypoints=True, hãy xác nhận annotation thực sự có keypoints.

Polygon có hình dạng như box từ annotation mask#

Nếu use_segments=True ghi log annotations without a usable polygon, một số annotation 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 xuất chỉ dành cho detection, khiến trường này bị thiếu hoặc rỗng, và mã hóa run-length COCO ({"counts": ..., "size": ...}), được các công cụ 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 viền 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ự. Annotation giữ lại các polygon còn hợp lệ; nếu không còn polygon nào, annotation sẽ chuyển sang một dòng segment có hình dạng như bounding box, nên nhãn vẫn hợp lệ nhưng các dòng đó không chứa chi tiết mask.

Giải pháp: Xuất lại annotation 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.

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. Đặt cls91to80=False cho 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ục images/, sau đó tạo tệp dataset.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 trong save_dir/labels/ (ví dụ: save_dir/labels/train/), thay vì lưu trực tiếp vào labels/train/ của dataset, cạnh images/train/. YOLO yêu cầu nhãn nằm song song với ảnh — ví dụ, images/train/img.jpg cầ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ố cls91to80 kiểm soát cách ánh xạ các giá trị COCO category_id sang ID lớp YOLO. Khi True (mặc định), tham số này áp dụng bảng tra cứu coco91_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 đặt cls91to80=False — tùy chọn này chỉ trừ 1 khỏi mỗi category_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 .txt với một tệp cho mỗi ảnh, vì vậy hãy chạy convert_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ó, sử dụng use_segments=True khi gọi convert_coco() để đưa mask segmentation dạng polygon vào nhãn YOLO đã chuyển đổi. Thao tác này tạo ra 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_segments và use_keypoints đều được đặt thành True, 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