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]], # P1 (left) landmarks[eye_landmarks[1]], # P2 (top-left) landmarks[eye_landmarks[2]], # P3 (top-right) landmarks[eye_landmarks[3]], # P4 (right) landmarks[eye_landmarks[4]], # P5 (bottom-right) landmarks[eye_landmarks[5]] # P6 (bottom-left) ] 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, # Required for iris landmarks 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 # Left iris center RIGHT_IRIS = 468 # Right iris center 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 # Iris coordinates 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()