Testimony Adekoya commited on
Commit
929f736
·
1 Parent(s): 2a23c85

Making this commit to retest issues fiX

Browse files
pages/1_Live_Detection.py CHANGED
@@ -8,24 +8,29 @@ from dotenv import load_dotenv
8
  import base64
9
  import queue
10
  import time
 
11
 
 
12
  from src.detection.factory import get_detector
13
  from src.alerting.alert_system import get_alerter
14
 
 
 
15
  if "status_queue" not in st.session_state:
16
  st.session_state.status_queue = queue.Queue()
17
  if "audio_queue" not in st.session_state:
18
  st.session_state.audio_queue = queue.Queue()
19
- if "last_status" not in st.session_state:
20
  st.session_state.last_status = {"drowsiness_level": "Awake", "lighting": "Good"}
21
 
22
-
23
  # --- Load Configuration and Environment Variables ---
24
  @st.cache_resource
25
  def load_app_config():
26
  """Loads config from yaml and .env files."""
27
  load_dotenv()
28
- config_path = "/config.yaml" if os.path.exists("/config.yaml") else "config.yaml"
 
29
  with open(config_path, 'r') as f:
30
  config = yaml.safe_load(f)
31
  # Load secrets from environment
@@ -51,12 +56,13 @@ def autoplay_audio(audio_bytes: bytes):
51
 
52
  # --- WebRTC Video Processor ---
53
  class VideoProcessor(VideoProcessorBase):
54
- def __init__(self):
 
 
 
 
55
  self._detector = get_detector(config)
56
  self._alerter = get_alerter(config, secrets["gemini_api_key"])
57
- # Thread-safe queues for communication
58
- self.status_queue = queue.Queue()
59
- self.audio_queue = queue.Queue()
60
 
61
  def recv(self, frame: av.VideoFrame) -> av.VideoFrame:
62
  img = frame.to_ndarray(format="bgr24")
@@ -64,30 +70,16 @@ class VideoProcessor(VideoProcessorBase):
64
  strategy = config.get('detection_strategy')
65
 
66
  # The return signature of process_frame varies by strategy.
67
- # We need to handle each case correctly.
68
- if strategy == 'hybrid':
69
- processed_frame, alert_triggered, active_alerts = self._detector.process_frame(img)
70
- self.status_queue.put(active_alerts if alert_triggered or 'Low Light' in active_alerts else {"status": "Awake"})
71
- elif strategy == 'geometric':
72
- # The geometric processor returns frame, indicators, and landmarks.
73
- processed_frame, indicators, _ = self._detector.process_frame(img)
74
- alert_triggered = any(v for k, v in indicators.items() if k not in ['low_light', 'details'])
75
- self.status_queue.put(indicators if alert_triggered or indicators.get('low_light') else {"status": "Awake"})
76
- print(f"Indicators: {indicators}") # Debugging line
77
- print(self.status_queue) # Debugging line
78
- elif strategy == 'cnn_model':
79
- # The cnn_model processor returns frame and indicators.
80
- processed_frame, indicators = self._detector.process_frame(img)
81
- alert_triggered = any(indicators.values())
82
- self.status_queue.put(indicators if alert_triggered else {"status": "Awake"})
83
- else:
84
- # Default case if strategy is unknown
85
- processed_frame = img
86
- alert_triggered = False
87
 
88
- if alert_triggered:
89
- audio_data = self._alerter.trigger_alert()
90
  if audio_data:
 
91
  self.audio_queue.put(audio_data)
92
  else:
93
  self._alerter.reset_alert()
@@ -109,19 +101,25 @@ if secrets["turn_username"] and secrets["turn_credential"]:
109
 
110
  RTC_CONFIGURATION = RTCConfiguration({"iceServers": ice_servers})
111
 
 
112
  col1, col2 = st.columns([3, 1])
113
 
114
  with col1:
115
  webrtc_ctx = webrtc_streamer(
116
  key="drowsiness-detection",
117
- video_processor_factory=VideoProcessor,
118
- rtc_configuration=RTC_CONFIGURATION, # Use the new robust configuration
 
 
 
 
119
  media_stream_constraints={"video": True, "audio": False},
120
  async_processing=True,
121
  )
122
 
123
  with col2:
124
  st.header("System Status")
 
125
  if not webrtc_ctx.state.playing:
126
  st.warning("System Inactive.")
127
  else:
@@ -129,14 +127,10 @@ with col2:
129
 
130
  st.subheader("Live Status:")
131
  status_placeholder = st.empty()
132
- audio_placeholder = st.empty()
133
 
134
  if webrtc_ctx.state.playing:
135
- if "last_status" not in st.session_state:
136
- st.session_state.last_status = {"status": "Awake"}
137
-
138
  try:
139
- # This line will now work because st.session_state.status_queue was initialized
140
  status_result = st.session_state.status_queue.get(timeout=0.1)
141
  st.session_state.last_status = status_result
142
  except queue.Empty:
@@ -149,13 +143,16 @@ if webrtc_ctx.state.playing:
149
  score = last_status.get("details", {}).get("Score", 0)
150
 
151
  st.metric(label="Lighting Condition", value=lighting)
152
-
153
- if lighting == "Low": st.warning("Detection paused due to low light.")
154
 
155
- if drowsiness_level == "Awake": st.info(f"✔️ Awake (Score: {score:.2f})")
156
- elif drowsiness_level == "Slightly Drowsy": st.warning(f"⚠️ Slightly Drowsy (Score: {score:.2f})")
157
- elif drowsiness_level == "Very Drowsy": st.error(f"🚨 Very Drowsy! (Score: {score:.2f})")
158
-
 
 
 
159
  try:
160
  audio_data = st.session_state.audio_queue.get(timeout=0.1)
161
  with audio_placeholder.container():
@@ -168,5 +165,3 @@ if webrtc_ctx.state.playing:
168
  else:
169
  with status_placeholder.container():
170
  st.info("✔️ Driver is Awake")
171
-
172
-
 
8
  import base64
9
  import queue
10
  import time
11
+ from typing import List, Dict, Union
12
 
13
+ # Correctly import from the drive_paddy package structure
14
  from src.detection.factory import get_detector
15
  from src.alerting.alert_system import get_alerter
16
 
17
+ # --- Initialize Session State at the TOP of the script ---
18
+ # This is the single source of truth for our queues and must run on every page load.
19
  if "status_queue" not in st.session_state:
20
  st.session_state.status_queue = queue.Queue()
21
  if "audio_queue" not in st.session_state:
22
  st.session_state.audio_queue = queue.Queue()
23
+ if "last_status" not in st.session_state:
24
  st.session_state.last_status = {"drowsiness_level": "Awake", "lighting": "Good"}
25
 
26
+
27
  # --- Load Configuration and Environment Variables ---
28
  @st.cache_resource
29
  def load_app_config():
30
  """Loads config from yaml and .env files."""
31
  load_dotenv()
32
+ # Navigate up to the root to find the config file
33
+ config_path = os.path.join(os.path.dirname(__file__), '..', '..', 'config.yaml')
34
  with open(config_path, 'r') as f:
35
  config = yaml.safe_load(f)
36
  # Load secrets from environment
 
56
 
57
  # --- WebRTC Video Processor ---
58
  class VideoProcessor(VideoProcessorBase):
59
+ # The __init__ method now accepts the queues as arguments
60
+ def __init__(self, status_queue: queue.Queue, audio_queue: queue.Queue):
61
+ # It uses the queues passed in from session_state, not new ones.
62
+ self.status_queue = status_queue
63
+ self.audio_queue = audio_queue
64
  self._detector = get_detector(config)
65
  self._alerter = get_alerter(config, secrets["gemini_api_key"])
 
 
 
66
 
67
  def recv(self, frame: av.VideoFrame) -> av.VideoFrame:
68
  img = frame.to_ndarray(format="bgr24")
 
70
  strategy = config.get('detection_strategy')
71
 
72
  # The return signature of process_frame varies by strategy.
73
+ processed_frame, indicators, _ = self._detector.process_frame(img)
74
+ drowsiness_level = indicators.get("drowsiness_level", "Awake")
75
+
76
+ # This now correctly puts data into the shared session_state queue.
77
+ self.status_queue.put(indicators)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
+ if drowsiness_level != "Awake":
80
+ audio_data = self._alerter.trigger_alert(level=drowsiness_level)
81
  if audio_data:
82
+ # This now correctly puts audio data into the shared queue.
83
  self.audio_queue.put(audio_data)
84
  else:
85
  self._alerter.reset_alert()
 
101
 
102
  RTC_CONFIGURATION = RTCConfiguration({"iceServers": ice_servers})
103
 
104
+
105
  col1, col2 = st.columns([3, 1])
106
 
107
  with col1:
108
  webrtc_ctx = webrtc_streamer(
109
  key="drowsiness-detection",
110
+ # The factory now correctly passes the queues from session_state
111
+ video_processor_factory=lambda: VideoProcessor(
112
+ status_queue=st.session_state.status_queue,
113
+ audio_queue=st.session_state.audio_queue
114
+ ),
115
+ rtc_configuration=RTC_CONFIGURATION,
116
  media_stream_constraints={"video": True, "audio": False},
117
  async_processing=True,
118
  )
119
 
120
  with col2:
121
  st.header("System Status")
122
+ audio_placeholder = st.empty()
123
  if not webrtc_ctx.state.playing:
124
  st.warning("System Inactive.")
125
  else:
 
127
 
128
  st.subheader("Live Status:")
129
  status_placeholder = st.empty()
 
130
 
131
  if webrtc_ctx.state.playing:
 
 
 
132
  try:
133
+ # This now reads from the correct queue that the processor is writing to.
134
  status_result = st.session_state.status_queue.get(timeout=0.1)
135
  st.session_state.last_status = status_result
136
  except queue.Empty:
 
143
  score = last_status.get("details", {}).get("Score", 0)
144
 
145
  st.metric(label="Lighting Condition", value=lighting)
146
+ if lighting == "Low":
147
+ st.warning("Detection paused due to low light.")
148
 
149
+ if drowsiness_level == "Awake":
150
+ st.info(f"✔️ Awake (Score: {score:.2f})")
151
+ elif drowsiness_level == "Slightly Drowsy":
152
+ st.warning(f"⚠️ Slightly Drowsy (Score: {score:.2f})")
153
+ elif drowsiness_level == "Very Drowsy":
154
+ st.error(f"🚨 Very Drowsy! (Score: {score:.2f})")
155
+
156
  try:
157
  audio_data = st.session_state.audio_queue.get(timeout=0.1)
158
  with audio_placeholder.container():
 
165
  else:
166
  with status_placeholder.container():
167
  st.info("✔️ Driver is Awake")
 
 
pages/2_Test_Streamlit.py ADDED
File without changes
src/detection/strategies/geometric.py CHANGED
@@ -3,7 +3,7 @@ import cv2
3
  import mediapipe as mp
4
  import numpy as np
5
  import math
6
- from src.detection.base_processor import BaseProcessor
7
 
8
  # --- Helper Functions (No changes here) ---
9
  def calculate_ear(eye_landmarks, frame_shape):
@@ -37,9 +37,7 @@ class GeometricProcessor(BaseProcessor):
37
  }
38
  face_landmarks = None
39
 
40
- if is_low_light:
41
- drowsiness_indicators["lighting"] = "Low"
42
- else:
43
  img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
44
  results = self.face_mesh.process(img_rgb)
45
  face_landmarks = results.multi_face_landmarks
@@ -49,22 +47,28 @@ class GeometricProcessor(BaseProcessor):
49
  score = 0
50
  weights = self.settings['indicator_weights']
51
 
52
- # Eye Closure
 
 
 
 
 
 
 
 
53
  ear = (calculate_ear([landmarks[i] for i in self.L_EYE],(h,w)) + calculate_ear([landmarks[i] for i in self.R_EYE],(h,w)))/2.0
54
  if ear < self.settings['eye_ar_thresh']: self.counters['eye_closure']+=1
55
  else: self.counters['eye_closure']=0
56
  if self.counters['eye_closure'] >= self.settings['eye_ar_consec_frames']: score += weights['eye_closure']
57
 
58
- # Yawning
59
  mar = calculate_mar([landmarks[i] for i in self.MOUTH], (h, w))
60
  if mar > self.settings['yawn_mar_thresh']: self.counters['yawning']+=1
61
  else: self.counters['yawning']=0
62
  if self.counters['yawning'] >= self.settings['yawn_consec_frames']: score += weights['yawning']
63
 
64
- # Head Pose
65
- face_3d = np.array([[0.0,0.0,0.0], [0.0,-330.0,-65.0], [-225.0,170.0,-135.0], [225.0,170.0,-135.0], [-150.0,-150.0,-125.0], [150.0,-150.0,-125.0]], dtype=np.float64)
66
- face_2d = np.array([(landmarks[1].x*w, landmarks[1].y*h), (landmarks[152].x*w, landmarks[152].y*h), (landmarks[263].x*w, landmarks[263].y*h), (landmarks[33].x*w, landmarks[33].y*h), (landmarks[287].x*w, landmarks[287].y*h), (landmarks[57].x*w, landmarks[57].y*h)], dtype=np.float64)
67
- cam_matrix = np.array([[w,0,w/2],[0,w,h/2],[0,0,1]], dtype=np.float64)
68
  _, rot_vec, _ = cv2.solvePnP(face_3d, face_2d, cam_matrix, np.zeros((4,1),dtype=np.float64))
69
  rmat, _ = cv2.Rodrigues(rot_vec); angles, _, _, _, _, _ = cv2.RQDecomp3x3(rmat)
70
  pitch, yaw = angles[0], angles[1]
@@ -76,13 +80,14 @@ class GeometricProcessor(BaseProcessor):
76
  if abs(yaw) > self.settings['head_look_away_thresh']: self.counters['looking_away']+=1
77
  else: self.counters['looking_away']=0
78
  if self.counters['looking_away'] >= self.settings['head_pose_consec_frames']: score += weights['looking_away']
79
-
80
- # Determine Drowsiness Level
81
  levels = self.settings['drowsiness_levels']
82
  if score >= levels['very_drowsy_threshold']: drowsiness_indicators['drowsiness_level'] = "Very Drowsy"
83
  elif score >= levels['slightly_drowsy_threshold']: drowsiness_indicators['drowsiness_level'] = "Slightly Drowsy"
84
 
85
  drowsiness_indicators['details']['Score'] = score
 
 
86
 
87
  # --- Visualization on Video Frame ---
88
  level = drowsiness_indicators['drowsiness_level']
@@ -98,12 +103,9 @@ class GeometricProcessor(BaseProcessor):
98
  elif level == "Very Drowsy":
99
  color = (0, 0, 255) # Red
100
 
101
- # Draw a colored border around the frame
102
  cv2.rectangle(frame, (0, 0), (w, h), color, 10)
103
-
104
- # Display status text
105
  status_text = f"Status: {level} (Score: {score_val:.2f})"
106
  print(status_text)
107
  cv2.putText(frame, status_text, (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
108
 
109
- return frame, drowsiness_indicators, face_landmarks
 
3
  import mediapipe as mp
4
  import numpy as np
5
  import math
6
+ from ..base_processor import BaseProcessor
7
 
8
  # --- Helper Functions (No changes here) ---
9
  def calculate_ear(eye_landmarks, frame_shape):
 
37
  }
38
  face_landmarks = None
39
 
40
+ if not is_low_light:
 
 
41
  img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
42
  results = self.face_mesh.process(img_rgb)
43
  face_landmarks = results.multi_face_landmarks
 
47
  score = 0
48
  weights = self.settings['indicator_weights']
49
 
50
+ # --- Draw Facial Landmarks (Logic Added Back) ---
51
+ # This will draw the green dots for eyes and mouth.
52
+ eye_mouth_landmarks = self.L_EYE + self.R_EYE + self.MOUTH
53
+ for idx in eye_mouth_landmarks:
54
+ lm = landmarks[idx]
55
+ x, y = int(lm.x * w), int(lm.y * h)
56
+ cv2.circle(frame, (x, y), 1, (0, 255, 0), -1)
57
+
58
+ # --- Drowsiness Calculations ---
59
  ear = (calculate_ear([landmarks[i] for i in self.L_EYE],(h,w)) + calculate_ear([landmarks[i] for i in self.R_EYE],(h,w)))/2.0
60
  if ear < self.settings['eye_ar_thresh']: self.counters['eye_closure']+=1
61
  else: self.counters['eye_closure']=0
62
  if self.counters['eye_closure'] >= self.settings['eye_ar_consec_frames']: score += weights['eye_closure']
63
 
 
64
  mar = calculate_mar([landmarks[i] for i in self.MOUTH], (h, w))
65
  if mar > self.settings['yawn_mar_thresh']: self.counters['yawning']+=1
66
  else: self.counters['yawning']=0
67
  if self.counters['yawning'] >= self.settings['yawn_consec_frames']: score += weights['yawning']
68
 
69
+ face_3d = np.array([[0.0,0.0,0.0],[0.0,-330.0,-65.0],[-225.0,170.0,-135.0],[225.0,170.0,-135.0],[-150.0,-150.0,-125.0],[150.0,-150.0,-125.0]],dtype=np.float64)
70
+ face_2d = np.array([(landmarks[1].x*w,landmarks[1].y*h),(landmarks[152].x*w,landmarks[152].y*h),(landmarks[263].x*w,landmarks[263].y*h),(landmarks[33].x*w,landmarks[33].y*h),(landmarks[287].x*w,landmarks[287].y*h),(landmarks[57].x*w,landmarks[57].y*h)],dtype=np.float64)
71
+ cam_matrix = np.array([[w,0,w/2],[0,w,h/2],[0,0,1]],dtype=np.float64)
 
72
  _, rot_vec, _ = cv2.solvePnP(face_3d, face_2d, cam_matrix, np.zeros((4,1),dtype=np.float64))
73
  rmat, _ = cv2.Rodrigues(rot_vec); angles, _, _, _, _, _ = cv2.RQDecomp3x3(rmat)
74
  pitch, yaw = angles[0], angles[1]
 
80
  if abs(yaw) > self.settings['head_look_away_thresh']: self.counters['looking_away']+=1
81
  else: self.counters['looking_away']=0
82
  if self.counters['looking_away'] >= self.settings['head_pose_consec_frames']: score += weights['looking_away']
83
+
 
84
  levels = self.settings['drowsiness_levels']
85
  if score >= levels['very_drowsy_threshold']: drowsiness_indicators['drowsiness_level'] = "Very Drowsy"
86
  elif score >= levels['slightly_drowsy_threshold']: drowsiness_indicators['drowsiness_level'] = "Slightly Drowsy"
87
 
88
  drowsiness_indicators['details']['Score'] = score
89
+ else: # is_low_light is True
90
+ drowsiness_indicators["lighting"] = "Low"
91
 
92
  # --- Visualization on Video Frame ---
93
  level = drowsiness_indicators['drowsiness_level']
 
103
  elif level == "Very Drowsy":
104
  color = (0, 0, 255) # Red
105
 
 
106
  cv2.rectangle(frame, (0, 0), (w, h), color, 10)
 
 
107
  status_text = f"Status: {level} (Score: {score_val:.2f})"
108
  print(status_text)
109
  cv2.putText(frame, status_text, (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
110
 
111
+ return frame, drowsiness_indicators, face_landmarks