Blog
Detección de sueño y parpadeo
- Publicado por: Rafael Fernandez
- Categoría: DLib Intermedio Procesamiento de Imagenes

-Aquí veremos como se puede detectar el sueño y el parpadeo mediante un código en Python.
-De manera similar, te dejamos el vídeo de ejemplo, el código y los requisitos para correrlo.
Detección de sueño y parpadeo con Python
A partir de los anteriores artículos del curso “Procesamiento de Imágenes” hemos aprendido a detectar marcas faciales como bocas, mandibulas, nariz, cejas y ojos. Si nos centramos solo en los ojos podemos medir indicios de sueño y contar los parpadeos que hacemos a tiempo real. En el mundo de la automoción el sueño provoca muchos accidentes y muchas marcas de coches estan investigando con el objetivo de detectar la somnolencia de los conductores. Aquí podemos leer un artículo sobre “Dispositivos para el coche: detección de la fatiga y distracción al volante”
En el siguiente código en python presentamos como podemos medir la somnolencia a partir de la detección de los ojos con una función que nos dice si el ojo esta cerrado o abierto. Para este ejemplo hemos usado el siguiente vídeo que los puedes descargar desde aqui.
Vídeo de ejemplo:
Requisitos para correr el algoritmo
- cv2 (librería openCV)
- dlib (Un juego de herramientas para hacer aplicaciones de learning machine y análisis de datos para el mundo real)
- numpy
- archivo : shape_predictor_68_face_landmarks.dat (lo puedes descargar desde este link)
- Un video donde aparezca alguien con sueño y parpadeando
Código Python para Detección de Sueño, Somnolencia y Parpadeo:
from scipy.spatial import distance as dist from imutils.video import FileVideoStream from imutils.video import VideoStream from imutils import face_utils import imutils import time import dlib import cv2 def eye_aspect_ratio(eye): # Calcula las distancias euclidianas entre los dos conjuntos de # Señales verticales del ojo (x, y) -coordenadas A = dist.euclidean(eye[1], eye[5]) B = dist.euclidean(eye[2], eye[4]) # Calcular la distancia euclidiana entre la horizontal C = dist.euclidean(eye[0], eye[3]) # calcula AR del ojo ear = (A + B) / (2.0 * C) # devuelve AR del ojo return ear mStart=48 mEnd=68 jStart=0 jEnd= 17 rlStart=17 rlEnd= 22 leStart=22 leEnd= 27 nStart=27 nEnd= 36 # Se definen dos constantes, una para la relación de aspecto del ojo para indicar # el parpadeo y luego una segunda constante para el número de # Frames en que el ojo debe estar por debajo del umbral EYE_AR_THRESH = 0.3 EYE_AR_CONSEC_FRAMES = 2 sleep1_FRAMES = 3*5 sleep2_FRAMES = 7*5 sleep3_FRAMES = 8*5 sleep4_FRAMES = 10*5 sleep01_FRAMES = 6 sleep00_FRAMES = 2 # contadores para parpadeo COUNTER = 0 TOTAL = 0 sleep=0; gradsleep=0 # Inicializar la detección de cara con la libreria dlib (HOG-based) y luego # usa el predictor del hito facial print("[INFO] loading facial landmark predictor...") detector = dlib.get_frontal_face_detector() PREDICTOR_PATH = "shape_predictor_68_face_landmarks.dat" predictor = dlib.shape_predictor(PREDICTOR_PATH) #se selecionan los indices del los ojos (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"] (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"] (mStart, mEnd) = face_utils.FACIAL_LANDMARKS_IDXS["mouth"] (jStart,jEnd) = face_utils.FACIAL_LANDMARKS_IDXS["jaw"] (reStart,reEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eyebrow"] (leStart,leEnd)= face_utils.FACIAL_LANDMARKS_IDXS["left_eyebrow"] (nStart,nEnd)= face_utils.FACIAL_LANDMARKS_IDXS["nose"] cap = cv2.VideoCapture('chicaseduerme1.mp4') fileStream = False time.sleep(1.0) # ciclo de procesado ppal while True: # Si se trata de un archivo de flujo de vídeo, entonces tenemos que comprobar si # Hay más cuadros dejados en el búfer para procesar # Si fileStream y no vs.more (): # descanso # Agarrar el marco de la secuencia de archivo de vídeo de rosca, cambiar el tamaño # It, y convertirlo a escala de grises # Canales) ret, frame = cap.read() #frame = imutils.resize(frame, width=450) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # detecta caras en la imagen en escala de grises rects = detector(gray, 0) # ciclo sobre las detecciones de la cara for rect in rects: # Determina las marcas faciales para la región de la cara, luego # Convierte el punto de referencia facial (x, y) - a coordenada NumPy #Array shape = predictor(gray, rect) shape = face_utils.shape_to_np(shape) # Extrae las coordenadas de los ojos izquierdo y derecho y calcula # la relación de aspecto (AR) del ojo para ambos ojos leftEye = shape[lStart:lEnd] rightEye = shape[rStart:rEnd] mouth = shape[mStart:mEnd] jaw = shape[jStart:jEnd] re = shape[reStart:reEnd] le = shape[leStart:leEnd] nose= shape[nStart:nEnd] leftEAR = eye_aspect_ratio(leftEye) rightEAR = eye_aspect_ratio(rightEye) # media de AR para los dos ojos ear = (leftEAR + rightEAR) / 2.0 # hace la convex hull para los dos ojos # se dibujan los dos ojos leftEyeHull = cv2.convexHull(leftEye) mouthHull = cv2.convexHull(mouth) jawHull = cv2.convexHull(jaw) reHull = cv2.convexHull(re) leHull = cv2.convexHull(le) noseHull = cv2.convexHull(nose) rightEyeHull = cv2.convexHull(rightEye) cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1) cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1) # cv2.drawContours(frame, [mouthHull], -1, (255, 255, 0), 1) # cv2.drawContours(frame, [jaw], -1, (255, 55, 0), 1) # cv2.drawContours(frame, [reHull], -1, (255, 2, 60), 1) # cv2.drawContours(frame, [leHull], -1, (255, 255, 200), 1) # cv2.drawContours(frame, [noseHull], -1, (5, 255, 200), 1) # Compruebe si la relación de aspecto del ojo está por debajo del parpadeo #, Y si es así se incrementa el contador del marco intermitente if ear < EYE_AR_THRESH: COUNTER += 1 # De lo contrario, la relación de aspecto del ojo no está por debajo del parpadeo # límite else: # Si los ojos estaban cerrados por un número suficiente de # Luego incrementar el número total de parpadeos</pre> if COUNTER >= EYE_AR_CONSEC_FRAMES: TOTAL += 1 if COUNTER >= sleep00_FRAMES: gradsleep = 0.2 if COUNTER >= sleep01_FRAMES: gradsleep = 0.5 if COUNTER >= sleep1_FRAMES: gradsleep = 1 if COUNTER >= sleep2_FRAMES: gradsleep = 2 if COUNTER >= sleep3_FRAMES: gradsleep = 4 if COUNTER >= sleep4_FRAMES: gradsleep = 10 # reseteo del contador COUNTER = 0 # Dibuja el número total de destellos en el marco junto con # la relación de aspecto calculada del ojo para el marco cv2.putText(frame, "Parpadeo: {}".format(TOTAL), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.putText(frame, "sleeping grade (0:10): {:.2f}".format(gradsleep), (300, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) # mostramos el frame cv2.imshow("Frame", frame) key = cv2.waitKey(1) & 0xFF # si pulsa q se rompe el ciclo if key == ord("q"): break # limpiamos un poco cap.release() cv2.destroyAllWindows()
El código se compone de:
- 1 función eye_aspect_ratio : nos dice si los ojos estan abierto o cerrados
- 1 ciclo while : es el ciclo principal donde se ejecuta el video que queremos procesar y:
- 1 ciclo for : este ciclo se encarga de detectar las marcas faciales (solo son necesarias las de los ojos), se llama a la función eye_aspect_ratio donde con varias sentencias if se estima el grado de sueño o somnolencia
- Se imprimen en pantalla el número de parpadeos y el grado de sueño
- 1 sentencia if : se rompe el procesado por si se quiere detener el programa
Una vez ejecutado el algoritmo pulsa “q” si quieres detenerlo.
Los parámetros de grado de sueño y somnolencia han sido ajustados para el video de ejemplo. Para procesar otros vídeos deberán ser ajustados a partir del frame rate (nº de imagenes por segundo de la camara).
No olvideis cambiar la línea 73 si vais a procesar otro vídeo y de tener en una misma carpeta el vídeo, el código y el archivo shape_predictor_68_face_landmarks.dat
➡ Si has llegado hasta aquí enhorabuena. Continúa aprendiendo con nosotros en nuestro curso de Dlib y OpenCV
Excelente algortimo.. como podria implementarse una aplicacion que active tu webcam y detecte los parpadeos?..logre hacer una con este algoritmo y Flask, pero se me activa es la camara del servidor y no se como hacerlo desde el cliente. Alguna idea?