Last active
September 1, 2021 07:03
-
-
Save Asadullah-Dal17/fd71c31bac74ee84e6a31af50fa62961 to your computer and use it in GitHub Desktop.
Mediapipe landmarks detection for each individual part, like an face-oval, eyes, lips, and Eyebrows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import cv2 as cv | |
| import mediapipe as mp | |
| import time | |
| import utils | |
| import math | |
| # variables | |
| FRAME_COUNTER =0 | |
| TOTAL_BLINKS = 0 | |
| CLOSED_EYES_FRAME_COUNTER =0 # Counts frames while Eyes are closed | |
| # constants | |
| FONTS =cv.FONT_HERSHEY_COMPLEX | |
| CLOSED_EYES_FRAME = 3 # threshold to count a blink, after number of frames | |
| # face bounder indices | |
| FACE_OVAL=[ 10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103,67, 109] | |
| # lips indices for Landmarks | |
| LIPS=[ 61, 146, 91, 181, 84, 17, 314, 405, 321, 375,291, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,185, 40, 39, 37,0 ,267 ,269 ,270 ,409, 415, 310, 311, 312, 13, 82, 81, 42, 183, 78 ] | |
| LOWER_LIPS =[61, 146, 91, 181, 84, 17, 314, 405, 321, 375, 291, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95] | |
| UPPER_LIPS=[ 185, 40, 39, 37,0 ,267 ,269 ,270 ,409, 415, 310, 311, 312, 13, 82, 81, 42, 183, 78] | |
| # Left eyes indices | |
| LEFT_EYE =[ 362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385,384, 398 ] | |
| LEFT_EYEBROW =[ 336, 296, 334, 293, 300, 276, 283, 282, 295, 285 ] | |
| # right eyes indices | |
| RIGHT_EYE=[ 33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161 , 246 ] | |
| RIGHT_EYEBROW=[ 70, 63, 105, 66, 107, 55, 65, 52, 53, 46 ] | |
| map_face_mesh = mp.solutions.face_mesh | |
| # camera object | |
| camera = cv.VideoCapture(0) | |
| # landmark detection function | |
| def landmarksDetection(img, results, draw=False): | |
| img_height, img_width= img.shape[:2] | |
| # list[(x,y), (x,y)....] | |
| mesh_coord = [(int(point.x * img_width), int(point.y * img_height)) for point in results.multi_face_landmarks[0].landmark] | |
| if draw : | |
| [cv.circle(img, p, 2, utils.GREEN, -1) for p in mesh_coord] | |
| # returning the list of tuples for each landmarks | |
| return mesh_coord | |
| # finding distance between two points | |
| def euclideanDistance(point1, point2): | |
| x, y = point1 | |
| x1, y1 = point2 | |
| euclidean_dist = math.sqrt((x1 - x) ** 2 + (y1 - y) ** 2) | |
| return euclidean_dist | |
| # Blinking Detector Function | |
| def blinkRatio(frame, landmarks, right_eye_indics, left_eye_indics): | |
| # getting the points | |
| # Right Eye horizontal points | |
| rh_right = landmarks[right_eye_indics[0]] | |
| rh_left = landmarks[right_eye_indics[7]] | |
| # left Eyes horizontal points | |
| lh_right = landmarks[left_eye_indics[0]] | |
| lh_left = landmarks[left_eye_indics[7]] | |
| cv.circle(frame, rh_right, 2, utils.GREEN, -1) | |
| cv.circle(frame, rh_left, 2, utils.WHITE, -1) | |
| cv.circle(frame, lh_right, 2, utils.GREEN, -1) | |
| cv.circle(frame, lh_left, 2, utils.WHITE, -1) | |
| # vertical points for Right Eyes | |
| rv_top= landmarks[right_eye_indics[12]] | |
| rv_bottom = landmarks[right_eye_indics[4]] | |
| # Vertical points for Left Eye | |
| lv_top = landmarks[left_eye_indics[12]] | |
| lv_bottom = landmarks[left_eye_indics[4]] | |
| cv.circle(frame, rv_top, 2, utils.GREEN, -1) | |
| cv.circle(frame, rv_bottom, 2, utils.WHITE, -1) | |
| cv.circle(frame, lv_top, 2, utils.GREEN, -1) | |
| cv.circle(frame, lv_bottom, 2, utils.WHITE, -1) | |
| # finding euclidean distance | |
| h_right_euclid_dist =euclideanDistance(rh_right, rh_left) | |
| v_right_euclid_dist =euclideanDistance(rv_top, rv_bottom) | |
| h_left_euclid_dist = euclideanDistance(lh_right, lh_left) | |
| v_left_euclid_dist = euclideanDistance(lv_top, lv_bottom) | |
| eyes_ratio =( (h_right_euclid_dist/v_right_euclid_dist) + (h_left_euclid_dist/ v_left_euclid_dist))/2 | |
| return eyes_ratio | |
| with map_face_mesh.FaceMesh(min_detection_confidence =0.5, min_tracking_confidence=0.5) as face_mesh: | |
| # starting time here | |
| start_time = time.time() | |
| # starting Video loop here. | |
| while True: | |
| FRAME_COUNTER +=1 # frame counter | |
| ret, frame = camera.read() # getting frame from camera | |
| fr_height, fr_width = frame.shape[:2] | |
| if not ret: | |
| break # no more frames break | |
| # frame = cv.resize(frame, None, fx=2, fy=2, interpolation=cv.INTER_AREA) | |
| rgb_frame = cv.cvtColor(frame, cv.COLOR_RGB2BGR) | |
| results = face_mesh.process(rgb_frame) | |
| if results.multi_face_landmarks: | |
| mesh_coords = landmarksDetection(frame, results, False) | |
| # blinking ratio | |
| ratio = blinkRatio(frame, mesh_coords, RIGHT_EYE, LEFT_EYE) | |
| if ratio >4.1: | |
| CLOSED_EYES_FRAME_COUNTER +=1 | |
| print(CLOSED_EYES_FRAME_COUNTER, ratio) | |
| cv.putText(frame, f'Blink', (int(fr_width/2), 30), FONTS, 1.4, utils.GREEN, 2, cv.LINE_AA) | |
| else: | |
| if CLOSED_EYES_FRAME_COUNTER>CLOSED_EYES_FRAME: | |
| TOTAL_BLINKS +=1 | |
| CLOSED_EYES_FRAME_COUNTER=0 | |
| cv.putText(frame, f'Total Blinks: {TOTAL_BLINKS}', (int(20), 100), FONTS, 0.5, utils.GREEN, 1, cv.LINE_AA) | |
| # calculating frame per seconds FPS | |
| end_time = time.time()-start_time | |
| fps = FRAME_COUNTER/end_time | |
| frame =utils.textWithBackground(frame,f'FPS: {round(fps,1)}',FONTS, 1.0, (20, 50), bgOpacity=0.9, textThickness=2) | |
| cv.imshow('frame', frame) | |
| key = cv.waitKey(2) | |
| if key==ord('q') or key ==ord('Q'): | |
| break | |
| cv.destroyAllWindows() | |
| camera.release() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment