Etichettare e addestrare modelli di posa
Scrivi etichette dei keypoint con visibilità, imposta correttamente kpt_shape e flip_idx, addestra il modello su COCO8-pose e trasforma i keypoint in angoli articolari subordinati a una soglia di confidenza.
La stima della posa trasforma ogni persona in 17 punti numerati, dai quali si ricavano i valori che i prodotti offrono: angoli del gomito per l'ergonomia, angoli del ginocchio per la riabilitazione, altezza dell'anca per rilevare le cadute. Due chiavi del dataset determinano se puoi fidarti di quei punti; se sbagli anche una sola delle due, non ricevi errori: ottieni soltanto un modello che confonde destra e sinistra oppure si addestra silenziosamente senza la sua più economica forma di augmentation.
Pratico
La riga di etichetta dei keypoint#

Una riga di etichetta della posa è una riga di rilevamento a cui vengono aggiunti i keypoint. Ogni coordinata è normalizzata tra 0 e 1 e i keypoint compaiono nello stesso ordine fisso in ogni riga. Una vera riga di COCO8-pose, abbreviata dopo i primi quattro keypoint:
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 ...| Valori | Significato |
|---|---|
0 | indice della classe (persona) |
0.671279 0.617945 0.645759 0.726859 | centro x, centro y, larghezza e altezza del riquadro |
0.519751 0.381250 2 | keypoint 0 (naso): x, y, visibilità |
| … | altri 16 keypoint con lo stesso schema: 5 + 17 × 3 = 56 valori per riga |
Il flag di visibilità segue la convenzione COCO: 2 visibile, 1 etichettato ma occluso, 0 non etichettato. Ultralytics esclude dalla loss i keypoint con v = 0, così un polso mancante non insegna al modello a prevedere polsi in (0, 0).

Usa 1 per un'articolazione occlusa che riesci a posizionare con sicurezza — un'anca dietro un tavolo, un polso dietro la schiena; verrà comunque usata per l'addestramento. Riserva 0 alle articolazioni che nessuno potrebbe individuare. Assegnare 0 a ogni articolazione nascosta elimina proprio i casi difficili di cui il modello ha bisogno.
La guida ai dataset di posa descrive anche la variante a due valori (x y soltanto, kpt_shape: [K, 2]) per i dataset senza informazioni sulla visibilità.
kpt_shape e flip_idx#
Il file YAML del dataset aggiunge due chiavi ai consueti path, train, val e names:
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 indica all'augmentation con ribaltamento orizzontale quale keypoint corrisponde a ciascun keypoint nell'immagine speculare. Se ribalti una persona, il polso sinistro (9) finisce nel punto in cui si trovava il polso destro (10); quindi, la posizione 9 di flip_idx contiene 10 e la posizione 10 contiene 9, mentre il naso (0) corrisponde a sé stesso. Ogni augmentation sposta lo scheletro insieme ai pixel, ma il ribaltamento è l'unica che richiede anche la mappa degli indici:

Cosa succede quando flip_idx è errato, come verificato nel codice sorgente installato:
- Assente: durante l'addestramento viene registrato un avviso e
fliplreflipudvengono impostati a 0, quindi perdi la tua aumentazione più economica. - Lunghezza errata: l'addestramento si interrompe con un
ValueErrorche segnala la mancata corrispondenza: è l'unico errore esplicito. - Lunghezza corretta, coppie errate: non viene generato alcun avviso. Metà dei campioni ribaltati insegna al modello che sinistra è destra, e nelle immagini di validazione compaiono inversioni destra-sinistra nelle pose simmetriche.
Addestra su 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.326Come per la segmentazione, la tabella ha due sezioni. Box valuta i riquadri delle persone; Pose valuta il posizionamento dei punti chiave con la Similarità dei punti chiave degli oggetti (OKS), all'interno dello stesso meccanismo mAP. Pose mAP50-95 è il criterio di rilascio per un prodotto di stima della posa: la prossima lezione mostra come OKS stabilisce se un punto chiave è corretto.
Dai punti chiave agli angoli delle articolazioni#
result.keypoints contiene xy (N × K × 2, pixel), xyn (normalizzati) e conf (N × K), nell'ordine COCO mostrato sopra. L'angolo di un'articolazione è l'angolo tra i due vettori degli arti che si incontrano in quel punto: si calcola con un prodotto scalare:
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 enoughRiga per riga: kpts.xy[p] è costituito dai 17 punti della persona p espressi in pixel; a - b e c - b sono i vettori del braccio e dell'avambraccio, con origine nel gomito; il loro prodotto scalare normalizzato è il coseno dell'angolo che li separa. Un braccio disteso indica un valore vicino a 180°, uno completamente piegato un valore vicino a 30°. Le persone 3 e 4 hanno almeno un'articolazione del braccio sinistro con confidenza inferiore a 0.5, quindi lo script non ne misura l'angolo.
Sintomo: angoli apparentemente affidabili per persone con le braccia fuori dall'inquadratura. Causa: il modello restituisce una coordinata per ogni keypoint, che sia occluso o meno. Soluzione: salta qualsiasi calcolo le cui articolazioni non raggiungano una soglia minima di confidenza (0.5 in questo caso) e registra la frequenza con cui li salti: un aumento indica in anticipo un cambiamento della videocamera o dell'illuminazione.
Mostra la soluzione
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 degPer le persone 0 e 2 è possibile misurare entrambe le braccia. Il braccio destro della persona 1 e entrambe le braccia delle persone 3 e 4 sono al di sotto della soglia di confidenza di 0.5, quindi lo script le ignora invece di fare supposizioni.
Se flip_idx è assente, l'aumentazione tramite ribaltamento viene disattivata con un avviso; una lunghezza errata interrompe l'addestramento con un ValueError; un flip_idx dall'aspetto valido ma con coppie errate addestra silenziosamente su etichette specchiate, causando inversioni destra-sinistra durante la validazione.