tanujd commited on
Commit
e27048d
·
1 Parent(s): dc0c7a3
README.md CHANGED
@@ -1,12 +1 @@
1
- ---
2
- title: Aps
3
- emoji: 💻
4
- colorFrom: gray
5
- colorTo: indigo
6
- sdk: streamlit
7
- sdk_version: 1.17.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ # aps-streamlit-share
 
 
 
 
 
 
 
 
 
 
 
additional/requirements.txt ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py==1.4.0
2
+ aioice==0.7.6
3
+ aiortc==1.4.0
4
+ altair==4.2.2
5
+ attrs==22.2.0
6
+ av==10.0.0
7
+ blinker==1.5
8
+ cachetools==5.3.0
9
+ certifi==2022.12.7
10
+ cffi==1.15.1
11
+ charset-normalizer==3.0.1
12
+ click==8.1.3
13
+ colorama==0.4.6
14
+ contourpy==1.0.7
15
+ cryptography==38.0.4
16
+ cycler==0.11.0
17
+ decorator==5.1.1
18
+ Deprecated==1.2.13
19
+ dlib
20
+ dnspython==2.3.0
21
+ entrypoints==0.4
22
+ flatbuffers==23.1.21
23
+ fonttools==4.38.0
24
+ gcloud==0.18.3
25
+ gitdb==4.0.10
26
+ GitPython==3.1.30
27
+ google-crc32c==1.5.0
28
+ googleapis-common-protos==1.58.0
29
+ httplib2==0.21.0
30
+ idna==3.4
31
+ importlib-metadata==6.0.0
32
+ imutils==0.5.4
33
+ Jinja2==3.1.2
34
+ jsonschema==4.17.3
35
+ jwcrypto==1.4.2
36
+ kiwisolver==1.4.4
37
+ markdown-it-py==2.1.0
38
+ MarkupSafe==2.1.2
39
+ matplotlib==3.6.3
40
+ mdurl==0.1.2
41
+ mediapipe==0.9.1.0
42
+ netifaces==0.11.0
43
+ numpy==1.24.1
44
+ oauth2client==4.1.3
45
+ opencv-contrib-python==4.7.0.68
46
+ opencv-python==4.7.0.68
47
+ packaging==23.0
48
+ pandas==1.5.3
49
+ Pillow==9.4.0
50
+ protobuf==3.20.3
51
+ pyarrow==11.0.0
52
+ pyasn1==0.4.8
53
+ pyasn1-modules==0.2.8
54
+ pycparser==2.21
55
+ pycryptodome==3.17
56
+ pydeck==0.8.0
57
+ pydub==0.25.1
58
+ pyee==9.0.4
59
+ Pygments==2.14.0
60
+ pylibsrtp==0.8.0
61
+ pymongo==4.3.3
62
+ Pympler==1.0.1
63
+ pyOpenSSL==23.0.0
64
+ pyparsing==3.0.9
65
+ Pyrebase4==4.6.0
66
+ pyrsistent==0.19.3
67
+ python-dateutil==2.8.2
68
+ python-jwt==4.0.0
69
+ pytz==2022.7.1
70
+ pytz-deprecation-shim==0.1.0.post0
71
+ requests==2.28.2
72
+ requests-toolbelt==0.10.1
73
+ rich==13.3.1
74
+ rsa==4.9
75
+ scipy==1.10.0
76
+ semver==2.13.0
77
+ six==1.16.0
78
+ smmap==5.0.0
79
+ streamlit==1.17.0
80
+ streamlit-nested-layout==0.1.1
81
+ streamlit-option-menu==0.3.2
82
+ streamlit-webrtc==0.44.2
83
+ toml==0.10.2
84
+ toolz==0.12.0
85
+ tornado==6.2
86
+ typing_extensions==4.4.0
87
+ tzdata==2022.7
88
+ tzlocal==4.2
89
+ urllib3==1.26.14
90
+ validators==0.20.0
91
+ watchdog==2.2.1
92
+ wincertstore==0.2
93
+ wrapt==1.14.1
94
+ zipp==3.12.0
app.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import threading
3
+
4
+ import av
5
+ import bcrypt
6
+ import pymongo
7
+ import streamlit as st
8
+ from streamlit_option_menu import option_menu
9
+ from streamlit_webrtc import VideoHTMLAttributes, webrtc_streamer
10
+ from twilio.rest import Client
11
+
12
+ from audio_handling import AudioFrameHandler
13
+ from drowsy_detection import VideoFrameHandler
14
+
15
+ # Define the audio file to use.
16
+ alarm_file_path = os.path.join("audio", "wake_up.wav")
17
+ logged_in = False
18
+
19
+ def update_sliders():
20
+ slider_wait = 1.0
21
+ eye_thresh = 0.18
22
+ lip_thresh = 0.2
23
+
24
+ client = pymongo.MongoClient("mongodb+srv://admin:Admin123@aps.agcjjww.mongodb.net/?retryWrites=true&w=majority")
25
+ db = client["aps-db"]
26
+ slider_values = db["slider-values"]
27
+
28
+ if slider_wait != WAIT_TIME:
29
+ slider_values.update_one({"slider_name": "Wait_Time"}, {"$set": {"value": WAIT_TIME}}, upsert=True)
30
+ if eye_thresh != EAR_THRESH:
31
+ slider_values.update_one({"slider_name": "Eye_Threshold"}, {"$set": {"value": EAR_THRESH}}, upsert=True)
32
+ if lip_thresh != LIP_THRESH:
33
+ slider_values.update_one({"slider_name": "Lip_Threshold"}, {"$set": {"value": LIP_THRESH}}, upsert=True)
34
+
35
+ # Streamlit Components
36
+ st.set_page_config(
37
+ page_title="Drowsiness Detection | APS",
38
+ page_icon="https://framerusercontent.com/modules/466qV2P53XLpEfUjjmZC/FNnZTYISEsUGPpjII54W/assets/VebAVoINVBFxBTpsrQVLHznVo.png",
39
+ initial_sidebar_state="expanded",
40
+ layout="wide",
41
+ )
42
+
43
+ menu_choice = option_menu(
44
+ menu_title=None,
45
+ options=["Home", "Login", "Signup", "OTP Login", "About"],
46
+ icons=["house", "box-arrow-in-right", "pencil-square","telephone", "question-circle"],
47
+ menu_icon="cast",
48
+ default_index=0,
49
+ orientation="horizontal",
50
+ )
51
+
52
+ if menu_choice == "Home":
53
+ st.title("Drowsiness Detection")
54
+ with st.container():
55
+ c1, c2, c3 = st.columns(spec=[1, 1, 1])
56
+ with c1:
57
+ # The amount of time (in seconds) to wait before sounding the alarm.
58
+ WAIT_TIME = st.slider("Time to wait before sounding alarm:", 0.0, 5.0, 1.0, 0.25)
59
+
60
+ with c2:
61
+ # Lowest valid value of Eye Aspect Ratio. Ideal values [0.15, 0.2].
62
+ EAR_THRESH = st.slider("Eye Aspect Ratio threshold:", 0.0, 0.4, 0.18, 0.01)
63
+
64
+ with c3:
65
+ # Lip threshold to detect yawning
66
+ LIP_THRESH = st.slider("Lip threshold:", 0.0, 0.4, 0.2, 0.01)
67
+ LIP_THRESH = LIP_THRESH*100
68
+
69
+ thresholds = {
70
+ "EAR_THRESH": EAR_THRESH,
71
+ "WAIT_TIME": WAIT_TIME,
72
+ "LIP_THRESH": LIP_THRESH
73
+ }
74
+ update_sliders()
75
+
76
+ # For streamlit-webrtc
77
+ video_handler = VideoFrameHandler()
78
+ audio_handler = AudioFrameHandler(sound_file_path=alarm_file_path)
79
+
80
+ lock = threading.Lock() # For thread-safe access & to prevent race-condition.
81
+ shared_state = {"play_alarm": False} # Shared state between callbacks.
82
+
83
+ def video_frame_callback(frame: av.VideoFrame):
84
+ frame = frame.to_ndarray(format="bgr24") # Decode and convert frame to RGB
85
+
86
+ frame, play_alarm = video_handler.process(frame, thresholds) # Process frame
87
+ with lock:
88
+ shared_state["play_alarm"] = play_alarm # Update shared state
89
+
90
+ return av.VideoFrame.from_ndarray(frame, format="bgr24") # Encode and return BGR frame
91
+
92
+ def audio_frame_callback(frame: av.AudioFrame):
93
+ with lock: # access the current “play_alarm” state
94
+ play_alarm = shared_state["play_alarm"]
95
+
96
+ new_frame: av.AudioFrame = audio_handler.process(frame, play_sound=play_alarm)
97
+ return new_frame
98
+
99
+ ctx = webrtc_streamer(
100
+ key="drowsiness-detection",
101
+ video_frame_callback=video_frame_callback,
102
+ audio_frame_callback=audio_frame_callback,
103
+ rtc_configuration={"iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]}, # Add this to config for cloud deployment.
104
+ media_stream_constraints={"video": {"height": {"ideal": 480}}, "audio": True},
105
+ video_html_attrs=VideoHTMLAttributes(autoPlay=True, controls=False, muted=False),
106
+ )
107
+
108
+ #handling login
109
+
110
+ if menu_choice == "Login":
111
+ st.text('Login')
112
+ email = st.text_input('Enter email')
113
+ password = st.text_input('Enter password', type="password")
114
+ if st.button('Login'):
115
+ try:
116
+ client = pymongo.MongoClient("mongodb+srv://admin:Admin123@aps.agcjjww.mongodb.net/?retryWrites=true&w=majority")
117
+ db = client["aps-db"]
118
+ users = db["users"]
119
+ user = users.find_one({"email": email})
120
+ if user:
121
+ if bcrypt.checkpw(password.encode(), user["password"]):
122
+ st.success("Successfully logged in!")
123
+ menu_choice = "Home"
124
+ os.environ["email"] = email
125
+ os.environ["user_id"] = str(user["_id"])
126
+ os.environ["logged_in"] = "True"
127
+ else:
128
+ st.error("Invalid password")
129
+ else:
130
+ st.error("Email not found")
131
+ except Exception as e:
132
+ st.error("An error occurred: {}".format(str(e)))
133
+
134
+ if menu_choice == "Signup":
135
+ st.text('Signup')
136
+ email = st.text_input('Enter email')
137
+ password = st.text_input('Enter password', type="password")
138
+ if st.button('Signup'):
139
+ try:
140
+ client = pymongo.MongoClient("mongodb+srv://admin:Admin123@aps.agcjjww.mongodb.net/?retryWrites=true&w=majority")
141
+ db = client["aps-db"]
142
+ users = db["users"]
143
+ if users.find_one({"email": email}):
144
+ st.error("Email already exists")
145
+ else:
146
+ hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
147
+ users.insert_one({"email": email, "password": hashed_password})
148
+ st.success("Successfully signed up!")
149
+ except Exception as e:
150
+ st.error("An error occurred: {}".format(str(e)))
151
+
152
+ #phone number login
153
+ account_sid = "AC97fd9a03c4637fe246adcecc613bb153"
154
+ auth_token = os.environ["TWILIO_AUTH_TOKEN"]
155
+ verify_sid = "VA629a29a82eedcde1e6c89e5f586fdbfd"
156
+
157
+ if menu_choice == "OTP Login":
158
+ st.text("Please enter country code followed by phone number")
159
+ st.text("For example: +919255520023")
160
+ verified_number = st.text_input("Enter your phone number")
161
+
162
+ client = Client(account_sid, auth_token)
163
+ otp_sent = False
164
+ if st.button("Send OTP"):
165
+ verification = client.verify.v2.services(verify_sid) \
166
+ .verifications \
167
+ .create(to=verified_number, channel="sms")
168
+ if (verification.status == "pending" or verification.status == "started"):
169
+ st.success("OTP sent successfully to " + verified_number)
170
+ otp_sent = True
171
+ else:
172
+ st.error("Error sending OTP")
173
+
174
+ otp_code = st.text_input("Please enter the OTP:",type="password")
175
+ if st.button("Verify OTP"):
176
+ verification_check = client.verify.v2.services(verify_sid) \
177
+ .verification_checks \
178
+ .create(to=verified_number, code=otp_code)
179
+ if (verification_check.status == "approved"):
180
+ st.success("OTP Verified")
181
+ client = pymongo.MongoClient("mongodb+srv://admin:Admin123@aps.agcjjww.mongodb.net/?retryWrites=true&w=majority")
182
+ db = client["aps-db"]
183
+ users = db["users"]
184
+ users.insert_one({"phone": verified_number})
185
+ else:
186
+ st.error("OTP Verification Failed")
187
+
188
+
189
+ hide_streamlit_style = """
190
+ <style>
191
+ #MainMenu {visibility: hidden;}
192
+ footer {visibility: hidden;}
193
+ </style>
194
+ """
195
+ st.markdown(hide_streamlit_style, unsafe_allow_html=True)
audio/wake_up.wav ADDED
Binary file (28.9 kB). View file
 
audio_handling.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import av
2
+ import numpy as np
3
+ from pydub import AudioSegment
4
+
5
+
6
+ class AudioFrameHandler:
7
+ """To play/pass custom audio based on some event"""
8
+
9
+ def __init__(self, sound_file_path: str = ""):
10
+
11
+ self.custom_audio = AudioSegment.from_file(file=sound_file_path, format="wav")
12
+ self.custom_audio_len = len(self.custom_audio)
13
+
14
+ self.ms_per_audio_segment: int = 20
15
+ self.audio_segment_shape: tuple
16
+
17
+ self.play_state_tracker: dict = {"curr_segment": -1} # Currently playing segment
18
+ self.audio_segments_created: bool = False
19
+ self.audio_segments: list = []
20
+
21
+ def prepare_audio(self, frame: av.AudioFrame):
22
+ raw_samples = frame.to_ndarray()
23
+ sound = AudioSegment(
24
+ data=raw_samples.tobytes(),
25
+ sample_width=frame.format.bytes,
26
+ frame_rate=frame.sample_rate,
27
+ channels=len(frame.layout.channels),
28
+ )
29
+
30
+ self.ms_per_audio_segment = len(sound)
31
+ self.audio_segment_shape = raw_samples.shape
32
+
33
+ self.custom_audio = self.custom_audio.set_channels(sound.channels)
34
+ self.custom_audio = self.custom_audio.set_frame_rate(sound.frame_rate)
35
+ self.custom_audio = self.custom_audio.set_sample_width(sound.sample_width)
36
+
37
+ self.audio_segments = [
38
+ self.custom_audio[i : i + self.ms_per_audio_segment]
39
+ for i in range(0, self.custom_audio_len - self.custom_audio_len % self.ms_per_audio_segment, self.ms_per_audio_segment)
40
+ ]
41
+ self.total_segments = len(self.audio_segments) - 1 # -1 because we start from 0.
42
+
43
+ self.audio_segments_created = True
44
+
45
+ def process(self, frame: av.AudioFrame, play_sound: bool = False):
46
+
47
+ """
48
+ Takes in the current input audio frame and based on play_sound boolean value
49
+ either starts sending the custom audio frame or dampens the frame wave to emulate silence.
50
+
51
+ For eg. playing a notification based on some event.
52
+ """
53
+
54
+ if not self.audio_segments_created:
55
+ self.prepare_audio(frame)
56
+
57
+ raw_samples = frame.to_ndarray()
58
+ _curr_segment = self.play_state_tracker["curr_segment"]
59
+
60
+ if play_sound:
61
+ if _curr_segment < self.total_segments:
62
+ _curr_segment += 1
63
+ else:
64
+ _curr_segment = 0
65
+
66
+ sound = self.audio_segments[_curr_segment]
67
+
68
+ else:
69
+ if -1 < _curr_segment < self.total_segments:
70
+ _curr_segment += 1
71
+ sound = self.audio_segments[_curr_segment]
72
+ else:
73
+ _curr_segment = -1
74
+ sound = AudioSegment(
75
+ data=raw_samples.tobytes(),
76
+ sample_width=frame.format.bytes,
77
+ frame_rate=frame.sample_rate,
78
+ channels=len(frame.layout.channels),
79
+ )
80
+ sound = sound.apply_gain(-100)
81
+
82
+ self.play_state_tracker["curr_segment"] = _curr_segment
83
+
84
+ channel_sounds = sound.split_to_mono()
85
+ channel_samples = [s.get_array_of_samples() for s in channel_sounds]
86
+
87
+ new_samples = np.array(channel_samples).T
88
+
89
+ new_samples = new_samples.reshape(self.audio_segment_shape)
90
+ new_frame = av.AudioFrame.from_ndarray(new_samples, layout=frame.layout.name)
91
+ new_frame.sample_rate = frame.sample_rate
92
+
93
+ return new_frame
drowsy_detection.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+
4
+ import cv2
5
+ import dlib
6
+ import mediapipe as mp
7
+ import numpy as np
8
+ import pymongo
9
+ import pyrebase
10
+ import streamlit as st
11
+ from bson.objectid import ObjectId
12
+ from imutils import face_utils
13
+ from mediapipe.python.solutions.drawing_utils import \
14
+ _normalized_to_pixel_coordinates as denormalize_coordinates
15
+ from scipy.spatial import distance as dist
16
+
17
+ config = {"apiKey": "AIzaSyCNPBcskQFs2tn5UfdFbP8LzbnEMIarsWc",
18
+ "authDomain": "aps-csia.firebaseapp.com",
19
+ "databaseURL": "https://aps-csia-default-rtdb.asia-southeast1.firebasedatabase.app/",
20
+ "projectId": "aps-csia",
21
+ "storageBucket": "aps-csia.appspot.com",
22
+ "messagingSenderId": "1069559357849",
23
+ "appId": "1:1069559357849:web:39e9d0139d42a206973308",
24
+ "measurementId": "G-FVTG7XGLN7"}
25
+
26
+ firebase = pyrebase.initialize_app(config)
27
+ db = firebase.database()
28
+
29
+ print("-> Loading the predictor and detector...")
30
+ detector = cv2.CascadeClassifier("./haarcascade_frontalface_default.xml") #Faster but less accurate
31
+ predictor = dlib.shape_predictor('./shape_predictor_68_face_landmarks.dat')
32
+
33
+ def get_mediapipe_app(
34
+ max_num_faces=1,
35
+ refine_landmarks=True,
36
+ min_detection_confidence=0.5,
37
+ min_tracking_confidence=0.5,
38
+ ):
39
+ """Initialize and return Mediapipe FaceMesh Solution Graph object"""
40
+ face_mesh = mp.solutions.face_mesh.FaceMesh(
41
+ max_num_faces=max_num_faces,
42
+ refine_landmarks=refine_landmarks,
43
+ min_detection_confidence=min_detection_confidence,
44
+ min_tracking_confidence=min_tracking_confidence,
45
+ )
46
+
47
+ return face_mesh
48
+
49
+ def distance(point_1, point_2):
50
+ """Calculate l2-norm between two points"""
51
+ dist = sum([(i - j) ** 2 for i, j in zip(point_1, point_2)]) ** 0.5
52
+ return dist
53
+
54
+
55
+ def get_ear(landmarks, refer_idxs, frame_width, frame_height):
56
+ """
57
+ Calculate Eye Aspect Ratio for one eye.
58
+
59
+ Args:
60
+ landmarks: (list) Detected landmarks list
61
+ refer_idxs: (list) Index positions of the chosen landmarks
62
+ in order P1, P2, P3, P4, P5, P6
63
+ frame_width: (int) Width of captured frame
64
+ frame_height: (int) Height of captured frame
65
+
66
+ Returns:
67
+ ear: (float) Eye aspect ratio
68
+ """
69
+ try:
70
+ # Compute the euclidean distance between the horizontal
71
+ coords_points = []
72
+ for i in refer_idxs:
73
+ lm = landmarks[i]
74
+ coord = denormalize_coordinates(lm.x, lm.y, frame_width, frame_height)
75
+ coords_points.append(coord)
76
+
77
+ # Eye landmark (x, y)-coordinates
78
+ P1_P4 = dist.euclidean(coords_points[0], coords_points[3])
79
+ P2_P6 = dist.euclidean(coords_points[1], coords_points[5])
80
+ P3_P5 = dist.euclidean(coords_points[2], coords_points[4])
81
+
82
+ # Compute the eye aspect ratio
83
+ ear = (P2_P6 + P3_P5) / (2.0 * P1_P4)
84
+
85
+ except:
86
+ ear = 0.0
87
+ coords_points = None
88
+
89
+ return ear, coords_points
90
+
91
+
92
+ def calculate_avg_ear(landmarks, left_eye_idxs, right_eye_idxs, image_w, image_h):
93
+ # Calculate Eye aspect ratio
94
+
95
+ left_ear, left_lm_coordinates = get_ear(landmarks, left_eye_idxs, image_w, image_h)
96
+ right_ear, right_lm_coordinates = get_ear(landmarks, right_eye_idxs, image_w, image_h)
97
+ Avg_EAR = (left_ear + right_ear) / 2.0
98
+
99
+ return Avg_EAR, (left_lm_coordinates, right_lm_coordinates)
100
+
101
+
102
+ def plot_eye_landmarks(frame, left_lm_coordinates, right_lm_coordinates, color):
103
+ for lm_coordinates in [left_lm_coordinates, right_lm_coordinates]:
104
+ if lm_coordinates:
105
+ for coord in lm_coordinates:
106
+ cv2.circle(frame, coord, 2, color, -1)
107
+
108
+ return frame
109
+
110
+ def lip_distance(shape):
111
+ top_lip = shape[50:53]
112
+ top_lip = np.concatenate((top_lip, shape[61:64]))
113
+
114
+ low_lip = shape[56:59]
115
+ low_lip = np.concatenate((low_lip, shape[65:68]))
116
+
117
+ top_mean = np.mean(top_lip, axis=0)
118
+ low_mean = np.mean(low_lip, axis=0)
119
+
120
+ distance = abs(top_mean[1] - low_mean[1])
121
+ return distance
122
+
123
+
124
+ def plot_text(image, text, origin, color, font=cv2.FONT_HERSHEY_SIMPLEX, fntScale=0.8, thickness=2):
125
+ image = cv2.putText(image, text, origin, font, fntScale, color, thickness)
126
+ return image
127
+
128
+ class VideoFrameHandler:
129
+ def __init__(self):
130
+ """
131
+ Initialize the necessary constants, mediapipe app
132
+ and tracker variables
133
+ """
134
+ # Left and right eye chosen landmarks.
135
+ self.count_drowsy = 0
136
+ self.count_yawn = 0
137
+ self.eye_idxs = {
138
+ "left": [362, 385, 387, 263, 373, 380],
139
+ "right": [33, 160, 158, 133, 153, 144],
140
+ }
141
+
142
+ # Used for coloring landmark points.
143
+ # Its value depends on the current EAR value.
144
+ self.RED = (0, 0, 255) # BGR
145
+ self.GREEN = (0, 255, 0) # BGR
146
+
147
+ # Initializing Mediapipe FaceMesh solution pipeline
148
+ self.facemesh_model = get_mediapipe_app()
149
+
150
+ # For tracking counters and sharing states in and out of callbacks.
151
+ self.state_tracker = {
152
+ "start_time": time.perf_counter(),
153
+ "DROWSY_TIME": 0.0, # Holds the amount of time passed with EAR < EAR_THRESH
154
+ "COLOR": self.GREEN,
155
+ "play_alarm": False,
156
+ }
157
+
158
+ self.EAR_txt_pos = (10, 30)
159
+
160
+ def process(self, frame: np.array, thresholds: dict):
161
+ """
162
+ This function is used to implement our Drowsy detection algorithm
163
+
164
+ Args:
165
+ frame: (np.array) Input frame matrix.
166
+ thresholds: (dict) Contains the two threshold values
167
+ WAIT_TIME and EAR_THRESH.
168
+
169
+ Returns:
170
+ The processed frame and a boolean flag to
171
+ indicate if the alarm should be played or not.
172
+ """
173
+
174
+ # To improve performance,
175
+ # mark the frame as not writeable to pass by reference.
176
+ frame = cv2.flip(frame,1)
177
+ frame.flags.writeable = False
178
+ frame_h, frame_w, _ = frame.shape
179
+
180
+ DROWSY_TIME_txt_pos = (10, int(frame_h // 2 * 1.7))
181
+ ALM_txt_pos = (10, int(frame_h // 2 * 1.85))
182
+
183
+ #frame = cv2.flip(frame, 1)
184
+ results = self.facemesh_model.process(frame)
185
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
186
+ rects = detector.detectMultiScale(gray, scaleFactor=1.1,
187
+ minNeighbors=5, minSize=(30, 30),
188
+ flags=cv2.CASCADE_SCALE_IMAGE)
189
+ for (x, y, w, h) in rects:
190
+ rect = dlib.rectangle(int(x), int(y), int(x + w),int(y + h))
191
+
192
+ shape = predictor(gray, rect)
193
+ shape = face_utils.shape_to_np(shape)
194
+
195
+ distance = lip_distance(shape)
196
+ lip = shape[48:60]
197
+ cv2.drawContours(frame, [lip], -1, (0, 255, 0), 1)
198
+ if (distance > thresholds["LIP_THRESH"]):
199
+ plot_text(frame, "Yawn Alert", (460, 440), (0, 0, 255))
200
+ time.sleep(1)
201
+ self.count_yawn += 1
202
+ if os.environ.get("logged_in") == "True":
203
+ user_id = os.environ.get("user_id")
204
+ email = os.environ.get("email")
205
+ client = pymongo.MongoClient("mongodb+srv://admin:Admin123@aps.agcjjww.mongodb.net/?retryWrites=true&w=majority")
206
+ db = client["aps-db"]
207
+ users = db["count"]
208
+ user = users.find_one({"_id": ObjectId(user_id)})
209
+ if user:
210
+ users.update_one({"_id": ObjectId(user_id)}, {"$set": {"count_yawn": self.count_yawn, "email": email}})
211
+ else:
212
+ users.insert_one({"_id": ObjectId(user_id), "count_yawn": self.count_yawn, "email": email})
213
+ else:
214
+ st.error("Not logged in")
215
+ frame = plot_text(frame, f"Yawn count: {self.count_yawn}",(420,410), color=(0, 255, 0), thickness=2)
216
+
217
+ if results.multi_face_landmarks:
218
+ landmarks = results.multi_face_landmarks[0].landmark
219
+ EAR, coordinates = calculate_avg_ear(landmarks, self.eye_idxs["left"], self.eye_idxs["right"], frame_w, frame_h)
220
+ frame = plot_eye_landmarks(frame, coordinates[0], coordinates[1], self.state_tracker["COLOR"])
221
+
222
+ if EAR < thresholds["EAR_THRESH"]:
223
+
224
+ # Increase DROWSY_TIME to track the time period with EAR less than the threshold
225
+ # and reset the start_time for the next iteration.
226
+ end_time = time.perf_counter()
227
+
228
+ self.state_tracker["DROWSY_TIME"] += end_time - self.state_tracker["start_time"]
229
+ self.state_tracker["start_time"] = end_time
230
+ self.state_tracker["COLOR"] = self.RED
231
+
232
+ if self.state_tracker["DROWSY_TIME"] >= thresholds["WAIT_TIME"]:
233
+ self.state_tracker["play_alarm"] = True
234
+ plot_text(frame, "WAKE UP! WAKE UP", ALM_txt_pos, self.state_tracker["COLOR"])
235
+ time.sleep(1)
236
+ self.count_drowsy += 1
237
+ if os.environ.get("logged_in") == "True":
238
+ user_id = os.environ.get("user_id")
239
+ email = os.environ.get("email")
240
+ client = pymongo.MongoClient("mongodb+srv://admin:Admin123@aps.agcjjww.mongodb.net/?retryWrites=true&w=majority")
241
+ db = client["aps-db"]
242
+ users = db["count"]
243
+ user = users.find_one({"_id": ObjectId(user_id)})
244
+ if user:
245
+ users.update_one({"_id": ObjectId(user_id)}, {"$set": {"count_drowsy": self.count_drowsy, "email": email}})
246
+ else:
247
+ users.insert_one({"_id": ObjectId(user_id), "count_drowsy": self.count_drowsy, "email": email})
248
+ else:
249
+ self.state_tracker["start_time"] = time.perf_counter()
250
+ self.state_tracker["DROWSY_TIME"] = 0.0
251
+ self.state_tracker["COLOR"] = self.GREEN
252
+ self.state_tracker["play_alarm"] = False
253
+
254
+ EAR_txt = f"EAR: {round(EAR, 2)}"
255
+ DROWSY_TIME_txt = f"DROWSY: {round(self.state_tracker['DROWSY_TIME'], 3)} Secs"
256
+ plot_text(frame, EAR_txt, self.EAR_txt_pos, self.state_tracker["COLOR"])
257
+ plot_text(frame, DROWSY_TIME_txt, DROWSY_TIME_txt_pos, self.state_tracker["COLOR"])
258
+ frame = plot_text(frame, f"Drowsy count: {self.count_drowsy}",(400,30), color=(0, 255, 0), thickness=2)
259
+
260
+ else:
261
+ self.state_tracker["start_time"] = time.perf_counter()
262
+ self.state_tracker["DROWSY_TIME"] = 0.0
263
+ self.state_tracker["COLOR"] = self.GREEN
264
+ self.state_tracker["play_alarm"] = False
265
+
266
+ return frame, self.state_tracker["play_alarm"]
environment.yml ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: CSIA_APS
2
+ channels:
3
+ - anaconda
4
+ - conda-forge
5
+ - defaults
6
+ dependencies:
7
+ - bzip2=1.0.8=he774522_0
8
+ - ca-certificates=2022.12.7=h5b45459_0
9
+ - certifi=2022.12.7=pyhd8ed1ab_0
10
+ - cmake=3.22.1=h9ad04ae_0
11
+ - console_shortcut=0.1.1=4
12
+ - dlib=19.24.0=py310he5227f5_0
13
+ - intel-openmp=2023.0.0=h57928b3_25922
14
+ - jpeg=9e=h8ffe710_2
15
+ - libblas=3.9.0=8_mkl
16
+ - libcblas=3.9.0=8_mkl
17
+ - libffi=3.4.2=hd77b12b_6
18
+ - liblapack=3.9.0=8_mkl
19
+ - libpng=1.6.37=h2a8f88b_0
20
+ - libuv=1.40.0=he774522_0
21
+ - lz4-c=1.9.3=h2bbff1b_1
22
+ - mkl=2020.4=hb70f87d_311
23
+ - openssl=1.1.1s=h2bbff1b_0
24
+ - pip=22.3.1=py310haa95532_0
25
+ - python=3.10.9=h966fe2a_0
26
+ - python_abi=3.10=2_cp310
27
+ - setuptools=65.6.3=py310haa95532_0
28
+ - sqlite=3.40.1=h2bbff1b_0
29
+ - tk=8.6.12=h2bbff1b_0
30
+ - vc=14.2=h21ff451_1
31
+ - vs2015_runtime=14.27.29016=h5e58377_2
32
+ - wheel=0.37.1=pyhd3eb1b0_0
33
+ - wincertstore=0.2=py310haa95532_2
34
+ - xz=5.2.10=h8cc25b3_1
35
+ - zlib=1.2.13=h8cc25b3_0
36
+ - zstd=1.5.2=h19a0ad4_0
37
+ - pip:
38
+ - absl-py==1.4.0
39
+ - aioice==0.7.6
40
+ - aiortc==1.4.0
41
+ - altair==4.2.2
42
+ - attrs==22.2.0
43
+ - av==10.0.0
44
+ - blinker==1.5
45
+ - cachetools==5.3.0
46
+ - cffi==1.15.1
47
+ - charset-normalizer==3.0.1
48
+ - click==8.1.3
49
+ - colorama==0.4.6
50
+ - contourpy==1.0.7
51
+ - cryptography==38.0.4
52
+ - cycler==0.11.0
53
+ - decorator==5.1.1
54
+ - deprecated==1.2.13
55
+ - dnspython==2.3.0
56
+ - entrypoints==0.4
57
+ - flatbuffers==23.1.21
58
+ - fonttools==4.38.0
59
+ - gcloud==0.18.3
60
+ - gitdb==4.0.10
61
+ - gitpython==3.1.30
62
+ - google-crc32c==1.5.0
63
+ - googleapis-common-protos==1.58.0
64
+ - httplib2==0.21.0
65
+ - idna==3.4
66
+ - importlib-metadata==6.0.0
67
+ - imutils==0.5.4
68
+ - jinja2==3.1.2
69
+ - jsonschema==4.17.3
70
+ - jwcrypto==1.4.2
71
+ - kiwisolver==1.4.4
72
+ - markdown-it-py==2.1.0
73
+ - markupsafe==2.1.2
74
+ - matplotlib==3.6.3
75
+ - mdurl==0.1.2
76
+ - mediapipe==0.9.1.0
77
+ - netifaces==0.11.0
78
+ - numpy==1.24.1
79
+ - oauth2client==4.1.3
80
+ - opencv-contrib-python==4.7.0.68
81
+ - opencv-python==4.7.0.68
82
+ - packaging==23.0
83
+ - pandas==1.5.3
84
+ - pillow==9.4.0
85
+ - protobuf==3.20.3
86
+ - pyarrow==11.0.0
87
+ - pyasn1==0.4.8
88
+ - pyasn1-modules==0.2.8
89
+ - pycparser==2.21
90
+ - pycryptodome==3.17
91
+ - pydeck==0.8.0
92
+ - pydub==0.25.1
93
+ - pyee==9.0.4
94
+ - pygments==2.14.0
95
+ - pylibsrtp==0.8.0
96
+ - pymongo==4.3.3
97
+ - pympler==1.0.1
98
+ - pyopenssl==23.0.0
99
+ - pyparsing==3.0.9
100
+ - pyrebase4==4.6.0
101
+ - pyrsistent==0.19.3
102
+ - python-dateutil==2.8.2
103
+ - python-jwt==4.0.0
104
+ - pytz==2022.7.1
105
+ - pytz-deprecation-shim==0.1.0.post0
106
+ - requests==2.28.2
107
+ - requests-toolbelt==0.10.1
108
+ - rich==13.3.1
109
+ - rsa==4.9
110
+ - scipy==1.10.0
111
+ - semver==2.13.0
112
+ - six==1.16.0
113
+ - smmap==5.0.0
114
+ - streamlit==1.17.0
115
+ - streamlit-nested-layout==0.1.1
116
+ - streamlit-option-menu==0.3.2
117
+ - streamlit-webrtc==0.44.2
118
+ - toml==0.10.2
119
+ - toolz==0.12.0
120
+ - tornado==6.2
121
+ - typing-extensions==4.4.0
122
+ - tzdata==2022.7
123
+ - tzlocal==4.2
124
+ - urllib3==1.26.14
125
+ - validators==0.20.0
126
+ - watchdog==2.2.1
127
+ - wrapt==1.14.1
128
+ - zipp==3.12.0
129
+ prefix: C:\ProgramData\Anaconda3\envs\CSIA_APS
haarcascade_frontalface_default.xml ADDED
The diff for this file is too large to render. See raw diff
 
packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ cmake
requirements.txt ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py==1.4.0
2
+ aioice==0.7.6
3
+ aiortc==1.4.0
4
+ altair==4.2.2
5
+ attrs==22.2.0
6
+ av==10.0.0
7
+ blinker==1.5
8
+ cachetools==5.3.0
9
+ certifi==2022.12.7
10
+ cffi==1.15.1
11
+ charset-normalizer==3.0.1
12
+ click==8.1.3
13
+ colorama==0.4.6
14
+ contourpy==1.0.7
15
+ cryptography==38.0.4
16
+ cycler==0.11.0
17
+ decorator==5.1.1
18
+ Deprecated==1.2.13
19
+ dlib
20
+ dnspython==2.3.0
21
+ entrypoints==0.4
22
+ flatbuffers==23.1.21
23
+ fonttools==4.38.0
24
+ gcloud==0.18.3
25
+ gitdb==4.0.10
26
+ GitPython==3.1.30
27
+ google-crc32c==1.5.0
28
+ googleapis-common-protos==1.58.0
29
+ httplib2==0.21.0
30
+ idna==3.4
31
+ importlib-metadata==6.0.0
32
+ imutils==0.5.4
33
+ Jinja2==3.1.2
34
+ jsonschema==4.17.3
35
+ jwcrypto==1.4.2
36
+ kiwisolver==1.4.4
37
+ markdown-it-py==2.1.0
38
+ MarkupSafe==2.1.2
39
+ matplotlib==3.6.3
40
+ mdurl==0.1.2
41
+ mediapipe==0.9.1.0
42
+ netifaces==0.11.0
43
+ numpy==1.24.1
44
+ oauth2client==4.1.3
45
+ opencv-contrib-python==4.7.0.68
46
+ opencv-python==4.7.0.68
47
+ packaging==23.0
48
+ pandas==1.5.3
49
+ Pillow==9.4.0
50
+ protobuf==3.20.3
51
+ pyarrow==11.0.0
52
+ pyasn1==0.4.8
53
+ pyasn1-modules==0.2.8
54
+ pycparser==2.21
55
+ pycryptodome==3.17
56
+ pydeck==0.8.0
57
+ pydub==0.25.1
58
+ pyee==9.0.4
59
+ Pygments==2.14.0
60
+ pylibsrtp==0.8.0
61
+ pymongo==4.3.3
62
+ Pympler==1.0.1
63
+ pyOpenSSL==23.0.0
64
+ pyparsing==3.0.9
65
+ Pyrebase4==4.6.0
66
+ pyrsistent==0.19.3
67
+ python-dateutil==2.8.2
68
+ python-jwt==4.0.0
69
+ pytz==2022.7.1
70
+ pytz-deprecation-shim==0.1.0.post0
71
+ requests==2.28.2
72
+ requests-toolbelt==0.10.1
73
+ rich==13.3.1
74
+ rsa==4.9
75
+ scipy==1.10.0
76
+ semver==2.13.0
77
+ six==1.16.0
78
+ smmap==5.0.0
79
+ streamlit==1.17.0
80
+ streamlit-nested-layout==0.1.1
81
+ streamlit-option-menu==0.3.2
82
+ streamlit-webrtc==0.44.2
83
+ toml==0.10.2
84
+ toolz==0.12.0
85
+ tornado==6.2
86
+ typing_extensions==4.4.0
87
+ tzdata==2022.7
88
+ tzlocal==4.2
89
+ urllib3==1.26.14
90
+ validators==0.20.0
91
+ watchdog==2.2.1
92
+ wincertstore==0.2
93
+ wrapt==1.14.1
94
+ zipp==3.12.0