Ultralytics YOLO27: Iscriviti alla lista d'attesa.
Get Started
Lezioneintermedio

Punti chiave personalizzati e metriche OKS

Progetta uno schema di punti chiave per oggetti non umani, scrivi e verifica il relativo YAML del dataset e imposta i valori sigma di OKS in base alla precisione con cui è possibile posizionare ciascun punto.

La stima della posa non riguarda solo le persone. Le punte delle forche di un carrello elevatore, gli angoli dell'etichetta di un pacco, il naso e la coda di un animale: qualsiasi oggetto con punti stabili e identificabili può diventare un dataset per la stima della posa. Lo schema che definisci il primo giorno è permanente: ogni etichetta, coppia di ribaltamento e metrica dipende da esso, e cambiarlo in seguito significa rietichettare tutto. In questa lezione definisci correttamente lo schema prima che qualcuno etichetti un migliaio di immagini e scopri esattamente come la metrica lo valuterà.

Esito

Produci un YAML del dataset per uno schema personalizzato di punti chiave con coppie di ribaltamento e valori sigma di OKS, un verificatore di etichette che superi il controllo su dati reali e una stima dell'errore in pixel tollerato da ciascun valore sigma.

Percorso rapido
Se sai già come muoverti, ecco la versione breve.
  1. Scegli da 3 a 12 punti inequivocabili, presenti in ogni posa e utilizzati dal codice a valle.

  2. Definisci il loro ordine, quindi scrivi flip_idx scambiando i punti corrispondenti a sinistra e a destra; i punti centrali corrispondono a se stessi.

  3. Aggiungi kpt_oks_sigmas quando i punti differiscono per la precisione con cui gli annotatori possono posizionarli.

  4. Prima dell'addestramento, esegui un verificatore delle etichette per controllare il numero di colonne, i valori di visibilità e che flip_idx sia involutivo.

Pratico

Progetta lo schema dei punti chiave#

I punti chiave validi condividono tre proprietà: inequivocabili (due annotatori posizionano il punto nello stesso luogo), stabili (il punto è presente in ogni posa assunta dall'oggetto) e utili (un calcolo a valle li utilizza). Considera un carrello elevatore ripreso da una telecamera a soffitto, con la domanda a cui rispondere: "le forche sono sollevate mentre il carrello si muove?":

IndicePunto chiaveMotivo della sua presenzaPunto corrispondente speculare
0left_fork_tipestensione e direzione delle forche1
1right_fork_tipestensione e direzione delle forche0
2mast_topaltezza delle forche rispetto al montante2 (se stesso)
3mast_basepunto di riferimento sul telaio3 (se stesso)

Ogni punto si giustifica contribuendo al calcolo: le punte delle forche indicano la direzione, mentre la coppia sul montante indica l'altezza. Valuta ogni punto candidato con lo stesso criterio:

Resisti alla tentazione di aggiungere punti "per ogni evenienza": ognuno richiede tempo per l'etichettatura di ogni istanza, e i punti che nessuno utilizza aggiungono solo rumore alla metrica. I dataset pubblici mostrano la varietà: Tiger-Pose usa 12 punti senza visibilità, Dog-Pose ne usa 24 e Hand Keypoints 21:

Schema dei punti chiave della mano con 21 punti di riferimento numerati, dal polso (0) alle articolazioni di ogni dito fino alla punta del mignolo (20)

Esempi di Dog-Pose: un cucciolo con un riquadro attorno al cane e punti chiave colorati sul muso, sulle zampe e sul corpo

Scrivi il YAML del 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 documenta l'ordine dei punti per persone e strumenti, per ciascuna classe.
  • flip_idx deve essere involutivo: applicandolo due volte, ogni indice deve tornare a se stesso.
  • kpt_oks_sigmas è facoltativo e determina i pesi sia della loss dei punti chiave durante l'addestramento sia di OKS durante la validazione; se presente, deve contenere un valore positivo per ogni punto chiave, altrimenti l'esecuzione si interrompe con un ValueError.

Su Ultralytics Platform, lo stesso schema diventa un modello di scheletro. Puoi creare un nuovo modello partendo da uno esistente (Parti da…), rinominando e riducendo i punti anziché posizionare manualmente ogni punto chiave: mantieni l'ordine degli indici della tabella dello schema:

Finestra di dialogo New Skeleton Template di Ultralytics Platform: scheletro umano a 17 punti creato a partire da un modello esistente, con i punti chiave dal naso al polso sinistro elencati per rinominarli

Verifica le etichette prima dell'addestramento#

I dataset di stima della posa possono presentare errori difficili da notare: una riga a cui manca un punto chiave sposta tutti i valori successivi, mentre una visibilità pari a 3 generata da un esportatore difettoso viene letta come un qualsiasi altro numero. Questo verificatore carica il YAML tramite il loader di Ultralytics e convalida ogni riga. Puoi eseguirlo così com'è su COCO8-pose; sostituisci il percorso del tuo 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] inizia dal primo flag di visibilità (classe, quattro valori del riquadro, quindi x y v) e prende ogni terzo valore successivo.

Come OKS valuta un punto chiave#

Pose mAP sostituisce la IoU dei riquadri con la Similarità dei punti chiave degli oggetti (OKS). Per ogni punto chiave etichettato, OKS trasforma la distanza in pixel tra la previsione e l'etichetta in un punteggio da 0 a 1, scalato in base alle dimensioni dell'oggetto e al valore sigma del punto chiave, quindi calcola la media sui punti chiave etichettati. Pose mAP50-95 calcola la media della precisione a soglie OKS da 0.50 a 0.95, proprio come box mAP sulle soglie IoU; la guida alle metriche di prestazione descrive il meccanismo condiviso.

Il modo più semplice per capire i valori sigma è misurarli. Questo script valuta la stessa previsione del carrello elevatore con due valori sigma, usando la funzione kpt_iou richiamata dal validatore della stima della posa:

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

Su un carrello elevatore di 200 × 120 pixel, una previsione con un errore di 10 pixel non supera la soglia OKS di 0.50 con un valore sigma di 0.025, ma supera tutte le soglie fino a 0.90 con un valore sigma di 0.10. Stesso modello, stesso errore, pose mAP molto diversa.

SigmaEffettoUso
Piccolo (≈ 0.025–0.05)anche pochi pixel di errore riducono già il punteggiopunti ben definiti: occhi, punte delle forche, angoli
Grande (≈ 0.08–0.10)tolleranza ampiapunti poco definiti: fianchi, centro di un montante
PredefinitaValori sigma COCO per kpt_shape: [17, 3]; altrimenti 1/K per ogni puntoinizia da qui, poi misura

I valori sigma dovrebbero riflettere la coerenza con cui gli annotatori posizionano ciascun punto. Misurala: chiedi a due persone di etichettare le stesse 50 immagini e confronta le distanze per punto chiave. I punti su cui non sono d'accordo richiedono valori sigma più alti; altrimenti la metrica penalizza il modello per il rumore umano.

Le metriche sono confrontabili solo all'interno dello stesso schema

Modificare kpt_oks_sigmas cambia la mAP di pose senza modificare il modello, come mostra la tabella qui sopra. Imposta i valori sigma prima del primo riferimento, registrali con ogni risultato e non confrontare mai esecuzioni convalidate usando valori diversi.

Provalo

Progetta uno schema di punti chiave per un oggetto del tuo settore: elenca da 3 a 8 punti chiave, ciascuno con una motivazione di una riga, un punto corrispondente speculare e un valore sigma. Scrivi il YAML, esegui check_pose_labels.py su COCO8-pose e poi su alcune delle tue etichette, quindi usa oks.py per scoprire quanti pixel di errore tollera il tuo punto più preciso prima che OKS scenda sotto 0.5.

Criteri di completamento
La lezione è completata quando tutte le seguenti condizioni sono soddisfatte.
  • La tabella dello schema indica il nome, la funzione, il punto corrispondente speculare e il valore sigma di ogni punto chiave.

  • Il tuo YAML contiene kpt_shape, un flip_idx involutivo e, se utilizzato, un valore sigma positivo per ogni punto chiave; il verificatore segnala 0 righe non valide nel campione.

  • Sai indicare l'errore in pixel oltre il quale il tuo punto chiave più preciso non conta più per OKS 0.5.

Mostra la soluzione

Esempio di un pacco su un nastro trasportatore, visto dall'alto, con la domanda "l'etichetta è rivolta verso l'alto e in che direzione è ruotata?":

IndicePunto chiaveFunzionePunto corrispondente speculareSigma
0label_top_leftorientamento dell'etichetta10.04
1label_top_rightorientamento dell'etichetta00.04
2label_bottom_rightorientamento dell'etichetta30.04
3label_bottom_leftorientamento dell'etichetta20.04
kpt_shape: [4, 3]
flip_idx: [1, 0, 3, 2]
kpt_oks_sigmas: [0.04, 0.04, 0.04, 0.04]

flip_idx supera il controllo di involutività: 0 → 1 → 0 e 2 → 3 → 2. Un'etichetta specchiata scambia sinistra e destra ma mantiene alto e basso, quindi l'angolo superiore sinistro corrisponde all'angolo superiore destro, non a quello inferiore destro.

Per determinare la tolleranza, imposta sigma=[0.04] * 4 in oks.py e amplia gli scostamenti; la scala è definita dal riquadro dell'etichetta, quindi usa un riquadro delle dimensioni tipiche di un'etichetta dei pacchi nelle tue immagini. Lo scostamento al quale OKS scende per la prima volta sotto 0.5 è l'errore massimo compatibile con la precisione dei tuoi annotatori.

Prossimi passi

Nel prossimo passaggio ci occupiamo di oggetti il cui orientamento è importante ma la cui forma è un semplice rettangolo: i riquadri di delimitazione orientati.

Accedi per salvare i progressi