7.13. Supressão de não-máximos¶
Uma rede de deteção produz tipicamente várias caixas candidatas sobrepostas em torno do mesmo objeto real: cada âncora próxima do objeto dispara com uma pontuação semelhante, e o pós-processador vê-as todas. A supressão de não-máximos (NMS) é o passo que transforma esse agrupamento numa única caixa.
O algoritmo é simples: ordenar as caixas candidatas por pontuação, tomar a de maior pontuação, suprimir todas as outras caixas que se sobreponham a ela acima de um limiar escolhido, em seguida tomar a próxima mais alta do que resta, e repetir. A métrica de sobreposição é a interseção sobre união (IoU) – a área partilhada de duas caixas dividida pela sua área combinada, um valor entre 0 (sem sobreposição) e 1 (caixas idênticas). O argumento construtor nms_threshold em todos os pós-processadores incluídos é o corte acima do qual as caixas são tratadas como duplicados de uma caixa já mantida.
O NMS colapsa um agrupamento de deteções sobrepostas para a de maior pontuação.¶
7.13.1. Soft-NMS¶
A classe ml.utils.NMS incluída implementa o Soft-NMS, um refinamento que decai a pontuação de uma caixa sobreposta numa quantidade que depende do grau de sobreposição, em vez de descartar a caixa imediatamente. Se a pontuação reduzida cair abaixo do limiar, a caixa é descartada; caso contrário, sobrevive com a pontuação reduzida e compete na próxima ronda.
O parâmetro nms_sigma controla a agressividade do decaimento. Com um nms_sigma pequeno (o valor predefinido incluído de 0.1), o decaimento é acentuado: uma caixa com grande sobreposição tem a sua pontuação reduzida quase a zero e o Soft-NMS reduz-se 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 reais da mesma classe se sobrepõem genuinamente (uma multidão de rostos, um agrupamento de palmas).
Definir nms_sigma para <= 0 desativa o decaimento por completo: as caixas sobrepostas passam com as suas pontuações originais, e apenas o limiar de pontuação as filtra.
7.13.2. Construir um diretamente¶
Todos os pós-processadores incluídos constroem um NMS novo por inferência, adicionam cada candidato a ele e chamam 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 altura de entrada da rede em pixéis e a ROI da imagem original contra a qual o modelo foi executado; add_bounding_box() recebe coordenadas de caixa nesse espaço de pixéis de entrada da rede, e get_bounding_boxes() remapeia os sobreviventes de volta para coordenadas de imagem usando a ROI. O remapeamento contabiliza o estiramento de Normalização automaticamente – a mesma ROI que o preditor viu é usada para projetar caixas de volta – pelo que as caixas retornadas estão prontas para desenhar no fotograma capturado.
A forma de retorno é uma lista de listas por classe, indexada pelo label_index passado a 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 juntamente com as suas deteções.