|
import cv2
|
|
import numpy as np
|
|
import mediapipe as mp
|
|
from scipy.spatial import distance as dist
|
|
|
|
def eye_aspect_ratio(eye_landmarks, landmarks, image_shape):
|
|
eye = [
|
|
landmarks[eye_landmarks[0]],
|
|
landmarks[eye_landmarks[1]],
|
|
landmarks[eye_landmarks[2]],
|
|
landmarks[eye_landmarks[3]],
|
|
landmarks[eye_landmarks[4]],
|
|
landmarks[eye_landmarks[5]]
|
|
]
|
|
eye = [(int(p.x * image_shape[1]), int(p.y * image_shape[0])) for p in eye]
|
|
|
|
A = dist.euclidean(eye[1], eye[5])
|
|
B = dist.euclidean(eye[2], eye[4])
|
|
C = dist.euclidean(eye[0], eye[3])
|
|
ear = (A + B) / (2.0 * C)
|
|
return ear, eye
|
|
|
|
class BlinkDetector:
|
|
def __init__(self):
|
|
self.mp_face_mesh = mp.solutions.face_mesh
|
|
self.face_mesh = self.mp_face_mesh.FaceMesh(
|
|
max_num_faces=1,
|
|
refine_landmarks=True,
|
|
min_detection_confidence=0.5,
|
|
min_tracking_confidence=0.5
|
|
)
|
|
self.EAR_THRESHOLD = 0.25
|
|
self.EAR_CONSEC_FRAMES = 3
|
|
|
|
def detect_blinks(self, frame):
|
|
image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
results = self.face_mesh.process(image_rgb)
|
|
|
|
if not results.multi_face_landmarks:
|
|
return None, None, None, None, None, None
|
|
|
|
landmarks = results.multi_face_landmarks[0].landmark
|
|
h, w = frame.shape[:2]
|
|
|
|
LEFT_EYE = [33, 160, 158, 133, 153, 144]
|
|
RIGHT_EYE = [362, 385, 387, 263, 373, 380]
|
|
LEFT_IRIS = 473
|
|
RIGHT_IRIS = 468
|
|
|
|
left_ear, left_eye_points = eye_aspect_ratio(LEFT_EYE, landmarks, (h, w))
|
|
right_ear, right_eye_points = eye_aspect_ratio(RIGHT_EYE, landmarks, (h, w))
|
|
avg_ear = (left_ear + right_ear) / 2.0
|
|
|
|
nose_tip = landmarks[1]
|
|
head_pose = (nose_tip.x - 0.5) * 2
|
|
|
|
|
|
left_iris = (int(landmarks[LEFT_IRIS].x * w), int(landmarks[LEFT_IRIS].y * h))
|
|
right_iris = (int(landmarks[RIGHT_IRIS].x * w), int(landmarks[RIGHT_IRIS].y * h))
|
|
|
|
return avg_ear, left_eye_points, right_eye_points, head_pose, left_iris, right_iris
|
|
|
|
def __del__(self):
|
|
self.face_mesh.close() |