Detección facial en Python con OpenCV

D

Introducción

La detección facial es un caso de uso común y poderoso del Machine Learning. Se puede utilizar para automatizar tareas manuales como la asistencia a la escuela y la aplicación de la ley. Por otro lado, se puede utilizar para la autorización biométrica.

En este artículo, realizaremos la detección facial en Python, usando OpenCV.

OpenCV

OpenCV es una de las bibliotecas de visión por computadora más populares. Fue escrito en C y C ++ y también proporciona soporte para Python, además de Java y MATLAB. Si bien no es la biblioteca más rápida que existe, es fácil trabajar con ella y proporciona una interfaz de alto nivel, lo que permite a los desarrolladores escribir código estable.

Instalemos OpenCV para que podamos usarlo en nuestro código Python:

$ pip install opencv-contrib-python

Alternativamente, puede instalar opencv-python solo para los módulos principales de OpenCV. los opencv-contrib-python contiene los módulos principales, así como los módulos contrib que proporcionan funcionalidad ampliada.

Detectar rostros en una imagen usando OpenCV

Con OpenCV instalado, podemos importarlo como cv2 en nuestro código.

Para leer una imagen, usaremos el imread() función, junto con la ruta a la imagen que queremos procesar. los imread() función simplemente carga la imagen del archivo especificado en una ndarray. Si la imagen no se pudo leer, por ejemplo, en caso de que falte un archivo o un formato no compatible, la función volverá None.

Usaremos una imagen de Conjunto de datos de Kaggle:

import cv2

path_to_image="Parade_12.jpg"
original_image = cv2.imread(path_to_image)

La información RGB completa no es necesaria para la detección facial. El color contiene mucha información irrelevante en la imagen, por lo que es más eficiente simplemente eliminarlo y trabajar con una imagen en escala de grises. Además, el algoritmo Viola-Jones, que funciona bajo el capó con OpenCV, verifica la diferencia de intensidad del área de una imagen. Las imágenes en escala de grises señalan esta diferencia de manera más dramática.

Nota: En el caso de las imágenes en color, las imágenes decodificadas tendrán los canales almacenados en orden BGR, por lo que al cambiarlos a escala de grises, necesitamos utilizar el cv2.COLOR_BGR2GRAY bandera:

image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)

Esto podría haberse hecho directamente al usar imread(), configurando el cv2.IMREAD_GRAYSCALE bandera:

original_image = cv2.imread(path_to_image, cv2.IMREAD_GRAYSCALE)

La biblioteca OpenCV viene con varios clasificadores entrenados previamente que están entrenados para encontrar diferentes cosas, como caras, ojos, sonrisas, parte superior del cuerpo, etc.

los Haar Las funciones para detectar estos objetos se almacenan como XML y, según cómo haya instalado OpenCV, se pueden encontrar con mayor frecuencia en Libsite-packagescv2data. También se pueden encontrar en el Repositorio OpenCV GitHub.

Para acceder a ellos desde el código, puede utilizar un cv2.data.haarcascades y agregue el nombre del archivo XML que le gustaría usar.

Podemos elegir qué funciones de Haar queremos usar para nuestra detección de objetos, agregando la ruta del archivo al CascadeClassifier() constructor, que utiliza modelos previamente entrenados para la detección de objetos:

face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")

Ahora, podemos usar esto face_cascade objeto para detectar caras en la imagen:

detected_faces = face_cascade.detectMultiScale(image=image, scaleFactor=1.3, minNeighbors=4)

Cuando los modelos de detección de objetos están entrenados, están entrenados para detectar rostros de cierto tamaño y pueden pasar por alto rostros más grandes o más pequeños de lo esperado. Teniendo esto en cuenta, se cambia el tamaño de la imagen varias veces con la esperanza de que una cara termine siendo de un tamaño “detectable”. los scaleFactor le permite a OpenCV saber cuánto escalar las imágenes. En nuestro caso, 1.3 significa que puede escalar 30% abajo para intentar emparejar mejor las caras.

En cuanto a minNeighbors parámetro, se utiliza para controlar el número de falsos positivos y falsos negativos. Define el número mínimo de rectángulos positivos (detectar rasgos faciales) que deben estar adyacentes a un rectángulo positivo para que se considere realmente positivo. Si minNeighbors se establece en 0, el menor indicio de un rostro se contará como un rostro definitivo, incluso si no se detectan otros rasgos faciales cerca de él.

Ambos scaleFactor y minNeighbors los parámetros son algo arbitrarios y se establecen experimentalmente. Hemos elegido valores que funcionaron bien para nosotros y no dieron falsos positivos, con la compensación de más falsos negativos (caras no detectadas).

los detectMultiScale() El método devuelve una lista de rectángulos de todos los objetos detectados (caras en nuestro primer caso). Cada elemento de la lista representa una cara única. Esta lista contiene tuplas, (x, y, w, h), donde el x, y Los valores representan las coordenadas superior izquierda del rectángulo, mientras que w, h los valores representan el ancho y el alto del rectángulo, respectivamente.

Podemos usar la lista devuelta de rectángulos y usar el cv2.rectangle() función para dibujar fácilmente los rectángulos donde se detectó una cara. Tenga en cuenta que el color proporcionado debe ser una tupla en orden RGB:

for (x, y, width, height) in detected_faces:
    cv2.rectangle(
        image,
        (x, y),
        (x + width, y + height),
        color,
        thickness=2
    )

Ahora, pongámoslo todo junto:

import cv2

def draw_found_faces(detected, image, color: tuple):
    for (x, y, width, height) in detected:
        cv2.rectangle(
            image,
            (x, y),
            (x + width, y + height),
            color,
            thickness=2
        )

path_to_image="Parade_12.jpg"
original_image = cv2.imread(path_to_image)

if original_image is not None:
    # Convert image to grayscale
    image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)

    # Create Cascade Classifiers
    face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
    profile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_profileface.xml")
    
    # Detect faces using the classifiers
    detected_faces = face_cascade.detectMultiScale(image=image, scaleFactor=1.3, minNeighbors=4)
    detected_profiles = profile_cascade.detectMultiScale(image=image, scaleFactor=1.3, minNeighbors=4)

    # Filter out profiles
    profiles_not_faces = [x for x in detected_profiles if x not in detected_faces]

    # Draw rectangles around faces on the original, colored image
    draw_found_faces(detected_faces, original_image, (0, 255, 0)) # RGB - green
    draw_found_faces(detected_profiles, original_image, (0, 0, 255)) # RGB - red

    # Open a window to display the results
    cv2.imshow(f'Detected Faces in {path_to_image}', original_image)
    # The window will close as soon as any key is pressed (not a mouse click)
    cv2.waitKey(0) 
    cv2.destroyAllWindows()
else:
    print(f'En error occurred while trying to load {path_to_image}')

Usamos dos modelos diferentes en esta imagen. El modelo predeterminado para detectar caras frontales y un modelo creado para detectar mejor las caras que miran hacia los lados.

Caras detectadas con el frontalface El modelo está delineado en verde y las caras detectadas con el profileface El modelo está delineado en rojo. La mayoría de las caras que encontró el primer modelo también se habrían encontrado con el segundo, por lo que solo dibujamos rectángulos rojos donde profileface modelo detectó una cara pero frontalface no:

profiles_not_faces = [x for x in detected_profiles if x not in detected_faces]

los imshow() El método simplemente muestra la imagen pasada en una ventana con el título proporcionado. Con la imagen que seleccionamos, esto proporcionaría el siguiente resultado:

Usando diferentes valores para scaleFactor y minNeighbors nos dará resultados diferentes. Por ejemplo, usando scaleFactor = 1.1 y minNeighbors = 4 nos da más falsos positivos y verdaderos positivos con ambos modelos:

Podemos ver que el algoritmo no es perfecto, pero es muy eficiente. Esto es más notable cuando se trabaja con datos en tiempo real, como una transmisión de video de una cámara web.

Detección de rostros en tiempo real mediante una cámara web

Las secuencias de video son simplemente secuencias de imágenes. Con la eficiencia del algoritmo Viola-Jones, podemos realizar la detección de rostros en tiempo real.

Los pasos que debemos seguir son muy similares al ejemplo anterior con una sola imagen; lo realizaremos en cada imagen de la transmisión.

Para obtener la transmisión de video, usaremos el cv2.VideoCapture clase. El constructor de esta clase toma un parámetro entero que representa el flujo de video. En la mayoría de las máquinas, se puede acceder a la cámara web pasando 0, pero en máquinas con varias transmisiones de video, es posible que deba probar diferentes valores.

A continuación, necesitamos leer imágenes individuales del flujo de entrada. Esto se hace con el read() función, que devuelve retval y image. los image es simplemente el marco recuperado. los retval El valor de retorno se utiliza para detectar si un marco se ha recuperado o no, y será False si no es así.

Sin embargo, tiende a ser inconsistente con las transmisiones de entrada de video (no detecta que la cámara web se haya desconectado, por ejemplo), por lo que ignoraremos este valor.

Sigamos adelante y modifiquemos el código anterior para manejar una transmisión de video:

import cv2

def draw_found_faces(detected, image, color: tuple):
    for (x, y, width, height) in detected:
        cv2.rectangle(
            image,
            (x, y),
            (x + width, y + height),
            color,
            thickness=2
        )

# Capturing the Video Stream
video_capture = cv2.VideoCapture(0)

# Creating the cascade objects
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_eye_tree_eyeglasses.xml")

while True:
    # Get individual frame
    _, frame = video_capture.read()
    # Covert the frame to grayscale
    grayscale_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
	# Detect all the faces in that frame
    detected_faces = face_cascade.detectMultiScale(image=grayscale_image, scaleFactor=1.3, minNeighbors=4)
    detected_eyes = eye_cascade.detectMultiScale(image=grayscale_image, scaleFactor=1.3, minNeighbors=4)
    draw_found_faces(detected_faces, frame, (0, 0, 255))
    draw_found_faces(detected_eyes, frame, (0, 255, 0))

    # Display the updated frame as a video stream
    cv2.imshow('Webcam Face Detection', frame)

    # Press the ESC key to exit the loop
    # 27 is the code for the ESC key
    if cv2.waitKey(1) == 27:
        break

# Releasing the webcam resource
video_capture.release()

# Destroy the window that was showing the video stream
cv2.destroyAllWindows()

Conclusión

En este artículo, creamos una aplicación de detección facial usando Python y OpenCV.

El uso de la biblioteca OpenCV es muy sencillo para los programas básicos de detección de objetos. Ajustando experimentalmente el scaleFactor y minNeighbors Los parámetros para los tipos de imágenes que le gustaría procesar pueden dar resultados bastante precisos de manera muy eficiente.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad