👨 DETECCIÓN DE ROSTROS 👩 con Haar Cascades Python – OpenCV

Por Administrador

En el post de hoy vamos a tratar el tema de la detección de rostros y como emplear OpenCV para detectarlos usando un clasificador basado en Haar Cascades. Puedes encotrar la infomación de esde post en el video que aparece en un principio y puedes visitar mi repositorio en Github.

El contenido que veremos hoy será el  siguiente:

  • ¿Qué es la detección de rostros?
    • Pero, ¿Te has preguntado cómo es posible detectar un rostro en una imagen?
  • Haar Cascades Python – OpenCV
    • detectMultiScale
    • ¡Ahora, programemos! Detección de rostros sobre una imagen
      • Manipulando image
      • Manipulando scaleFactor
      • Manipulando minNeighbors
      • Manipulando minSize
      • Manipulando maxSize
    • Detección de rostros en video

¿Qué es la detección de rostros? 

La detección de rostros es una técnica que permite encontrar en una imagen, el rostro o cara de una o varias personas, mientras que ignora el fondo de la imagen u otros objetos que estén presentes dentro de ella. 

Está presente en muchas aplicaciones que usamos día a día, por ejemplo en facebook con las imágenes que subimos que ya detectan el rostro y lo que muchas veces nos pide es adicionar el nombre de la o las personas que allí aparecen. También tenemos los filtros de instagram en donde se necesita detectar el rostro para que los filtros que deseemos aplicar sean empleados. Incluso está presente en algunas aplicaciones bancarias o cuando usamos un smartphone para tomar una foto y muestra en un recuadro o círculo los rostros de las personas que aparecen allí.

Pero, ¿Te has preguntado cómo es posible detectar un rostro en una imagen? 

Sin duda esta no es una tarea fácil para el computador, por ello necesitamos que este ‘aprenda’ para lo cual empleamos machine learning o aprendizaje automático. 

En un principio se necesita de una gran cantidad de imágenes para entrenar a un clasificador, para que este pueda discernir entre la presencia de un objeto y la no presencia del mismo. Por ejemplo para realizar un detector de rostros se requieren imágenes positivas (es decir imágenes con rostros) e imágenes negativas (que serían imágenes que no contengan rostros). Luego se procederá a la extracción de características de todas las imágenes, para después emplear un enfoque de machine learning y proceder con el entrenamiento. Finalmente se podrá obtener el clasificador. 

Figura 1: Proceso para crear un clasificador de rostros

Ahora que tenemos en cuenta este proceso, veamos brevemente la detección facial usando Haar Cascade. Este detector de objetos usa el método propuesto en el paper “Rapid Object Detection using a Boosted Cascade of Simple Features”.

Figura 2: Proceso para crear un clasificador de rostros empleando Haar-Cascade

Y teniendo el mismo esquema que estamos tratando, tenemos las imágenes positivas y negativas. Para este caso la extracción de características es realizada con características de Haar, mientras que para entrenar el clasificador se usa una cascada de clasificadores que descartará las áreas que no sean considerados rostros, mientras que seguirá analizando los posibles candidatos a ser un rostro. Obteniendo finalmente el detector de rostros. 

Haar Cascades Python – OpenCV

OpenCV nos ofrece clasificadores pre entrenados no solo de rostros de personas (como el que usaremos en este post), sino de ojos, sonrisa, caras de gatitos, entre otros. Puedes encontrar estos archivos XML en la carpeta: opencv/data/haarcascades/, o puedes encontrarlos en el repositorio de OpenCV en github: https://github.com/opencv/opencv/tree/master/data/haarcascades.

detectMultiScale

Para emplear la detección de rostros con haar cascade en OpenCV vamos a necesitar del módulo detectMultiScale que ayudará a detectar los objetos de acuerdo al clasificador que se utilice. Este nos permitirá obtener un rectángulo delimitador en donde se encuentre el objeto que se desea encontrar dentro de una imagen, para ello se deben especificar algunos argumentos que veremos a continuación (Puedes consultar otros que se pueden usar en la documentación de  OpenCV):

Image:  Es la imagen en donde va a actuar el detector de rostros.

ScaleFactor: Este parámetro especifica que tanto va a ser reducida la imagen. Por ejemplo si se ingresa 1.1, quiere decir que se va a ir reduciendo la imagen en 10%, con 1.3 se reducirá 30%, creando de esta manera una pirámide de imágenes. Hay algo que debemos tener en cuenta y es que si damos un número muy alto, se pierden algunas detecciones. Mientras que para valores muy pequeños como por ejemplo 1,01 (es decir reducir en un 1% la imagen), llevará mayor tiempo de procesamiento, ya que se tendrán más imágenes a analizar, además de que pueden incrementar los falsos positivos (que son detecciones presentadas como objetos u rostros, pero que en realidad no lo son).

Figura 3: Ejemplo de pirámide de imágenes (fuente).

Pero, te estarás preguntando ¿por qué se realiza una pirámide de imágenes?, esto es debido al tamaño que presenten los rostros en la imagen, unos pueden ocupar mayor o menor área que otros, por lo que para que se trate de detectar todos en sus distintos tamaños se aplica la pirámide de imágenes. 

MinNeighbors: Este parámetro especifica cuántos vecinos debe tener cada rectángulo candidato para retenerlo. 

Veamos, te voy a explicar como yo veo esto. Tenemos una pequeña ventana que va a ir pasando por una imagen buscando rostros, entonces puede que te encuentres que al final de todo el proceso ha identificado varios rostros (figura 4), pero puede que muchos de ellos correspondan a la misma persona. Entonces este parámetro, hace relación a todos esos rectángulos delimitadores de un mismo rostro. Por lo cual minNeighbors especifica el número mínimo de cuadros delimitadores o vecinos, que debe tener un rostro para que detectado como tal. 

Ahora, debemos tener en cuenta que entre más alto sea el valor que pongamos, menos caras se detectaran, mientras que si se da un valor muy bajo se pueden presentar falsos positivos.  

MinSize: Este parámetro indica el tamaño mínimo posible del objeto. Objetos más pequeños son ignorados.  

MaxSize: Este parámetro indica el tamaño máximo posible del objeto. Objetos más grandes son ignorados. 

NOTA: Recuerda que la combinación de los valores que le des a cada parámetro (especialmente ScaleFactor y MinNeighbors) afectará a las detecciones. Y por cierto este procedimiento puede aplicarse a cualquier otro detector de objetos que use esta técnica, como los clasificadores pre entrenados que te comenté o podrías realizar el tuyo propio.

¡Ahora, programemos! Detección de rostros sobre una imagen

Para empezar he guardado el archivo xml que contiene el clasificador de rostros frontales (haarcascade_frontalface_default.xml) en la misma carpeta donde está almacenado el script.

Vamos a detectar los rostros que aparecen en la siguiente imagen:

Figura 6: Imagen de entrada para detección de rostros.

La programación será la siguiente:

ACTUALIZACIÓN: Para versiones tales como OpenCV 4.2.0 cambiar la tercera línea por cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")

import cv2

faceClassif = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

image = cv2.imread('oficina.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

faces = faceClassif.detectMultiScale(gray,
  scaleFactor=1.1,
  minNeighbors=5,
  minSize=(30,30),
  maxSize=(200,200))

for (x,y,w,h) in faces:
  cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)

cv2.imshow('image',image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Línea 1: Importamos OpenCV.

Línea 3: Cargamos el clasificador con extensión xml con ayuda de cv2.CascadeClassifier, dentro entre comillas especificamos el nombre y extensión del archivo. 

Línea 5 y 6: Leemos la imagen en donde se van a detectar los rostros y luego la transformamos a escala de grises.

Línea 8: Una vez que se ha cargado el clasificador en la línea 3 es necesario aplicarlo en la imagen, para ello utilizaremos detectMultiScale que debe estar seguido de la variable con la cual se asignó la carga del clasificador y es aquí en donde tenemos que tener en cuenta los argumentos que vimos recientemente: image, scaleFactor, minNeighbors, minSize y maxSize.

Línea 14: Cuando se aplica este detector sobre una imagen, en caso de que sea detectado algún rostro, se almacenarán los puntos x, y, ancho y alto del rostro que ha sido detectado (esto lo obtendremos de la variable ‘face’ que aquí se ha utilizado), es por ello que se utiliza un for para poder desempaquetar toda la información de los rostros obtenidos y posteriormente dibujar un rectángulo que los contenga. 

A continuación te voy a mostrar las detecciones obtenidas luego de manipular los valores de los argumentos, veamos:

Manipulando image

He probado especificar una imagen en BGR, así como en escala de grises, y en ambos casos ya detección funcionó.

Figura 7: Detección de rostros sobre una imagen en BGR y escala de grises.

Manipulando scaleFactor

Con el código que hemos realizado vamos a hacer pruebas cambiando los valores de scaleFactor en: 1.1, 1.3, 1.5 y 1.01. Claro que los resultados estarán directamente relacionados también a los otros argumentos sin embargo esto nos servirá para darnos cuenta como actúa este argumento.

Figura 8: Manipulando el argumento scaleFactor.

Analicemos la imagen superior izquierda de la figura 8, en ella podemos apreciar que todos los rostros que aparecen en la imagen han sido detectados.

En la imagen superior derecha de la figura 8, al usar un scaleFactor =1.3 podemos ver que se ha perdido un rostro ya que solo se han identificado 3.

En la imagen inferior izquierda de la figura 8, al usar un scaleFactor =1.5 se ha detectado solo un rostro.

Finalmente en la imagen inferior derecha de la figura 8,  al usar un scaleFactor =1.01 tenemos detectados muchísimos más rostros de los cuatro que realmente tenemos. Además si estás probando el código, te habrás dado cuenta que se ha tardado más tiempo en procesar la detección, esto es debido al scaleFactor de 1.01  ya que se  tiene muchas más imágenes a procesar.

CONCLUSIÓN: En esta sección solo hemos modificado el argumento scaleFactor para entender mejor su funcionamiento. Cuando usamos 1.1 en esta imagen (tú puedes buscar el mejor valor para tu aplicación) se detectaron correctamente los cuatro rostros, pero conforme subimos el valor a 1.3 o 1.5 vimos que los rostros se fueron perdiendo. Por otro lado al usar 1.01 obtuvimos muchísimos falsos positivos que no nos servirían si lo que deseamos es conocer los rostros de esta imagen. Por lo tanto debes experimentar con varios valores para encontrar el que mejor se adapte a lo que buscas.

Manipulando minNeighbors

Figura 9: Manipulando el argumento minNeighbors.

Si analizamos la imagen superior izquierda de la figura 9, con minNeighbors=1 tenemos muchísimas más detecciones (falsos positivos) que las cuatro que deberían aparecer.

La imagen superior derecha de la figura 9 con minNeighbors=3 se ha conseguido detectar 5 rostros de los 4 que aparecen en ella.

En la imagen inferior izquierda de la figura 9 con minNeighbors=5 se ha conseguido detectar los cuatro rostros.

Finalmente en la imagen inferior derecha de la figura 9 con minNeighbors=25 solo se ha conseguido detectar un rostro.

CONCLUSIÓN: Como te había dicho antes, este parámetro especifica el número mínimo de cuadros delimitadores o vecinos, que debe tener un rostro para que detectado como tal. Entonces podemos ver que si se le da un valor de 1 es más propenso a aceptar rostros en lugares donde no los hay, mientas que si incrementamos ese valor como a 25 por ejemplo, las detecciones descienden. Por ello debes experimentar también con este parámetro hasta que encontrar el valor que se adaptea tus necesidades.

Manipulando minSize

Figura 10: Manipulando el argumento minSize

En la imagen de la izquierda en la figura 10 se ha establecido minSize=(30,30). En este caso se están dibujando los cuatros rotros.

En la imagen de la derecha en la figura 10 se ha establecido en cambio minSize=(70,70). Dado este tamaño hay 2 rostros que se han rodeado, mientras que los rostros más pequeños han sido ignorados.

Manipulando maxSize

Figura 11: Manipulando el argumento maxSize

En la imagen de la izquierda en la figura 11 se ha establecido maxSize=(60,60). Dado que este tamaño es el máximo permitido, los rostros más grandes no han sido dibujados.

En la imagen de la derecha, en la figura 11 con maxSize=(200,200) se han dibujado todos los rostros.

En fin…

Ahora que ya conoces como funcionan los argumentos para la detección de rostros, debes tomar en cuenta que debes encontrar tú los valores adecuados dependiendo de tu aplicación.

Detección de rostros en video

Por último, vamos a emplear la detección de rostros en un video streaming, para ello seguimos el mismo procedimiento que hicimos para la imagen. Toma en cuenta que por ahora los parámetros usados son solo: image, scaleFactor y minNeighbors. 

import cv2

cap = cv2.VideoCapture(0)
faceClassif = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

while True:
  ret,frame = cap.read()
  gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

  faces = faceClassif.detectMultiScale(gray, 1.3, 5)

  for (x,y,w,h) in faces:
    cv2.rectangle(frame, (x,y),(x+w,y+h),(0,255,0),2)

  cv2.imshow('frame',frame)
  
  if cv2.waitKey(1) & 0xFF == ord('q'):
    break
cap.release()
cv2.destroyAllWindows()

Al  momento de la visualización puedes hacer pruebas para que te des cuenta como está actuando el detector, por ejemplo moviendo la cebeza de un lado a otro, o trata de ocultar un poco tu cara para saber hasta qué punto funciona. Puedes acercarte a la cámara y alejarte para que des cuenta como realiza la detección a rostros en distintos tamaños. 

Y obtendrás un resultado parecido a este:

Los parámetros que hemos visto hoy aplican a otros detectores que utilicen detectMultiScale. Así que puedes experimentar con otros clasificadores pre entrenados. Por último, trata de experimentar con los valores de los parámetros, para que ellos se adapten a tus imágenes o videos. 

Y esto fue todo por este post, espero que te haya ayudado. Recuerda que puedes visitar mi repositorio en Github, o mis redes sociales. ¡Cuídate!

Referencias:

  • https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html
  • https://www.bogotobogo.com/python/OpenCV_Python/python_opencv3_Image_Object_Detection_Face_Detection_Haar_Cascade_Classifiers.php https://realpython.com/face-recognition-with-python/
  • https://docs.opencv.org/2.4/modules/objdetect/doc/cascade_classification.html#cascadeclassifier-detectmultiscale
  • https://becominghuman.ai/face-detection-using-opencv-with-haar-cascade-classifiers-941dbb25177
  • https://www.pyimagesearch.com/2016/06/20/detecting-cats-in-images-with-opencv/

#infoOmes