En esta entrada explicaremos que tan importante es la Non-Maximum Suppression cuya traducción seria "supresión no máxima".

Cuando se utiliza HOG para la clasificación de objetos, casi siempre existe detección de múltiples cuadros delimitadores que rodean el objeto que se desea detectar (en palabras simples detecta el objeto varias veces).

Por ejemplo, echar un vistazo a la imagen de Isidora Jiménez en la parte superior de este post, claramente, se ha encontrado cara de Isidora Jiménez en la imagen, pero la detección se ha disparado un total de seis veces!

Mientras que cada detección puede de hecho ser válida, tengo la certeza que no es lo optimo que el clasificador le informe de nuevo, diciendo que encontraron  seis caras cuando claramente existe una. Como he dicho, esto es "problema" común cuando se utilizan métodos de detección de objetos.

De hecho, no se debería considerar un "problema" mas bien solo indica que el detector está funcionando como se esperaba. Sería mucho peor un falso positivo (es decir, detectar una cara, donde no existe) o simplemente no detectar una cara.

Para solucionar esta situación tendremos que aplicar la Non-Maximum Suppression (NMS).

Así que sin mucha demora, vamos con el código.


void nms(const std::vector<cv::Rect>& srcRects, std::vector<cv::Rect>& resRects, float thresh)
{
    resRects.clear();
    const size_t size = srcRects.size();
    if (!size){
        return;
    }

    // Ordena los cuadros de límite desde la parte inferior (eje y derecho) 
    std::multimap<int, size_t> idxs;
    for (size_t i = 0; i < size; ++i)
    {
        idxs.insert(std::pair<int, size_t>(srcRects[i].br().y, i));
    }

    // mantendrá el bucle mientras los índices siguen en la lista
    while (idxs.size() > 0){
        // obtiene el último rectángulo
        auto lastElem = --std::end(idxs);
        const cv::Rect& rect1 = srcRects[lastElem->second];
        resRects.push_back(rect1);
        idxs.erase(lastElem);

        for (auto pos = std::begin(idxs); pos != std::end(idxs); ) {
            // obtiene el rectángulo actual
            const cv::Rect& rect2 = srcRects[pos->second];
            float intArea = (rect1 & rect2).area();
            float unionArea = rect1.area() + rect2.area() - intArea;
            float overlap = intArea / unionArea;

            // si hay superposición, suprime el cuadro de actual
            if (overlap > thresh){
                pos = idxs.erase(pos);
            }else{
                ++pos;
            }
        }
    }
}