Rotular e treinar modelos de pose
Cria rótulos de pontos-chave com visibilidade, define corretamente kpt_shape e flip_idx, treina com COCO8-pose e converte os pontos-chave em ângulos articulares sujeitos a um limiar de confiança.
A estimativa de pose converte cada pessoa em 17 pontos numerados, e desses pontos vêm os valores que os produtos vendem: ângulos dos cotovelos para ergonomia, ângulos dos joelhos para reabilitação, altura das ancas para deteção de quedas. Duas chaves do conjunto de dados determinam se esses pontos são fiáveis, e qualquer erro numa delas não gera erro algum — apenas um modelo que confunde esquerda e direita ou que treina silenciosamente sem a sua ampliação mais barata.
Prático
A linha de rótulo dos pontos-chave#

Uma linha de rótulo de pose é uma linha de deteção com pontos-chave acrescentados. Cada coordenada é normalizada para 0–1, e os pontos-chave aparecem sempre na mesma ordem fixa em todas as linhas. Uma linha real de COCO8-pose, truncada após quatro pontos-chave:
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 da classe (pessoa) |
0.671279 0.617945 0.645759 0.726859 | centro x da caixa, centro y, largura, altura |
0.519751 0.381250 2 | ponto-chave 0 (nariz): x, y, visibilidade |
| … | Mais 16 pontos-chave, com o mesmo padrão: 5 + 17 × 3 = 56 valores por linha |
O indicador de visibilidade segue a convenção COCO: 2 visível, 1 rotulado, mas oculto, 0 não rotulado. O Ultralytics mascara os pontos-chave com v = 0 na função de perda, para que um pulso em falta não ensine o modelo a prever pulsos em (0, 0).

Usa 1 para uma articulação ocluída cuja posição consigas determinar com confiança — uma anca atrás de uma mesa, um pulso atrás das costas; continua a ser útil para o treino. Reserva 0 para articulações que ninguém conseguiria localizar. Etiquetar todas as articulações ocultas com 0 elimina precisamente os casos difíceis de que o modelo precisa.
O guia de conjuntos de dados de pose também aborda a variante de dois valores (x y apenas, kpt_shape: [K, 2]) para conjuntos de dados sem visibilidade.
kpt_shape e flip_idx#
O YAML do conjunto de dados acrescenta duas chaves às habituais 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 à ampliação de inversão horizontal qual ponto-chave corresponde a cada um na imagem espelhada. Espelha uma pessoa e o seu pulso esquerdo (9) passa para onde estava o pulso direito (10), por isso a posição 9 de flip_idx contém 10 e a posição 10 contém 9; o nariz (0) corresponde a si próprio. Cada ampliação desloca o esqueleto juntamente com os pixels, e a inversão é a única que também precisa do mapa de índices:

O que acontece quando flip_idx está errado, confirmado no código-fonte instalado:
- Ausente: o treinamento registra um aviso e define
fliplreflipudcomo 0, então você perde seu aumento de dados mais barato. - Comprimento incorreto: o treinamento para com um
ValueErrorque identifica a incompatibilidade — a única falha explícita. - Comprimento correto, pares errados: nenhum aviso é emitido. Metade das amostras invertidas ensina ao modelo que esquerda é direita, e as imagens de validação mostram trocas entre esquerda e direita em poses simétricas.
Treine com 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.326Assim como na segmentação, a tabela tem dois blocos. Box avalia as caixas de pessoas; Pose avalia o posicionamento dos pontos-chave com a Similaridade de Pontos-Chave de Objetos (OKS), dentro do mesmo mecanismo de mAP. O mAP50-95 de pose é o critério de aprovação para lançar um produto de pose — na próxima lição, você verá como o OKS decide se um ponto-chave está correto.
Dos pontos-chave aos ângulos das articulações#
result.keypoints contém xy (N × K × 2, pixels), xyn (normalizado) e conf (N × K), na ordem COCO mostrada acima. O ângulo de uma articulação é o ângulo entre os dois vetores dos membros que se encontram nela — um produto 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 enoughLinha por linha: kpts.xy[p] é os 17 pontos da pessoa p em pixels; a - b e c - b são o braço superior e o antebraço representados como vetores a partir do cotovelo; o produto escalar normalizado deles é o cosseno do ângulo entre eles. Um braço esticado indica aproximadamente 180°, e um totalmente dobrado, aproximadamente 30°. As pessoas 3 e 4 têm pelo menos uma articulação do braço esquerdo com confiança abaixo de 0.5, então o script não faz a medição.
Sintoma: ângulos aparentemente fiáveis para pessoas com os braços fora do enquadramento. Causa: o modelo devolve uma coordenada para cada ponto-chave, esteja ou não ocluído. Solução: ignora qualquer cálculo cujas articulações fiquem abaixo de um limiar de confiança (0.5 neste caso) e regista a frequência com que o fazes — uma taxa crescente é um sinal precoce de uma alteração na câmara ou na iluminação.
Mostrar solução
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 degAs pessoas 0 e 2 podem ter os dois braços medidos. O braço direito da pessoa 1 e os dois braços das pessoas 3 e 4 ficam abaixo do limite mínimo de confiança de 0.5, então o script os ignora em vez de tentar adivinhar.
A ausência de flip_idx desativa o aumento de dados por inversão e emite um aviso; um comprimento incorreto interrompe o treinamento com um ValueError; um flip_idx aparentemente válido, mas com pares errados, treina silenciosamente com rótulos espelhados e causa trocas entre esquerda e direita na validação.