Etiqueta y entrena modelos de pose
Escribe etiquetas de puntos clave con visibilidad, configura correctamente kpt_shape y flip_idx, entrena con COCO8-pose y convierte los puntos clave en ángulos articulares sujetos a un umbral de confianza.
La estimación de pose convierte a cada persona en 17 puntos numerados, y de esos puntos se obtienen las cifras que venden los productos: ángulos de codo para ergonomía, ángulos de rodilla para rehabilitación y altura de cadera para detectar caídas. Dos claves del conjunto de datos determinan si esos puntos son fiables, y si te equivocas en cualquiera de ellas no se produce ningún error: solo obtienes un modelo que confunde la izquierda con la derecha o que se entrena silenciosamente sin su método de aumento de datos más sencillo.
Práctico
La fila de etiquetas de puntos clave#

Una fila de etiquetas de pose es una fila de detección con puntos clave añadidos. Todas las coordenadas están normalizadas entre 0 y 1, y los puntos clave aparecen siempre en el mismo orden fijo en cada fila. Una fila real de COCO8-pose, recortada tras los cuatro primeros puntos clave:
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 ...| Valores | Significado |
|---|---|
0 | índice de clase (persona) |
0.671279 0.617945 0.645759 0.726859 | centro x de la caja, centro y, anchura y altura |
0.519751 0.381250 2 | punto clave 0 (nariz): x, y, visibilidad |
| … | 16 puntos clave más, con el mismo patrón: 5 + 17 × 3 = 56 valores por fila |
El indicador de visibilidad sigue la convención de COCO: 2 visible, 1 etiquetado pero oculto, 0 sin etiquetar. Ultralytics enmascara los puntos clave con v = 0 en la función de pérdida, así que la ausencia de una muñeca no enseña al modelo a predecir muñecas en (0, 0).

Usa 1 para una articulación ocluida que puedas ubicar con seguridad —una cadera detrás de una mesa o una muñeca detrás de la espalda—; sigue sirviendo para entrenar. Reserva el 0 para las articulaciones que nadie podría localizar. Etiquetar como 0 todas las articulaciones ocultas descarta precisamente los casos difíciles que necesita el modelo.
La guía de conjuntos de datos de pose también cubre la variante de dos valores (x y solamente, kpt_shape: [K, 2]) para conjuntos de datos sin información de visibilidad.
kpt_shape y flip_idx#
El YAML del conjunto de datos añade dos claves a las habituales path, train, val y 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 al aumento de datos con volteo horizontal qué punto clave corresponde a cada uno en la imagen reflejada. Refleja a una persona y su muñeca izquierda (9) acaba donde estaba la muñeca derecha (10), así que la posición 9 de flip_idx contiene el 10 y la posición 10 contiene el 9; la nariz (0) se corresponde consigo misma. Cada aumento de datos mueve el esqueleto junto con los píxeles, y el volteo es el único que también necesita el mapa de índices:

Qué ocurre cuando flip_idx es incorrecto, verificado en el código fuente instalado:
- Falta: el entrenamiento muestra una advertencia y establece
fliplryflipuden 0, así que pierdes tu aumento más barato. - Longitud incorrecta: el entrenamiento se detiene con un
ValueErrorque indica la discrepancia: es el único error que se comunica claramente. - Longitud correcta, pares incorrectos: no se muestra ninguna advertencia. La mitad de las muestras volteadas enseñan al modelo que la izquierda es la derecha, y las imágenes de validación muestran intercambios izquierda-derecha en poses simétricas.
Entrena con 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.326Al igual que en segmentación, la tabla tiene dos bloques. Box evalúa los cuadros de las personas; Pose evalúa la ubicación de los puntos clave mediante la similitud de puntos clave de objetos (OKS), dentro del mismo sistema de mAP. La mAP50-95 de pose es el criterio de lanzamiento de un producto de pose; en la siguiente lección se muestra cómo OKS decide si un punto clave es correcto.
De los puntos clave a los ángulos articulares#
result.keypoints contiene xy (N × K × 2, píxeles), xyn (normalizado) y conf (N × K), en el orden COCO mostrado arriba. El ángulo de una articulación es el ángulo entre los dos vectores de las extremidades que se unen en ella: un producto escalar:
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 enoughLínea por línea: kpts.xy[p] son los 17 puntos de la persona p en píxeles; a - b y c - b son el brazo y el antebrazo como vectores desde el codo; su producto escalar normalizado es el coseno del ángulo que forman. Un brazo recto marca cerca de 180° y uno completamente doblado, cerca de 30°. Las personas 3 y 4 tienen al menos una articulación del brazo izquierdo con una confianza inferior a 0.5, por lo que el script se niega a medirlas.
Síntoma: ángulos aparentemente fiables para personas cuyos brazos quedan fuera del encuadre. Causa: el modelo devuelve una coordenada para cada punto clave, esté o no ocluido. Solución: omite cualquier cálculo cuyas articulaciones estén por debajo de un umbral de confianza (0,5 en este caso) y registra con qué frecuencia lo haces; un aumento de la frecuencia de omisión es una señal temprana de cambios en la cámara o la iluminación.
Mostrar solución
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 degSe pueden medir ambos brazos de las personas 0 y 2. El brazo derecho de la persona 1 y ambos brazos de las personas 3 y 4 no alcanzan el umbral mínimo de confianza de 0.5, así que el script los omite en vez de hacer suposiciones.
Si falta flip_idx, se desactiva el aumento por volteo y se muestra una advertencia; una longitud incorrecta detiene el entrenamiento con un ValueError; un flip_idx que parece válido, pero tiene pares incorrectos, entrena silenciosamente con etiquetas reflejadas y muestra intercambios izquierda-derecha en la validación.