7.13. Supresión de no máximos

Una red de detección normalmente produce varios cuadros candidatos superpuestos alrededor del mismo objeto del mundo real: cada ancla cercana al objeto se activa con una puntuación similar, y el postprocesador los ve todos. La supresión de no máximos (NMS) es el paso que convierte ese grupo en un solo cuadro.

El algoritmo es breve: ordena los cuadros candidatos por puntuación, toma el de mayor puntuación, suprime todos los demás cuadros que se superponen con él por encima de un umbral elegido, luego toma el siguiente más alto de los que quedan, y repite. La métrica de superposición es la intersección sobre unión (IoU): el área compartida de dos cuadros dividida por su área combinada, un valor entre 0 (sin superposición) y 1 (cuadros idénticos). El argumento del constructor nms_threshold de todos los postprocesadores incluidos es el límite por encima del cual los cuadros se tratan como duplicados de un cuadro ya conservado.

A la izquierda, tres cuadros delimitadores superpuestos alrededor de un único sujeto, etiquetados con las puntuaciones 0.92, 0.83 y 0.71. Una flecha apunta a la derecha, donde queda un cuadro superviviente con la puntuación 0.92. Los dos cuadros de menor puntuación son suprimidos por el de mayor puntuación porque su intersección sobre unión con él supera el umbral.

NMS reduce un grupo de detecciones superpuestas a la de mayor puntuación.

7.13.1. Soft-NMS

La clase ml.utils.NMS incluida implementa Soft-NMS, un refinamiento que reduce la puntuación de un cuadro superpuesto en una cantidad que depende de cuánto se superponga, en lugar de descartar el cuadro por completo. Si la puntuación reducida cae por debajo del umbral, el cuadro se descarta; de lo contrario, sobrevive con la puntuación reducida y compite en la siguiente ronda.

El parámetro nms_sigma controla cuán agresiva es la reducción. Con un nms_sigma pequeño (el valor predeterminado incluido de 0.1) la reducción es pronunciada: un cuadro fuertemente superpuesto ve su puntuación llevada casi a cero y Soft-NMS se reduce al NMS clásico. Con un nms_sigma mayor la reducción es suave y los cuadros superpuestos de distintos objetos sobreviven con más frecuencia, lo que importa cuando objetos del mundo real de la misma clase se superponen realmente (una multitud de caras, un grupo de palmas).

Establecer nms_sigma en <= 0 desactiva la reducción por completo: los cuadros superpuestos pasan con sus puntuaciones originales, y solo el umbral de puntuación los filtra.

7.13.2. Construir uno directamente

Todos los postprocesadores incluidos construyen un nuevo NMS por inferencia, agregan cada candidato a él y llaman a get_bounding_boxes() al final. Un postprocesador personalizado sigue el mismo patrón:

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)

El constructor toma el ancho y alto de entrada de la red en píxeles y la ROI de la imagen original contra la que se ejecutó el modelo; add_bounding_box() toma las coordenadas del cuadro en ese espacio de píxeles de entrada de la red, y get_bounding_boxes() reasigna los supervivientes de vuelta a las coordenadas de la imagen usando la ROI. La reasignación tiene en cuenta el estiramiento de normalización automáticamente – se usa la misma ROI que vio el predictor para proyectar los cuadros de vuelta – de modo que los cuadros devueltos están listos para dibujarse sobre el fotograma capturado.

La forma del retorno es una lista de listas por clase, indexada por el label_index pasado a add_bounding_box. Las listas de clase vacías se conservan para que el índice coincida con el índice de clase del modelo; enumerate(result) recorre las clases junto con sus detecciones.