Supressão Não Máxima (Non-Maximum Suppression, NMS)
O que é Supressão Não Máxima (NMS)?
Supressão Não Máxima (NMS, Non-Maximum Suppression) é uma etapa padrão de pós-processamento (post-processing) em Detecção de Objetos que remove caixas delimitadoras (bounding boxes) redundantes e altamente sobrepostas, mantendo a predição mais confiável para cada objeto. A maioria dos detectores modernos (por exemplo, detectores baseados em âncoras e detectores densos) produz muitas caixas candidatas por objeto. Sem NMS, você pode ver 10–50 caixas agrupadas em torno da mesma pessoa/carro, o que infla falsos positivos e prejudica as métricas de avaliação.
Em alto nível, a NMS:
- Ordena as caixas preditas por pontuação de confiança (maior primeiro).
- Escolhe a melhor caixa atual.
- Suprime (remove) outras caixas que se sobrepõem demais a ela, medido por IoU.
- Repete até não restarem caixas (ou até atingir um número máximo).
A NMS é “não máxima” porque, dentro de uma vizinhança local de predições sobrepostas, ela mantém apenas a de maior pontuação.
Por que detectores produzem caixas duplicadas?
Caixas redundantes surgem por várias razões:
- Múltiplas âncoras / priors podem corresponder ao mesmo objeto (comum em designs do tipo SSD/Faster R-CNN/RetinaNet).
- Cabeças de predição densas produzem uma caixa por localização no mapa de características, então localizações vizinhas predizem caixas semelhantes.
- Pirâmides de características multiescala (FPN) fazem com que o mesmo objeto seja proposto em múltiplas resoluções.
- Pontuações de classe são independentes (cabeças multi-rótulo), fazendo com que a mesma caixa apareça em diferentes classes.
A NMS é o mecanismo prático que converte “muitos candidatos plausíveis” em “um pequeno conjunto de detecções finais”.
Interseção sobre União (IoU): o critério de sobreposição
A NMS normalmente usa Interseção sobre União (IoU, Intersection over Union) para medir a sobreposição entre duas caixas:
[ \mathrm{IoU}(A, B) = \frac{|A \cap B|}{|A \cup B|} ]
- IoU = 0 significa nenhuma sobreposição
- IoU = 1 significa caixas idênticas
Se você precisar de mais detalhes sobre como calcular a IoU e como ela é usada na avaliação, veja Interseção sobre União (IoU).
Por que IoU é usada na NMS
A IoU se correlaciona com a redundância de “mesmo objeto”: se duas caixas têm IoU muito alta, elas provavelmente se referem ao mesmo objeto subjacente. A NMS usa um limiar de IoU (IoU threshold) (por exemplo, 0.5) para decidir quando duas caixas são “parecidas demais”.
NMS gulosa (padrão)
A forma mais comum é a NMS gulosa (greedy NMS) (também chamada de “NMS rígida (hard NMS)”). Ela toma decisões localmente ótimas, sempre selecionando primeiro a caixa de maior confiança.
Algoritmo (por classe, típico)
Dadas caixas (B = {b_i}) com pontuações (s_i):
- Remova caixas com pontuação abaixo de um limiar de pontuação (score threshold) (opcional, mas comum).
- Ordene as caixas restantes por pontuação decrescente.
- Inicialize o conjunto vazio
keep. - Enquanto restarem caixas:
- Pegue a caixa de maior pontuação
me adicione-a akeep. - Remova toda outra caixa
bcom (\mathrm{IoU}(m, b) \ge t), onde (t) é o limiar de IoU.
- Pegue a caixa de maior pontuação
- Retorne
keep(frequentemente truncado paramax_detections).
Pseudocode
def greedy_nms(boxes, scores, iou_thresh):
# boxes: (N, 4) in [x1, y1, x2, y2]
# scores: (N,)
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
ious = iou(boxes[i], boxes[order[1:]]) # vector IoU with remaining
remaining = order[1:][ious < iou_thresh]
order = remaining
return keep
Exemplo prático (intuição)
Suponha que seu detector preveja três caixas ao redor do mesmo cachorro:
- Caixa A: score 0.92
- Caixa B: score 0.86, IoU(A, B) = 0.73
- Caixa C: score 0.60, IoU(A, C) = 0.20
Com limiar de IoU (t = 0.5):
- Mantém A (0.92)
- Suprime B (sobrepõe demais A: 0.73 ≥ 0.5)
- Mantém C (a sobreposição é pequena: 0.20 < 0.5)
Resultado: A e C permanecem — A para o cachorro, e C pode ser outro objeto próximo ou um falso positivo parcial, dependendo da imagem.
Prós e contras da NMS gulosa
Prós
- Simples, rápida, amplamente implementada (kernels CUDA existem na maioria dos frameworks).
- Funciona bem em muitos cenários padrão de detecção.
Contras
- Pode prejudicar o recall em cenas lotadas (por exemplo, pessoas em uma multidão), onde objetos reais se sobrepõem.
- A supressão rígida é frágil: uma caixa “melhor” ligeiramente errada pode eliminar uma alternativa melhor localizada.
- Decisões gulosas não são globalmente ótimas.
Hiperparâmetros-chave e ajuste prático
O comportamento da NMS é altamente sensível a um pequeno número de hiperparâmetros.
1) Limiar de IoU (`nms_iou_threshold`)
Controla quão agressivamente caixas sobrepostas são removidas.
- Limiar mais baixo (ex.: 0.3): supressão mais agressiva
- Tende a aumentar a precisão (precision) (menos duplicatas)
- Pode diminuir o recall (pode suprimir objetos verdadeiros vizinhos)
- Limiar mais alto (ex.: 0.6–0.7): supressão menos agressiva
- Tende a aumentar o recall (mantém mais caixas)
- Pode diminuir a precisão (mais duplicatas / poluição)
Pontos de partida típicos:
- Detecção geral de objetos: 0.5
- Cenas lotadas (pedestres): 0.6–0.7
- Objetos pequenos / conjuntos de dados densos: frequentemente limiares mais altos
2) Limiar de pontuação (`score_threshold`)
Filtra caixas de baixa confiança antes da NMS.
- Melhora a velocidade (menos caixas entram na NMS)
- Reduz detecções “lixo”
- Se for alto demais, você perde recall cedo e a NMS não consegue recuperá-lo
Valores típicos: 0.05–0.3 dependendo da calibração do detector.
3) Máximo de detecções (`max_detections`, `top_k`)
A maioria dos pipelines limita o número de saídas por imagem (por exemplo, 100 ou 300) por vazão e restrições de API. Alguns também aplicam top-k pré-NMS (pre-NMS top-k) por classe para limitar computação.
4) NMS por classe vs NMS independente de classe
- NMS por classe (per-class NMS): executa NMS separadamente para cada rótulo de classe.
- Comum em detectores multiclasse; evita que uma caixa de “pessoa” suprima uma caixa de “mochila”.
- NMS independente de classe (class-agnostic NMS): ignora rótulos de classe.
- Pode reduzir duplicatas quando classes são confundíveis, mas pode suprimir sobreposições legítimas entre classes diferentes.
Muitos sistemas em produção usam NMS por classe por padrão, às vezes com tratamento especial para classes mutuamente exclusivas.
Soft-NMS: reduzindo pontuações em vez de deletar caixas
Soft-NMS (soft-NMS) é uma variante popular introduzida para reduzir a perda de recall causada pela supressão rígida. Em vez de remover caixas sobrepostas, ela decaí suas pontuações de confiança como uma função da IoU com a caixa máxima selecionada.
Ideia do Soft-NMS
Quando você seleciona a melhor caixa atual (m), para cada caixa restante (b_i), atualize sua pontuação:
- Decaimento linear (forma comum): [ s_i \leftarrow \begin{cases} s_i & \text{if IoU}(m, b_i) < t\ s_i (1 - \text{IoU}(m, b_i)) & \text{otherwise} \end{cases} ]
- Decaimento Gaussiano (suave, sem limiar): [ s_i \leftarrow s_i \cdot e^{-\frac{\text{IoU}(m,b_i)^2}{\sigma}} ]
Então você reordena (ou mantém uma ordem) conforme as pontuações mudam.
Por que ajuda
Em cenas lotadas, dois objetos reais podem se sobrepor fortemente. A NMS rígida pode descartar um deles por completo; o Soft-NMS, em vez disso, reduz sua pontuação, permitindo que ele sobreviva se ainda for forte o suficiente em relação a outros candidatos.
Hiperparâmetros específicos do Soft-NMS
sigma(Gaussiano): controla quão rapidamente as pontuações decaem. Sigma menor = decaimento mais forte.- opcional
t(Linear): limiar de IoU onde o decaimento começa.
Quando o Soft-NMS é usado
- Detecção de pedestres em multidões
- Conjuntos de dados com forte oclusão
- Aplicações em que perder objetos é mais custoso do que retornar candidatos extras (você pode filtrar depois)
Tradeoff: o Soft-NMS normalmente é mais lento do que a NMS rígida otimizada (embora existam implementações em GPU).
Outras variantes comuns de NMS (que você encontrará na prática)
Mesmo que a NMS gulosa e o Soft-NMS sejam as mais comuns, várias variantes aparecem em pipelines modernos:
DIoU-NMS / CIoU-NMS (supressão sensível a distância/forma)
Esses métodos modificam o critério de sobreposição para levar em conta distância entre centros e/ou proporção de aspecto (aspect ratio), não apenas sobreposição de área. Eles buscam suprimir caixas que não estão apenas sobrepostas, mas também mal alinhadas.
Útil quando:
- Caixas se sobrepõem mas representam objetos diferentes (a IoU sozinha pode ser ambígua)
- A qualidade de localização é crítica
Relacionado a perdas baseadas em IoU usadas durante o treinamento; veja Regressão de Caixa Delimitadora (se disponível no seu wiki) e perdas da família IoU.
NMS com rotação
Para caixas delimitadoras orientadas (por exemplo, detecção de texto, imagens aéreas), a IoU é calculada em retângulos rotacionados, e a NMS usa IoU rotacionada (rotated IoU). Isso é computacionalmente mais caro e geralmente depende de kernels geométricos especializados.
NMS de máscara (segmentação de instância)
Em segmentação de instância, você pode suprimir com base em IoU de máscara (mask IoU) em vez de IoU de caixa, porque máscaras capturam melhor a sobreposição real (por exemplo, duas pessoas com caixas intertravadas mas silhuetas separáveis). Isso é comum em sistemas do tipo Mask R-CNN; veja Segmentação de Instância.
“NMS aprendida”
Alguns sistemas treinam um pequeno modelo para decidir quais caixas manter (ranqueamento e supressão passam a ser aprendidos). Embora promissor, adiciona complexidade e é menos padrão do que o Soft-NMS.
Alternativas: Fusão Ponderada de Caixas (WBF)
Não é uma variante de NMS, mas frequentemente é discutida junto com ela. WBF (Weighted Box Fusion) mescla múltiplas caixas sobrepostas em uma média ponderada (pela pontuação), frequentemente usada em ensembling. Pode melhorar a localização, mas pode ser menos adequada para pipelines em tempo real.
Como a NMS afeta precisão, recall e mAP
A NMS muda diretamente o conjunto final de caixas preditas, o que afeta as métricas de avaliação.
Impacto em precisão e recall
- Sem NMS, um único objeto pode produzir muitas predições de alta pontuação. Na avaliação, apenas uma predição pode casar com um objeto de referência (ground truth) (dependendo das regras de correspondência), e o restante vira falsos positivos → a precisão cai.
- Com NMS agressiva demais (limiar de IoU baixo), você pode suprimir caixas de objetos distintos que se sobrepõem (por exemplo, duas pessoas abraçadas) → falsos negativos aumentam → o recall cai.
Veja Precisão e Recall para as definições das métricas.
Impacto em mAP (e por que a NMS importa)
mAP (mean Average Precision) integra o comportamento precisão-recall ao longo de limiares de pontuação (veja Mean Average Precision (mAP)). A NMS influencia a curva PR ao:
- Remover duplicatas → melhorar a precisão em muitos níveis de recall
- Potencialmente remover verdadeiros positivos em casos lotados → reduzir o recall alcançável
- Alterar o ranqueamento das detecções (especialmente com Soft-NMS), o que afeta AP porque AP depende da ordem das detecções por confiança
Cenário concreto: duplicatas prejudicam AP
Se uma imagem tem um objeto de referência e o modelo produz:
- 1 detecção correta (TP)
- 9 duplicatas ao redor dela (FP após a correspondência)
Mesmo com pontuações altas, essas duplicatas acumulam rapidamente falsos positivos, fazendo a precisão colapsar em alto recall. A NMS impede que a maioria dessas duplicatas apareça na lista final, frequentemente dando um grande ganho de AP “de graça”.
NMS e limiares de IoU na avaliação
Tenha cuidado para não confundir:
- Limiar de IoU da NMS (regra de supressão)
- Limiar de IoU da avaliação (o que conta como detecção correta, por exemplo, AP@0.50 vs AP@[.50:.95])
Eles interagem, mas são controles diferentes. Por exemplo:
- Se a avaliação exige alta qualidade de localização (por exemplo, AP@0.75), uma NMS agressiva demais pode manter uma caixa top-score ligeiramente mal localizada e suprimir outra com melhor ajuste, prejudicando AP@0.75.
Considerações práticas em sistemas reais
Complexidade computacional e velocidade
A NMS ingênua é (O(N^2)) no número de caixas (N). Detectores reais podem produzir milhares de candidatos por imagem. Truques comuns de velocidade:
- Pré-filtrar por limiar de pontuação
- Seleção top-k pré-NMS (por classe)
- Kernels de NMS em lote na GPU
- NMS independente de classe para reduzir trabalho repetido (às vezes)
No PyTorch, por exemplo, torchvision.ops.nms e batched_nms são blocos de construção comuns.
Pipelines multiclasse: o que “por classe” significa operacionalmente
Uma cabeça de detecção multiclasse típica produz:
- Caixas: (N \times 4)
- Pontuações de classe: (N \times C)
Um padrão comum de implementação:
- Para cada classe (c):
- Selecione caixas com pontuação > limiar para a classe (c)
- Execute NMS
- Concatene resultados entre classes
- Mantenha o top global
max_detections
Isso é simples, mas pode ser caro quando (C) é grande.
Calibração importa
A NMS assume que as pontuações de confiança são comparáveis dentro de uma classe (ou entre classes se for independente de classe). Pontuações mal calibradas podem causar:
- A caixa “errada” vencer cedo e suprimir caixas melhores
- Resultados instáveis entre imagens
A calibração pode ser melhorada via escolhas de treinamento, funções de perda ou métodos pós-hoc; veja Calibração de Probabilidade.
Detectores sem NMS (contexto)
Algumas famílias mais novas de detectores (por exemplo, predição de conjuntos no estilo DETR) visam reduzir ou eliminar a necessidade de NMS ao impor correspondência um-para-um durante o treinamento. No entanto, muitos pipelines de produção ainda usam NMS devido a:
- Arquiteturas legadas
- Restrições de latência
- A simplicidade e confiabilidade da NMS em detectores densos
Veja DETR e Arquitetura Transformer para ideias relacionadas.
Exemplo: implementando NMS gulosa em Python (mínimo, ilustrativo)
Abaixo está uma implementação educacional simplificada. Em produção, prefira operações otimizadas de biblioteca.
import numpy as np
def iou_1_to_many(box, boxes):
# box: (4,), boxes: (N,4) in [x1,y1,x2,y2]
x1 = np.maximum(box[0], boxes[:, 0])
y1 = np.maximum(box[1], boxes[:, 1])
x2 = np.minimum(box[2], boxes[:, 2])
y2 = np.minimum(box[3], boxes[:, 3])
inter_w = np.maximum(0, x2 - x1)
inter_h = np.maximum(0, y2 - y1)
inter = inter_w * inter_h
area_box = (box[2] - box[0]) * (box[3] - box[1])
area_boxes = (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])
union = area_box + area_boxes - inter
return inter / np.maximum(union, 1e-9)
def greedy_nms(boxes, scores, iou_thresh=0.5, score_thresh=0.0):
keep = []
idx = np.where(scores >= score_thresh)[0]
idx = idx[np.argsort(scores[idx])[::-1]] # sort by descending score
while idx.size > 0:
i = idx[0]
keep.append(i)
if idx.size == 1:
break
ious = iou_1_to_many(boxes[i], boxes[idx[1:]])
idx = idx[1:][ious < iou_thresh]
return keep
Escolhendo configurações de NMS: heurísticas práticas
Se você vê muitas caixas duplicadas
- Aumente ligeiramente o limiar de pontuação
- Diminua o limiar de IoU da NMS (mais agressivo)
- Garanta que a NMS por classe esteja habilitada (a menos que você intencionalmente queira independente de classe)
Se você perde objetos em multidões/oclusão
- Aumente o limiar de IoU da NMS
- Experimente Soft-NMS (Gaussiano costuma ser robusto)
- Considere aumentar
max_detections - Verifique se a avaliação favorece desempenho em multidões (por exemplo, benchmarks de pedestres)
Se AP@0.75 está pior do que o esperado
- Sua seleção de caixa “o vencedor leva tudo” pode estar suprimindo caixas melhor localizadas.
- Tente um limiar de IoU da NMS um pouco maior ou Soft-NMS.
- Investigue calibração de pontuação e qualidade de localização (questão de treinamento).
Armadilhas comuns e casos de borda
- Objetos diferentes com alta sobreposição: a NMS pode remover verdadeiros positivos em cenas lotadas.
- Objetos grandes contendo pequenos: uma caixa grande pode suprimir uma caixa de objeto pequeno próxima se for independente de classe e a IoU for alta o suficiente.
- Empates de pontuação / ordenação instável: podem causar não determinismo a menos que seja cuidadosamente implementado.
- Convenções de coordenadas:
[x1,y1,x2,y2]vs[cx,cy,w,h], indexação de pixels inclusiva vs exclusiva — pequenos erros mudam a IoU. - Agrupamento (batching) entre imagens: garanta que a NMS não esteja misturando acidentalmente detecções de imagens diferentes (frameworks lidam com isso com offsets ou utilitários de batching).
Resumo
A Supressão Não Máxima é uma etapa crucial de pós-processamento que transforma saídas densas e redundantes de detectores em um conjunto limpo de detecções finais. A NMS gulosa (rígida) padrão é rápida e eficaz, enquanto o Soft-NMS frequentemente melhora o recall em cenas lotadas ou ocluídas ao decair pontuações em vez de deletar caixas. Os controles mais importantes — limiar de IoU, limiar de pontuação e máximo de detecções — fazem a troca direta entre precisão e recall e podem mudar significativamente o mAP.
Na prática, NMS é menos sobre “uma configuração correta” e mais sobre escolher um comportamento de supressão que combine com seus dados (multidões vs cenas esparsas), restrições de latência e critérios de avaliação.