fix: reduzir sobreposição de boxes em /detectar

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>
This commit is contained in:
2026-08-04 14:07:40 -03:00
co-authored by Claude Sonnet 5
parent 25ce7fd39f
commit 3efdb08c9b
2 changed files with 41 additions and 2 deletions
+29 -1
View File
@@ -66,6 +66,33 @@ def log_print(msg):
if len(_training_logs) > 60:
_training_logs.pop(0)
def _suprimir_boxes_contidas(deteccoes: list, limiar: float = 0.90) -> list:
"""Remove boxes da mesma classe quase totalmente contidas em outra (ex.: caixa grande
envolvendo uma caixa menor do mesmo produto). IoU sozinho não pega esse caso: a união fica
dominada pela caixa maior, então o IoU continua baixo mesmo com contenção quase total.
Aqui a métrica é intersecção / área da MENOR das duas caixas do par."""
ordenadas = sorted(deteccoes, key=lambda d: d["conf"], reverse=True)
mantidas: list = []
for d in ordenadas:
x1, y1, x2, y2 = d["box"]
area_d = max(0.0, x2 - x1) * max(0.0, y2 - y1)
contida = False
for m in mantidas:
if m["class"] != d["class"]:
continue
mx1, my1, mx2, my2 = m["box"]
area_m = max(0.0, mx2 - mx1) * max(0.0, my2 - my1)
ix1, iy1 = max(x1, mx1), max(y1, my1)
ix2, iy2 = min(x2, mx2), min(y2, my2)
inter = max(0.0, ix2 - ix1) * max(0.0, iy2 - iy1)
menor_area = min(area_d, area_m)
if menor_area > 0 and inter / menor_area >= limiar:
contida = True
break
if not contida:
mantidas.append(d)
return mantidas
def carregar_modelo_do_s3(ambiente: str):
s3_key = f"modelos/{ambiente}/atual/cerebro.pt"
local_path = f"/tmp/cerebro_{ambiente}.pt"
@@ -258,11 +285,12 @@ async def detectar(ambiente: str = Form(...), file: UploadFile = File(...)):
modelo = carregar_modelo_do_s3(ambiente)
conteudo = await file.read()
imagem = Image.open(io.BytesIO(conteudo))
results = modelo(imagem, conf=0.25)
results = modelo(imagem, conf=0.25, iou=0.5)
deteccoes = [
{"box": [round(x, 2) for x in b.xyxy[0].tolist()], "conf": round(float(b.conf), 2), "class": int(b.cls)}
for r in results for b in r.boxes
]
deteccoes = _suprimir_boxes_contidas(deteccoes, limiar=0.90)
return {"status": "sucesso", "deteccoes": deteccoes}
except Exception as e:
log_print(f"Erro deteccao: {str(e)}")