Ultralytics YOLO27: Tham gia danh sách chờ.
Bắt đầu

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 hoặc mask RLE 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 của bạn 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

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:

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 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. Đặ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ó, hãy dùng use_segments=True khi gọi convert_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ằng faster-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_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.

Người đóng góp
glenn-jocherglenn-jocher5raimbekovmraimbekovm5DNDevulapalli Naga Sri Vaishnavi1pderrengerpderrenger1

Bình luận

Đặt câu hỏi, chia sẻ kết quả hoặc giúp đỡ người khác.