import numpy as np from scipy.spatial import distance as dist from imutils import face_utils (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"] (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"] def euclidean_distance(point1, point2): return np.linalg.norm(point1 - point2) def eyebrow(landmarks,sizes): eyebrow_dist=[] for landmark,size in zip(landmarks,sizes): if landmark is not None: right_eyebrow_inner = landmark[21] left_eyebrow_inner = landmark[22] eyebrow_distance = euclidean_distance(right_eyebrow_inner, left_eyebrow_inner) normalized_eyebrow_distance = eyebrow_distance / size[0] else: normalized_eyebrow_distance=None eyebrow_dist.append(normalized_eyebrow_distance) return eyebrow_dist def eye_aspect_ratio(eye): A = dist.euclidean(eye[1], eye[5]) # Vertical distance 1 B = dist.euclidean(eye[2], eye[4]) # Vertical distance 2 C = dist.euclidean(eye[0], eye[3]) # Horizontal distance ear = (A + B) / (2.0 * C) # EAR formula return ear def euclidean_distance(p1, p2): return np.linalg.norm(p1 - p2) # Function to detect smiles based on mouth aspect ratio def detect_smiles(landmarks_list, face_sizes, fps=30, consecutive_frames=2): smile_ratios = [] # Store the smile ratios for plotting smiles = [] smile_durations = [] # To store the duration of each smile total_smiles = 0 smile_in_progress = False smile_start_frame = None avg_dynamic_threshold=[] for frame_idx, (landmarks, face_size) in enumerate(zip(landmarks_list, face_sizes)): if landmarks is not None: # Use NumPy array indices for the relevant mouth landmarks left_corner = np.array(landmarks[48]) right_corner = np.array(landmarks[54]) top_lip = np.array(landmarks[51]) bottom_lip = np.array(landmarks[57]) mouth_width = euclidean_distance(left_corner, right_corner) mouth_height = euclidean_distance(top_lip, bottom_lip) face_width, face_height = face_size # face_size is (width, height) if face_width > 0 and face_height > 0: normalized_mouth_width = mouth_width / face_width normalized_mouth_height = mouth_height / face_height else: normalized_mouth_width = 0 normalized_mouth_height = 0 smile_ratios.append(normalized_mouth_width) dynamic_threshold = 0.2 + (0.05 * face_width / 100) avg_dynamic_threshold.append(dynamic_threshold) # print(dynamic_threshold) # Check if the smile meets the threshold if (normalized_mouth_width > dynamic_threshold) and (normalized_mouth_height > 0.06): smiles.append(True) if not smile_in_progress: smile_in_progress = True smile_start_frame = frame_idx # Record the start of the smile else: smiles.append(False) if smile_in_progress and (frame_idx - smile_start_frame >= consecutive_frames): smile_in_progress = False smile_end_frame = frame_idx smile_duration = (smile_end_frame - smile_start_frame) / fps # Calculate smile duration smile_durations.append(smile_duration) total_smiles += 1 # Increment total smile count else: smiles.append(None) try: avg_thr=sum(avg_dynamic_threshold)/len(avg_dynamic_threshold) except: avg_thr=0 return smiles, smile_ratios, total_smiles, smile_durations,avg_thr # Function to detect blinks based on the eye aspect ratio (EAR) import numpy as np # Function to detect blinks based on the eye aspect ratio (EAR) def detect_blinks(landmarks_list, face_sizes, ear_threshold=0.24, consecutive_frames=2): ear_ratios = [] # Store EAR for plotting blinks = [] # Variables to monitor consecutive low EAR values blink_count = 0 consec_low_ear = 0 for landmarks, face in zip(landmarks_list, face_sizes): if landmarks is not None: left_eye = landmarks[36:42] # Points 36-41 (inclusive) for the left eye right_eye = landmarks[42:48] def eye_aspect_ratio(eye): A = euclidean_distance(eye[1], eye[5]) B = euclidean_distance(eye[2], eye[4]) C = euclidean_distance(eye[0], eye[3]) ear = (A + B) / (2.0 * C) return ear left_ear = eye_aspect_ratio(left_eye) right_ear = eye_aspect_ratio(right_eye) avg_ear = (left_ear + right_ear) / 2.0 ear_ratios.append(avg_ear) if avg_ear < ear_threshold: consec_low_ear += 1 else: # If low EAR is detected for enough consecutive frames, count as a blink if consec_low_ear >= consecutive_frames: blink_count += 1 consec_low_ear = 0 # Reset the consecutive low EAR counter else: blinks.append(None) return blink_count, ear_ratios # Function to detect yawns based on the vertical distance between top and bottom lips # Function to detect yawns based on the vertical distance between top and bottom lips def detect_yawns(landmarks_list, face_sizes, fps=30, consecutive_frames=3): yawn_ratios = [] # Store the yawn ratios for plotting yawns = [] yawn_durations = [] # To store the duration of each yawn total_yawns = 0 yawn_in_progress = False yawn_start_frame = None for frame_idx, (landmarks, face_size) in enumerate(zip(landmarks_list, face_sizes)): if landmarks is not None: top_lip = np.array(landmarks[51]) bottom_lip = np.array(landmarks[57]) mouth_height = euclidean_distance(top_lip, bottom_lip) face_width, face_height = face_size # face_size is (width, height) if face_height > 0: normalized_mouth_height = mouth_height / face_height else: normalized_mouth_height = 0 yawn_ratios.append(normalized_mouth_height) # Check if the yawn meets the threshold if normalized_mouth_height > 0.24: yawns.append(True) if not yawn_in_progress: yawn_in_progress = True yawn_start_frame = frame_idx # Record the start of the yawn else: yawns.append(False) if yawn_in_progress and (frame_idx - yawn_start_frame >= consecutive_frames): yawn_in_progress = False yawn_end_frame = frame_idx yawn_duration = (yawn_end_frame - yawn_start_frame) / fps # Calculate yawn duration yawn_durations.append(yawn_duration) total_yawns += 1 # Increment total yawn count else: yawns.append(None) return yawns, yawn_ratios, total_yawns, yawn_durations