Guia de testes e nomenclatura do pipeline de calibração da visão computacional. Cobre cada fase (A–F), os termos usados no painel admin e os critérios para considerar cada passo aprovado.
O CV-Eval é uma ferramenta interna de P&D para medir e calibrar a precisão do motor de pose (MediaPipe / fitness_trainer) contra vídeos reais anotados por humanos. O objetivo é ajustar thresholds de contagem de repetições e classificação de execução — não treinar uma IA nova.
catalog_patch)close / standard e ângulos corner_diagonal / cramped_front. Isso calibra o motor para o ICP real (treino solo em academia lotada), não só cenários de estúdio.
/admin#cv-eval). A CLI (scripts em scripts/) continua disponível como alternativa. Exceção: aplicar o catalog_patch no produto (Fase E) é sempre manual, por revisão humana.
Termos que aparecem no painel e nos relatórios. Use exatamente estes nomes ao reportar bugs.
| Termo | O que significa |
|---|---|
| slug | Identificador do exercício (ex.: squat_free, push_up, lunge, bicep_curl, plank, hip_thrust). |
| ângulo | Posição da câmera. Inclui variantes de academia estreita: corner_diagonal, cramped_front. Rótulo bilíngue no dropdown — ex.: Frontal (front). Ver diagrama → |
| capture_distance | Distância câmera–usuário: Perto (close, <2 m), Padrão (standard, 2–4 m), Afastado (far, >4 m). Legado sem campo → unknown. Ver diagrama → |
| manifesto | manifest.json — lista os vídeos ingeridos (slug, URL, ângulo, caminho do arquivo, metadados). Versionado no git (sem o vídeo). |
| anotação / ground truth | Marcação humana do que é "verdade": total de reps e o fim de cada rep com sua zona. Arquivo data/eval_annotations/<slug>.json. |
| zona | Qualidade da rep: verde (ok), amarela (parcial), vermelha (incorreta). Ver seção 4. |
| total_reps | Quantidade de repetições que o humano contou no vídeo. |
| benchmark | Rodar o motor contra os vídeos anotados e medir o erro. Gera .json + .md. |
| MAE | Mean Absolute Error — erro médio de contagem de reps (quanto menor, melhor). |
| taxa ±1 | % de vídeos em que o motor errou no máximo 1 rep. |
| acurácia de zonas | % de reps em que a zona (verde/amarela/vermelha) prevista bate com a anotada. |
| Tuning e thresholds | |
| tuning | Otimização automática dos thresholds (busca coordinate descent — varre um parâmetro por vez — com split treino/validação). Requer ≥3 vídeos anotados por exercício. Ver diagrama do ciclo de rep → |
| catalog_patch | JSON com os thresholds vencedores sugeridos pelo tuning, para colar em src/exercises/catalog.py (aplicação manual, com revisão humana). |
| thresholds (limiares) | Valores de ângulo/config que o motor usa para decidir início/fim de cada rep e a zona de qualidade. O tuning ajusta exatamente esses números. |
| top_threshold | Ângulo da posição estendida ("em cima") que abre/fecha a rep. Deve ser maior que deep_threshold. |
| deep_threshold | Ângulo da posição profunda ("embaixo", fundo do movimento) que confirma a rep. |
| flexion_green | Faixa de ângulo no fundo do movimento tratada como execução boa (zona verde) — par min/max. |
| yellow_margin | Tolerância em graus fora da faixa verde ainda aceita como amarela; além disso, vira vermelha. |
| ema_alpha | Fator de suavização EMA (0–1) dos ângulos/landmarks. Maior = mais responsivo, menos suave. Global (unified.py). |
| median_size | Janela da mediana temporal (3 ou 5 frames) que remove ruído do sinal antes de contar. |
| model_sweep / sweep de modelo | Compara lite/full/heavy e recomenda o modelo mais leve aceitável por ângulo/distância. Relatório modelsweep_*. Não treina o MediaPipe. |
| landmark | Um dos 33 pontos-chave do corpo detectados pelo MediaPipe — cada um com x, y, z e visibilidade. |
| FSM (máquina de estados) | Lógica que conta reps alternando entre os estados "em cima" ↔ "embaixo" conforme os thresholds. |
| replay | Re-simulação offline do motor sobre landmarks já extraídos — não re-roda a câmera nem o MediaPipe (por isso o tuning é barato). |
| confidence (det_conf / trk_conf) | Confiança mínima de detecção / rastreamento da pose (padrão 0.65). |
| baseline vs. melhor | No tuning: score do catálogo atual contra a melhor combinação encontrada, em treino e validação. |
| objective (função objetivo) | O que o tuner maximiza: 0.6 × contagem + 0.4 × forma (só contagem se o vídeo não tiver zonas anotadas). |
| count_score | Score de contagem 0–1: 1 − erro relativo, médio entre os vídeos. |
| coordinate descent | Estratégia do tuning: varre um parâmetro por vez mantendo os outros fixos, repetindo até não melhorar (máx. 3 varreduras). |
| treino / validação | Split dos vídeos anotados: ~⅓ vai para validação. Com <4 vídeos, treino = validação (small_dataset). |
| changed_params | Quais thresholds o tuning alterou (from → to). Vazio = catálogo já estava ótimo no dataset. |
| strict_gate | Mantém o gate de calibração real (2 s + visibilidade) antes de contar reps; sem ele, o replay já começa contando. |
| rep_mode / isometric_hold | Modo do exercício. Sustentações (ex.: plank) são isometric_hold — sem contagem, então o tuning é pulado. |
| combos_evaluated | Quantas combinações de parâmetros o tuner testou (inclui baseline). Quanto maior, mais longo o job. |
| small_dataset | Flag no relatório quando há <4 vídeos: treino e validação usam os mesmos clips — confie menos na validação. |
| smoothing (patch) | Parte do catalog_patch com ema_alpha e/ou median_size — aplicar em src/exercises/unified.py, não no spec do exercício. |
| MediaPipe e motores | |
| custo relativo | Custo de inferência aproximado por modelo: lite 1× · full ~2,3× · heavy ~6×. |
| motor (engine) | native (motor nativo) ou fitness_trainer; both roda os dois. |
| modelo (model) | Variante do MediaPipe: lite (padrão, leve), full e heavy (mais pesados/precisos). O model_sweep ajuda a escolher. |
| job | Tarefa assíncrona (ingest / benchmark / tuning / sweep). Estados: queued → running → done / error. |
| cobertura X/50 | Card "Fase 2 ML" — quantos vídeos já anotados, meta 50. Fica verde com ≥50. |
| matriz | Tabela exercício × ângulo ou exercício × distância. Célula verde = ≥3 anotados naquela combinação. |
| ✓ tuning | Selo na cobertura: o exercício tem ≥3 anotados e está pronto para tuning. |
O motor alterna entre TOP (estendido) e DEEP (fundo). Os thresholds definem quando trocar de estado e como classificar a qualidade no fundo. O tuning busca os números que maximizam contagem + forma no dataset anotado.
top_threshold, deep_threshold, flexion_green, yellow_margin, ema_alpha e median_size até maximizar a função objetivo.Ângulo mínimo para considerar "em cima". Deve ser > deep_threshold.
Confirma que a rep desceu o suficiente antes de subir de novo.
Par min/max no fundo — execução boa.
Graus fora do verde ainda aceitos como parcial.
1 parâmetro por vez · até 3 varreduras · split treino/validação.
JSON com thresholds vencedores → revisão humana → catalog.py.
Use estes desenhos ao preencher os dropdowns Ângulo e Distância na Fase A. A posição da câmera é sempre em relação ao atleta (centro). O id entre parênteses é o valor exato do painel. A altura da câmera (diagrama 4) ajuda a entender o enquadramento — hoje não tem dropdown próprio no CV-Eval.
capture_guide (F-175) do produto. No CV-Eval, registre só ângulo + distância; se a altura for atípica (chão, banco, tripé), descreva nas notas da anotação.camera_angle e capture_distance. A altura influencia qual ângulo escolher (ex.: prancha no chão → perfil baixo, mais próximo de side_90 + close) — use as notas da anotação para casos extremos até existir taxonomia própria.
Prancha, flexão no chão — template lateral_chao.
Padrão da maioria dos exercícios em pé — standard.
Supino, rosca — template altura_banco_lateral.
Câmera de frente — ombros simétricos no quadro.
Entre frontal e perfil — agachamento, pernas.
Perfil puro — rosca, tríceps, extensora.
Corpo ligeiramente virado (~30–45°).
Academia estreita — câmera no canto, ~60°.
Corredor — só cabe filmar de frente.
Corredor estreito — corpo ocupa boa parte do quadro.
Distância recomendada pelo capture_guide.
Celular no fim do corredor — corpo pequeno no quadro.
cramped_front + close ou standardcorner_diagonal + closefront + standardside_90 + distância conforme tamanho no quadroside_90 ou side_45 + close; nota «lateral_chao»side_90 + standard; nota «altura_banco»unknown só se inevitável; prefira a combinação mais próximaAo anotar, cada repetição recebe uma zona pela qualidade do movimento. As teclas e cores são fixas em todo o painel.
Verde — correta
Rep completa e na amplitude esperada. Tecla G.
Amarela — parcial
Rep incompleta ou no limite (amplitude curta). Tecla Y.
Vermelha — incorreta
Execução errada / fora de padrão. Tecla X.
Cada vídeo na tabela "Inventário de vídeos" tem dois indicadores. QA deve validar que eles mudam no momento certo.
| Coluna | Valores | Quando muda |
|---|---|---|
| arquivo | sim não | Vira sim quando o MP4 foi baixado (ingest URL) ou enviado (upload). |
| anotado | sim não | Vira sim após salvar a anotação (Fase B). Logo após a ingestão deve estar não. |
| distância | close / standard / far / unknown |
Definida na ingestão (dropdown). Legado sem campo aparece como unknown. |
| Ações | Anotar / Editar | "Anotar" se ainda não anotado; "Editar" se já existe anotação. |
Trazer o vídeo para o dataset — por URL (YouTube preferível) ou por upload MP4 local (≤250 MB).
done.corner_diagonal ou cramped_front e distâncias close / standard conforme o espaço real.Marcar a "verdade" que o motor será comparado contra. Abre pelo botão Anotar do inventário ou pelo Próximo passo.
total_reps se a contagem não bater com as marcações.Pré-requisito: ≥1 vídeo anotado com MP4. Rodar o motor contra os anotados.
native / fitness_trainer / both) e modelo (lite / full / heavy) → Rodar benchmark.lite/full/heavy) — relatório modelsweep_* recomenda o mais leve aceitável por ângulo/distância..md ou viewer).by_distance).benchmark_<slug>_<engine>_<timestamp>.json + .md em data/eval_reports/ com as métricas preenchidas.Pré-requisito: ≥3 vídeos anotados no exercício (cobertura mostra ✓ tuning). Só motor native; exercícios isometric_hold (ex.: plank) são pulados. Consulte o diagrama FSM e o glossário (seção 2).
tuning_*.json: baseline vs. best (treino e validação), changed_params, combos_evaluated, flag small_dataset se <4 vídeos.objective (0.6×contagem + 0.4×forma) subiu na validação — não só no treino.changed_params vazio: catálogo já ótimo neste dataset; não force patch.catalog_patch.exercise_spec → catalog.py; smoothing → unified.py se presente.catalog_patch e permite Copiar JSON.Passo fora do painel, sempre com revisão humana. Só após validação positiva no tuning.
src/exercises/catalog.py com os valores do catalog_patch.# re-exportar catálogo para web/mobile/API
python scripts/export_catalog.py
python scripts/export_fitness_trainer_json.py
GET /exercises/pose-engine-catalog reflete os novos valores e o treino real não regride.| Frequência | Ação |
|---|---|
| Diária (15–30 min) | 1–2 ingestões + 1 anotação |
| Semanal | Benchmark Tier 1 · revisar card X/50 |
| Quinzenal | Tuning nos slugs com ≥3 anotados · aplicar patches vencedores |
| Ao aplicar patch | Export JSON + smoke de regressão |
| Tecla | Ação |
|---|---|
| espaço | Pausar / continuar |
| G | Fim de rep — zona verde |
| Y | Fim de rep — zona amarela |
| X | Fim de rep — zona vermelha |
| U | Desfazer última marcação |
| J / L | Retroceder / avançar no vídeo |
total_reps / notas não marca reps por engano.
Todos exigem token admin. Sem token → 401. Job já em andamento → 409.
| Método · rota | Uso |
|---|---|
| GET /admin/cv-eval/meta | Catálogo de exercícios, ângulos e capture_distances. |
| GET /admin/cv-eval/manifest | Inventário enriquecido (incl. capture_distance) + resumo do pipeline. |
| GET /admin/cv-eval/pipeline | Estado do wizard (fase atual + próximo passo). |
| POST /admin/cv-eval/ingest | Enfileira ingestão por URL (body: capture_distance). |
| POST /admin/cv-eval/ingest-upload | Upload MP4 (≤250 MB; form: capture_distance). |
| GET /admin/cv-eval/annotations/{slug}/{video_id} | Lê anotação existente. |
| PUT /admin/cv-eval/annotations/{slug}/{video_id} | Salva anotação (total_reps + reps). |
| GET /admin/cv-eval/videos/{slug}/{video_id}/stream | Stream do MP4 (Range + token na query). |
| POST /admin/cv-eval/benchmark | Roda benchmark; tune:true roda tuning. |
| GET /admin/cv-eval/tuning-patch | Último catalog_patch. |
| GET /admin/cv-eval/reports/{name} | Relatório JSON ou markdown (path traversal bloqueado). |
Marque cada item ao validar no painel. Espelha docs/PENDENTES_TESTE.md.
ingest-upload (≤250 MB) → inventário arquivo sim.total_reps → inventário anotado sim.catalog_patch + Copiar JSON.corner_diagonal e cramped_front no catálogo e dropdown.by_distance além de by_angle.X/50; verde com ≥50.meta e manifest retornam 401 sem token.409 se já há job rodando.reports/{name} retorna JSON/markdown; traversal bloqueado.| Caminho | Conteúdo |
|---|---|
data/eval_videos/manifest.json | Manifesto (URLs + metadados, versionado). |
data/eval_videos/<slug>/<video_id>.mp4 | Vídeos (gitignored). |
data/eval_annotations/<slug>.json | Anotações ground truth. |
data/eval_reports/benchmark_*.json/.md | Relatórios de benchmark. |
data/eval_reports/tuning_*.json | Relatórios de tuning + catalog_patch. |
data/eval_reports/_jobs/*.json | Estado dos jobs assíncronos. |
src/exercises/catalog.py | Thresholds do produto (alvo da Fase E). |