Detección facial en Python con OpenCV

    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.

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *