7.13. Non-max suppression

Uma rede de detecção normalmente produz várias caixas candidatas sobrepostas em torno do mesmo objeto do mundo real: cada âncora próxima ao objeto dispara com uma pontuação semelhante, e o pós-processador vê todas elas. O non-max suppression (NMS) é a etapa que transforma esse agrupamento em uma única caixa.

O algoritmo é curto: ordene as caixas candidatas pela pontuação, pegue a de maior pontuação, suprima todas as outras caixas que se sobrepõem a ela acima de um limiar escolhido, depois pegue a próxima de maior pontuação dentre as restantes e repita. A métrica de sobreposição é a intersection-over-union (IoU) – a área compartilhada de duas caixas dividida pela área combinada delas, um valor entre 0 (nenhuma sobreposição) e 1 (caixas idênticas). O argumento de construtor nms_threshold em cada pós-processador fornecido é o ponto de corte acima do qual as caixas são tratadas como duplicatas de uma caixa já mantida.

À esquerda, três caixas delimitadoras sobrepostas em torno de um único sujeito, rotuladas com as pontuações 0.92, 0.83 e 0.71. Uma seta aponta para a direita, onde uma caixa sobrevivente permanece com a pontuação 0.92. As duas caixas de menor pontuação são suprimidas pela de maior pontuação porque sua intersection-over-union com ela excede o limiar.

O NMS reduz um agrupamento de detecções sobrepostas àquela de maior pontuação.

7.13.1. Soft-NMS

A classe ml.utils.NMS fornecida implementa o Soft-NMS, um refinamento que decai a pontuação de uma caixa sobreposta em uma quantidade que depende de quanto ela se sobrepõe, em vez de descartar a caixa imediatamente. Se a pontuação reduzida cair abaixo do limiar, a caixa é descartada; caso contrário, ela sobrevive com a pontuação reduzida e compete na próxima rodada.

O parâmetro nms_sigma controla o quão agressivo é o decaimento. Com um nms_sigma pequeno (o padrão fornecido de 0.1), o decaimento é acentuado: uma caixa fortemente sobreposta tem sua pontuação reduzida a quase zero e o Soft-NMS se reduz ao NMS clássico. Com um nms_sigma maior, o decaimento é suave e caixas sobrepostas de objetos diferentes sobrevivem com mais frequência, o que importa quando objetos do mundo real da mesma classe realmente se sobrepõem (uma multidão de rostos, um aglomerado de palmas das mãos).

Definir nms_sigma como <= 0 desabilita o decaimento inteiramente: caixas sobrepostas passam com suas pontuações originais, e apenas o limiar de pontuação as filtra.

7.13.2. Construindo um diretamente

Cada pós-processador fornecido constrói um novo NMS por inferência, adiciona cada candidato a ele e chama get_bounding_boxes() no final. Um pós-processador personalizado segue o mesmo padrão:

from ml.utils import NMS

iw = model.input_shape[0][2]
ih = model.input_shape[0][1]

nms = NMS(iw, ih, inputs[0].roi)
for box, score, class_idx in candidates:
    nms.add_bounding_box(box.xmin, box.ymin,
                         box.xmax, box.ymax,
                         score, class_idx)
result = nms.get_bounding_boxes(threshold=nms_threshold,
                                sigma=nms_sigma)

O construtor recebe a largura e a altura de entrada da rede em pixels e a ROI da imagem original contra a qual o modelo foi executado; add_bounding_box() recebe coordenadas de caixa nesse espaço de pixels de entrada da rede, e get_bounding_boxes() remapeia os sobreviventes de volta para coordenadas de imagem usando a ROI. O remapeamento leva em conta o alongamento de normalização automaticamente – a mesma ROI que o preditor viu é usada para projetar as caixas de volta – portanto, as caixas retornadas estão prontas para serem desenhadas no quadro capturado.

O formato de retorno é uma lista de listas por classe, indexada pelo label_index passado para add_bounding_box. As listas de classes vazias são preservadas para que o índice corresponda ao índice de classe do modelo; enumerate(result) percorre as classes junto com suas detecções.