Pose-Modelle kennzeichnen und trainieren
Erstelle Keypoint-Labels mit Sichtbarkeit, setze kpt_shape und flip_idx korrekt, trainiere mit COCO8-pose und wandle Keypoints in Konfidenzschwellen unterliegende Gelenkwinkel um.
Die Posenschätzung bildet jede Person durch 17 nummerierte Punkte ab. Aus diesen Punkten lassen sich Kennzahlen ableiten, die sich für Produkte nutzen lassen: Ellbogenwinkel für Ergonomie, Kniebeugewinkel für die Rehabilitation und Hüfthöhe zur Sturzerkennung. Zwei Schlüssel im Datensatz entscheiden darüber, ob du diesen Punkten vertrauen kannst. Fehler bei einem der beiden verursachen keine Fehlermeldung – das Modell verwechselt lediglich links und rechts oder trainiert unbemerkt ohne die kostengünstigste Augmentierung.
Praxisnah
Die Keypoint-Labelzeile#

Eine Pose-Labelzeile ist eine Erkennungszeile mit angehängten Keypoints. Jede Koordinate ist auf 0–1 normalisiert, und die Keypoints stehen in jeder Zeile in derselben festen Reihenfolge. Eine echte COCO8-pose-Zeile, gekürzt nach vier Keypoints:
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 ...| Werte | Bedeutung |
|---|---|
0 | Klassenindex (Person) |
0.671279 0.617945 0.645759 0.726859 | Mittelpunkt x, Mittelpunkt y, Breite, Höhe der Box |
0.519751 0.381250 2 | Keypoint 0 (Nase): x, y, Sichtbarkeit |
| … | 16 weitere Keypoints im selben Format: 5 + 17 × 3 = 56 Werte pro Zeile |
Die Sichtbarkeitskennzeichnung folgt der COCO-Konvention: 2 sichtbar, 1 gelabelt, aber verdeckt, 0 nicht gelabelt. Ultralytics blendet Keypoints mit v = 0 in der Verlustfunktion aus. So lernt das Modell bei einem fehlenden Handgelenk nicht, Handgelenke an der Position (0, 0) vorherzusagen.

Verwende 1 für einen verdeckten Gelenkpunkt, den du zuverlässig lokalisieren kannst – etwa eine Hüfte hinter einem Tisch oder ein Handgelenk hinter dem Rücken; er kann trotzdem zum Training beitragen. Verwende 0 nur für Gelenkpunkte, die niemand lokalisieren kann. Jeden verdeckten Gelenkpunkt mit 0 zu beschriften, verwirft genau die schwierigen Fälle, die das Modell braucht.
Die Anleitung zu Posendatensätzen behandelt auch die Variante mit zwei Werten (x y allein, kpt_shape: [K, 2]) für Datensätze ohne Sichtbarkeitsangaben.
kpt_shape und flip_idx#
Die YAML-Datei des Datensatzes ergänzt die beiden üblichen Schlüssel path, train, val und names um zwei weitere:
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]
flip_idx gibt der Augmentierung durch horizontales Spiegeln an, welchem Keypoint jeder Punkt im Spiegelbild entspricht. Wird eine Person gespiegelt, landet ihr linkes Handgelenk (9) dort, wo zuvor das rechte Handgelenk (10) war. Deshalb steht an Position 9 von flip_idx der Wert 10 und an Position 10 der Wert 9; die Nase (0) wird sich selbst zugeordnet. Jede Augmentierung verschiebt das Skelett zusammen mit den Pixeln. Nur beim Spiegeln wird zusätzlich die Indexzuordnung benötigt:

Was passiert, wenn flip_idx falsch ist, überprüft im installierten Quellcode:
- Fehlt: Beim Training wird eine Warnung ausgegeben und
fliplrsowieflipudwerden auf 0 gesetzt. Dadurch geht deine kostengünstigste Augmentierung verloren. - Falsche Länge: Das Training wird mit einem
ValueErrorabgebrochen, das auf die Abweichung hinweist – der einzige Fehler, der deutlich gemeldet wird. - Richtige Länge, falsche Paare: Es gibt keine Warnung. Bei der Hälfte der gespiegelten Beispiele lernt das Modell, dass links rechts ist, und Validierungsbilder zeigen bei symmetrischen Posen vertauschte Seiten.
Mit COCO8-pose trainieren#

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.326Wie bei der Segmentierung besteht die Tabelle aus zwei Blöcken. Box bewertet die Personen-Bounding-Boxes; Pose bewertet die Platzierung der Keypoints mit Object-Keypoint-Ähnlichkeit (OKS) innerhalb derselben mAP-Berechnung. Pose mAP50-95 ist die Freigabeschwelle für ein Pose-Produkt – die nächste Lektion zeigt, wie OKS entscheidet, ob ein Keypoint richtig platziert ist.
Von Keypoints zu Gelenkwinkeln#
result.keypoints enthält xy (N × K × 2, Pixel), xyn (normalisiert) und conf (N × K) in der oben gezeigten COCO-Reihenfolge. Der Winkel an einem Gelenk ist der Winkel zwischen den beiden dort zusammentreffenden Gliedmaßenvektoren – berechnet mit einem Skalarprodukt:
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 enoughZeile für Zeile: kpts.xy[p] sind die 17 Punkte von Person p in Pixeln; a - b und c - b sind Oberarm und Unterarm als Vektoren vom Ellbogen aus; ihr normalisiertes Skalarprodukt ist der Kosinus des Winkels zwischen ihnen. Ein gestreckter Arm ergibt einen Wert nahe 180°, ein vollständig angewinkelter einen Wert nahe 30°. Bei Personen 3 und 4 liegt mindestens ein Gelenk des linken Arms unter einer Konfidenz von 0,5, daher misst das Skript diese Personen nicht.
Symptom: Winkel, die zuverlässig wirken, obwohl die Arme der Personen außerhalb des Bildes liegen. Ursache: Das Modell gibt für jeden Schlüsselpunkts eine Koordinate zurück, unabhängig davon, ob er verdeckt ist. Lösung: Überspringe jede Berechnung, deren Gelenkpunkte unter einem Konfidenzschwellenwert liegen (hier 0.5), und protokolliere, wie oft du sie überspringst – eine steigende Überspringrate ist ein frühes Anzeichen für veränderte Kamera- oder Lichtverhältnisse.
Lösung anzeigen
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 degBei Personen 0 und 2 sind beide Arme messbar. Der rechte Arm von Person 1 und beide Arme von Personen 3 und 4 liegen unter dem Konfidenzgrenzwert von 0,5. Daher überspringt das Skript diese Arme, statt Werte zu schätzen.
Ein fehlendes flip_idx deaktiviert mit einer Warnung die Spiegelungsaugmentierung; eine falsche Länge bricht das Training mit einem ValueError ab; ein plausibel wirkendes flip_idx mit falschen Paaren führt dazu, dass stillschweigend mit gespiegelten Labels trainiert wird und bei der Validierung vertauschte Seiten auftreten.