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

ポーズモデルにラベルを付けて学習する

可視性を含むキーポイントラベルを作成し、kpt_shapeとflip_idxを正しく設定し、COCO8-poseで学習して、信頼度で判定した関節角度をキーポイントから算出します。

ポーズ推定では、人物を番号付きの17個の点に変換します。点からは、製品で提供される数値、たとえば人間工学向けの肘の角度、リハビリ向けの膝の角度、転倒検出向けの腰の高さを算出できます。2つのデータセットキーによって、これらの点を信頼できるかどうかが決まります。どちらか一方でも間違えるとエラーは発生せず、左右を取り違えるモデルになったり、最も低コストのデータ拡張を使わずに学習したりします。

成果

coco8-pose.yaml を使って yolo26n-pose.pt を学習し、result.keypoints から人物ごとの左右の肘の角度を計算します。モデルの信頼度が低い関節はスキップします。

短期集中コース
すでに使い方をご存じの方は、こちらをご覧ください。
  1. ラベル行:クラス、バウンディングボックス(4つの値)、続いてキーポイントごとの x y v。すべて正規化されています。v は0(ラベルなし)、1(遮蔽)、2(可視)です。

  2. kpt_shape: [K, 3] と、左右のすべての対応点を入れ替える flip_idx を設定します。

  3. yolo pose train data=coco8-pose.yaml model=yolo26n-pose.pt epochs=1 imgsz=320.

  4. result.keypoints.xy と .conf を読み取り、関節の信頼度がしきい値を下回る計算はスキップします。

実践的

キーポイントのラベル行#

3人のランナーそれぞれに人物バウンディングボックスと17個のキーポイントからなる骨格が描画された、Ultralytics YOLOのポーズ推定

ポーズのラベル行は、キーポイントを追加した検出ラベル行です。すべての座標は0~1に正規化され、キーポイントはすべての行で同じ固定順序で並びます。実際のCOCO8-poseの行を、最初の4つのキーポイントまで抜粋します。

0 0.671279 0.617945 0.645759 0.726859 0.519751 0.381250 2.000000 0.550936 0.348438 2.000000 0.488565 0.367188 2.000000 0.642412 0.354687 2.000000 ...
値意味
0クラスインデックス(person)
0.671279 0.617945 0.645759 0.726859バウンディングボックスの中心x、中心y、幅、高さ
0.519751 0.381250 2キーポイント0(鼻):x、y、可視性
…残り16個のキーポイントも同じ形式:1行あたり5 + 17 × 3 = 56個の値

可視性フラグはCOCOの規則に従います。2は可視、1はラベル付け済みだが遮蔽、0はラベルなしです。Ultralyticsは損失で v = 0 のキーポイントをマスクするため、手首が欠損していても、(0, 0)に手首を予測するようモデルが学習することはありません。

ケーキを切り分ける人々に17個のキーポイントの骨格が描画された、COCO8-pose画像用のUltralytics Platformアノテーションエディター

位置を特定できる遮蔽された関節にはラベルを付ける

テーブルの後ろに隠れた腰や、背中の後ろに隠れた手首など、確信を持って位置を特定できる遮蔽された関節には1を使ってください。これらもトレーニングに使われます。誰にも位置を特定できない関節に限り0を使ってください。隠れた関節すべてに0を付けると、モデルが必要とする難しいケースをまさに捨てることになります。

ポーズデータセットガイドでは、可視性のないデータセット向けの2値形式(x yのみ、kpt_shape: [K, 2])についても説明しています。

kpt_shapeとflip_idx#

データセットのYAMLには、通常の path、train、val、names に加えて、2つのキーを追加します。

kpt_shape: [17, 3] # 17 keypoints, each x, y, visibility
flip_idx: [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15]

立っている人物のCOCO 17キーポイントの順序。0の鼻から16の右足首まで番号が付いています

flip_idx は、左右反転のデータ拡張で、各キーポイントが鏡像内のどのキーポイントに対応するかを指定します。人物を鏡像にすると左手首(9)は右手首(10)の位置に来るため、flip_idx の位置9には10、位置10には9が入ります。鼻(0)は自分自身に対応します。すべてのデータ拡張はピクセルに合わせて骨格を移動させますが、左右反転だけはインデックスマップも必要です。

1人のランナーに対するポーズ拡張: 元画像、水平反転、回転、スケール、HSVカラー、モザイクと、各変換に追従するキーポイントの骨格

インストール済みのソースで確認した、flip_idxが誤っている場合の動作:

  • 欠落している場合: 学習時に警告が記録され、fliplrとflipudが0に設定されるため、最も低コストなデータ拡張を利用できなくなります。
  • 長さが不正な場合: 学習は不一致を示すValueErrorで停止します。これだけが明確に通知されるエラーです。
  • 長さは正しいが、対応関係が誤っている場合: 警告は出ません。反転されたサンプルの半分で、モデルは左と右が逆だと学習し、検証画像では対称なポーズの左右が入れ替わって表示されます。

COCO8-poseで学習する#

COCO8-poseの学習モザイク: バイクに乗る人、ピンクの傘の下にいる人、スケートパークにいる人、馬に乗る人。それぞれに人物ボックスと色分けされたキーポイントの骨格が付いています

from ultralytics import YOLO

model = YOLO("yolo26n-pose.pt")
model.train(data="coco8-pose.yaml", epochs=1, imgsz=320, device="cpu")
metrics = model.val()
print(f"box  mAP50-95: {metrics.box.map:.3f}")
print(f"pose mAP50-95: {metrics.pose.map:.3f}")
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95)     Pose(P          R      mAP50  mAP50-95)
                   all          4         14          1      0.783      0.893      0.643      0.966      0.571      0.575      0.326
box  mAP50-95: 0.643
pose mAP50-95: 0.326

セグメンテーションと同様に、テーブルは2つのブロックに分かれています。Boxは人物ボックスを評価し、Poseは同じmAPの仕組みを使って、Object Keypoint Similarity (OKS)でキーポイントの配置を評価します。Pose mAP50-95はポーズ製品のリリース判定基準です。次のレッスンでは、OKSがキーポイントの正しさをどのように判定するかを説明します。

キーポイントから関節角度へ#

result.keypointsには、COCOの順序で、xy(N × K × 2、ピクセル単位)、xyn(正規化済み)、conf(N × K)が格納されています。関節の角度は、そこで交わる2つの肢ベクトル間の角度です。内積を使って求めます:

import numpy as np

from ultralytics import YOLO

model = YOLO("yolo26n-pose.pt")
result = model("https://ultralytics.com/images/bus.jpg")[0]
kpts = result.keypoints.cpu().numpy()  # people x 17 x (x, y, conf)

SHOULDER, ELBOW, WRIST = 5, 7, 9  # left arm, COCO keypoint order
for person, (xy, conf) in enumerate(zip(kpts.xy, kpts.conf)):
    if conf[[SHOULDER, ELBOW, WRIST]].min() < 0.5:
        print(f"person {person}: left arm not visible enough")
        continue
    a, b, c = xy[SHOULDER], xy[ELBOW], xy[WRIST]
    cos = np.dot(a - b, c - b) / (np.linalg.norm(a - b) * np.linalg.norm(c - b))
    print(f"person {person}: left elbow angle {np.degrees(np.arccos(cos)):.0f} deg")
person 0: left elbow angle 71 deg
person 1: left elbow angle 96 deg
person 2: left elbow angle 59 deg
person 3: left arm not visible enough
person 4: left arm not visible enough

順に説明します。kpts.xy[p]は、人物pの17個の点をピクセル単位で表します。a - bとc - bは、肘を始点とする上腕と前腕のベクトルです。正規化された内積は、それらの間の角度の余弦になります。腕を伸ばすと約180°、完全に曲げると約30°になります。人物3と4は、少なくとも1つの左腕の関節の信頼度が0.5未満のため、スクリプトは測定を行いません。

すべての関節を信頼度で判定する

症状: 腕がフレーム外にある人物について、角度が確からしく見えます。原因: モデルは、関節が遮蔽されていても、すべてのキーポイントの座標を返します。対処: 関節の信頼度がしきい値(ここでは0.5)を下回る場合は計算をスキップし、スキップの頻度を記録してください。スキップ率の上昇は、カメラや照明の変化を早期に示す兆候です。

試してみる

coco8-pose.yamlで1エポック学習し、ボックスとポーズのmAP50-95を記録してください。次にpose.pyを拡張して右肘の角度も出力し(キーポイント6、8、10)、バスの画像で両腕を測定できる人物を列挙してください。

完了条件
以下のすべてを満たすと、レッスンは完了です。
  • 学習が完了し、ボックスとポーズのmAP50-95を記録しました。

  • スクリプトは人物ごとに左右両方の肘の角度を出力し、信頼度の下限を下回る腕はスキップします。

  • flip_idxが欠落している場合、長さが不正な場合、対応関係が誤っている場合に何が起きるかを説明できます。

解答を表示
import numpy as np

from ultralytics import YOLO

ARMS = {"left": (5, 7, 9), "right": (6, 8, 10)}  # shoulder, elbow, wrist
kpts = YOLO("yolo26n-pose.pt")("https://ultralytics.com/images/bus.jpg")[0].keypoints.cpu().numpy()

for person, (xy, conf) in enumerate(zip(kpts.xy, kpts.conf)):
    for side, (s, e, w) in ARMS.items():
        if conf[[s, e, w]].min() < 0.5:
            continue
        a, b, c = xy[s], xy[e], xy[w]
        cos = np.dot(a - b, c - b) / (np.linalg.norm(a - b) * np.linalg.norm(c - b))
        print(f"person {person}: {side} elbow {np.degrees(np.arccos(cos)):.0f} deg")
person 0: left elbow 71 deg
person 0: right elbow 94 deg
person 1: left elbow 96 deg
person 2: left elbow 59 deg
person 2: right elbow 135 deg

人物0と2は両腕を測定できます。人物1の右腕と、人物3および4の両腕は信頼度の下限0.5を下回るため、スクリプトは推測せずにスキップします。

flip_idxが欠落すると、警告とともに反転拡張が無効になります。長さが不正だと、ValueErrorで学習が停止します。正しい形式に見えるflip_idxでも対応関係が誤っていると、ミラー反転したラベルで警告なく学習し、検証時に左右の入れ替わりとして現れます。

次のステップ

次に、人物以外のオブジェクトのキーポイントを設計し、OKSがキーポイントの正しさをどのように判定するかを学びます。