Spaces:
Sleeping
Sleeping
Update pages/1_Live_Detection.py
Browse files- pages/1_Live_Detection.py +64 -94
pages/1_Live_Detection.py
CHANGED
@@ -6,45 +6,31 @@ import av
|
|
6 |
import os
|
7 |
from dotenv import load_dotenv
|
8 |
import base64
|
9 |
-
import requests
|
10 |
import queue
|
11 |
import time
|
12 |
-
from typing import List, Dict, Union
|
13 |
|
14 |
-
# Correctly import from the drive_paddy package structure
|
15 |
from src.detection.factory import get_detector
|
16 |
from src.alerting.alert_system import get_alerter
|
17 |
|
18 |
-
# --- Initialize Session State at the TOP of the script ---
|
19 |
-
# This is the single source of truth for our queues and must run on every page load.
|
20 |
-
if "status_queue" not in st.session_state:
|
21 |
-
st.session_state.status_queue = queue.Queue()
|
22 |
-
if "audio_queue" not in st.session_state:
|
23 |
-
st.session_state.audio_queue = queue.Queue()
|
24 |
-
if "last_status" not in st.session_state:
|
25 |
-
st.session_state.last_status = {"drowsiness_level": "Awake", "lighting": "Good"}
|
26 |
-
|
27 |
-
|
28 |
# --- Load Configuration and Environment Variables ---
|
29 |
@st.cache_resource
|
30 |
def load_app_config():
|
31 |
"""Loads config from yaml and .env files."""
|
32 |
load_dotenv()
|
|
|
33 |
# Navigate up to the root to find the config file
|
34 |
-
config_path = "
|
35 |
with open(config_path, 'r') as f:
|
36 |
config = yaml.safe_load(f)
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
config, secrets = load_app_config()
|
48 |
|
49 |
# --- Client-Side Audio Playback Function ---
|
50 |
def autoplay_audio(audio_bytes: bytes):
|
@@ -59,83 +45,62 @@ def autoplay_audio(audio_bytes: bytes):
|
|
59 |
|
60 |
# --- WebRTC Video Processor ---
|
61 |
class VideoProcessor(VideoProcessorBase):
|
62 |
-
# The __init__ method now accepts the queues as arguments
|
63 |
def __init__(self):
|
64 |
-
# It uses the queues passed in from session_state, not new ones.
|
65 |
-
self.status_queue = queue.Queue
|
66 |
-
self.audio_queue = queue.Queue
|
67 |
self._detector = get_detector(config)
|
68 |
-
self._alerter = get_alerter(config,
|
69 |
|
70 |
def recv(self, frame: av.VideoFrame) -> av.VideoFrame:
|
71 |
img = frame.to_ndarray(format="bgr24")
|
72 |
|
73 |
strategy = config.get('detection_strategy')
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
if
|
83 |
-
audio_data = self._alerter.trigger_alert(
|
84 |
if audio_data:
|
85 |
-
|
86 |
-
self.audio_queue.put(audio_data)
|
87 |
else:
|
88 |
self._alerter.reset_alert()
|
89 |
|
90 |
return av.VideoFrame.from_ndarray(processed_frame, format="bgr24")
|
91 |
|
92 |
# --- Page UI ---
|
|
|
|
|
93 |
st.title("📹 Live Drowsiness Detection")
|
94 |
st.info("Press 'START' to activate your camera and begin monitoring.")
|
95 |
|
|
|
|
|
96 |
RTC_CONFIGURATION = RTCConfiguration({
|
97 |
-
|
98 |
-
|
99 |
-
"urls": ["stun:
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
"username": "086986a440b20fe48229738b",
|
104 |
-
"credential": "mOC7fVSg00zjlsTD",
|
105 |
-
},
|
106 |
-
{
|
107 |
-
"urls": ["turn:global.relay.metered.ca:80?transport=tcp"],
|
108 |
-
"username": "086986a440b20fe48229738b",
|
109 |
-
"credential": "mOC7fVSg00zjlsTD",
|
110 |
-
},
|
111 |
-
{
|
112 |
-
"urls": ["turn:global.relay.metered.ca:443"],
|
113 |
-
"username": "086986a440b20fe48229738b",
|
114 |
-
"credential": "mOC7fVSg00zjlsTD",
|
115 |
-
},
|
116 |
-
{
|
117 |
-
"urls": ["turns:global.relay.metered.ca:443?transport=tcp"],
|
118 |
-
"username": "086986a440b20fe48229738b",
|
119 |
-
"credential": "mOC7fVSg00zjlsTD",
|
120 |
-
},
|
121 |
-
]
|
122 |
})
|
123 |
|
|
|
124 |
col1, col2 = st.columns([3, 1])
|
125 |
|
126 |
with col1:
|
127 |
webrtc_ctx = webrtc_streamer(
|
128 |
key="drowsiness-detection",
|
129 |
-
# The factory now correctly passes the queues from session_state
|
130 |
video_processor_factory=VideoProcessor,
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
)
|
135 |
|
136 |
with col2:
|
137 |
st.header("System Status")
|
138 |
-
audio_placeholder = st.empty()
|
139 |
if not webrtc_ctx.state.playing:
|
140 |
st.warning("System Inactive.")
|
141 |
else:
|
@@ -143,41 +108,46 @@ with col2:
|
|
143 |
|
144 |
st.subheader("Live Status:")
|
145 |
status_placeholder = st.empty()
|
|
|
146 |
|
147 |
if webrtc_ctx.state.playing:
|
|
|
148 |
try:
|
149 |
-
# This now reads from the correct queue that the processor is writing to.
|
150 |
status_result = st.session_state.status_queue.get(timeout=0.1)
|
151 |
-
st.session_state.last_status = status_result
|
152 |
except queue.Empty:
|
153 |
-
|
154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
with status_placeholder.container():
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
score = last_status.get("details", {}).get("Score", 0)
|
160 |
-
|
161 |
-
st.metric(label="Lighting Condition", value=lighting)
|
162 |
-
if lighting == "Low":
|
163 |
-
st.warning("Detection paused due to low light.")
|
164 |
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
elif
|
170 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
171 |
|
172 |
-
|
173 |
-
audio_data = st.session_state.audio_queue.get(timeout=0.1)
|
174 |
with audio_placeholder.container():
|
175 |
autoplay_audio(audio_data)
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
time.sleep(0.1)
|
180 |
st.rerun()
|
|
|
181 |
else:
|
182 |
with status_placeholder.container():
|
183 |
st.info("✔️ Driver is Awake")
|
|
|
6 |
import os
|
7 |
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 |
# --- Load Configuration and Environment Variables ---
|
16 |
@st.cache_resource
|
17 |
def load_app_config():
|
18 |
"""Loads config from yaml and .env files."""
|
19 |
load_dotenv()
|
20 |
+
gemini_api_key = os.getenv("GEMINI_API_KEY")
|
21 |
# Navigate up to the root to find the config file
|
22 |
+
config_path = "/config.yaml" if os.path.exists("/config.yaml") else "config.yaml"
|
23 |
with open(config_path, 'r') as f:
|
24 |
config = yaml.safe_load(f)
|
25 |
+
return config, gemini_api_key
|
26 |
+
|
27 |
+
config, gemini_api_key = load_app_config()
|
28 |
+
|
29 |
+
# --- Initialize Session State (if not already done in main.py) ---
|
30 |
+
if "play_audio" not in st.session_state:
|
31 |
+
st.session_state.play_audio = None
|
32 |
+
if "active_alerts" not in st.session_state:
|
33 |
+
st.session_state.active_alerts = {"status": "Awake"}
|
|
|
|
|
34 |
|
35 |
# --- Client-Side Audio Playback Function ---
|
36 |
def autoplay_audio(audio_bytes: bytes):
|
|
|
45 |
|
46 |
# --- WebRTC Video Processor ---
|
47 |
class VideoProcessor(VideoProcessorBase):
|
|
|
48 |
def __init__(self):
|
|
|
|
|
|
|
49 |
self._detector = get_detector(config)
|
50 |
+
self._alerter = get_alerter(config, gemini_api_key)
|
51 |
|
52 |
def recv(self, frame: av.VideoFrame) -> av.VideoFrame:
|
53 |
img = frame.to_ndarray(format="bgr24")
|
54 |
|
55 |
strategy = config.get('detection_strategy')
|
56 |
+
if strategy == 'hybrid':
|
57 |
+
processed_frame, alert_triggered, active_alerts = self._detector.process_frame(img)
|
58 |
+
st.session_state.active_alerts = active_alerts if alert_triggered else {"status": "Awake"}
|
59 |
+
else: # Fallback for simpler strategies
|
60 |
+
processed_frame, indicators = self._detector.process_frame(img)
|
61 |
+
alert_triggered = any(indicators.values())
|
62 |
+
st.session_state.active_alerts = indicators if alert_triggered else {"status": "Awake"}
|
63 |
+
|
64 |
+
if alert_triggered:
|
65 |
+
audio_data = self._alerter.trigger_alert()
|
66 |
if audio_data:
|
67 |
+
st.session_state.play_audio = audio_data
|
|
|
68 |
else:
|
69 |
self._alerter.reset_alert()
|
70 |
|
71 |
return av.VideoFrame.from_ndarray(processed_frame, format="bgr24")
|
72 |
|
73 |
# --- Page UI ---
|
74 |
+
# The st.set_page_config() call has been removed from this file.
|
75 |
+
# The configuration from main.py will apply to this page.
|
76 |
st.title("📹 Live Drowsiness Detection")
|
77 |
st.info("Press 'START' to activate your camera and begin monitoring.")
|
78 |
|
79 |
+
# --- Robust RTC Configuration ---
|
80 |
+
# Provide a list of STUN servers for better reliability.
|
81 |
RTC_CONFIGURATION = RTCConfiguration({
|
82 |
+
"iceServers": [
|
83 |
+
{"urls": ["stun:stun.l.google.com:19302"]},
|
84 |
+
{"urls": ["stun:stun1.l.google.com:19302"]},
|
85 |
+
{"urls": ["stun:stun2.l.google.com:19302"]},
|
86 |
+
{"urls": ["stun:stun.services.mozilla.com:3478"]},
|
87 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
})
|
89 |
|
90 |
+
|
91 |
col1, col2 = st.columns([3, 1])
|
92 |
|
93 |
with col1:
|
94 |
webrtc_ctx = webrtc_streamer(
|
95 |
key="drowsiness-detection",
|
|
|
96 |
video_processor_factory=VideoProcessor,
|
97 |
+
rtc_configuration=RTC_CONFIGURATION, # Use the new robust configuration
|
98 |
+
media_stream_constraints={"video": True, "audio": False},
|
99 |
+
async_processing=True,
|
100 |
)
|
101 |
|
102 |
with col2:
|
103 |
st.header("System Status")
|
|
|
104 |
if not webrtc_ctx.state.playing:
|
105 |
st.warning("System Inactive.")
|
106 |
else:
|
|
|
108 |
|
109 |
st.subheader("Live Status:")
|
110 |
status_placeholder = st.empty()
|
111 |
+
audio_placeholder = st.empty()
|
112 |
|
113 |
if webrtc_ctx.state.playing:
|
114 |
+
# --- Polling Loop ---
|
115 |
try:
|
|
|
116 |
status_result = st.session_state.status_queue.get(timeout=0.1)
|
|
|
117 |
except queue.Empty:
|
118 |
+
status_result = None
|
119 |
|
120 |
+
# Check for new audio alerts
|
121 |
+
try:
|
122 |
+
audio_data = st.session_state.audio_queue.get(timeout=0.1)
|
123 |
+
except queue.Empty:
|
124 |
+
audio_data = None
|
125 |
+
|
126 |
with status_placeholder.container():
|
127 |
+
# Persist the last known status if there's no new one
|
128 |
+
if status_result:
|
129 |
+
st.session_state.last_status = status_result
|
|
|
|
|
|
|
|
|
|
|
130 |
|
131 |
+
last_status = getattr(st.session_state, 'last_status', {"status": "Awake"})
|
132 |
+
|
133 |
+
if last_status.get("Low Light"):
|
134 |
+
st.warning("⚠️ Low Light Detected! Accuracy may be affected.")
|
135 |
+
elif last_status.get("status") == "Awake":
|
136 |
+
st.info("✔️ Driver is Awake")
|
137 |
+
else:
|
138 |
+
st.error("🚨 DROWSINESS DETECTED!")
|
139 |
+
for key, value in last_status.items():
|
140 |
+
if key != "Low Light":
|
141 |
+
st.warning(f"-> {key}: {value:.2f}" if isinstance(value, float) else f"-> {key}")
|
142 |
|
143 |
+
if audio_data:
|
|
|
144 |
with audio_placeholder.container():
|
145 |
autoplay_audio(audio_data)
|
146 |
+
|
147 |
+
# Force a rerun to keep the polling active
|
|
|
148 |
time.sleep(0.1)
|
149 |
st.rerun()
|
150 |
+
|
151 |
else:
|
152 |
with status_placeholder.container():
|
153 |
st.info("✔️ Driver is Awake")
|