Detección de parpadeo con OpenCV, Python y dlib

En este artículo, vamos a ver cómo detectar el parpadeo usando OpenCV , Python y dlib. Esta es una tarea bastante simple y requiere que tenga una comprensión básica de OpenCV y cómo implementar programas de detección de puntos de referencia faciales usando OpenCV y dlib, ya que lo usaremos como base para el proyecto de hoy.

Implementación paso a paso

Paso 1: Instalación de todos los paquetes necesarios 

Así que instalaremos todas nuestras dependencias en este paso. Vamos a usar OpenCV para la visión por computadora, la biblioteca dlib para el reconocimiento facial y también el paquete imutils para usar algunas funciones que nos ayudarán a convertir los puntos de referencia en una array NumPy y nos facilitarán el uso, así que instalemos estos primero. .

pip install opencv-python numpy dlib imutils

Paso 2: inicialice y lea desde la cámara web

Python3

import cv2
 
cam = cv2.VideoCapture(0)
while True:
    _, frame = cam.read()
    cv2.imshow('Camera Feed', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cam.release()

Paso 3: Detección de puntos de referencia faciales con dlib

Nota: El detector de puntos de referencia faciales incluido en la biblioteca dlib es una implementación del documento One Millisecond Face Alignment with an Ensemble of Regression Trees de Kazemi y Sullivan (2014).

Los puntos de referencia faciales son los atributos clave de un rostro en una imagen, como los ojos, las cejas, la nariz, la boca y la mandíbula. Dado que los pasos 1 a 3 no son el enfoque principal de este artículo, no profundizaremos, sino que escribiré comentarios sobre el código para facilitar la comprensión. 

Este es el código básico del código para la detección de puntos de referencia faciales, que usaremos más adelante para la detección del parpadeo.

Python3

# Importing the required dependencies
import cv2  # for video rendering
import dlib  # for face and landmark detection
import imutils
 
# for calculating dist b/w the eye landmarks
from scipy.spatial import distance as dist
 
# to get the landmark ids of the left
# and right eyes ----you can do this
# manually too
from imutils import face_utils
 
cam = cv2.VideoCapture('assets/Video.mp4')
 
 
# Initializing the Models for Landmark and
# face Detection
detector = dlib.get_frontal_face_detector()
landmark_predict = dlib.shape_predictor(
    'Model/shape_predictor_68_face_landmarks.dat')
 
while 1:
 
    # If the video is finished then reset it
    # to the start
    if cam.get(cv2.CAP_PROP_POS_FRAMES) == cam.get(
      cv2.CAP_PROP_FRAME_COUNT):
        cam.set(cv2.CAP_PROP_POS_FRAMES, 0)
 
    else:
        _, frame = cam.read()
        frame = imutils.resize(frame, width=640)
 
        # converting frame to gray scale to pass
        # to detector
        img_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
         
        # detecting the faces---#
        faces = detector(img_gray)
        for face in faces:
            cv2.rectangle(frame, face[0], face[1],
                          (200, 0, 0), 1)
 
        cv2.imshow("Video", frame)
        if cv2.waitKey(5) & 0xFF == ord('q'):
            break
 
cam.release()
cv2.destroyAllWindows()

Ahora surge la pregunta, ¿cómo vamos a utilizar estos puntos de referencia para la detección ocular? 

Puntos de referencia de los ojos 

Vimos que podemos extraer cualquier estructura facial de los 68 puntos de referencia faciales que detectamos. Por lo tanto, extraeremos los puntos de referencia de los ojos, es decir, 6 coordenadas (x, y) para cada ojo, para cualquier rostro dado en una imagen. Y luego calcularemos el EAR para estos puntos de referencia.

Relación de aspecto de los ojos (EAR) 

Este método es muy simple, eficiente y no requiere nada parecido al procesamiento de imágenes. Básicamente, esta relación nos da una cierta relación entre las medidas horizontales y verticales del ojo. Esta es la ecuación para calcular el EAR usando los seis parámetros del ojo: 

imagen creada por el autor usando canvas

Podemos usar la función dada para calcular el EAR: 

Python3

def calculate_EAR(eye):
     
    # calculate the vertical distances
    # euclidean distance is basically
    # the same when you calculate the
    # hypotenuse in a right triangle
    y1 = dist.euclidean(eye[1], eye[5])
    y2 = dist.euclidean(eye[2], eye[4])
 
    # calculate the horizontal distance
    x1 = dist.euclidean(eye[0], eye[3])
 
    # calculate the EAR
    EAR = (y1+y2) / x1
 
    return EAR

¿Qué tiene de mágico este EAR?

Esta es la parte más importante, cuando calculas el EAR de un ojo, permanece constante cuando el ojo está abierto pero de repente cae cuando el ojo parpadea. A continuación, he mostrado un gráfico para mostrar que está funcionando: 

. ~imagen del autor usando lienzo

Como puede ver en la imagen, el valor general de EAR fue constante en todo momento, excepto en un punto, es decir, cuando el ojo parpadea, lo que lo convierte en una de las formas más simples y eficientes de detectar un parpadeo. 

Dado que tenemos dos EAR para cada ojo, respectivamente, tomaremos el promedio de EAR para el ojo derecho y EAR para el ojo izquierdo y luego verificaremos si es inferior a cierto umbral (crearemos una variable para establecer su valor) y este umbral puede variar un poco, a mí me funcionó con 0.4 o 0.5 pero en algunos casos también funciona con 0.25 o 0.3. Depende del FPS de tu video o cámara web. 

A continuación: Mantendremos el conteo de cuadros cuando el EAR esté por debajo del umbral y si el conteo es de 3 (o 5 dependiendo de los fps) cuadros, entonces consideraremos un parpadeo detectado .

A continuación se muestra la implementación completa. 

Python3

# Importing the required dependencies
import cv2  # for video rendering
import dlib  # for face and landmark detection
import imutils
# for calculating dist b/w the eye landmarks
from scipy.spatial import distance as dist
# to get the landmark ids of the left and right eyes
# you can do this manually too
from imutils import face_utils
 
# from imutils import
 
cam = cv2.VideoCapture('assets/my_blink.mp4')
 
# defining a function to calculate the EAR
def calculate_EAR(eye):
 
    # calculate the vertical distances
    y1 = dist.euclidean(eye[1], eye[5])
    y2 = dist.euclidean(eye[2], eye[4])
 
    # calculate the horizontal distance
    x1 = dist.euclidean(eye[0], eye[3])
 
    # calculate the EAR
    EAR = (y1+y2) / x1
    return EAR
 
# Variables
blink_thresh = 0.45
succ_frame = 2
count_frame = 0
 
# Eye landmarks
(L_start, L_end) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
(R_start, R_end) = face_utils.FACIAL_LANDMARKS_IDXS['right_eye']
 
# Initializing the Models for Landmark and
# face Detection
detector = dlib.get_frontal_face_detector()
landmark_predict = dlib.shape_predictor(
    'Model/shape_predictor_68_face_landmarks.dat')
while 1:
 
    # If the video is finished then reset it
    # to the start
    if cam.get(cv2.CAP_PROP_POS_FRAMES) == cam.get(
            cv2.CAP_PROP_FRAME_COUNT):
        cam.set(cv2.CAP_PROP_POS_FRAMES, 0)
 
    else:
        _, frame = cam.read()
        frame = imutils.resize(frame, width=640)
 
        # converting frame to gray scale to
        # pass to detector
        img_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
 
        # detecting the faces
        faces = detector(img_gray)
        for face in faces:
 
            # landmark detection
            shape = landmark_predict(img_gray, face)
 
            # converting the shape class directly
            # to a list of (x,y) coordinates
            shape = face_utils.shape_to_np(shape)
 
            # parsing the landmarks list to extract
            # lefteye and righteye landmarks--#
            lefteye = shape[L_start: L_end]
            righteye = shape[R_start:R_end]
 
            # Calculate the EAR
            left_EAR = calculate_EAR(lefteye)
            right_EAR = calculate_EAR(righteye)
 
            # Avg of left and right eye EAR
            avg = (left_EAR+right_EAR)/2
            if avg < blink_thresh:
                count_frame += 1  # incrementing the frame count
            else:
                if count_frame >= succ_frame:
                    cv2.putText(frame, 'Blink Detected', (30, 30),
                                cv2.FONT_HERSHEY_DUPLEX, 1, (0, 200, 0), 1)
                else:
                    count_frame = 0
 
        cv2.imshow("Video", frame)
        if cv2.waitKey(5) & 0xFF == ord('q'):
            break
 
cam.release()
cv2.destroyAllWindows()

Producción:

Si está usando un video diferente o si está usando una cámara web, su FPS será diferente, por lo que podría intentar cambiar los valores de las variables que definimos, aunque funcionan bien en la mayoría de los casos.

Publicación traducida automáticamente

Artículo escrito por bobde_yagyesh y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

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