Ir al contenido principal

Detección de caras con OpenCV

En visión artificial, una de las tareas básicas es el reconocimiento de objetos, ya que es muy interesante disponer de algún modo de discriminar entre clases de objetos. Por ejemplo, un coche autónomo ha de ser capaz de reconocer a otros coches y distinguirlo de un peatón. Otra utilidad más mundana y que seguro conoces, es el reconocimiento de caras que hace tu móvil cuando vas a hacer una foto.
Detección caras OpenCV
En este artículos vamos a ver cómo reconocer caras usando OpenCV. Ojo, cuando hablo de reconocer caras, me refiero a distinguir una cara de cualquier otra cosa, no de distinguir entre caras para, por ejemplo, reconocer a quién pertenece esa cara. Ese es otro problema diferente del que os hablaré en otro momento. Como quiero abordar el problema desde un punto de vista práctico, vamos a usar la librería OpenCV con Python. Como imagen de ejemplo vamos a tomar la siguiente.
Star Trek TOS
Nuestra misión es detectar las caras que hay en esta imagen, así que fije el rumbo señor Sulu.
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('TOS-crew.jpg')
Tras importar las librerías OpenCV y Matplotlib para visualizar y manipular la imagen, comprobamos que se ha cargado correctamente y verificamos su tamaño.
img.shape
(1115, 2329, 3)
Si queremos visualizar la imagen con Matplotlib primero debemos convertirla a la codificación RGB, ya que OpenCV por defecto trabaja con imagenes BGR.
img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img_RGB)
Star Trek RGB
Si vamos a trabajar con imágenes grandes, a veces es mejor redimensionarlas para hacer las más pequeñas e incluso convertirlas a escala de grises para aliviar su tamaño. Como ejemplo, las líneas siguientes realizan estas dos operaciones.
img_gray = cv2.cvtColor(img_RGB, cv2.COLOR_RGB2GRAY)
img_small = cv2.resize(img_gray, (800, 400))
plt.imshow(img_small)
Ahora vamos a realizar las busqueda de las caras en la imagen. Con OpenCV es de lo más sencillo. Sólo necesitamos dos líneas de código Python. Sin entrar en detalles, os cuento que el clasificador que vamos a usar se conoce como Haar Cascade o también como algoritmo de Viola-Jones. Vamos a usarlo porque es de los clásicos (que no el mejor). Si queréis conocer más a fondo como funciona el algoritmo podéis consultar el artículo original de 2004 donde Paul Viola y Michael J. Jones presentaron este método en sociedad. Para que este algoritmo funcione hay que entrenarlo mostrandole caras, y otras cosas que no sean caras, para que aprenda a distinguirlos. El proceso puede ser tedioso, ya que son necesaria una relativamente grande cantidad de imágenes. Por suerte ya hay clasificadores preentrenados para reconocer caras (y otras muchas cosas), que están almacenas en formato .xml y que podemos cargar desde OpenCV. El que vamos a usar se llama haarcascade_frontalface_default.xml y se puede descargar de aquí. Está entrenado con caras mirando al frente, cómo las que queremos detectar. En esta página hay más clasificadores preentrenados.
# cargamos el clasificador preentrenado
detector_caras = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
detecciones = detector_caras.detectMultiScale(img_RGB)
# array de detecciones. una fila por cada cara. Las columnas con [pos_x, pos_y, size_x, size_y]
detecciones
array([[ 936,  211,   58,   58],
       [ 495,   76,  191,  191],
       [ 708,  109,  235,  235],
       [1199,  120,  195,  195],
       [ 221,  124,  212,  212],
       [1810,  225,  216,  216],
       [1464,  325,  215,  215],
       [1540,  326,  215,  215],
       [1435,  564,  352,  352],
       [ 622,  572,  348,  348],
       [1296,  992,   62,   62]], dtype=int32)
El array detecciones contiene una fila por cada cara detectada, con cuatro columnas cada una indicando la posición X e Y, así como la anchura y altura del recuadro imaginario que contiene la cara que ha detectado. En este caso vemos que ha detectado 11 caras, cuando en nuestra imagen sólo hay seis personajes. Algo no parece ir bien, así que vamos a mostrar la imagen sobre la que vamos a dibujar estos cuadrados que nos ha devuelto el clasificador.
for (x, y, w, h) in detecciones:
    cv2.rectangle(img_RGB, (x, y), (x+w, y+h), (0,255,0), 10)

plt.imshow(img_RGB)
La buena noticia es que ha detectado todas las caras (no ha habido falsos negativos), pero además de las autenticas caras, ha detectado como caras otras partes de la imagen (falsos positivos). Vamos a tratar de realizar algunos ajustes a ver si conseguimos mejorar el resultado. La función detectMultiScale busca las caras realizando varios barridos de la imagen con ventanas de diferentes tamaños (ya que puede haber caras grandes cercanas o pequeñas que estén más lejanas). El tamaño en que se incrementa la ventana en cada barrido se puede cambiar con el parámetro scaleFactor. Por ejemplo un valor de 1.1 (valor por defecto) quiere decir que la ventana seingrementa en un 10% en cada barrido, y un valor de 1.05, aumentaría la ventana en un 5%. Aquí hay que jugar con el valor según sea el tamaño de las caras que aparecen en la imagen. Para nuestra imagen, un valor de 1.7 parece funcionar bien
detecciones = detector_caras.detectMultiScale(img_RGB, scaleFactor = 1.7)
Otro parámetro con el que podemos jugar es minNeighbors. Cuando el algoritmo va recorriendo la imagen, acaba reconociendo la misma cara en múltiples ocasiones (aunque sólo nos devuelva una ventana). Por ejemplo, puede reconocer la misma cara con la misma ventana al desplazarla sólo unos pocos pixeles, o en sus múltiples pasadas con diferentes tamaños de ventana. Con minNeighbors podemos indicar el número mínimo de detecciones de una cara que han de darse para que se considere una detección válida. Por ejemplo, la siguiente línea es capaz de detectar correctamente las caras a pesar de no utilizar el parámetro scaleFactor.
detecciones = detector_caras.detectMultiScale(img_RGB, minNeighbors = 20)
Por último tenemos los parámetros minSize y maxSize, que permiten indicar el tamaño mínimo y máximo de la cara a detectar. Podemos ver la utilidad práctica con un ejemplo. Veamos qué detecta el clasificador con los siguientes parámetros.
detecciones = detector_caras.detectMultiScale(img_RGB, minNeighbors = 10)
Vemos que hay un falso positivo en la oreja del capitán Kirk. Una forma de manejar este tipo de problemas, si sabemos que las caras van a ser mayores (o menores) de cierto tamaño, es indicar un tamaño mínimo (o máximo) para la detección.
detecciones = detector_caras.detectMultiScale(img_RGB, minNeighbors = 10, minSize = (100,100))
Como dije al principio, hay otros tipos de clasificadores que tienen mayor rendimiento, pero este es bastante sencillo y eficiente incluso con baja capacidad de proceso, por lo que es muy interesante. En otra ocasión veremos si es posible, no sólo reconocer que hay una cara, sino, además, intentar saber de quién es la cara. Es lo que se denomina reconocimiento facial, o en general reconocimiento de objetos (o de caracteres, o de tipos de naves de la federación, etc.). Hasta entonces... larga vida y prosperidad.

Comentarios

  1. Ecco Car
    El post es muy interesante, y es que, es increíble cómo lo que hace años parecía ser ciencia ficción, actualmente es realidad, eso pasa con los coches en la actualidad, los coches eléctricos con todo tipo de extras en tecnología están a la orden del día.

    ResponderEliminar

Publicar un comentario

Entradas populares de este blog

Creando firmas de virus para ClamAV

ClamAv es un antivirus opensource y multiplataforma creado por Tomasz Kojm muy utilizado en los servidores de correo Linux. Este antivirus es desarrollado por la comunidad, y su utilidad práctica depende de que su base de datos de firmas sea lo suficientemente grande y actualizado. Para ello es necesario que voluntarios contribuyan activamente aportando firmas. El presente artículo pretende describir de manera sencilla cómo crear firmas de virus para ClamAV y contribuir con ellas a la comunidad.

Manejo de grafos con NetworkX en Python

El aprendizaje computacional es un área de investigación que en los últimos años ha tenido un auge importante, sobre todo gracias al aprendizaje profundo (Deep Learning). Pero no todo son redes neuronales. Paralelamente a estas técnicas, más bien basadas en el aprendizaje de patrones, también hay un auge de otras técnicas, digamos, más basadas en el aprendizaje simbólico. Si echamos la vista algunos años atrás, podemos considerar que quizá, la promesa de la web semántica como gran base de conocimiento ha fracasado, pero no es tan así. Ha ido transmutándose y evolucionando hacia bases de conocimiento basadas en ontologías a partir de las cuales es posible obtener nuevo conocimiento. Es lo que llamamos razonamiento automático y empresas como Google ya lo utilizan para ofrecerte información adicional sobre tus búsquedas. Ellos lo llaman Grafos de Conocimiento o Knowledge Graphs . Gracias a estos grafos de conocimiento, Google puede ofrecerte información adicional sobre tu búsqueda, ad

Scripts en NMAP

Cuando pensamos en NMAP, pensamos en el escaneo de puertos de un host objetivo al que estamos relizando una prueba de intrusión, pero gracias a las posibilidades que nos ofrecen su Scripting Engine , NMAP es mucho más que eso. Antes de continuar, un aviso: algunas de posibilidades que nos ofrecen los scripts de NMAP son bastante intrusivas, por lo que recomiendo hacerlas contra hosts propios, máquinas virtuales como las de Metasploitable, o contrato de pentesting mediante. Para este artículo voy a usar las máquinas de Metasploitable3 . No voy a entrar en los detalles sobre el uso básico de NMAP, ya que hay miles de tutoriales en Internet que hablan sobre ello. Lo cierto es que NMAP tiene algunas opciones que permiten obtener información extra, además de qué puertos están abiertos y cuales no. Por ejemplo, la opción -sV trata de obtener el servicio concreto, e incluso la versión del servicio que está corriendo en cada puerto. Otro ejemplo es la opción -O, que intenta averiguar el