Rudraaaa76 commited on
Commit
ca0bd8e
·
verified ·
1 Parent(s): 5af7591

Create drowsy_detection.py

Browse files
Files changed (1) hide show
  1. drowsy_detection.py +188 -0
drowsy_detection.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import time
3
+ import numpy as np
4
+ import mediapipe as mp
5
+ from mediapipe.python.solutions.drawing_utils import _normalized_to_pixel_coordinates as denormalize_coordinates
6
+
7
+
8
+ def get_mediapipe_app(
9
+ max_num_faces=1,
10
+ refine_landmarks=True,
11
+ min_detection_confidence=0.5,
12
+ min_tracking_confidence=0.5,
13
+ ):
14
+ """Initialize and return Mediapipe FaceMesh Solution Graph object"""
15
+ face_mesh = mp.solutions.face_mesh.FaceMesh(
16
+ max_num_faces=max_num_faces,
17
+ refine_landmarks=refine_landmarks,
18
+ min_detection_confidence=min_detection_confidence,
19
+ min_tracking_confidence=min_tracking_confidence,
20
+ )
21
+
22
+ return face_mesh
23
+
24
+
25
+ def distance(point_1, point_2):
26
+ """Calculate l2-norm between two points"""
27
+ dist = sum([(i - j) ** 2 for i, j in zip(point_1, point_2)]) ** 0.5
28
+ return dist
29
+
30
+
31
+ def get_ear(landmarks, refer_idxs, frame_width, frame_height):
32
+ """
33
+ Calculate Eye Aspect Ratio for one eye.
34
+
35
+ Args:
36
+ landmarks: (list) Detected landmarks list
37
+ refer_idxs: (list) Index positions of the chosen landmarks
38
+ in order P1, P2, P3, P4, P5, P6
39
+ frame_width: (int) Width of captured frame
40
+ frame_height: (int) Height of captured frame
41
+
42
+ Returns:
43
+ ear: (float) Eye aspect ratio
44
+ """
45
+ try:
46
+ # Compute the euclidean distance between the horizontal
47
+ coords_points = []
48
+ for i in refer_idxs:
49
+ lm = landmarks[i]
50
+ coord = denormalize_coordinates(lm.x, lm.y, frame_width, frame_height)
51
+ coords_points.append(coord)
52
+
53
+ # Eye landmark (x, y)-coordinates
54
+ P2_P6 = distance(coords_points[1], coords_points[5])
55
+ P3_P5 = distance(coords_points[2], coords_points[4])
56
+ P1_P4 = distance(coords_points[0], coords_points[3])
57
+
58
+ # Compute the eye aspect ratio
59
+ ear = (P2_P6 + P3_P5) / (2.0 * P1_P4)
60
+
61
+ except:
62
+ ear = 0.0
63
+ coords_points = None
64
+
65
+ return ear, coords_points
66
+
67
+
68
+ def calculate_avg_ear(landmarks, left_eye_idxs, right_eye_idxs, image_w, image_h):
69
+ # Calculate Eye aspect ratio
70
+
71
+ left_ear, left_lm_coordinates = get_ear(landmarks, left_eye_idxs, image_w, image_h)
72
+ right_ear, right_lm_coordinates = get_ear(landmarks, right_eye_idxs, image_w, image_h)
73
+ Avg_EAR = (left_ear + right_ear) / 2.0
74
+
75
+ return Avg_EAR, (left_lm_coordinates, right_lm_coordinates)
76
+
77
+
78
+ def plot_eye_landmarks(frame, left_lm_coordinates, right_lm_coordinates, color):
79
+ for lm_coordinates in [left_lm_coordinates, right_lm_coordinates]:
80
+ if lm_coordinates:
81
+ for coord in lm_coordinates:
82
+ cv2.circle(frame, coord, 2, color, -1)
83
+
84
+ frame = cv2.flip(frame, 1)
85
+ return frame
86
+
87
+
88
+ def plot_text(image, text, origin, color, font=cv2.FONT_HERSHEY_SIMPLEX, fntScale=0.8, thickness=2):
89
+ image = cv2.putText(image, text, origin, font, fntScale, color, thickness)
90
+ return image
91
+
92
+
93
+ class VideoFrameHandler:
94
+ def __init__(self):
95
+ """
96
+ Initialize the necessary constants, mediapipe app
97
+ and tracker variables
98
+ """
99
+ # Left and right eye chosen landmarks.
100
+ self.eye_idxs = {
101
+ "left": [362, 385, 387, 263, 373, 380],
102
+ "right": [33, 160, 158, 133, 153, 144],
103
+ }
104
+
105
+ # Used for coloring landmark points.
106
+ # Its value depends on the current EAR value.
107
+ self.RED = (0, 0, 255) # BGR
108
+ self.GREEN = (0, 255, 0) # BGR
109
+
110
+ # Initializing Mediapipe FaceMesh solution pipeline
111
+ self.facemesh_model = get_mediapipe_app()
112
+
113
+ # For tracking counters and sharing states in and out of callbacks.
114
+ self.state_tracker = {
115
+ "start_time": time.perf_counter(),
116
+ "DROWSY_TIME": 0.0, # Holds the amount of time passed with EAR < EAR_THRESH
117
+ "COLOR": self.GREEN,
118
+ "play_alarm": False,
119
+ "EAR":0
120
+ }
121
+
122
+ self.EAR_txt_pos = (10, 30)
123
+
124
+ def process(self, frame: np.array, thresholds: dict):
125
+ """
126
+ This function is used to implement our Drowsy detection algorithm
127
+
128
+ Args:
129
+ frame: (np.array) Input frame matrix.
130
+ thresholds: (dict) Contains the two threshold values
131
+ WAIT_TIME and EAR_THRESH.
132
+
133
+ Returns:
134
+ The processed frame and a boolean flag to
135
+ indicate if the alarm should be played or not.
136
+ """
137
+
138
+ # To improve performance,
139
+ # mark the frame as not writeable to pass by reference.
140
+ frame.flags.writeable = True
141
+ frame_h, frame_w, _ = frame.shape
142
+
143
+ DROWSY_TIME_txt_pos = (10, int(frame_h // 2 * 1.7))
144
+ ALM_txt_pos = (10, int(frame_h // 2 * 1.85))
145
+
146
+ results = self.facemesh_model.process(frame)
147
+
148
+ if results.multi_face_landmarks:
149
+ landmarks = results.multi_face_landmarks[0].landmark
150
+ EAR, coordinates = calculate_avg_ear(landmarks, self.eye_idxs["left"], self.eye_idxs["right"], frame_w, frame_h)
151
+ frame = plot_eye_landmarks(frame, coordinates[0], coordinates[1], self.state_tracker["COLOR"])
152
+
153
+ if EAR < thresholds["EAR_THRESH"]:
154
+
155
+ # Increase DROWSY_TIME to track the time period with EAR less than the threshold
156
+ # and reset the start_time for the next iteration.
157
+ end_time = time.perf_counter()
158
+
159
+ self.state_tracker["DROWSY_TIME"] += end_time - self.state_tracker["start_time"]
160
+ self.state_tracker["start_time"] = end_time
161
+ self.state_tracker["COLOR"] = self.RED
162
+ self.state_tracker["EAR"] = EAR
163
+
164
+ if self.state_tracker["DROWSY_TIME"] >= thresholds["WAIT_TIME"]:
165
+ self.state_tracker["play_alarm"] = True
166
+ plot_text(frame, "WAKE UP! WAKE UP", ALM_txt_pos, self.state_tracker["COLOR"])
167
+
168
+ else:
169
+ self.state_tracker["start_time"] = time.perf_counter()
170
+ self.state_tracker["DROWSY_TIME"] = 0.0
171
+ self.state_tracker["COLOR"] = self.GREEN
172
+ self.state_tracker["play_alarm"] = False
173
+
174
+ EAR_txt = f"EAR: {round(EAR, 2)}"
175
+ DROWSY_TIME_txt = f"DROWSY: {round(self.state_tracker['DROWSY_TIME'], 3)} Secs"
176
+ plot_text(frame, EAR_txt, self.EAR_txt_pos, self.state_tracker["COLOR"])
177
+ plot_text(frame, DROWSY_TIME_txt, DROWSY_TIME_txt_pos, self.state_tracker["COLOR"])
178
+
179
+ else:
180
+ self.state_tracker["start_time"] = time.perf_counter()
181
+ self.state_tracker["DROWSY_TIME"] = 0.0
182
+ self.state_tracker["COLOR"] = self.GREEN
183
+ self.state_tracker["play_alarm"] = False
184
+
185
+ # Flip the frame horizontally for a selfie-view display.
186
+ frame = cv2.flip(frame, 1)
187
+
188
+ return frame, self.state_tracker["play_alarm"]