BV BodyVision.IA · QA

Manual QA — Pipeline CV-Eval (F-184)

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.

Público: Equipe de QA Painel: /admin#cv-eval Feature: F-184 Guia técnico: docs/CV_EVAL_OPERACIONAL.md Atualização: 2026-06-15
01 · Contexto

O que é o CV-Eval

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.

✅ O CV-Eval faz

  • Mede precisão atual (MAE, ±1 rep, acurácia de zonas)
  • Compara o que o motor conta vs. o que o humano marcou
  • Sugere novos thresholds (catalog_patch)
  • Quebra os resultados por exercício × ângulo e exercício × distância (academia estreita)

🚫 O CV-Eval NÃO faz

  • Não treina modelo de IA novo
  • Não aplica mudanças no produto sozinho (passo manual)
  • Não redistribui vídeos (só guarda URLs no manifesto)
  • Não roda no app do usuário — é só admin / P&D
Academia estreita (foco do dataset) O pipeline prioriza vídeos filmados em corredores e cantos de academia: distâncias 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.
Painel admin × CLI Quase tudo pode ser feito pelo painel admin (/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.
02 · Glossário

Nomenclatura

Termos que aparecem no painel e nos relatórios. Use exatamente estes nomes ao reportar bugs.

TermoO que significa
slugIdentificador do exercício (ex.: squat_free, push_up, lunge, bicep_curl, plank, hip_thrust).
ânguloPosiçã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_distanceDistâ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 →
manifestomanifest.json — lista os vídeos ingeridos (slug, URL, ângulo, caminho do arquivo, metadados). Versionado no git (sem o vídeo).
anotação / ground truthMarcação humana do que é "verdade": total de reps e o fim de cada rep com sua zona. Arquivo data/eval_annotations/<slug>.json.
zonaQualidade da rep: verde (ok), amarela (parcial), vermelha (incorreta). Ver seção 4.
total_repsQuantidade de repetições que o humano contou no vídeo.
benchmarkRodar o motor contra os vídeos anotados e medir o erro. Gera .json + .md.
MAEMean 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
tuningOtimizaçã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_patchJSON 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_greenFaixa de ângulo no fundo do movimento tratada como execução boa (zona verde) — par min/max.
yellow_marginTolerância em graus fora da faixa verde ainda aceita como amarela; além disso, vira vermelha.
ema_alphaFator de suavização EMA (0–1) dos ângulos/landmarks. Maior = mais responsivo, menos suave. Global (unified.py).
median_sizeJanela da mediana temporal (3 ou 5 frames) que remove ruído do sinal antes de contar.
model_sweep / sweep de modeloCompara lite/full/heavy e recomenda o modelo mais leve aceitável por ângulo/distância. Relatório modelsweep_*. Não treina o MediaPipe.
landmarkUm 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.
replayRe-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. melhorNo 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_scoreScore de contagem 0–1: 1 − erro relativo, médio entre os vídeos.
coordinate descentEstratégia do tuning: varre um parâmetro por vez mantendo os outros fixos, repetindo até não melhorar (máx. 3 varreduras).
treino / validaçãoSplit dos vídeos anotados: ~⅓ vai para validação. Com <4 vídeos, treino = validação (small_dataset).
changed_paramsQuais thresholds o tuning alterou (from → to). Vazio = catálogo já estava ótimo no dataset.
strict_gateManté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_holdModo do exercício. Sustentações (ex.: plank) são isometric_hold — sem contagem, então o tuning é pulado.
combos_evaluatedQuantas combinações de parâmetros o tuner testou (inclui baseline). Quanto maior, mais longo o job.
small_datasetFlag 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 relativoCusto 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.
jobTarefa assíncrona (ingest / benchmark / tuning / sweep). Estados: queuedrunningdone / error.
cobertura X/50Card "Fase 2 ML" — quantos vídeos já anotados, meta 50. Fica verde com ≥50.
matrizTabela exercício × ângulo ou exercício × distância. Célula verde = ≥3 anotados naquela combinação.
✓ tuningSelo na cobertura: o exercício tem ≥3 anotados e está pronto para tuning.

Ciclo de uma rep — o que o tuning calibra

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 estendido ângulo ↑ top_threshold DEEP fundo · rep confirmada ângulo ↓ deep_threshold flexion_green ± yellow_margin desce sobe TOP → DEEP → TOP = +1 rep objective 0.6×contagem + 0.4×forma
Exemplo ilustrativo (agachamento). O tuning varre top_threshold, deep_threshold, flexion_green, yellow_margin, ema_alpha e median_size até maximizar a função objetivo.
top_threshold
Posição estendida

Ângulo mínimo para considerar "em cima". Deve ser > deep_threshold.

deep_threshold
Fundo do movimento

Confirma que a rep desceu o suficiente antes de subir de novo.

flexion_green
Faixa verde

Par min/max no fundo — execução boa.

yellow_margin
Tolerância amarela

Graus fora do verde ainda aceitos como parcial.

coordinate descent
Busca do tuning

1 parâmetro por vez · até 3 varreduras · split treino/validação.

catalog_patch
Saída aplicável

JSON com thresholds vencedores → revisão humana → catalog.py.

03 · Guia visual

Ângulos e distâncias — como classificar na ingestão

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.

Vista de cima ângulos

45° 90° atleta · olha p/ cima front Frontal side_90 Lateral 90° side_45 Diagonal ~45° three_quarter Três quartos corner_diagonal canto ~60° corredor estreito cramped_front
Setas apontam da câmera para o atleta. Ângulos em azul = estúdio/aberto; em vermelho = academia estreita.

Vista lateral distâncias

close < 2 m standard 2 – 4 m far > 4 m corpo grande corpo médio corpo pequeno como o corpo aparece na tela do celular corta / cheio ideal pequeno
Tamanho aparente do corpo no quadro ajuda a escolher a faixa. Na dúvida entre duas faixas, escolha a mais próxima e anote no campo de notas da anotação.

Academia estreita cenários reais

corner_diagonal equip. câmera no canto, ~60° entre parede e equipamento cramped_front equip. equip. só cabe de frente — corpo grande no quadro → close ou standard
Priorize vídeos destes cenários no dataset — é o ICP (treino solo em academia lotada). Combine ângulo + distância na ingestão.

Altura da câmera referência F-175

~1,5 m · tripé alto ~1,2 m · cotovelo / banco ~1,0 m · quadril / peito ~0,2 m · chão altura em pé quadril cotovelo quadril · standard chão · lateral_chao banco altura_banco evitar bird's eye
Alturas espelham o 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.
Altura ainda não é campo do manifesto O pipeline F-184 taggeia 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.

Cartões de referência — alturas (F-175)

~0,2 m
Chão / suporte baixo

Prancha, flexão no chão — template lateral_chao.

~1,0 m
Quadril / peito

Padrão da maioria dos exercícios em pé — standard.

~1,2 m
Cotovelo / banco

Supino, rosca — template altura_banco_lateral.

Cartões de referência — ângulos

front
Frontal

Câmera de frente — ombros simétricos no quadro.

side_45
Diagonal (~45°)

Entre frontal e perfil — agachamento, pernas.

side_90
Lateral (90°)

Perfil puro — rosca, tríceps, extensora.

three_quarter
Três quartos

Corpo ligeiramente virado (~30–45°).

corner_diagonal
Diagonal no canto

Academia estreita — câmera no canto, ~60°.

cramped_front
Frontal apertado

Corredor — só cabe filmar de frente.

Cartões de referência — distâncias

close
Perto (<2 m)

Corredor estreito — corpo ocupa boa parte do quadro.

standard
Padrão (2–4 m)

Distância recomendada pelo capture_guide.

far
Afastado (>4 m)

Celular no fim do corredor — corpo pequeno no quadro.

Regra rápida na ingestão (Fase A)
  • Corredor, só frontal?cramped_front + close ou standard
  • Canto entre parede e máquina?corner_diagonal + close
  • Espaço aberto, de frente?front + standard
  • Perfil claro?side_90 + distância conforme tamanho no quadro
  • Prancha / chão? → perfil baixo → side_90 ou side_45 + close; nota «lateral_chao»
  • Supino / banco? → lateral na altura do banco → side_90 + standard; nota «altura_banco»
  • Não sabe? → use unknown só se inevitável; prefira a combinação mais próxima
04 · Classificação

Zonas de execução

Ao 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.

05 · Inventário

Estados no inventário de vídeos

Cada vídeo na tabela "Inventário de vídeos" tem dois indicadores. QA deve validar que eles mudam no momento certo.

ColunaValoresQuando 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.
06 · Visão geral

Fluxo do pipeline

AIngestãoURL ou upload MP4
BAnotaçãomarcar reps + zonas
CBenchmarkmedir precisão
DTuning≥3 anotados
EAplicarpatch manual
Card "Pipeline CV" + "Próximo passo" O painel mostra em qual fase você está e o botão Próximo passo leva para a ação seguinte (anotar / benchmark / tuning / ingerir). QA valida que o botão aponta para a fase correta conforme o estado dos dados.
07 · Passo a passo

Fases A–F (como testar)

A

Ingestão do vídeo

/admin#cv-eval

Trazer o vídeo para o dataset — por URL (YouTube preferível) ou por upload MP4 local (≤250 MB).

  • Por URL: preencher URL + exercício + ângulo + distânciaIngerir vídeo → job vai a done.
  • Por upload: escolher MP4 + exercício + ângulo + distância → enviar.
  • Para academia estreita, preferir ângulos corner_diagonal ou cramped_front e distâncias close / standard conforme o espaço real.
Aprovado quando: o vídeo aparece no inventário com coluna distância preenchida, arquivo sim e anotado não. Os dropdowns de ângulo e distância mostram rótulos bilíngues (não ids crus). Consulte a seção 3 — Guia visual se houver dúvida na classificação.
B

Anotação ground truth (player no browser)

card "Anotar vídeo"

Marcar a "verdade" que o motor será comparado contra. Abre pelo botão Anotar do inventário ou pelo Próximo passo.

  • O player carrega o MP4 (vídeo seekável).
  • Marcar o fim de cada rep com G / Y / X no tempo do vídeo.
  • U desfaz a última marcação errada.
  • Ajustar total_reps se a contagem não bater com as marcações.
  • Salvar anotação.
Aprovado quando: após salvar, o inventário mostra anotado sim e ao reabrir as marcações persistem (merge correto).
C

Benchmark — medir precisão atual

card "Benchmark CV"

Pré-requisito: ≥1 vídeo anotado com MP4. Rodar o motor contra os anotados.

  • Escolher motor (native / fitness_trainer / both) e modelo (lite / full / heavy) → Rodar benchmark.
  • Opcional: Comparar modelos (sweep lite/full/heavy) — relatório modelsweep_* recomenda o mais leve aceitável por ângulo/distância.
  • Abrir o relatório (.md ou viewer).
  • Conferir MAE, taxa ±1, acurácia de zonas, breakdown por ângulo e breakdown por distância (by_distance).
Aprovado quando: gera benchmark_<slug>_<engine>_<timestamp>.json + .md em data/eval_reports/ com as métricas preenchidas.
D

Tuning — otimizar thresholds

botão "Rodar tuning"

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).

  • Rodar tuning (confirmação — pode levar minutos; usa landmarks cacheados + coordinate descent).
  • Ler tuning_*.json: baseline vs. best (treino e validação), changed_params, combos_evaluated, flag small_dataset se <4 vídeos.
  • Conferir objective (0.6×contagem + 0.4×forma) subiu na validação — não só no treino.
  • Se changed_params vazio: catálogo já ótimo neste dataset; não force patch.
  • Copiar catalog_patch.exercise_speccatalog.py; smoothingunified.py se presente.
  • No painel: Carregar patch mostra o último catalog_patch e permite Copiar JSON.
Aprovado quando: o relatório reporta baseline vs. melhor em validação, explica parâmetros alterados (ou ausência), e o card "Último catalog_patch" exibe + copia o JSON.
E

Aplicar no produto (manual)

repositório · CLI

Passo fora do painel, sempre com revisão humana. Só após validação positiva no tuning.

  • Editar src/exercises/catalog.py com os valores do catalog_patch.
  • Re-exportar os JSONs consumidos por web/mobile/API.
  • Re-rodar benchmark (validação pós-patch) + smoke PoC desktop e treino real.
# re-exportar catálogo para web/mobile/API
python scripts/export_catalog.py
python scripts/export_fitness_trainer_json.py
Aprovado quando: GET /exercises/pose-engine-catalog reflete os novos valores e o treino real não regride.
F

Cadência recomendada

rotina
FrequênciaAção
Diária (15–30 min)1–2 ingestões + 1 anotação
SemanalBenchmark Tier 1 · revisar card X/50
QuinzenalTuning nos slugs com ≥3 anotados · aplicar patches vencedores
Ao aplicar patchExport JSON + smoke de regressão
08 · Anotador

Atalhos de teclado (player)

TeclaAção
espaçoPausar / continuar
GFim de rep — zona verde
YFim de rep — zona amarela
XFim de rep — zona vermelha
UDesfazer última marcação
J / LRetroceder / avançar no vídeo
Atenção QA Os atalhos só devem disparar com o player ativo — testar que digitar nos campos total_reps / notas não marca reps por engano.
09 · API

Endpoints do painel

Todos exigem token admin. Sem token → 401. Job já em andamento → 409.

Método · rotaUso
GET /admin/cv-eval/metaCatálogo de exercícios, ângulos e capture_distances.
GET /admin/cv-eval/manifestInventário enriquecido (incl. capture_distance) + resumo do pipeline.
GET /admin/cv-eval/pipelineEstado do wizard (fase atual + próximo passo).
POST /admin/cv-eval/ingestEnfileira ingestão por URL (body: capture_distance).
POST /admin/cv-eval/ingest-uploadUpload 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}/streamStream do MP4 (Range + token na query).
POST /admin/cv-eval/benchmarkRoda 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).
10 · Aceite

Checklist QA (F-184)

Marque cada item ao validar no painel. Espelha docs/PENDENTES_TESTE.md.

11 · Referência

Onde ficam os arquivos

CaminhoConteúdo
data/eval_videos/manifest.jsonManifesto (URLs + metadados, versionado).
data/eval_videos/<slug>/<video_id>.mp4Vídeos (gitignored).
data/eval_annotations/<slug>.jsonAnotações ground truth.
data/eval_reports/benchmark_*.json/.mdRelatórios de benchmark.
data/eval_reports/tuning_*.jsonRelatórios de tuning + catalog_patch.
data/eval_reports/_jobs/*.jsonEstado dos jobs assíncronos.
src/exercises/catalog.pyThresholds do produto (alvo da Fase E).
Flag legal Vídeos de YouTube/Instagram/TikTok servem só para avaliação interna de P&D — não redistribuir. O repositório guarda apenas o manifesto com URLs (vídeos gitignored).