Non-Maximum Suppression en la detección de objetos
En esta entrada explicaremos lo importante que es la Non-Maximum Suppression, cuya traducción sería "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, si echamos un vistazo a la imagen de Isidora Jiménez en la parte superior de este post, claramente se ha encontrado la cara de Isidora Jiménez en la imagen, ¡pero la detección se ha disparado un total de seis veces!
Aunque cada detección puede, de hecho, ser válida, tengo la certeza de que no es lo óptimo que el clasificador nos informe diciendo que encontró seis caras cuando claramente solo existe una. Como he dicho, esto es un "problema" común cuando se utilizan métodos de detección de objetos.
De hecho, no se debería considerar un "problema"; más 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 más demora, veamos 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 delimitadores desde la parte inferior (eje Y hacia abajo)
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 sigan 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 actual
if (overlap > thresh){
pos = idxs.erase(pos);
}else{
++pos;
}
}
}
}