Ultralytics YOLO27: Inscreve-te na lista de espera.
Get Started
Aulaintermediário

Pontos-chave personalizados e métricas OKS

Projete um esquema de pontos-chave para objetos não humanos, crie e verifique o YAML do conjunto de dados e defina sigmas do OKS conforme a precisão com que cada ponto pode ser posicionado.

A pose não serve apenas para pessoas. As pontas dos garfos de uma empilhadeira, os cantos da etiqueta de um pacote, o focinho e a cauda de um animal — qualquer objeto com pontos estáveis e identificáveis pode compor um conjunto de dados de pose. O esquema que você define no primeiro dia é permanente: cada rótulo, cada par de inversão e cada métrica depende dele, e alterá-lo depois significa rotular tudo de novo. Nesta lição, você definirá o esquema corretamente antes que alguém rotule mil imagens e verá exatamente como a métrica o avaliará.

Resultado

Crie um YAML de conjunto de dados para um esquema de pontos-chave personalizado, com pares de inversão e sigmas do OKS, um verificador de rótulos que passe em dados reais e uma previsão de quantos pixels de erro cada sigma tolera.

Via rápida
Se já sabes orientar-te, aqui está a versão resumida.
  1. Escolha de 3 a 12 pontos inequívocos, presentes em todas as poses e usados pelo código subsequente.

  2. Ordene-os e, em seguida, defina flip_idx trocando os pares correspondentes à esquerda e à direita; os pontos centrais correspondem a si mesmos.

  3. Adicione kpt_oks_sigmas quando os pontos diferirem na precisão com que os anotadores conseguem posicioná-los.

  4. Antes do treinamento, execute um verificador de rótulos para conferir a quantidade de colunas, os valores de visibilidade e se flip_idx é sua própria inversa.

Prático

Projete o esquema de pontos-chave#

Pontos-chave bem definidos compartilham três propriedades: inequívocos (dois anotadores colocam o ponto no mesmo lugar), estáveis (o ponto existe em todas as poses do objeto) e úteis (algum cálculo subsequente usa o ponto). Considere uma empilhadeira vista por uma câmera no teto, quando a pergunta do produto é "os garfos estão levantados enquanto o veículo se move?":

ÍndicePonto-chavePor que existePonto correspondente na inversão
0left_fork_tipextensão e direção dos garfos1
1right_fork_tipextensão e direção dos garfos0
2mast_topaltura dos garfos em relação ao mastro2 (ele próprio)
3mast_baseponto de referência no chassi3 (ele próprio)

Cada ponto justifica sua inclusão ao alimentar o cálculo: as pontas dos garfos indicam a direção, e o par do mastro indica a altura. Avalie cada ponto candidato com os mesmos critérios:

Evite adicionar pontos "por precaução": cada um consome tempo de rotulagem em todas as instâncias, e pontos que ninguém usa só acrescentam ruído à métrica. Conjuntos de dados públicos mostram a variedade: Tiger-Pose usa 12 pontos sem visibilidade, Dog-Pose, 24, e Hand Keypoints, 21:

Esquema de pontos-chave da mão com 21 pontos de referência numerados, do pulso no ponto 0 às articulações de cada dedo até a ponta do dedo mínimo no ponto 20

Exemplos do Dog-Pose: um filhote rotulado com uma caixa de cão e pontos-chave coloridos no rosto, nas patas e no corpo

Escreva o YAML do conjunto de dados#

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 a ordem dos pontos para pessoas e ferramentas, por classe.
  • flip_idx precisa desfazer a si mesmo: aplicá-lo duas vezes retorna cada índice ao próprio valor.
  • kpt_oks_sigmas é opcional e pondera tanto a perda de pontos-chave durante o treinamento quanto o OKS durante a validação; se estiver presente, deve conter um valor positivo por ponto-chave ou a execução será interrompida com um ValueError.

Na Ultralytics Platform, o mesmo esquema é um modelo de esqueleto. Um novo modelo pode partir de um modelo existente (Começar de…), para que você renomeie e remova pontos em vez de posicionar cada ponto-chave manualmente — mantenha a ordem dos índices da tabela do esquema:

Caixa de diálogo New Skeleton Template da Ultralytics Platform: esqueleto de pessoa com 17 pontos iniciado a partir de um modelo existente, com os pontos-chave, de nose a left_wrist, listados para renomeação

Verifique os rótulos antes do treinamento#

Conjuntos de dados de pose podem falhar sem aviso: se faltar um ponto-chave em uma linha, todos os valores seguintes ficam deslocados; e uma visibilidade igual a 3, resultado de um exportador com defeito, é interpretada como qualquer outro número. Este verificador carrega o YAML com o próprio carregador da Ultralytics e valida cada linha. Ele funciona sem alterações com COCO8-pose; basta substituir pelo caminho do seu 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] começa no primeiro indicador de visibilidade (classe, quatro valores da caixa e, em seguida, x y v) e considera depois disso um valor a cada três.

Como o OKS avalia um ponto-chave#

O mAP de pose substitui a IoU da caixa pela Similaridade de Pontos-Chave de Objetos (OKS). Para cada ponto-chave rotulado, o OKS converte a distância em pixels entre a previsão e o rótulo em uma pontuação de 0 a 1, dimensionada pelo tamanho do objeto e pelo sigma desse ponto-chave, e calcula a média dos pontos-chave rotulados. O mAP50-95 de pose calcula a média da precisão nos limiares de OKS de 0.50 a 0.95, exatamente como o mAP de caixa faz com os limiares de IoU; o guia de métricas de desempenho explica o mecanismo compartilhado.

A maneira mais fácil de entender os sigmas é medi-los. Este script avalia a mesma previsão da empilhadeira com dois sigmas, usando a função kpt_iou chamada pelo 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

Em uma empilhadeira de 200 × 120 pixels, uma previsão com erro de 10 pixels não atinge o limiar de OKS de 0.50 com sigma 0.025 e passa por todos os limiares até 0.90 com sigma 0.10. Mesmo modelo, mesmo erro, mAP de pose bem diferente.

SigmaEfeitoUso
Pequeno (≈ 0.025–0.05)um erro de poucos pixels já reduz a pontuaçãopontos bem definidos: olhos, pontas dos garfos, cantos
Grande (≈ 0.08–0.10)tolerância amplapontos imprecisos: quadris, meio de um mastro
PadrãoSigmas COCO para kpt_shape: [17, 3]; caso contrário, 1/K para cada pontocomece por aqui e depois meça

Os sigmas devem refletir a consistência com que os anotadores posicionam cada ponto. Faça a medição: peça a duas pessoas que rotulem as mesmas 50 imagens e compare as distâncias por ponto-chave. Pontos com maior divergência entre anotadores devem receber sigmas maiores; caso contrário, a métrica penaliza o modelo pelo ruído humano.

As métricas só são comparáveis dentro do mesmo esquema

Alterar kpt_oks_sigmas altera o mAP de pose sem alterar o modelo, como mostra a tabela acima. Define os sigmas antes da primeira medição de referência, regista-os com cada resultado e nunca compares execuções validadas com valores diferentes.

Experimente

Projete um esquema de pontos-chave para um objeto do seu domínio: liste de 3 a 8 pontos-chave, cada um com uma justificativa de uma linha, um ponto correspondente na inversão e um sigma. Escreva o YAML, execute check_pose_labels.py em COCO8-pose e depois em alguns dos seus próprios rótulos, e use oks.py para descobrir quantos pixels de erro seu ponto mais preciso tolera antes que o OKS caia abaixo de 0.5.

Concluído quando
Você concluiu a aula quando todos estes critérios forem atendidos.
  • Sua tabela do esquema nomeia cada ponto-chave, sua finalidade, seu ponto correspondente na inversão e seu sigma.

  • Seu YAML contém kpt_shape, um flip_idx que é sua própria inversa e (se usado) um sigma positivo por ponto-chave; o verificador informa que 0 linhas apresentam erros na sua amostra.

  • Você consegue indicar o erro em pixels a partir do qual seu ponto-chave mais preciso deixa de contar para um OKS de 0.5.

Mostrar solução

Exemplo de um pacote em uma esteira transportadora, visto de cima, quando a pergunta é "a etiqueta está virada para cima e em que direção está girada?":

ÍndicePonto-chaveFinalidadePonto correspondente na inversãoSigma
0label_top_leftorientação da etiqueta10.04
1label_top_rightorientação da etiqueta00.04
2label_bottom_rightorientação da etiqueta30.04
3label_bottom_leftorientação da 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 passa na verificação de ser sua própria inversa: 0 → 1 → 0 e 2 → 3 → 2. Uma etiqueta espelhada troca esquerda e direita, mas mantém a parte de cima e a de baixo; portanto, o canto superior esquerdo corresponde ao superior direito, não ao inferior direito.

Para encontrar a tolerância, defina sigma=[0.04] * 4 em oks.py e aumente os deslocamentos; a caixa da etiqueta define a escala, então use uma caixa do tamanho de uma etiqueta de pacote típica nas suas imagens. O deslocamento em que o OKS cai pela primeira vez abaixo de 0.5 é o maior erro que a precisão dos seus anotadores permite ao modelo.

Próximos passos

A seguir, lidaremos com objetos cuja orientação importa, mas cuja forma é um retângulo simples: caixas delimitadoras orientadas.

Entre para salvar seu progresso