File size: 5,383 Bytes
19f420a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0bcaa4e
929f736
0bcaa4e
19f420a
 
0bcaa4e
 
 
 
 
 
 
 
 
19f420a
 
 
 
 
 
 
 
 
 
 
 
 
 
45edd80
19f420a
0bcaa4e
19f420a
 
 
 
 
0bcaa4e
 
 
 
 
 
 
 
 
 
19f420a
0bcaa4e
19f420a
 
 
 
5030574
19f420a
0bcaa4e
 
19f420a
 
 
0bcaa4e
 
ed2439c
0bcaa4e
 
 
 
 
 
ed2439c
929f736
0bcaa4e
19f420a
 
 
 
 
f1c9a67
0bcaa4e
 
 
19f420a
 
 
 
 
 
 
 
 
 
 
0bcaa4e
19f420a
 
0bcaa4e
19f420a
 
 
0bcaa4e
19f420a
0bcaa4e
 
 
 
 
 
19f420a
0bcaa4e
 
 
f37553c
0bcaa4e
 
 
 
 
 
 
 
 
 
 
929f736
0bcaa4e
19f420a
 
0bcaa4e
 
19f420a
 
0bcaa4e
19f420a
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# drive_paddy/pages/1_Live_Detection.py
import streamlit as st
from streamlit_webrtc import webrtc_streamer, RTCConfiguration, VideoProcessorBase
import yaml
import av
import os
from dotenv import load_dotenv
import base64
import queue
import time

from src.detection.factory import get_detector
from src.alerting.alert_system import get_alerter

# --- Load Configuration and Environment Variables ---
@st.cache_resource
def load_app_config():
    """Loads config from yaml and .env files."""
    load_dotenv()
    gemini_api_key = os.getenv("GEMINI_API_KEY")
    # Navigate up to the root to find the config file
    config_path = "/config.yaml" if os.path.exists("/config.yaml") else "config.yaml"
    with open(config_path, 'r') as f:
        config = yaml.safe_load(f)
    return config, gemini_api_key

config, gemini_api_key = load_app_config()

# --- Initialize Session State (if not already done in main.py) ---
if "play_audio" not in st.session_state:
    st.session_state.play_audio = None
if "active_alerts" not in st.session_state:
    st.session_state.active_alerts = {"status": "Awake"}

# --- Client-Side Audio Playback Function ---
def autoplay_audio(audio_bytes: bytes):
    """Injects HTML to autoplay audio in the user's browser."""
    b64 = base64.b64encode(audio_bytes).decode()
    md = f"""
        <audio controls autoplay="true" style="display:none;">
        <source src="data:audio/mp3;base64,{b64}" type="audio/mp3">
        </audio>
        """
    st.markdown(md, unsafe_allow_html=True)

# --- WebRTC Video Processor ---
class VideoProcessor(VideoProcessorBase):
    def __init__(self):
        self._detector = get_detector(config)
        self._alerter = get_alerter(config, gemini_api_key)

    def recv(self, frame: av.VideoFrame) -> av.VideoFrame:
        img = frame.to_ndarray(format="bgr24")
        
        strategy = config.get('detection_strategy')
        if strategy == 'hybrid':
            processed_frame, alert_triggered, active_alerts = self._detector.process_frame(img)
            st.session_state.active_alerts = active_alerts if alert_triggered else {"status": "Awake"}
        else: # Fallback for simpler strategies
            processed_frame, indicators = self._detector.process_frame(img)
            alert_triggered = any(indicators.values())
            st.session_state.active_alerts = indicators if alert_triggered else {"status": "Awake"}

        if alert_triggered:
            audio_data = self._alerter.trigger_alert()
            if audio_data:
                st.session_state.play_audio = audio_data
        else:
            self._alerter.reset_alert()
            
        return av.VideoFrame.from_ndarray(processed_frame, format="bgr24")

# --- Page UI ---
# The st.set_page_config() call has been removed from this file.
# The configuration from main.py will apply to this page.
st.title("๐Ÿ“น Live Drowsiness Detection")
st.info("Press 'START' to activate your camera and begin monitoring.")

# --- Robust RTC Configuration ---
# Provide a list of STUN servers for better reliability.
RTC_CONFIGURATION = RTCConfiguration({
    "iceServers": [
        {"urls": ["stun:stun.l.google.com:19302"]},
        {"urls": ["stun:stun1.l.google.com:19302"]},
        {"urls": ["stun:stun2.l.google.com:19302"]},
        {"urls": ["stun:stun.services.mozilla.com:3478"]},
    ]
})


col1, col2 = st.columns([3, 1])

with col1:
    webrtc_ctx = webrtc_streamer(
        key="drowsiness-detection",
        video_processor_factory=VideoProcessor,
        rtc_configuration=RTC_CONFIGURATION, # Use the new robust configuration
        media_stream_constraints={"video": True, "audio": False},
        async_processing=True,
    )

with col2:
    st.header("System Status")
    if not webrtc_ctx.state.playing:
        st.warning("System Inactive.")
    else:
        st.success("โœ… System Active & Monitoring")

    st.subheader("Live Status:")
    status_placeholder = st.empty()
    audio_placeholder = st.empty()

if webrtc_ctx.state.playing:
    # --- Polling Loop ---
    try:
        status_result = st.session_state.status_queue.get(timeout=0.1)
    except queue.Empty:
        status_result = None

    # Check for new audio alerts
    try:
        audio_data = st.session_state.audio_queue.get(timeout=0.1)
    except queue.Empty:
        audio_data = None
    
    with status_placeholder.container():
        # Persist the last known status if there's no new one
        if status_result:
            st.session_state.last_status = status_result
        
        last_status = getattr(st.session_state, 'last_status', {"status": "Awake"})
        
        if last_status.get("Low Light"):
             st.warning("โš ๏ธ Low Light Detected! Accuracy may be affected.")
        elif last_status.get("status") == "Awake":
            st.info("โœ”๏ธ Driver is Awake")
        else:
            st.error("๐Ÿšจ DROWSINESS DETECTED!")
            for key, value in last_status.items():
                if key != "Low Light":
                    st.warning(f"-> {key}: {value:.2f}" if isinstance(value, float) else f"-> {key}")
    
    if audio_data:
        with audio_placeholder.container():
            autoplay_audio(audio_data)
    
    # Force a rerun to keep the polling active
    time.sleep(0.1)
    st.rerun()

else:
    with status_placeholder.container():
        st.info("โœ”๏ธ Driver is Awake")