simran0608 commited on
Commit
4d85734
·
verified ·
1 Parent(s): 4dae9e8

Update drowsiness_detection.py

Browse files
Files changed (1) hide show
  1. drowsiness_detection.py +107 -36
drowsiness_detection.py CHANGED
@@ -7,18 +7,28 @@ import numpy as np
7
  import cv2 as cv
8
  import imutils
9
  import dlib
10
- import pygame
11
  import argparse
12
  import os
13
 
14
- # --- MODELS AND CONSTANTS ---
15
- # Use absolute paths relative to this script file for robustness
16
  script_dir = os.path.dirname(os.path.abspath(__file__))
17
  haar_cascade_face_detector = os.path.join(script_dir, "haarcascade_frontalface_default.xml")
18
  dlib_facial_landmark_predictor = os.path.join(script_dir, "shape_predictor_68_face_landmarks.dat")
19
 
20
- face_detector = cv.CascadeClassifier(haar_cascade_face_detector)
21
- landmark_predictor = dlib.shape_predictor(dlib_facial_landmark_predictor)
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  font = cv.FONT_HERSHEY_SIMPLEX
24
  EYE_ASPECT_RATIO_THRESHOLD = 0.25
@@ -27,7 +37,7 @@ MOUTH_ASPECT_RATIO_THRESHOLD = 0.5
27
  MOUTH_OPEN_THRESHOLD = 15
28
  FACE_LOST_THRESHOLD = 25
29
 
30
- # --- GLOBAL STATE VARIABLES (managed by reset_counters) ---
31
  EYE_THRESH_COUNTER = 0
32
  DROWSY_COUNTER = 0
33
  drowsy_alert = False
@@ -38,39 +48,57 @@ FACE_LOST_COUNTER = 0
38
  HEAD_DOWN_COUNTER = 0
39
  head_down_alert = False
40
 
41
- # --- LAZY AUDIO INITIALIZATION ---
42
  _audio_initialized = False
43
- _drowsiness_sound = None
44
- _yawn_sound = None
45
 
46
  def _initialize_audio():
47
- """Initializes pygame mixer only when needed and handles errors."""
48
- global _audio_initialized, _drowsiness_sound, _yawn_sound
49
  if _audio_initialized:
50
  return
 
51
  try:
52
- pygame.mixer.init()
53
- _drowsiness_sound = pygame.mixer.Sound(os.path.join(script_dir, "drowsiness-detected.mp3"))
54
- _yawn_sound = pygame.mixer.Sound(os.path.join(script_dir, "yawning-detected.mp3"))
55
- print("Audio initialized successfully.")
56
- except pygame.error as e:
57
- print(f"Warning: Could not initialize audio. Alert sounds will be disabled. Error: {e}")
 
 
 
 
 
 
 
58
  _audio_initialized = True
59
 
60
- def play_alarm(sound_to_play):
61
- """Plays an alarm sound if the audio system is available."""
62
- _initialize_audio() # Ensure audio is initialized
63
- if sound_to_play and not pygame.mixer.get_busy():
64
- sound_to_play.play()
 
 
 
 
 
 
 
 
65
 
66
  def generate_alert(final_eye_ratio, final_mouth_ratio):
67
  global EYE_THRESH_COUNTER, YAWN_THRESH_COUNTER, drowsy_alert, yawn_alert, DROWSY_COUNTER, YAWN_COUNTER
 
68
  if final_eye_ratio < EYE_ASPECT_RATIO_THRESHOLD:
69
  EYE_THRESH_COUNTER += 1
70
  if EYE_THRESH_COUNTER >= EYE_CLOSED_THRESHOLD and not drowsy_alert:
71
  DROWSY_COUNTER += 1
72
  drowsy_alert = True
73
- Thread(target=play_alarm, args=(_drowsiness_sound,)).start()
 
 
74
  else:
75
  EYE_THRESH_COUNTER = 0
76
  drowsy_alert = False
@@ -80,23 +108,31 @@ def generate_alert(final_eye_ratio, final_mouth_ratio):
80
  if YAWN_THRESH_COUNTER >= MOUTH_OPEN_THRESHOLD and not yawn_alert:
81
  YAWN_COUNTER += 1
82
  yawn_alert = True
83
- Thread(target=play_alarm, args=(_yawn_sound,)).start()
 
 
84
  else:
85
  YAWN_THRESH_COUNTER = 0
86
  yawn_alert = False
87
 
88
  def detect_facial_landmarks(x, y, w, h, gray_frame):
 
 
 
 
89
  face = dlib.rectangle(int(x), int(y), int(x + w), int(y + h))
90
  face_landmarks = landmark_predictor(gray_frame, face)
91
  return face_utils.shape_to_np(face_landmarks)
92
 
93
  def eye_aspect_ratio(eye):
 
94
  A = dist.euclidean(eye[1], eye[5])
95
  B = dist.euclidean(eye[2], eye[4])
96
  C = dist.euclidean(eye[0], eye[3])
97
  return (A + B) / (2.0 * C)
98
 
99
  def final_eye_aspect_ratio(shape):
 
100
  (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
101
  (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]
102
  left_ear = eye_aspect_ratio(shape[lStart:lEnd])
@@ -104,12 +140,14 @@ def final_eye_aspect_ratio(shape):
104
  return (left_ear + right_ear) / 2.0
105
 
106
  def mouth_aspect_ratio(mouth):
 
107
  A = dist.euclidean(mouth[2], mouth[10])
108
  B = dist.euclidean(mouth[4], mouth[8])
109
  C = dist.euclidean(mouth[0], mouth[6])
110
  return (A + B) / (2.0 * C)
111
 
112
  def final_mouth_aspect_ratio(shape):
 
113
  (mStart, mEnd) = face_utils.FACIAL_LANDMARKS_IDXS["mouth"]
114
  return mouth_aspect_ratio(shape[mStart:mEnd])
115
 
@@ -129,31 +167,56 @@ def process_frame(frame):
129
  # The output frame will have a fixed width of 640px
130
  frame = imutils.resize(frame, width=640)
131
  gray_frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
132
- faces = face_detector.detectMultiScale(gray_frame, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30), flags=cv.CASCADE_SCALE_IMAGE)
 
 
 
 
 
 
 
 
133
 
134
  if len(faces) > 0:
135
  FACE_LOST_COUNTER = 0
136
  head_down_alert = False
137
  (x, y, w, h) = faces[0]
 
 
 
 
 
138
  face_landmarks = detect_facial_landmarks(x, y, w, h, gray_frame)
139
- final_ear = final_eye_aspect_ratio(face_landmarks)
140
- final_mar = final_mouth_aspect_ratio(face_landmarks)
141
- generate_alert(final_ear, final_mar)
142
- cv.putText(frame, f"EAR: {final_ear:.2f}", (10, 30), font, 0.7, (0, 0, 255), 2)
143
- cv.putText(frame, f"MAR: {final_mar:.2f}", (10, 60), font, 0.7, (0, 0, 255), 2)
 
 
 
 
 
 
 
144
  else:
145
  FACE_LOST_COUNTER += 1
146
  if FACE_LOST_COUNTER >= FACE_LOST_THRESHOLD and not head_down_alert:
147
  HEAD_DOWN_COUNTER += 1
148
  head_down_alert = True
149
 
150
- # Draw status text
151
  cv.putText(frame, f"Drowsy: {DROWSY_COUNTER}", (480, 30), font, 0.7, (255, 255, 0), 2)
152
  cv.putText(frame, f"Yawn: {YAWN_COUNTER}", (480, 60), font, 0.7, (255, 255, 0), 2)
153
  cv.putText(frame, f"Head Down: {HEAD_DOWN_COUNTER}", (480, 90), font, 0.7, (255, 255, 0), 2)
154
- if drowsy_alert: cv.putText(frame, "DROWSINESS ALERT!", (150, 30), font, 0.9, (0, 0, 255), 2)
155
- if yawn_alert: cv.putText(frame, "YAWN ALERT!", (200, 60), font, 0.9, (0, 0, 255), 2)
156
- if head_down_alert: cv.putText(frame, "HEAD NOT VISIBLE!", (180, 90), font, 0.9, (0, 0, 255), 2)
 
 
 
 
 
157
 
158
  return frame
159
 
@@ -164,6 +227,12 @@ if __name__ == "__main__":
164
  parser.add_argument('--input', type=str, help='Input video file path for video mode')
165
  args = parser.parse_args()
166
 
 
 
 
 
 
 
167
  if args.mode == 'webcam':
168
  print("Starting webcam detection... Press 'q' to quit.")
169
  cap = cv.VideoCapture(0)
@@ -173,10 +242,12 @@ if __name__ == "__main__":
173
  reset_counters()
174
  while True:
175
  ret, frame = cap.read()
176
- if not ret: break
 
177
  processed_frame = process_frame(frame)
178
  cv.imshow("Live Drowsiness Detection", processed_frame)
179
- if cv.waitKey(1) & 0xFF == ord('q'): break
 
180
  cap.release()
181
  cv.destroyAllWindows()
182
 
 
7
  import cv2 as cv
8
  import imutils
9
  import dlib
 
10
  import argparse
11
  import os
12
 
13
+ # --- FIXED: Models and Constants with better error handling ---
 
14
  script_dir = os.path.dirname(os.path.abspath(__file__))
15
  haar_cascade_face_detector = os.path.join(script_dir, "haarcascade_frontalface_default.xml")
16
  dlib_facial_landmark_predictor = os.path.join(script_dir, "shape_predictor_68_face_landmarks.dat")
17
 
18
+ # Check if required files exist
19
+ if not os.path.exists(haar_cascade_face_detector):
20
+ print(f"Warning: Face detector file not found at {haar_cascade_face_detector}")
21
+ # Try to use OpenCV's built-in cascade
22
+ face_detector = cv.CascadeClassifier(cv.data.haarcascades + 'haarcascade_frontalface_default.xml')
23
+ else:
24
+ face_detector = cv.CascadeClassifier(haar_cascade_face_detector)
25
+
26
+ if not os.path.exists(dlib_facial_landmark_predictor):
27
+ print(f"Error: Dlib predictor file not found at {dlib_facial_landmark_predictor}")
28
+ print("Please download shape_predictor_68_face_landmarks.dat from dlib's website")
29
+ landmark_predictor = None
30
+ else:
31
+ landmark_predictor = dlib.shape_predictor(dlib_facial_landmark_predictor)
32
 
33
  font = cv.FONT_HERSHEY_SIMPLEX
34
  EYE_ASPECT_RATIO_THRESHOLD = 0.25
 
37
  MOUTH_OPEN_THRESHOLD = 15
38
  FACE_LOST_THRESHOLD = 25
39
 
40
+ # --- GLOBAL STATE VARIABLES ---
41
  EYE_THRESH_COUNTER = 0
42
  DROWSY_COUNTER = 0
43
  drowsy_alert = False
 
48
  HEAD_DOWN_COUNTER = 0
49
  head_down_alert = False
50
 
51
+ # --- FIXED: Audio handling for cloud deployment ---
52
  _audio_initialized = False
53
+ _audio_available = False
 
54
 
55
  def _initialize_audio():
56
+ """Initializes audio only if available (for local deployment)."""
57
+ global _audio_initialized, _audio_available
58
  if _audio_initialized:
59
  return
60
+
61
  try:
62
+ # Check if we're in a cloud environment
63
+ if os.getenv("SPACE_ID") or os.getenv("HUGGINGFACE_SPACE"):
64
+ print("Cloud environment detected - audio disabled")
65
+ _audio_available = False
66
+ else:
67
+ import pygame
68
+ pygame.mixer.init()
69
+ _audio_available = True
70
+ print("Audio initialized successfully.")
71
+ except Exception as e:
72
+ print(f"Audio not available: {e}")
73
+ _audio_available = False
74
+
75
  _audio_initialized = True
76
 
77
+ def play_alarm(sound_file=None):
78
+ """Plays an alarm sound if audio is available."""
79
+ _initialize_audio()
80
+ if not _audio_available:
81
+ return
82
+
83
+ try:
84
+ import pygame
85
+ if sound_file and os.path.exists(sound_file) and not pygame.mixer.get_busy():
86
+ sound = pygame.mixer.Sound(sound_file)
87
+ sound.play()
88
+ except Exception as e:
89
+ print(f"Could not play sound: {e}")
90
 
91
  def generate_alert(final_eye_ratio, final_mouth_ratio):
92
  global EYE_THRESH_COUNTER, YAWN_THRESH_COUNTER, drowsy_alert, yawn_alert, DROWSY_COUNTER, YAWN_COUNTER
93
+
94
  if final_eye_ratio < EYE_ASPECT_RATIO_THRESHOLD:
95
  EYE_THRESH_COUNTER += 1
96
  if EYE_THRESH_COUNTER >= EYE_CLOSED_THRESHOLD and not drowsy_alert:
97
  DROWSY_COUNTER += 1
98
  drowsy_alert = True
99
+ # Try to play sound if available
100
+ drowsiness_sound = os.path.join(script_dir, "drowsiness-detected.mp3")
101
+ Thread(target=play_alarm, args=(drowsiness_sound,)).start()
102
  else:
103
  EYE_THRESH_COUNTER = 0
104
  drowsy_alert = False
 
108
  if YAWN_THRESH_COUNTER >= MOUTH_OPEN_THRESHOLD and not yawn_alert:
109
  YAWN_COUNTER += 1
110
  yawn_alert = True
111
+ # Try to play sound if available
112
+ yawn_sound = os.path.join(script_dir, "yawning-detected.mp3")
113
+ Thread(target=play_alarm, args=(yawn_sound,)).start()
114
  else:
115
  YAWN_THRESH_COUNTER = 0
116
  yawn_alert = False
117
 
118
  def detect_facial_landmarks(x, y, w, h, gray_frame):
119
+ """Detect facial landmarks using dlib predictor."""
120
+ if landmark_predictor is None:
121
+ return None
122
+
123
  face = dlib.rectangle(int(x), int(y), int(x + w), int(y + h))
124
  face_landmarks = landmark_predictor(gray_frame, face)
125
  return face_utils.shape_to_np(face_landmarks)
126
 
127
  def eye_aspect_ratio(eye):
128
+ """Calculate eye aspect ratio."""
129
  A = dist.euclidean(eye[1], eye[5])
130
  B = dist.euclidean(eye[2], eye[4])
131
  C = dist.euclidean(eye[0], eye[3])
132
  return (A + B) / (2.0 * C)
133
 
134
  def final_eye_aspect_ratio(shape):
135
+ """Calculate final eye aspect ratio from both eyes."""
136
  (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
137
  (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]
138
  left_ear = eye_aspect_ratio(shape[lStart:lEnd])
 
140
  return (left_ear + right_ear) / 2.0
141
 
142
  def mouth_aspect_ratio(mouth):
143
+ """Calculate mouth aspect ratio."""
144
  A = dist.euclidean(mouth[2], mouth[10])
145
  B = dist.euclidean(mouth[4], mouth[8])
146
  C = dist.euclidean(mouth[0], mouth[6])
147
  return (A + B) / (2.0 * C)
148
 
149
  def final_mouth_aspect_ratio(shape):
150
+ """Calculate final mouth aspect ratio."""
151
  (mStart, mEnd) = face_utils.FACIAL_LANDMARKS_IDXS["mouth"]
152
  return mouth_aspect_ratio(shape[mStart:mEnd])
153
 
 
167
  # The output frame will have a fixed width of 640px
168
  frame = imutils.resize(frame, width=640)
169
  gray_frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
170
+
171
+ # Detect faces
172
+ faces = face_detector.detectMultiScale(
173
+ gray_frame,
174
+ scaleFactor=1.1,
175
+ minNeighbors=5,
176
+ minSize=(30, 30),
177
+ flags=cv.CASCADE_SCALE_IMAGE
178
+ )
179
 
180
  if len(faces) > 0:
181
  FACE_LOST_COUNTER = 0
182
  head_down_alert = False
183
  (x, y, w, h) = faces[0]
184
+
185
+ # Draw rectangle around face
186
+ cv.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
187
+
188
+ # Detect landmarks if predictor is available
189
  face_landmarks = detect_facial_landmarks(x, y, w, h, gray_frame)
190
+
191
+ if face_landmarks is not None:
192
+ final_ear = final_eye_aspect_ratio(face_landmarks)
193
+ final_mar = final_mouth_aspect_ratio(face_landmarks)
194
+ generate_alert(final_ear, final_mar)
195
+
196
+ # Display ratios
197
+ cv.putText(frame, f"EAR: {final_ear:.2f}", (10, 30), font, 0.7, (0, 0, 255), 2)
198
+ cv.putText(frame, f"MAR: {final_mar:.2f}", (10, 60), font, 0.7, (0, 0, 255), 2)
199
+ else:
200
+ # If no landmarks detected, show warning
201
+ cv.putText(frame, "Landmarks not available", (10, 30), font, 0.7, (0, 0, 255), 2)
202
  else:
203
  FACE_LOST_COUNTER += 1
204
  if FACE_LOST_COUNTER >= FACE_LOST_THRESHOLD and not head_down_alert:
205
  HEAD_DOWN_COUNTER += 1
206
  head_down_alert = True
207
 
208
+ # Draw status information
209
  cv.putText(frame, f"Drowsy: {DROWSY_COUNTER}", (480, 30), font, 0.7, (255, 255, 0), 2)
210
  cv.putText(frame, f"Yawn: {YAWN_COUNTER}", (480, 60), font, 0.7, (255, 255, 0), 2)
211
  cv.putText(frame, f"Head Down: {HEAD_DOWN_COUNTER}", (480, 90), font, 0.7, (255, 255, 0), 2)
212
+
213
+ # Draw alerts
214
+ if drowsy_alert:
215
+ cv.putText(frame, "DROWSINESS ALERT!", (150, 30), font, 0.9, (0, 0, 255), 2)
216
+ if yawn_alert:
217
+ cv.putText(frame, "YAWN ALERT!", (200, 60), font, 0.9, (0, 0, 255), 2)
218
+ if head_down_alert:
219
+ cv.putText(frame, "HEAD NOT VISIBLE!", (180, 90), font, 0.9, (0, 0, 255), 2)
220
 
221
  return frame
222
 
 
227
  parser.add_argument('--input', type=str, help='Input video file path for video mode')
228
  args = parser.parse_args()
229
 
230
+ # Check if landmark predictor is available
231
+ if landmark_predictor is None:
232
+ print("Error: Dlib facial landmark predictor not found!")
233
+ print("Please download shape_predictor_68_face_landmarks.dat")
234
+ exit(1)
235
+
236
  if args.mode == 'webcam':
237
  print("Starting webcam detection... Press 'q' to quit.")
238
  cap = cv.VideoCapture(0)
 
242
  reset_counters()
243
  while True:
244
  ret, frame = cap.read()
245
+ if not ret:
246
+ break
247
  processed_frame = process_frame(frame)
248
  cv.imshow("Live Drowsiness Detection", processed_frame)
249
+ if cv.waitKey(1) & 0xFF == ord('q'):
250
+ break
251
  cap.release()
252
  cv.destroyAllWindows()
253