Caixa grande "engolindo" uma caixa menor do mesmo produto continuava
aparecendo mesmo com sobreposição óbvia — IoU sozinho não resolve isso
porque a união fica dominada pela caixa maior, então o IoU continua baixo
mesmo com contenção quase total (ex.: caixa 1/5 da área da outra, 100%
contida, IoU ≈ 0.2).
- iou=0.5 explícito no NMS do YOLO (antes usava o default 0.7, permissivo
demais)
- novo pós-filtro _suprimir_boxes_contidas(): entre duas boxes da mesma
classe onde a intersecção cobre ≥90% da área da MENOR das duas, mantém
só a de maior confiança
Afeta tanto as boxes da revisão quanto as do TrainingTab, já que ambas vêm
do mesmo /detectar.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
_executar_treino_sync tinha um `import random, shutil` local depois de já
usar shutil.rmtree() mais cedo na mesma função — o import local faz o
Python tratar 'shutil' como variável local pra toda a função, então o uso
anterior lançava "local variable 'shutil' referenced before assignment"
antes mesmo de chegar no split de treino/validação. shutil já vem
importado no escopo do módulo; só falta random no import local.
O fix anterior (forçar project="runs/detect") duplicava o caminho para
"runs/detect/runs/detect/{ambiente}" porque o Ultralytics já resolve esse
project por padrão a partir do runs_dir configurado. Resultado: o "best"
nunca era encontrado e NENHUM modelo era enviado ao S3 (pior que o bug
anterior, que ao menos subia um modelo antigo). Agora só passamos
name/exist_ok e lemos o caminho real via modelo_base.trainer.save_dir,
sem adivinhar a string.
Também adiciona allowlist para 'ambiente' em /detectar e /treinar — o
valor é usado sem sanitização para montar paths locais (/tmp/...,
runs/detect/...) e chaves S3, o que permitia path traversal.
O Ultralytics incrementa o diretório de saída (train, train2, train3...)
quando o anterior já existe, mas o código sempre lia de um caminho fixo
"runs/detect/train/weights/best.pt". Depois do primeiro treino nesse
container, todo treino seguinte silenciosamente subia pro S3 o best.pt
de uma rodada anterior em vez do modelo recém-treinado — por isso as
métricas de antes/depois batiam sempre igual, mesmo com o modelo
internamente convergindo bem (mAP50 90%+ no treino do-zero mais recente).
Agora project/name/exist_ok fixam o diretório de saída por ambiente,
garantindo que o best.pt lido seja sempre o do treino que acabou de rodar.
Permite disparar treino com yolov8n limpo (sem fine-tuning) sem apagar
o cerebro.pt em produção, que continua servindo o /detectar normalmente
enquanto o novo treino roda.
Triagem, download da base, contagem de imagens e mensagens de conclusão
agora aparecem no console de logs do frontend em tempo real, não apenas
nos logs do container.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Antes, val e train apontavam para o mesmo diretório, causando
overfitting silencioso e métricas infladas. Agora 20% das imagens
são movidas para val/ antes do treino.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Imagens onde o YOLO não detecta nada (conf == 0) passavam direto para
base-oficial, contaminando o dataset. Agora vão para descartados e
precisam de revisão humana antes de entrar no treino.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>