import math import mediapipe import time import cv2 from tqdm import tqdm import numpy as np def EuclideanDistance(point1, point2): x1, y1 = point1 x2, y2 = point2 distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2) return distance class BlinkDetector: def __init__(self): self.COUNTER = 0 self.TOTAL_BLINKS = 0 # put 1 when divide self.blink_start_time = 0 self.blink_durations = [] self.LEFT_EYE = [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398] self.RIGHT_EYE = [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246] def FaceMeshInitialiser(self, max_num_faces, min_detection_confidence, min_tracking_confidence): face_mesh = mediapipe.solutions.face_mesh.FaceMesh(max_num_faces=max_num_faces, min_detection_confidence=min_detection_confidence, min_tracking_confidence=min_tracking_confidence) return face_mesh def LandmarksDetector(self, frame, face_mesh_results, draw: bool=False ): image_height, image_width = frame.shape[:2] mesh_coordinates = [(int(point.x * image_width), int(point.y * image_height)) for point in face_mesh_results.multi_face_landmarks[0].landmark] if draw: [cv2.circle(frame, i, 2, (0, 255, 0), -1) for i in mesh_coordinates] return mesh_coordinates def BlinkRatioCalculator(self, landmarks): right_eye_landmark1 = landmarks[self.RIGHT_EYE[0]] right_eye_landmark2 = landmarks[self.RIGHT_EYE[8]] right_eye_landmark3 = landmarks[self.RIGHT_EYE[12]] right_eye_landmark4 = landmarks[self.RIGHT_EYE[4]] left_eye_landmark1 = landmarks[self.LEFT_EYE[0]] left_eye_landmark2 = landmarks[self.LEFT_EYE[8]] left_eye_landmark3 = landmarks[self.LEFT_EYE[12]] left_eye_landmark4 = landmarks[self.LEFT_EYE[4]] right_eye_horizontal_distance = EuclideanDistance(right_eye_landmark1, right_eye_landmark2) right_eye_verticle_distance = EuclideanDistance(right_eye_landmark3, right_eye_landmark4) left_eye_horizontal_distance = EuclideanDistance(left_eye_landmark1, left_eye_landmark2) left_eye_verticle_distance = EuclideanDistance(left_eye_landmark3, left_eye_landmark4) try: right_eye_ratio = right_eye_horizontal_distance / right_eye_verticle_distance except: right_eye_ratio = 0 try: left_eye_ratio = left_eye_horizontal_distance / left_eye_verticle_distance except: left_eye_ratio=0 # eyes_ratio = (right_eye_ratio + left_eye_ratio) / 2 return [right_eye_ratio, left_eye_ratio] def BlinkCounter(self, eyes_ratio): if eyes_ratio[0] > 4 or eyes_ratio[1] > 4: if self.COUNTER == 0: self.blink_start_time = time.time() self.COUNTER += 1 else: if self.COUNTER > 4: self.TOTAL_BLINKS += 1 blink_duration = time.time() - self.blink_start_time self.blink_durations.append(blink_duration) self.COUNTER = 0 return [self.TOTAL_BLINKS, self.blink_durations] def InitialiseVariables(): return BlinkDetector() class Facetrack(BlinkDetector): def __init__(self): super().__init__() # create object for mediapipe face_mesh self.mediapipe_face_mesh = self.FaceMeshInitialiser(max_num_faces=1, min_detection_confidence=0.6, min_tracking_confidence=0.7) self.frame = None self.avg_blink_duration=0 self.list_blinks=[] def predict(self,img): self.rgb_frame = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) self.results = self.mediapipe_face_mesh.process(self.rgb_frame) if self.results.multi_face_landmarks: self.mesh_coordinates = self.LandmarksDetector(img, self.results, draw=True) self.eyes_ratio = self.BlinkRatioCalculator(self.mesh_coordinates) self.list_blinks = self.BlinkCounter(self.eyes_ratio) if self.list_blinks[1]: try: self.avg_blink_duration = sum(self.list_blinks[1]) / len(self.list_blinks[1]) except: self.avg_blink_duration = sum(self.list_blinks[1]) self.blink_durations = self.list_blinks[1] if len(self.list_blinks)>0 : self.TOTAL_BLINKS = self.list_blinks[0] else: self.TOTAL_BLINKS = 0 def eye_track_predict(fc,frames,fps): preds=[] for frame in tqdm(frames): if frame is not None: frame=np.copy(frame) fc.predict(frame) data=fc.TOTAL_BLINKS else: data='frame error' preds.append(data) return preds,fc.blink_durations,fc.TOTAL_BLINKS