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