Ultralytics YOLO27: Tham gia danh sách chờ.
Get Started
Bài họctrung cấp

Keypoint tùy chỉnh và chỉ số OKS

Thiết kế schema keypoint cho các đối tượng không phải người, viết và kiểm tra YAML của dataset, đồng thời thiết lập sigma OKS dựa trên độ chính xác có thể đạt được khi đặt từng điểm.

Pose không chỉ dành cho người. Đầu càng xe nâng, các góc của nhãn bưu kiện, mũi và đuôi động vật — bất kỳ đối tượng nào có các điểm ổn định, dễ gọi tên đều có thể trở thành dataset pose. Schema bạn viết ngay từ đầu sẽ được cố định: mọi nhãn, mọi cặp lật và mọi metric đều phụ thuộc vào schema đó; thay đổi về sau đồng nghĩa phải gán nhãn lại toàn bộ. Bài học này giúp hoàn thiện schema trước khi có người gán nhãn hàng nghìn ảnh, đồng thời giải thích chính xác cách metric đánh giá schema.

Kết quả

Tạo YAML dataset cho schema keypoint tùy chỉnh, gồm các cặp lật và sigma OKS; một trình kiểm tra nhãn chạy thành công trên dữ liệu thực; và dự đoán số pixel sai lệch mà mỗi sigma cho phép.

Lộ trình cấp tốc
Nếu bạn đã nắm rõ cách thực hiện, đây là phiên bản tóm tắt.
  1. Chọn 3–12 điểm rõ ràng, xuất hiện trong mọi pose và được code downstream sử dụng.

  2. Sắp xếp thứ tự, rồi viết flip_idx bằng cách hoán đổi các điểm đối xứng trái-phải; các điểm ở giữa ánh xạ về chính chúng.

  3. Thêm kpt_oks_sigmas khi độ chính xác mà người gán nhãn có thể đặt các điểm khác nhau.

  4. Chạy trình kiểm tra nhãn để kiểm tra số cột, giá trị visibility và flip_idx tự nghịch đảo trước khi huấn luyện.

Thực hành

Thiết kế schema keypoint#

Keypoint tốt có ba đặc tính: rõ ràng (hai người gán nhãn đặt điểm ở cùng một vị trí), ổn định (điểm tồn tại trong mọi pose của đối tượng) và hữu ích (có phép tính downstream sử dụng điểm đó). Hãy lấy ví dụ xe nâng nhìn từ camera gắn trên trần, với câu hỏi cần trả lời là "càng xe có được nâng lên khi xe di chuyển không?":

Chỉ sốKeypointLý do tồn tạiĐiểm đối xứng khi lật
0left_fork_tipPhạm vi và hướng của càng xe1
1right_fork_tipPhạm vi và hướng của càng xe0
2mast_topChiều cao càng xe so với trụ nâng2 (chính nó)
3mast_baseĐiểm tham chiếu trên khung xe3 (chính nó)

Mỗi điểm đều có lý do tồn tại vì góp phần vào phép tính: đầu càng xác định hướng, còn cặp điểm trên trụ nâng xác định chiều cao. Đánh giá từng điểm ứng viên theo cùng một bộ tiêu chí:

Đừng thêm điểm chỉ để "phòng khi cần": mỗi điểm làm tăng thời gian gán nhãn cho mọi đối tượng, còn những điểm không được sử dụng chỉ làm tăng nhiễu cho metric. Các dataset công khai cho thấy nhiều lựa chọn: Tiger-Pose dùng 12 điểm không có visibility, Dog-Pose dùng 24 điểm và Hand Keypoints dùng 21 điểm:

Schema keypoint bàn tay gồm 21 mốc được đánh số, từ 0 là cổ tay đến các khớp ngón tay và 20 là đầu ngón út

Ví dụ Dog-Pose: chó con được gán nhãn bằng bounding box chó và các keypoint nhiều màu trên mặt, chân và thân

Viết YAML cho dataset#

path: forklift-pose
train: images/train
val: images/val

kpt_shape: [4, 3] # 4 keypoints, each x, y, visibility
flip_idx: [1, 0, 2, 3] # fork tips swap, mast points stay
kpt_oks_sigmas: [0.05, 0.05, 0.08, 0.08] # tips are sharp, mast ends are fuzzier

names:
  0: forklift

kpt_names:
  0:
    - left_fork_tip
    - right_fork_tip
    - mast_top
    - mast_base
  • kpt_names mô tả thứ tự keypoint cho từng class, bao gồm người và dụng cụ.
  • flip_idx phải là phép biến đổi tự nghịch đảo: áp dụng hai lần sẽ đưa mọi chỉ số về chính nó.
  • kpt_oks_sigmas là tùy chọn và dùng để đặt trọng số cho cả loss keypoint khi huấn luyện lẫn OKS khi validation; nếu có, danh sách phải chứa một giá trị dương cho mỗi keypoint, nếu không run sẽ dừng với một ValueError.

Trên Ultralytics Platform, schema tương ứng là một mẫu bộ khung xương. Có thể tạo mẫu mới từ một mẫu có sẵn (Bắt đầu từ…), nhờ đó bạn chỉ cần đổi tên và xóa bớt điểm thay vì đặt từng keypoint bằng tay — hãy giữ nguyên thứ tự chỉ số trong bảng schema:

Hộp thoại New Skeleton Template của Ultralytics Platform: bộ khung xương người 17 điểm được tạo từ mẫu có sẵn, hiển thị danh sách keypoint từ nose đến left_wrist để đổi tên

Kiểm tra nhãn trước khi huấn luyện#

Dataset pose có thể gặp lỗi mà không báo rõ: một hàng thiếu một keypoint sẽ làm lệch mọi giá trị phía sau, còn visibility bằng 3 do exporter lỗi sẽ được đọc như mọi con số khác. Trình kiểm tra này tải YAML bằng loader của chính Ultralytics và xác thực từng hàng. Có thể chạy nguyên trạng trên COCO8-pose; thay bằng đường dẫn YAML của bạn:

from pathlib import Path

from ultralytics.data.utils import check_det_dataset

data = check_det_dataset("coco8-pose.yaml")  # resolves paths, downloads if needed
k, dims = data["kpt_shape"]
flip = data.get("flip_idx", [])
assert not flip or sorted(flip) == list(range(k)), "flip_idx must list every keypoint index once"
assert all(flip[flip[i]] == i for i in range(len(flip))), "flip_idx must undo itself: pairs swap, center points stay"

expected = 5 + k * dims  # class + 4 box values + k keypoints of `dims` values
bad = 0
for label in Path(data["path"], "labels").rglob("*.txt"):
    for n, row in enumerate(label.read_text().splitlines(), 1):
        values = row.split()
        if len(values) != expected:
            bad += 1
            print(f"{label.name}:{n} has {len(values)} values, expected {expected}")
        elif dims == 3 and not {float(v) for v in values[7::3]} <= {0, 1, 2}:
            bad += 1
            print(f"{label.name}:{n} has a visibility flag outside 0/1/2")
print(f"kpt_shape={data['kpt_shape']}, flip_idx ok, {bad} bad rows")
kpt_shape=[17, 3], flip_idx ok, 0 bad rows

values[7::3] bắt đầu từ cờ visibility đầu tiên (class, bốn giá trị box, sau đó là x y v) và lấy mỗi giá trị thứ ba kể từ đó.

Cách OKS tính điểm cho keypoint#

Pose mAP thay IoU của box bằng Độ tương đồng điểm khóa đối tượng (OKS). Với mỗi keypoint được gán nhãn, OKS chuyển khoảng cách pixel giữa dự đoán và nhãn thành điểm từ 0 đến 1, được điều chỉnh theo kích thước đối tượng và sigma của keypoint đó, rồi tính trung bình trên các keypoint đã gán nhãn. Pose mAP50-95 tính trung bình precision tại các ngưỡng OKS từ 0.50 đến 0.95, giống hệt cách box mAP tính trên các ngưỡng IoU; hướng dẫn về metric hiệu năng giải thích cơ chế chung này.

Cách dễ nhất để hiểu sigma là đo lường chúng. Script này chấm điểm cùng một dự đoán xe nâng với hai giá trị sigma, sử dụng hàm kpt_iou mà bộ kiểm định pose gọi:

import torch

from ultralytics.utils.metrics import kpt_iou

# One labeled forklift, 4 keypoints (x, y, visibility) in pixels, inside a 200 x 120 px box
label = torch.tensor([[[40.0, 30, 2], [40, 90, 2], [170, 20, 2], [170, 100, 2]]])
area = torch.tensor([200 * 120 * 0.53])  # Ultralytics scales box area by 0.53 to approximate object area

for offset in (2, 5, 10, 20):
    pred = label.clone()
    pred[..., 0] += offset  # every predicted keypoint lands `offset` pixels to the right
    sharp = kpt_iou(label, pred, area, sigma=[0.025] * 4).item()
    loose = kpt_iou(label, pred, area, sigma=[0.10] * 4).item()
    print(f"{offset:2d} px off: OKS {sharp:.2f} with sigma 0.025, {loose:.2f} with sigma 0.10")
 2 px off: OKS 0.94 with sigma 0.025, 1.00 with sigma 0.10
 5 px off: OKS 0.67 with sigma 0.025, 0.98 with sigma 0.10
10 px off: OKS 0.21 with sigma 0.025, 0.91 with sigma 0.10
20 px off: OKS 0.00 with sigma 0.025, 0.67 with sigma 0.10

Với xe nâng kích thước 200 × 120 pixel, dự đoán lệch 10 pixel không đạt ngưỡng OKS 0.50 với sigma 0.025, nhưng đạt mọi ngưỡng đến 0.90 với sigma 0.10. Cùng model, cùng sai số, nhưng pose mAP rất khác nhau.

SigmaTác độngDùng cho
Nhỏ (≈ 0.025–0.05)Sai lệch vài pixel đã làm giảm điểmCác điểm sắc nét: mắt, đầu càng, góc
Lớn (≈ 0.08–0.10)Dung sai rộngCác điểm khó xác định: hông, giữa trụ nâng
Mặc địnhSigma COCO cho kpt_shape: [17, 3]; nếu không có thì dùng 1/K cho mọi điểmbắt đầu từ đây, sau đó đo lường

Sigma nên phản ánh mức độ nhất quán khi người gán nhãn đặt từng điểm. Hãy đo lường: yêu cầu hai người gán nhãn cùng 50 ảnh rồi so sánh khoảng cách theo từng keypoint. Những điểm họ không thống nhất nên có sigma lớn hơn; nếu không, metric sẽ phạt model vì nhiễu do con người.

Chỉ có thể so sánh metric trong cùng một schema

Thay đổi kpt_oks_sigmas làm thay đổi pose mAP dù model không đổi, như bảng trên cho thấy. Hãy cố định sigmas trước baseline đầu tiên, ghi lại sigmas cùng mỗi kết quả và không bao giờ so sánh các lần chạy được validation bằng các giá trị khác nhau.

Dùng thử

Thiết kế schema keypoint cho một đối tượng thuộc lĩnh vực của bạn: liệt kê 3–8 keypoint, mỗi điểm có một dòng giải thích, điểm đối xứng khi lật và sigma. Viết YAML, chạy check_pose_labels.py trên COCO8-pose rồi trên một số nhãn của riêng bạn, và dùng oks.py để xác định sai lệch bao nhiêu pixel khiến điểm sắc nét nhất của bạn giảm xuống dưới OKS 0.5.

Điều kiện hoàn thành
Bạn đã hoàn thành bài học khi tất cả các điều kiện sau đều đúng.
  • Bảng schema của bạn nêu tên từng keypoint, mục đích, điểm đối xứng khi lật và sigma.

  • YAML của bạn có kpt_shape, flip_idx tự nghịch đảo và (nếu dùng) một sigma dương cho mỗi keypoint; trình kiểm tra báo 0 hàng lỗi trên mẫu của bạn.

  • Bạn có thể nêu sai lệch pixel khiến keypoint sắc nét nhất của bạn không còn được tính ở OKS 0.5.

Hiển thị lời giải

Ví dụ về bưu kiện trên băng chuyền, nhìn từ trên xuống, với câu hỏi "mặt có nhãn có hướng lên trên không và nhãn được xoay theo hướng nào?":

Chỉ sốKeypointMục đíchĐiểm đối xứng khi lậtSigma
0label_top_leftHướng của nhãn10.04
1label_top_rightHướng của nhãn00.04
2label_bottom_rightHướng của nhãn30.04
3label_bottom_leftHướng của nhãn20.04
kpt_shape: [4, 3]
flip_idx: [1, 0, 3, 2]
kpt_oks_sigmas: [0.04, 0.04, 0.04, 0.04]

flip_idx vượt qua bước kiểm tra tự nghịch đảo: 0 → 1 → 0 và 2 → 3 → 2. Nhãn bị lật sẽ hoán đổi trái và phải nhưng giữ nguyên trên và dưới, vì vậy góc trên bên trái ghép cặp với góc trên bên phải, không phải góc dưới bên phải.

Để tìm dung sai, đặt sigma=[0.04] * 4 trong oks.py rồi mở rộng các độ lệch; bounding box của nhãn xác định tỷ lệ, vì vậy hãy dùng box có kích thước tương đương nhãn bưu kiện điển hình trong ảnh của bạn. Độ lệch tại đó OKS lần đầu giảm dưới 0.5 là sai số lớn nhất mà độ chính xác của người gán nhãn cho phép model chịu được.

Tiếp theo là gì

Tiếp theo, chúng ta sẽ xử lý các đối tượng cần quan tâm đến hướng nhưng có hình dạng đơn giản như hình chữ nhật: bounding box xoay.

Đăng nhập để lưu tiến độ