Ultralytics YOLO27: Únete a la lista de espera.
Get Started
Lecciónintermedio

Puntos clave personalizados y métricas OKS

Diseña un esquema de puntos clave para objetos no humanos, escribe y comprueba el YAML del conjunto de datos y define sigmas OKS según la precisión con la que se puede colocar cada punto.

La pose no se limita a las personas. Las puntas de las horquillas de una carretilla elevadora, las esquinas de la etiqueta de un paquete, el hocico y la cola de un animal: cualquier objeto con puntos estables e identificables puede formar un conjunto de datos de pose. El esquema que escribas el primer día será permanente: cada etiqueta, cada par de volteo y cada métrica dependen de él, y cambiarlo más adelante implica volver a etiquetarlo todo. En esta lección definirás correctamente el esquema antes de que nadie etiquete mil imágenes y verás exactamente cómo se evaluará con la métrica.

Resultado

Prepara un YAML de conjunto de datos para un esquema personalizado de puntos clave con pares de volteo y sigmas OKS, un comprobador de etiquetas que funcione con datos reales y una predicción de cuántos píxeles de error tolera cada sigma.

Vía rápida
Si ya tienes experiencia, aquí tienes un resumen.
  1. Elige entre 3 y 12 puntos inequívocos, presentes en todas las poses y utilizados por el código posterior.

  2. Ordénalos y escribe flip_idx intercambiando los puntos correspondientes de izquierda y derecha; los puntos centrales se asignan a sí mismos.

  3. Añade kpt_oks_sigmas cuando los puntos difieran en la precisión con la que los anotadores pueden colocarlos.

  4. Antes del entrenamiento, ejecuta un comprobador de etiquetas que valide el número de columnas, los valores de visibilidad y que flip_idx sea una involución.

Práctico

Diseña el esquema de puntos clave#

Los buenos puntos clave comparten tres propiedades: inequívocos (dos anotadores colocan el punto en el mismo sitio), estables (el punto existe en todas las poses que adopta el objeto) y útiles (algún cálculo posterior los utiliza). Pensemos en una carretilla elevadora vista desde una cámara cenital, donde la pregunta del producto es «¿están elevadas las horquillas mientras se mueve la carretilla?»:

ÍndicePunto clavePor qué existePunto correspondiente al reflejar
0left_fork_tipExtensión y dirección de las horquillas1
1right_fork_tipExtensión y dirección de las horquillas0
2mast_topAltura de las horquillas respecto al mástil2 (sí mismo)
3mast_basePunto de referencia en el chasis3 (sí mismo)

Cada punto se justifica porque contribuye al cálculo: las puntas de las horquillas indican la dirección y el par del mástil indica la altura. Evalúa cada punto candidato con los mismos criterios:

Evita añadir puntos «por si acaso»: cada uno cuesta tiempo de etiquetado en cada instancia, y los puntos que nadie utiliza solo añaden ruido a la métrica. Los conjuntos de datos públicos muestran la variedad: Tiger-Pose utiliza 12 puntos sin visibilidad, Dog-Pose, 24, y Hand Keypoints, 21:

Esquema de puntos clave de la mano con 21 referencias numeradas, desde la muñeca (0) hasta las articulaciones de cada dedo y la punta del meñique (20)

Ejemplos de Dog-Pose: un cachorro etiquetado con un cuadro de perro y puntos clave de colores en la cara, las patas y el cuerpo

Escribe el YAML del conjunto de datos#

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 el orden de cada clase, tanto para personas como para herramientas.
  • flip_idx debe deshacerse a sí mismo: al aplicarlo dos veces, cada índice vuelve a sí mismo.
  • kpt_oks_sigmas es opcional y pondera tanto la pérdida de puntos clave durante el entrenamiento como OKS durante la validación; si se incluye, debe contener un valor positivo por cada punto clave o la ejecución se detiene con un ValueError.

En Ultralytics Platform, el mismo esquema se utiliza como plantilla de esqueleto. Una plantilla nueva puede partir de una existente (Partir de…), así que renombras y eliminas puntos en lugar de colocar cada punto clave a mano; mantén el orden de los índices de la tabla del esquema:

Cuadro de diálogo Nueva plantilla de esqueleto de Ultralytics Platform: un esqueleto de persona con 17 puntos, creado a partir de una plantilla existente, con los puntos clave desde nose hasta left_wrist listados para cambiarles el nombre

Comprueba las etiquetas antes del entrenamiento#

Los conjuntos de datos de pose pueden fallar sin avisar: si falta un punto clave en una fila, todos los valores posteriores se desplazan, y un valor de visibilidad de 3 procedente de un exportador defectuoso se interpreta como cualquier otro número. Este comprobador carga el YAML con el cargador propio de Ultralytics y valida todas las filas. Funciona tal cual con COCO8-pose; cambia la ruta del YAML por la tuya:

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] empieza en el primer indicador de visibilidad (clase, cuatro valores del cuadro y, después, x y v) y toma cada tercer valor a partir de ahí.

Cómo evalúa OKS un punto clave#

La mAP de pose sustituye la IoU de los cuadros por la similitud de puntos clave de objetos (OKS). Para cada punto clave etiquetado, OKS convierte la distancia en píxeles entre la predicción y la etiqueta en una puntuación de 0 a 1, escalada según el tamaño del objeto y el sigma de ese punto clave, y luego calcula la media de los puntos clave etiquetados. La mAP50-95 de pose calcula la media de la precisión en umbrales OKS de 0.50 a 0.95, igual que la mAP de cuadros en los umbrales IoU; la guía de métricas de rendimiento explica el sistema común.

La forma más sencilla de entender los sigmas es medirlos. Este script evalúa la misma predicción de una carretilla elevadora con dos sigmas mediante la función kpt_iou que utiliza el validador de pose:

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

En una carretilla elevadora de 200 × 120 píxeles, una predicción con 10 píxeles de error no supera el umbral OKS de 0.50 con un sigma de 0.025, pero supera todos los umbrales hasta 0.90 con un sigma de 0.10. Mismo modelo, mismo error, pero una mAP de pose muy distinta.

SigmaEfectoUso
Pequeño (≈ 0.025–0.05)unos pocos píxeles de error ya reducen la puntuaciónPuntos precisos: ojos, puntas de horquillas, esquinas
Grande (≈ 0.08–0.10)Tolerancia ampliaPuntos difusos: caderas, centro de un mástil
PredeterminadoSigmas de COCO para kpt_shape: [17, 3]; de lo contrario, 1/K para cada puntoEmpieza por aquí y luego mide

Los sigmas deben reflejar la consistencia con la que los anotadores colocan cada punto. Mídela: pide a dos personas que etiqueten las mismas 50 imágenes y compara las distancias de cada punto clave. Los puntos en los que discrepen deben tener sigmas mayores; de lo contrario, la métrica penaliza al modelo por el ruido humano.

Las métricas solo son comparables dentro de un mismo esquema

Cambiar kpt_oks_sigmas cambia el mAP de pose aunque el modelo no cambie, como muestra la tabla anterior. Fija los sigmas antes de establecer la primera línea de base, regístralos con cada resultado y no compares nunca ejecuciones validadas con valores distintos.

Pruébalo

Diseña un esquema de puntos clave para un objeto de tu propio ámbito: enumera entre 3 y 8 puntos clave, con una justificación de una línea para cada uno, su correspondencia al reflejar y un sigma. Escribe el YAML, ejecuta check_pose_labels.py con COCO8-pose y después con algunas de tus propias etiquetas, y utiliza oks.py para averiguar cuántos píxeles de error tolera tu punto más preciso antes de que OKS baje de 0.5.

Cuándo se considera terminado
Has terminado la lección cuando se cumplen todas estas condiciones.
  • La tabla del esquema indica el nombre, la finalidad, el punto correspondiente al reflejar y el sigma de cada punto clave.

  • Tu YAML contiene kpt_shape, un flip_idx que sea una involución y, si se utilizan, sigmas positivos para todos los puntos clave; el comprobador indica que no hay filas incorrectas en tu muestra.

  • Puedes indicar el error en píxeles a partir del cual tu punto clave más preciso deja de contar en OKS 0.5.

Mostrar solución

Ejemplo de un paquete en una cinta transportadora, visto desde arriba, donde la pregunta es «¿la etiqueta está boca arriba y en qué dirección está girada?»:

ÍndicePunto claveFinalidadPunto correspondiente al reflejarSigma
0label_top_leftOrientación de la etiqueta10.04
1label_top_rightOrientación de la etiqueta00.04
2label_bottom_rightOrientación de la etiqueta30.04
3label_bottom_leftOrientación de la etiqueta20.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 la comprobación de involución: 0 → 1 → 0 y 2 → 3 → 2. Una etiqueta reflejada intercambia izquierda y derecha, pero mantiene arriba y abajo, así que la esquina superior izquierda se corresponde con la superior derecha, no con la inferior derecha.

Para encontrar la tolerancia, define sigma=[0.04] * 4 en oks.py y amplía los desplazamientos; el cuadro de la etiqueta determina la escala, así que utiliza un cuadro del tamaño de una etiqueta de paquete típica de tus imágenes. El desplazamiento en el que OKS baja por primera vez de 0.5 es el error máximo que la precisión de tus anotadores permite al modelo.

Qué viene después

A continuación, trataremos objetos cuya orientación importa, pero cuya forma es un rectángulo sencillo: los cuadros delimitadores orientados.