tstone87 commited on
Commit
3b7f151
·
verified ·
1 Parent(s): 1058404

Create app.oy

Browse files
Files changed (1) hide show
  1. app.oy +191 -0
app.oy ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import streamlit as st
3
+ from ultralytics import YOLO
4
+ import time
5
+ import numpy as np
6
+ from datetime import datetime
7
+ import pytz
8
+
9
+ # Page config and header
10
+ st.set_page_config(
11
+ page_title="Fire Watch: AI-Powered Fire and Smoke Detection",
12
+ page_icon="🔥",
13
+ layout="wide",
14
+ initial_sidebar_state="expanded"
15
+ )
16
+ st.title("Fire Watch: Fire Detection with an AI Vision Model")
17
+
18
+ # --- Session State Initialization ---
19
+ if "streams" not in st.session_state:
20
+ st.session_state.streams = []
21
+ if "num_streams" not in st.session_state:
22
+ st.session_state.num_streams = 1
23
+ if "confidence" not in st.session_state:
24
+ st.session_state.confidence = 0.30 # default 30%
25
+ if "target_fps" not in st.session_state:
26
+ st.session_state.target_fps = 1.0 # default 1 FPS
27
+
28
+ # --- Default URLs and Names for 10 Streams ---
29
+ default_m3u8_urls = [
30
+ "https://publicstreamer4.cotrip.org/rtplive/070E27890CAM1RHS/playlist.m3u8", # EB at i270
31
+ "https://publicstreamer2.cotrip.org/rtplive/070E27555CAM1RP1/playlist.m3u8", # EB @ York St Denver
32
+ "https://publicstreamer1.cotrip.org/rtplive/225N00535CAM1RP1/playlist.m3u8", # NB at Iliff Denver
33
+ "https://publicstreamer2.cotrip.org/rtplive/070W28220CAM1RHS/playlist.m3u8", # WB Half Mile West of I225 Denver
34
+ "https://publicstreamer1.cotrip.org/rtplive/070W26805CAM1RHS/playlist.m3u8", # 1 mile E of Kipling Denver
35
+ "https://publicstreamer4.cotrip.org/rtplive/076W03150CAM1RP1/playlist.m3u8", # Main St Hudson
36
+ "https://publicstreamer2.cotrip.org/rtplive/070E27660CAM1NEC/playlist.m3u8", # EB Colorado Blvd i70 Denver
37
+ "https://publicstreamer2.cotrip.org/rtplive/070W27475CAM1RHS/playlist.m3u8", # E of Washington St Denver
38
+ "https://publicstreamer3.cotrip.org/rtplive/070W28155CAM1RHS/playlist.m3u8", # WB Peroia St Underpass Denver
39
+ "https://publicstreamer3.cotrip.org/rtplive/070E11660CAM1RHS/playlist.m3u8" # Grand Ave Glenwood
40
+ ]
41
+ default_names = [
42
+ "EB at i270",
43
+ "EB @ York St Denver",
44
+ "NB at Iliff Denver",
45
+ "WB Half Mile West of I225 Denver",
46
+ "1 mile E of Kipling Denver",
47
+ "Main St Hudson",
48
+ "EB Colorado Blvd i70 Denver",
49
+ "E of Washington St Denver",
50
+ "WB Peroia St Underpass Denver",
51
+ "Grand Ave Glenwood"
52
+ ]
53
+
54
+ # --- Sidebar Settings ---
55
+ with st.sidebar:
56
+ st.header("Stream Settings")
57
+ # Custom configuration for stream 1 only.
58
+ custom_m3u8 = st.text_input("Custom M3U8 URL for Stream 1 (optional)", value="", key="custom_m3u8")
59
+ custom_name = st.text_input("Custom Webcam Name for Stream 1 (optional)", value="", key="custom_name")
60
+
61
+ # Choose number of streams (1 to 10)
62
+ num_streams = st.selectbox("Number of Streams", list(range(1, 11)), index=0)
63
+ st.session_state.num_streams = num_streams
64
+
65
+ # Global settings for confidence and processing rate.
66
+ confidence = float(st.slider("Confidence Threshold", 5, 100, 30)) / 100
67
+ st.session_state.confidence = confidence
68
+ fps_options = {
69
+ "1 FPS": 1,
70
+ "1 frame/2s": 0.5,
71
+ "1 frame/3s": 0.3333,
72
+ "1 frame/5s": 0.2,
73
+ "1 frame/15s": 0.0667,
74
+ "1 frame/30s": 0.0333
75
+ }
76
+ video_option = st.selectbox("Processing Rate", list(fps_options.keys()), index=3)
77
+ st.session_state.target_fps = fps_options[video_option]
78
+
79
+ # Update or initialize the streams using defaults (with custom override for stream 1).
80
+ if len(st.session_state.streams) != st.session_state.num_streams:
81
+ st.session_state.streams = []
82
+ for i in range(st.session_state.num_streams):
83
+ if i == 0:
84
+ url = custom_m3u8.strip() if custom_m3u8.strip() else default_m3u8_urls[0]
85
+ display_name = custom_name.strip() if custom_name.strip() else default_names[0]
86
+ else:
87
+ url = default_m3u8_urls[i] if i < len(default_m3u8_urls) else ""
88
+ display_name = default_names[i] if i < len(default_names) else f"Stream {i+1}"
89
+ st.session_state.streams.append({
90
+ "current_m3u8_url": url,
91
+ "processed_frame": np.zeros((480, 640, 3), dtype=np.uint8),
92
+ "start_time": time.time(),
93
+ "processed_count": 0,
94
+ "detected_frames": [],
95
+ "last_processed_time": 0,
96
+ "stats_text": "Processing FPS: 0.00\nFrame Delay: 0.00 sec\nTensor Results: No detections",
97
+ "highest_match": 0.0,
98
+ "display_name": display_name
99
+ })
100
+ else:
101
+ if st.session_state.num_streams > 0:
102
+ url = custom_m3u8.strip() if custom_m3u8.strip() else default_m3u8_urls[0]
103
+ display_name = custom_name.strip() if custom_name.strip() else default_names[0]
104
+ st.session_state.streams[0]["current_m3u8_url"] = url
105
+ st.session_state.streams[0]["display_name"] = display_name
106
+
107
+ confidence = st.session_state.confidence
108
+ target_fps = st.session_state.target_fps
109
+
110
+ # --- Load Model ---
111
+ model_path = 'https://huggingface.co/spaces/tstone87/ccr-colorado/resolve/main/best.pt'
112
+ @st.cache_resource
113
+ def load_model():
114
+ return YOLO(model_path)
115
+
116
+ try:
117
+ model = load_model()
118
+ except Exception as ex:
119
+ st.error(f"Model loading failed: {str(ex)}")
120
+ st.stop()
121
+
122
+ # --- Create Placeholders for Streams in a 2-Column Grid ---
123
+ num_streams = st.session_state.num_streams
124
+ feed_placeholders = []
125
+ stats_placeholders = []
126
+ cols = st.columns(2)
127
+ for i in range(num_streams):
128
+ col_index = i % 2
129
+ if i >= 2 and col_index == 0:
130
+ cols = st.columns(2)
131
+ feed_placeholders.append(cols[col_index].empty())
132
+ stats_placeholders.append(cols[col_index].empty())
133
+ if num_streams == 1:
134
+ _ = st.columns(2)
135
+
136
+ def update_stream(i):
137
+ current_time = time.time()
138
+ sleep_time = 1.0 / target_fps
139
+ stream_state = st.session_state.streams[i]
140
+ if current_time - stream_state["last_processed_time"] >= sleep_time:
141
+ url = stream_state["current_m3u8_url"]
142
+ cap = cv2.VideoCapture(url)
143
+ if not cap.isOpened():
144
+ stats_placeholders[i].text("Failed to open M3U8 stream.")
145
+ return
146
+ ret, frame = cap.read()
147
+ cap.release()
148
+ if not ret:
149
+ stats_placeholders[i].text("Stream interrupted or ended.")
150
+ return
151
+ res = model.predict(frame, conf=confidence)
152
+ processed_frame = res[0].plot()[:, :, ::-1]
153
+ # Extract detection results.
154
+ tensor_info = "No detections"
155
+ max_conf = 0.0
156
+ try:
157
+ boxes = res[0].boxes
158
+ if boxes is not None and len(boxes) > 0:
159
+ max_conf = float(boxes.conf.max())
160
+ tensor_info = f"Detections: {len(boxes)} | Max Confidence: {max_conf:.2f}"
161
+ except Exception as ex:
162
+ tensor_info = f"Error extracting detections: {ex}"
163
+ # Only update if new detection's confidence is >= current highest.
164
+ if max_conf >= stream_state["highest_match"]:
165
+ stream_state["highest_match"] = max_conf
166
+ stream_state["detected_frames"].append(processed_frame)
167
+ stream_state["processed_count"] += 1
168
+ stream_state["last_processed_time"] = current_time
169
+ mt_time = datetime.now(pytz.timezone('America/Denver')).strftime('%Y-%m-%d %H:%M:%S MT')
170
+ stream_state["processed_frame"] = processed_frame
171
+ stream_state["stats_text"] = (
172
+ f"Processing FPS: {stream_state['processed_count'] / (current_time - stream_state['start_time']):.2f}\n"
173
+ f"{tensor_info}\n"
174
+ f"Highest Match: {stream_state['highest_match']:.2f}"
175
+ )
176
+ feed_placeholders[i].image(
177
+ processed_frame,
178
+ caption=f"Stream {i+1} - {stream_state['display_name']} - {mt_time}",
179
+ use_container_width=True
180
+ )
181
+ stats_placeholders[i].text(stream_state["stats_text"])
182
+
183
+ # --- Continuous Processing Loop ---
184
+ # Using Streamlit's experimental rerun mechanism to update frames.
185
+ # This loop runs once per script execution; Streamlit will re-run the script as needed.
186
+ for i in range(num_streams):
187
+ update_stream(i)
188
+
189
+ # Trigger a rerun after a short delay for continuous updates
190
+ time.sleep(1.0 / target_fps)
191
+ st.experimental_rerun()