Guía Rápida sobre Preprocesamiento de Imágenes Faciales para Redes Neuronales usando OpenCV en Python

Detección de rostro y ojos + Enderezamiento, recorte, redimensionado y normalización de imágenes.

Jaime Durán
MetaDatos
7 min readJul 2, 2019

--

Also available in English.

Actualmente estoy trabajando en un problema de visión artificial que involucra la clasificación de imágenes faciales. Eso generalmente significa tener que aplicar Deep Learning, por lo que se requiere una etapa de preprocesamiento especial antes de inyectar las imágenes a nuestra red neuronal.

Se trata de una tarea muy importante para aumentar el acierto de nuestro modelo, y se puede implementar fácilmente siguiendo unos simples pasos. Para ello podemos usar OpenCV: una librería de código abierto altamente optimizada para aplicaciones de visión artificial (en tiempo real), disponible en C++, Java y Python.

En este artículo se dan algunas pautas básicas, ejemplos y código para llevar a cabo las cosas que probablemente se deben aplicar en cualquier problema de clasificación o reconocimiento facial.

NOTA: todas las imágenes estáticas utilizadas en este artículo provienen de memes conocidos.

Carga de Imágenes

Usaremos la función imread()para cargar una imagen, especificando la ruta del archivo y el modo. Este segundo parámetro es importante para ejecutar conversiones básicas a nivel de canal y profundidad.

Para ver una imagen tenemos disponible la función imshow():

Success Kid

Escribiendo type(img) obtendremos las dimensiones en la forma: (alto, ancho, canales).

Nuestra imagen en color contiene información en 3 canales : azul, verde y rojo (en este orden dentro de OpenCV).

Podemos aislar un solo canal fácilmente:

Versión en escala de grises

Para evitar distracciones en la clasificación de imágenes faciales, puede ser una buena idea usar imágenes en blanco y negro (o tal vez no, siempre puedes probar las dos versiones). Para obtener la imagen en escala de grises tan sólo tenemos que especificarlo en la función de carga, indicando el valor adecuado como segundo parámetro:

¡Ahora nuestra imagen tiene un único canal!

Detección de rostro y ojos

Cuando trabajamos en un problema de clasificación facial, podríamos aplicar detección de rostros para validar la imagen (¿aparece alguna cara con un mínimo de resolución?), recortarla (para aislar la parte interesante) y por ejemplo enderezarla. Para esto usaremos el Clasificador en cascada basado en funciones de Haar para la detección de objetos incluido en OpenCV.

Primero elegimos los clasificadores pre-entrenados en la detección de rostro y de ojos. Hay una lista de archivos XML disponibles que podemos usar:

1) Para la detección de rostro, OpenCV ofrece los siguientes (desde el a priori más relajado hasta el más estricto):

  • haarcascade_frontalface_default.xml
  • haarcascade_frontalface_alt.xml
  • haarcascade_frontalface_alt2.xml
  • haarcascade_frontalface_alt_tree.xml

2) Para la detección de ojos, podemos elegir entre dos:

  • haarcascade_eye.xml
  • haarcascade_eye_tree_eyeglasses.xml (¡trata de lidiar con las gafas!)

Nosotros cargamos los clasificadores pre-entrenados de esta manera:

Se pueden probar varias combinaciones. Hay que tener en cuenta que no hay uno de ellos que sea el mejor en todos los casos (se puede probar con un segundo clasificador en caso de que falle el primero, o incluso probarlos todos).

Para la detección de rostro usamos este sencillo código:

El resultado es una lista que contiene todas las caras detectadas, o mejor dicho la posición de los rectángulos que las delimitan. Podemos visualizarlo fácilmente:

Para los ojos procedemos de una manera similar, pero reduciendo la búsqueda al rectángulo de la cara:

¡Y ya lo tenemos!

“¡Ya está bien!”

Aunque este era el resultado esperado, nos encontraremos con muchos problemas para obtener rostros en situaciones de todo tipo. Muchas veces no tendremos una imagen frontal y clara de la cara de una persona. Y a veces la tenemos pero…

No eyes, no party
¡Sí! ¡Un ojo es una mancha negra rodeada de blanco!
Aquí 4 ojos, y sólo 3 detectados

Enderezar una imagen

Podemos poner recta una imagen de una cara una vez que hayamos detectado los ojos, simplemente calculando el ángulo entre ambos (es una tarea fácil). Después del cálculo podemos rotar la imagen en solo 2 pasos:

Recorte de la cara

Para ayudar a nuestra red neuronal en la tarea de clasificación o reconocimiento, sería bueno deshacerse de la información externa que pudiera causarle algún tipo de distracción, como el fondo, la ropa o los accesorios. Un recorte de cara es muy conveniente en estos casos.

Lo primero que necesitamos es volver a obtener el rectángulo de la cara, para la imagen ya enderezada. Luego debemos tomar una decisión: podemos recortar el área del rectángulo tal como está, o bien podemos agregar un relleno adicional, para obtener una imagen más amplia. Depende un poco del problema específico a resolver (clasificación por edad, género, raza, …); tal vez queramos más pelo, o tal vez no.

No hagas eso…

Y por último, el propio recorte (pes el padding):

¡Voilà! la cara quedó aislada y casi lista para nuestra red neuronal :)

“¡¡¡MI OREJA!!!”

Redimensionamiento de imágenes

Las redes neuronales necesitan que todas las imágenes de entrada tengan la misma forma y tamaño, ya que la GPU aplicará la misma instrucción a un lote de imágenes al mismo tiempo para lograr ser súper rápida. Podemos cambiar su tamaño al vuelo, pero podría ser una mala idea ya que implicaría varias conversiones para cada archivo en tiempo de entrenamiento. Si nuestro conjunto de datos tiene muchas imágenes, deberíamos pensar en cambiar el tamaño a todas antes de empezar.

En OpenCV podemos realizar tanto la reducción como la ampliación con la función resize(), teniendo varios métodos de interpolación disponibles. Un ejemplo especificando el tamaño final:

Para reducir el tamaño de una imagen, OpenCV recomienda el método de interpolación INTER_AREA, mientras que para agrandarla se puede usar INTER_CUBIC(lento) o INTER_LINEAR(más rápido, aún con buenos resultados). Al final se trata de elegir la opción con un mejor balance entre calidad obtenida y tiempo de ejecución.

Aquí vemos una comparación rápida para la ampliación de una imagen:

¿Echas de menos el filtro de Warhol?

Las primeras dos imágenes parecen tener más calidad (aunque se pueden observar algunos artefactos de compresión). El resultado del método lineal es claramente más suave (le falta contraste) y menos ruidoso (más visible con imágenes en blanco y negro). La última foto está pixelada.

Normalización

Podemos utilizar la función normalize() para aplicar una normalización visual con el fin de corregir imágenes demasiado oscuras o excesivamente claras; e incluso se puede corregir el bajo contraste. El tipo de normalización se especifica en un parámetro de dicha función:

Ejemplos:

Cuando se usan imágenes como entrada para una red neuronal convolucional, no es necesario aplicar una normalización de este tipo (los resultados anteriores parecen buenos ante nuestros ojos, pero no para los suyos). En la práctica, realizaremos una normalización adecuada para cada canal, como por ejemplo restar la media y dividir por la desviación estándar a nivel de píxel (obteniendo valores con media 0 y desviación 1). Si utilizamos Transfer Learning, el mejor enfoque sería utilizar las estadísticas del modelo pre-entrenado.

Conclusiones

Detectar y aislar caras es una tarea común cuando se trata de problemas de reconocimiento y clasificación facial, siempre que las imágenes de entrada no sean perfectas imágenes de pasaporte :)

OpenCV es una muy buena librería para tareas de preprocesamiento de imágenes, pero no es solo eso; también es una gran herramienta para muchas aplicaciones de visión artificial…

https://www.youtube.com/watch?v=GebcshN4OdE
https://www.youtube.com/watch?v=z1Cvn3_4yGo
https://github.com/vjgpt/Face-and-Emotion-Recognition

¡Echa un vistazo a su documentación!

Espero que te haya gustado este artículo. ¡Más por venir en el campo de la visión artificial!

--

--

Jaime Durán
MetaDatos

Yet another data scientist with a blog. In fact I write two (uno en español)