Skip to content

Commit 8db603d

Browse files
authored
Create main.py
1 parent 8c30769 commit 8db603d

File tree

1 file changed

+279
-0
lines changed
  • AI Projects/Drowsiness Detector

1 file changed

+279
-0
lines changed
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
"""
2+
TODO:
3+
- Improve face landmark detection. Probably caused due to lighting changes. Eliminate the effect of lightinh with minimal computation.
4+
Solved by histogram equalization
5+
- Stabilize face landmark points
6+
- Gaze direction
7+
"""
8+
9+
import dlib
10+
import sys
11+
import cv2
12+
import time
13+
import numpy as np
14+
from scipy.spatial import distance as dist
15+
from threading import Thread
16+
import playsound
17+
import queue
18+
# from light_variability import adjust_gamma
19+
20+
FACE_DOWNSAMPLE_RATIO = 1.5
21+
RESIZE_HEIGHT = 460
22+
23+
thresh = 0.3
24+
modelPath = "models/shape_predictor_70_face_landmarks.dat"
25+
sound_path = "alarm.wav"
26+
27+
detector = dlib.get_frontal_face_detector()
28+
predictor = dlib.shape_predictor(modelPath)
29+
30+
leftEyeIndex = [36, 37, 38, 39, 40, 41]
31+
rightEyeIndex = [42, 43, 44, 45, 46, 47]
32+
33+
blinkCount = 0
34+
drowsy = 0
35+
state = 0
36+
blinkTime = 0.15 #150ms
37+
drowsyTime = 1.0 #1200ms
38+
ALARM_ON = False
39+
GAMMA = 1.5
40+
threadStatusQ = queue.Queue()
41+
42+
invGamma = 1.0/GAMMA
43+
table = np.array([((i / 255.0) ** invGamma) * 255 for i in range(0, 256)]).astype("uint8")
44+
45+
def gamma_correction(image):
46+
return cv2.LUT(image, table)
47+
48+
def histogram_equalization(image):
49+
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
50+
return cv2.equalizeHist(gray)
51+
52+
def soundAlert(path, threadStatusQ):
53+
while True:
54+
if not threadStatusQ.empty():
55+
FINISHED = threadStatusQ.get()
56+
if FINISHED:
57+
break
58+
playsound.playsound(path)
59+
60+
def eye_aspect_ratio(eye):
61+
A = dist.euclidean(eye[1], eye[5])
62+
B = dist.euclidean(eye[2], eye[4])
63+
C = dist.euclidean(eye[0], eye[3])
64+
ear = (A + B) / (2.0 * C)
65+
66+
return ear
67+
68+
69+
def checkEyeStatus(landmarks):
70+
mask = np.zeros(frame.shape[:2], dtype = np.float32)
71+
72+
hullLeftEye = []
73+
for i in range(0, len(leftEyeIndex)):
74+
hullLeftEye.append((landmarks[leftEyeIndex[i]][0], landmarks[leftEyeIndex[i]][1]))
75+
76+
cv2.fillConvexPoly(mask, np.int32(hullLeftEye), 255)
77+
78+
hullRightEye = []
79+
for i in range(0, len(rightEyeIndex)):
80+
hullRightEye.append((landmarks[rightEyeIndex[i]][0], landmarks[rightEyeIndex[i]][1]))
81+
82+
83+
cv2.fillConvexPoly(mask, np.int32(hullRightEye), 255)
84+
85+
# lenLeftEyeX = landmarks[leftEyeIndex[3]][0] - landmarks[leftEyeIndex[0]][0]
86+
# lenLeftEyeY = landmarks[leftEyeIndex[3]][1] - landmarks[leftEyeIndex[0]][1]
87+
88+
# lenLeftEyeSquared = (lenLeftEyeX ** 2) + (lenLeftEyeY ** 2)
89+
# eyeRegionCount = cv2.countNonZero(mask)
90+
91+
# normalizedCount = eyeRegionCount/np.float32(lenLeftEyeSquared)
92+
93+
#############################################################################
94+
leftEAR = eye_aspect_ratio(hullLeftEye)
95+
rightEAR = eye_aspect_ratio(hullRightEye)
96+
97+
ear = (leftEAR + rightEAR) / 2.0
98+
#############################################################################
99+
100+
eyeStatus = 1 # 1 -> Open, 0 -> closed
101+
if (ear < thresh):
102+
eyeStatus = 0
103+
104+
return eyeStatus
105+
106+
def checkBlinkStatus(eyeStatus):
107+
global state, blinkCount, drowsy
108+
if(state >= 0 and state <= falseBlinkLimit):
109+
if(eyeStatus):
110+
state = 0
111+
112+
else:
113+
state += 1
114+
115+
elif(state >= falseBlinkLimit and state < drowsyLimit):
116+
if(eyeStatus):
117+
blinkCount += 1
118+
state = 0
119+
120+
else:
121+
state += 1
122+
123+
124+
else:
125+
if(eyeStatus):
126+
state = 0
127+
drowsy = 1
128+
blinkCount += 1
129+
130+
else:
131+
drowsy = 1
132+
133+
def getLandmarks(im):
134+
imSmall = cv2.resize(im, None,
135+
fx = 1.0/FACE_DOWNSAMPLE_RATIO,
136+
fy = 1.0/FACE_DOWNSAMPLE_RATIO,
137+
interpolation = cv2.INTER_LINEAR)
138+
139+
rects = detector(imSmall, 0)
140+
if len(rects) == 0:
141+
return 0
142+
143+
newRect = dlib.rectangle(int(rects[0].left() * FACE_DOWNSAMPLE_RATIO),
144+
int(rects[0].top() * FACE_DOWNSAMPLE_RATIO),
145+
int(rects[0].right() * FACE_DOWNSAMPLE_RATIO),
146+
int(rects[0].bottom() * FACE_DOWNSAMPLE_RATIO))
147+
148+
points = []
149+
[points.append((p.x, p.y)) for p in predictor(im, newRect).parts()]
150+
return points
151+
152+
capture = cv2.VideoCapture(0)
153+
154+
for i in range(10):
155+
ret, frame = capture.read()
156+
157+
totalTime = 0.0
158+
validFrames = 0
159+
dummyFrames = 100
160+
161+
print("Caliberation in Progress!")
162+
while(validFrames < dummyFrames):
163+
validFrames += 1
164+
t = time.time()
165+
ret, frame = capture.read()
166+
height, width = frame.shape[:2]
167+
IMAGE_RESIZE = np.float32(height)/RESIZE_HEIGHT
168+
frame = cv2.resize(frame, None,
169+
fx = 1/IMAGE_RESIZE,
170+
fy = 1/IMAGE_RESIZE,
171+
interpolation = cv2.INTER_LINEAR)
172+
173+
# adjusted = gamma_correction(frame)
174+
adjusted = histogram_equalization(frame)
175+
176+
landmarks = getLandmarks(adjusted)
177+
timeLandmarks = time.time() - t
178+
179+
if landmarks == 0:
180+
validFrames -= 1
181+
cv2.putText(frame, "Unable to detect face, Please check proper lighting", (10, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
182+
cv2.putText(frame, "or decrease FACE_DOWNSAMPLE_RATIO", (10, 50), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
183+
cv2.imshow("Blink Detection Demo", frame)
184+
if cv2.waitKey(1) & 0xFF == 27:
185+
sys.exit()
186+
187+
else:
188+
totalTime += timeLandmarks
189+
# cv2.putText(frame, "Caliberation in Progress", (200, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
190+
# cv2.imshow("Blink Detection Demo", frame)
191+
192+
# if cv2.waitKey(1) & 0xFF == 27:
193+
# sys.exit()
194+
195+
print("Caliberation Complete!")
196+
197+
spf = totalTime/dummyFrames
198+
print("Current SPF (seconds per frame) is {:.2f} ms".format(spf * 1000))
199+
200+
drowsyLimit = drowsyTime/spf
201+
falseBlinkLimit = blinkTime/spf
202+
print("drowsy limit: {}, false blink limit: {}".format(drowsyLimit, falseBlinkLimit))
203+
204+
if __name__ == "__main__":
205+
vid_writer = cv2.VideoWriter('output-low-light-2.avi',cv2.VideoWriter_fourcc('M','J','P','G'), 15, (frame.shape[1],frame.shape[0]))
206+
while(1):
207+
try:
208+
t = time.time()
209+
ret, frame = capture.read()
210+
height, width = frame.shape[:2]
211+
IMAGE_RESIZE = np.float32(height)/RESIZE_HEIGHT
212+
frame = cv2.resize(frame, None,
213+
fx = 1/IMAGE_RESIZE,
214+
fy = 1/IMAGE_RESIZE,
215+
interpolation = cv2.INTER_LINEAR)
216+
217+
# adjusted = gamma_correction(frame)
218+
adjusted = histogram_equalization(frame)
219+
220+
landmarks = getLandmarks(adjusted)
221+
if landmarks == 0:
222+
validFrames -= 1
223+
cv2.putText(frame, "Unable to detect face, Please check proper lighting", (10, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
224+
cv2.putText(frame, "or decrease FACE_DOWNSAMPLE_RATIO", (10, 50), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
225+
cv2.imshow("Blink Detection Demo", frame)
226+
if cv2.waitKey(1) & 0xFF == 27:
227+
break
228+
continue
229+
230+
eyeStatus = checkEyeStatus(landmarks)
231+
checkBlinkStatus(eyeStatus)
232+
233+
for i in range(0, len(leftEyeIndex)):
234+
cv2.circle(frame, (landmarks[leftEyeIndex[i]][0], landmarks[leftEyeIndex[i]][1]), 1, (0, 0, 255), -1, lineType=cv2.LINE_AA)
235+
236+
for i in range(0, len(rightEyeIndex)):
237+
cv2.circle(frame, (landmarks[rightEyeIndex[i]][0], landmarks[rightEyeIndex[i]][1]), 1, (0, 0, 255), -1, lineType=cv2.LINE_AA)
238+
239+
if drowsy:
240+
cv2.putText(frame, "! ! ! DROWSINESS ALERT ! ! !", (70, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
241+
if not ALARM_ON:
242+
ALARM_ON = True
243+
threadStatusQ.put(not ALARM_ON)
244+
thread = Thread(target=soundAlert, args=(sound_path, threadStatusQ,))
245+
thread.setDaemon(True)
246+
thread.start()
247+
248+
else:
249+
cv2.putText(frame, "Blinks : {}".format(blinkCount), (460, 80), cv2.FONT_HERSHEY_COMPLEX, 0.8, (0,0,255), 2, cv2.LINE_AA)
250+
# (0, 400)
251+
ALARM_ON = False
252+
253+
254+
cv2.imshow("Blink Detection Demo", frame)
255+
vid_writer.write(frame)
256+
257+
k = cv2.waitKey(1)
258+
if k == ord('r'):
259+
state = 0
260+
drowsy = 0
261+
ALARM_ON = False
262+
threadStatusQ.put(not ALARM_ON)
263+
264+
elif k == 27:
265+
break
266+
267+
# print("Time taken", time.time() - t)
268+
269+
except Exception as e:
270+
print(e)
271+
272+
capture.release()
273+
vid_writer.release()
274+
cv2.destroyAllWindows()
275+
Footer
276+
© 2022 GitHub, Inc.
277+
Footer navigation
278+
Terms
279+
Privacy

0 commit comments

Comments
 (0)