Ultralytics YOLO27: ウェイトリストに登録する。
Get Started
レッスン中級

カスタムキーポイントとOKSメトリクス

人間以外のオブジェクト向けのキーポイントスキーマを設計し、データセットYAMLを作成して検証し、各点をどれほど正確に配置できるかに基づいてOKSシグマを設定します。

ポーズの対象は人物だけではありません。フォークリフトのフォーク先端、荷物のラベルの角、動物の鼻や尾など、安定していて名前を付けられる点があるオブジェクトなら、どれでもポーズデータセットにできます。初日に作成するスキーマは恒久的なものです。すべてのラベル、反転ペア、メトリクスがスキーマに依存し、後から変更するとすべてに再ラベル付けが必要になります。このレッスンでは、誰かが何千枚もの画像にラベルを付ける前にスキーマを適切に設計し、メトリクスによる評価方法を具体的に説明します。

成果

反転ペアとOKSシグマを含むカスタムキーポイントスキーマのデータセットYAML、実データで検証に合格するラベルチェッカー、各シグマで許容される誤差ピクセル数の予測を作成します。

短期集中コース
すでに使い方をご存じの方は、こちらをご覧ください。
  1. 曖昧でなく、すべてのポーズに存在し、後続のコードで使用される点を3~12個選びます。

  2. 点の順序を決め、左右の対応点を入れ替えてflip_idxを記述します。中央の点は自分自身に対応させます。

  3. 点によってアノテーターが配置できる精度に差がある場合は、kpt_oks_sigmasを追加します。

  4. 学習前に、列数、可視性の値、flip_idxが自分自身を逆写像することをラベルチェッカーで確認します。

実践的

キーポイントスキーマを設計する#

優れたキーポイントには、次の3つの性質があります。曖昧でない(2人のアノテーターが同じ場所に点を置く)、安定している(オブジェクトが取るすべてのポーズに点が存在する)、有用である(後続の計算で使用される)。天井カメラから見たフォークリフトを例にします。製品上の問いは「トラックが移動している間、フォークが上がっているか」です:

インデックスキーポイント存在する理由反転時の対応点
0left_fork_tipフォークの長さと向き1
1right_fork_tipフォークの長さと向き0
2mast_topマストに対するフォークの高さ2(自身)
3mast_baseシャーシ上の基準点3(自身)

各点には計算に使われる役割があります。フォークの先端から向きがわかり、マストの2点から高さがわかります。候補となる各点を同じ基準で評価してください:

「念のため」の点を追加するのは避けてください。点を1つ追加するごとに、すべてのインスタンスでラベル付けの時間がかかり、誰も使わない点はメトリクスにノイズを加えるだけです。公開データセットを見ると、その範囲がわかります。Tiger-Poseは可視性情報なしで12点、Dog-Poseは24点、Hand Keypointsは21点を使用しています:

手のキーポイントスキーマ。0の手首から各指の関節を経て20の小指の先端まで、21個のランドマークに番号が付いています

Dog-Poseの例: 顔、脚、胴体に色分けされたキーポイントが付いた犬のボックスでラベル付けされた子犬

データセットYAMLを記述する#

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には、クラスごとの人物や器具のキーポイント順序を記述します。
  • flip_idxは自己逆写像である必要があります。2回適用すると、すべてのインデックスが元に戻ります。
  • kpt_oks_sigmasは任意の項目で、学習時のキーポイント損失と検証時のOKSの両方に重みを付けます。指定する場合は、キーポイントごとに正の値を1つ設定する必要があります。設定しないと、実行はValueErrorで停止します。

Ultralytics Platformでは、同じスキーマがスケルトンテンプレートになります。新しいテンプレートは既存のテンプレートから開始できる(既存のテンプレートから開始…)ため、キーポイントを1つずつ手作業で配置するのではなく、名前を変更して不要な点を削除できます。スキーマの表に記載したインデックス順を維持してください:

Ultralytics Platformの新しいスケルトンテンプレートダイアログ: 既存のテンプレートを基に作成した17点の人物スケルトン。名前を変更するため、noseからleft_wristまでのキーポイントが一覧表示されています

学習前にラベルを確認する#

ポーズデータセットの不具合は気づかれにくいものです。キーポイントが1つ欠けた行があると後続の値がすべてずれ、バグのあるエクスポーターが出力した可視性の値3も、他の数値と同様に読み込まれます。このチェッカーはUltralytics独自のローダーでYAMLを読み込み、すべての行を検証します。そのままCOCO8-poseで実行できます。独自のYAMLパスに置き換えてください:

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]は最初の可視性フラグ(クラス、ボックスの4つの値、続いてx y v)から始まり、その後は3つおきに値を取得します。

OKSによるキーポイントのスコアリング#

Pose mAPでは、ボックスのIoUがObject Keypoint Similarity (OKS)に置き換わります。ラベル付きの各キーポイントについて、OKSは予測とラベルのピクセル距離を、オブジェクトのサイズとそのキーポイントのシグマに応じてスケーリングし、0~1のスコアに変換した後、ラベル付きキーポイント全体で平均します。Pose mAP50-95は、IoUのしきい値に対するボックスmAPと同じように、OKSのしきい値0.50~0.95における適合率を平均します。共通の仕組みについてはパフォーマンスメトリクスガイドで説明しています。

シグマは実際に測定すると理解しやすくなります。このスクリプトは、ポーズバリデーターが呼び出すkpt_iou関数を使い、シグマを2通りに設定して同じフォークリフト予測を評価します:

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

幅200 × 高さ120ピクセルのフォークリフトでは、予測が10ピクセルずれていると、シグマ0.025ではOKSしきい値0.50を満たさず、シグマ0.10では0.90までのすべてのしきい値を満たします。同じモデル、同じ誤差でも、Pose mAPは大きく異なります。

シグマ効果用途
小さい(≈ 0.025~0.05)数ピクセルのずれでもスコアが下がる位置が明確な点: 目、フォークの先端、角
大きい(≈ 0.08~0.10)許容範囲が広い位置が曖昧な点: 腰、マストの中央
デフォルトkpt_shape: [17, 3]のCOCOシグマ。それ以外の点にはすべて1/Kを使用まずはここから始めて、測定する

シグマには、各点をアノテーターがどれだけ一貫して配置できるかを反映させてください。実際に測定しましょう。2人に同じ50枚の画像にラベルを付けてもらい、キーポイントごとの距離を比較します。意見が分かれる点には大きなシグマを設定します。そうしないと、メトリクスが人のばらつきをモデルの誤差として評価してしまいます。

指標を比較できるのは同じスキーマ内だけ

上の表が示すように、kpt_oks_sigmas を変更するとモデル自体を変えなくてもpose mAPが変わります。最初のベースラインを取る前にsigmaを固定し、すべての結果とともに記録してください。異なる値で検証した実行結果は比較しないでください。

試してみる

自身の分野のオブジェクトを対象にキーポイントスキーマを設計してください。キーポイントを3~8個挙げ、それぞれに一行の理由、反転時の対応点、シグマを記載します。YAMLを作成し、check_pose_labels.pyをCOCO8-poseと少数の独自ラベルに対して実行します。次にoks.pyを使って、最も位置が明確な点でOKSが0.5を下回るまでに許容される誤差のピクセル数を調べます。

完了条件
以下のすべてを満たすと、レッスンは完了です。
  • スキーマの表には、すべてのキーポイント名、用途、反転時の対応点、シグマが記載されています。

  • YAMLにはkpt_shape、自己逆写像となるflip_idx、および(使用する場合)キーポイントごとに1つの正のシグマが設定されており、サンプルに対してチェッカーが不正な行を0件と報告します。

  • 最も位置が明確なキーポイントが、OKS 0.5でカウントされなくなるピクセル誤差を示せます。

解答を表示

上から見たコンベヤー上の荷物を例にします。確認したいのは「ラベル面が上を向いているか、またどちら向きに回転しているか」です:

インデックスキーポイント目的反転時の対応点シグマ
0label_top_leftラベルの向き10.04
1label_top_rightラベルの向き00.04
2label_bottom_rightラベルの向き30.04
3label_bottom_leftラベルの向き20.04
kpt_shape: [4, 3]
flip_idx: [1, 0, 3, 2]
kpt_oks_sigmas: [0.04, 0.04, 0.04, 0.04]

flip_idxは自己逆写像のチェックに合格します。0 → 1 → 0、2 → 3 → 2となります。ラベルをミラー反転すると左右は入れ替わりますが、上下は変わりません。そのため、左上は右下ではなく右上と対応します。

許容誤差を調べるには、oks.pyでsigma=[0.04] * 4を設定し、オフセットを増やしていきます。ラベルのボックスがスケールの基準となるため、画像内の一般的な荷札と同じサイズのボックスを使ってください。OKSが初めて0.5を下回るオフセットが、アノテーターの精度でモデルに許容される最大誤差です。

次のステップ

次に、向きは重要でも形状が単純な長方形のオブジェクト、つまり回転したバウンディングボックスを扱います。