7.13. Non-max suppression¶
Una rete di rilevamento produce tipicamente diversi box candidati sovrapposti attorno allo stesso oggetto del mondo reale: ogni anchor vicino all’oggetto si attiva con un punteggio simile e il post-processore li vede tutti. La non-max suppression (NMS) è il passo che trasforma quel gruppo in un singolo box.
L’algoritmo è breve: ordina i box candidati per punteggio, prende quello con il punteggio più alto, sopprime ogni altro box che si sovrappone a esso oltre una soglia scelta, poi prende il successivo più alto tra quelli rimasti e ripete. La metrica di sovrapposizione è l”intersection-over-union (IoU) – l’area condivisa di due box divisa per la loro area combinata, un valore compreso tra 0 (nessuna sovrapposizione) e 1 (box identici). L’argomento del costruttore nms_threshold presente su ogni post-processore fornito è la soglia al di sopra della quale i box vengono trattati come duplicati di un box già conservato.
La NMS riduce un gruppo di rilevamenti sovrapposti a quello con il punteggio più alto.¶
7.13.1. Soft-NMS¶
La classe ml.utils.NMS fornita implementa la Soft-NMS, un perfezionamento che riduce il punteggio di un box sovrapposto di una quantità che dipende da quanto si sovrappone, anziché eliminare il box del tutto. Se il punteggio abbassato scende al di sotto della soglia il box viene eliminato; altrimenti sopravvive con il punteggio ridotto e compete nel turno successivo.
Il parametro nms_sigma controlla quanto è aggressiva la riduzione. Con un valore piccolo di nms_sigma (il valore predefinito fornito di 0.1) la riduzione è ripida: a un box fortemente sovrapposto viene portato il punteggio quasi a zero e la Soft-NMS si riduce alla NMS classica. Con un valore più grande di nms_sigma la riduzione è graduale e i box sovrapposti di oggetti diversi sopravvivono più spesso, il che è importante quando oggetti del mondo reale della stessa classe si sovrappongono davvero (una folla di volti, un gruppo di palmi).
Impostare nms_sigma su <= 0 disabilita completamente la riduzione: i box sovrapposti passano con i loro punteggi originali e solo la soglia di punteggio li filtra.
7.13.2. Costruirne uno direttamente¶
Ogni post-processore fornito costruisce una nuova istanza NMS per ogni inferenza, vi aggiunge ciascun candidato e chiama get_bounding_boxes() alla fine. Un post-processore personalizzato segue lo stesso schema:
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)
Il costruttore prende la larghezza e l’altezza di input della rete in pixel e la ROI dell’immagine originale su cui il modello è stato eseguito; add_bounding_box() prende le coordinate del box in quello spazio di pixel di input della rete, e get_bounding_boxes() rimappa i sopravvissuti nelle coordinate dell’immagine usando la ROI. Il rimappaggio tiene conto automaticamente dello stiramento di normalizzazione – viene usata la stessa ROI che il predittore ha visto per riproiettare i box – così i box restituiti sono pronti per essere disegnati sul frame catturato.
La forma restituita è una lista di liste per classe, indicizzate dal label_index passato a add_bounding_box. Le liste di classi vuote vengono conservate in modo che l’indice corrisponda all’indice di classe del modello; enumerate(result) percorre le classi insieme ai loro rilevamenti.