import math import numpy as np import cv2 as cv LEFT_EYE = [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398] RIGHT_EYE = [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246] LEFT_IRIS = [474, 475, 476, 477] RIGHT_IRIS = [469, 470, 471, 472] def valid_float(n): if not n.isfloat(): raise argparse.ArgumentTypeError('Invalid integer value: {}'.format(n)) return float(n) def euclaideanDistance(point, point1): x, y = point x1, y1 = point1 distance = math.sqrt((x1 - x)**2 + (y1 - y)**2) return distance # Blinking Ratio def blinkRatio(img, landmarks, right_indices, left_indices): # Right eyes # horizontal line rh_right = landmarks[right_indices[0]] rh_left = landmarks[right_indices[8]] # vertical line rv_top = landmarks[right_indices[12]] rv_bottom = landmarks[right_indices[4]] # draw lines on right eyes # cv.line(img, rh_right, rh_left, utils.GREEN, 2) # cv.line(img, rv_top, rv_bottom, utils.WHITE, 2) # LEFT_EYE # horizontal line lh_right = landmarks[left_indices[0]] lh_left = landmarks[left_indices[8]] # vertical line lv_top = landmarks[left_indices[12]] lv_bottom = landmarks[left_indices[4]] # Finding Distance Right Eye rhDistance = euclaideanDistance(rh_right, rh_left) rvDistance = euclaideanDistance(rv_top, rv_bottom) # Finding Distance Left Eye lvDistance = euclaideanDistance(lv_top, lv_bottom) lhDistance = euclaideanDistance(lh_right, lh_left) # Finding ratio of LEFT and Right Eyes reRatio=0.0 leRatio=0.0 if (rvDistance > 0.0) & (lvDistance > 0.0): reRatio = rhDistance/rvDistance leRatio = lhDistance/lvDistance ratio = (reRatio+leRatio)/2 return ratio, reRatio, leRatio def process_frame(frame, overlay, LEFT_EYE, RIGHT_EYE, LEFT_IRIS, RIGHT_IRIS, mp_face_mesh, min_detection_confidence, min_tracking_confidence,alpha): with mp_face_mesh.FaceMesh( max_num_faces=1, refine_landmarks=True, min_detection_confidence=min_detection_confidence, min_tracking_confidence=min_tracking_confidence ) as face_mesh: # Convert frame to RGB rgb_frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB) # Convert RGB frame to RGBA rgba_frame = cv.cvtColor(frame, cv.COLOR_BGR2RGBA) # Get frame dimensions height, width = rgba_frame.shape[:2] # Process frame with face mesh results = face_mesh.process(rgb_frame) if results.multi_face_landmarks: # Initialize overlay with zeros zero_overlay = np.zeros_like(rgba_frame) # Get mesh points mesh_points = np.array([np.multiply([p.x, p.y], [width, height]).astype(int) for p in results.multi_face_landmarks[0].landmark]) # Initialize iris masks iris_mask_left = np.zeros(rgba_frame.shape, dtype=np.uint8) iris_mask_right = np.zeros(rgba_frame.shape, dtype=np.uint8) # Get blink ratio _, re_ratio, le_ratio = blinkRatio(rgb_frame, mesh_points, RIGHT_EYE, LEFT_EYE) # Get iris centers and radii (l_cx, l_cy), l_radius = cv.minEnclosingCircle(mesh_points[LEFT_IRIS]) (r_cx, r_cy), r_radius = cv.minEnclosingCircle(mesh_points[RIGHT_IRIS]) center_left = (int(l_cx), int(l_cy)) center_right = (int(r_cx), int(r_cy)) # Draw circles on iris masks cv.circle(iris_mask_left, center_left, int(l_radius), (255, 0, 0, 255), -1, cv.LINE_AA) cv.circle(iris_mask_right, center_right, int(r_radius), (255, 0, 0, 255), -1, cv.LINE_AA) # Get bounding box sizes bbx_size_l = int((l_radius * 2) / 2) bbx_size_r = int((r_radius * 2) / 2) # Resize overlay resized_overlay_l = cv.resize(overlay, (bbx_size_l * 2, bbx_size_l * 2), interpolation=cv.INTER_CUBIC) resized_overlay_r = cv.resize(overlay, (bbx_size_r * 2, bbx_size_r * 2), interpolation=cv.INTER_CUBIC) # Get bounding box coordinates y1_r = center_right[1] - bbx_size_r y2_r = center_right[1] + bbx_size_r x1_r = center_right[0] - bbx_size_r x2_r = center_right[0] + bbx_size_r y1_l = center_left[1] - bbx_size_l y2_l = center_left[1] + bbx_size_l x1_l = center_left[0] - bbx_size_l x2_l = center_left[0] + bbx_size_l # Add resized overlay to zero overlay if conditions are met if (resized_overlay_l.shape == zero_overlay[y1_l:y2_l, x1_l:x2_l].shape) & (le_ratio < 5.0) & (le_ratio > 2.0): zero_overlay[y1_l:y2_l, x1_l:x2_l] = resized_overlay_l if (resized_overlay_r.shape == zero_overlay[y1_r:y2_r, x1_r:x2_r].shape) & (re_ratio < 5.0) & (re_ratio > 2.0): zero_overlay[y1_r:y2_r, x1_r:x2_r] = resized_overlay_r # Initialize eye masks eye_mask_left = np.zeros(rgba_frame.shape, dtype=np.uint8) eye_mask_right = np.zeros(rgba_frame.shape, dtype=np.uint8) # Fill eye masks with polygons cv.fillPoly(eye_mask_left, [mesh_points[LEFT_EYE]], (255, 0, 0, 255)) cv.fillPoly(eye_mask_right, [mesh_points[RIGHT_EYE]], (255, 0, 0, 255)) # Use the 4-channel masks to create zero_overlay zero_overlay[np.where((iris_mask_left[:, :, 3] > 0) & (eye_mask_left[:, :, 3] == 0))] = 0 zero_overlay[np.where((iris_mask_right[:, :, 3] > 0) & (eye_mask_right[:, :, 3] == 0))] = 0 # Add weighted overlay to frame rgba_frame = cv.addWeighted(rgba_frame, 1, zero_overlay, alpha, 0) return rgba_frame