Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -5,10 +5,11 @@ import time
|
|
5 |
import json
|
6 |
import random
|
7 |
import logging
|
|
|
8 |
from datetime import datetime
|
9 |
from collections import Counter
|
10 |
from typing import Any, Dict, List, Optional, Tuple
|
11 |
-
import
|
12 |
|
13 |
# Suppress Ultralytics warning by setting a writable config directory
|
14 |
os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
|
@@ -144,10 +145,86 @@ def set_active_service(
|
|
144 |
logging.info("No service category enabled.")
|
145 |
return None, "No Service Category Enabled"
|
146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
def monitor_feed() -> Tuple[
|
148 |
Optional[np.ndarray],
|
149 |
str,
|
150 |
str,
|
|
|
|
|
|
|
151 |
List[str]
|
152 |
]:
|
153 |
"""
|
@@ -158,6 +235,9 @@ def monitor_feed() -> Tuple[
|
|
158 |
- Processed frame (numpy array or None).
|
159 |
- Detection metrics (JSON string with crack trend and severity data).
|
160 |
- Recent logs (string).
|
|
|
|
|
|
|
161 |
- List of captured image paths.
|
162 |
"""
|
163 |
global paused, frame_count, last_frame, last_detections, last_timestamp
|
@@ -170,6 +250,9 @@ def monitor_feed() -> Tuple[
|
|
170 |
None,
|
171 |
json.dumps({"error": "Video not loaded. Please upload a video file."}, indent=2),
|
172 |
"\n".join(log_entries[-10:]),
|
|
|
|
|
|
|
173 |
last_detected_images
|
174 |
)
|
175 |
|
@@ -188,6 +271,9 @@ def monitor_feed() -> Tuple[
|
|
188 |
None,
|
189 |
json.dumps(last_detections, indent=2),
|
190 |
"\n".join(log_entries[-10:]),
|
|
|
|
|
|
|
191 |
last_detected_images
|
192 |
)
|
193 |
|
@@ -300,9 +386,9 @@ def monitor_feed() -> Tuple[
|
|
300 |
last_frame = frame.copy()
|
301 |
last_detections = metrics
|
302 |
|
303 |
-
# Track cracks for metrics (for Operations Maintenance
|
304 |
-
crack_detected = len([item for item in all_detected_items if item.get("type") == "crack"]) if active_service
|
305 |
-
if active_service
|
306 |
crack_severity_all.extend([
|
307 |
item["severity"]
|
308 |
for item in all_detected_items
|
@@ -310,7 +396,7 @@ def monitor_feed() -> Tuple[
|
|
310 |
])
|
311 |
|
312 |
# Add crack trend and severity to metrics
|
313 |
-
if active_service
|
314 |
last_detections["crack_count_last_50_frames"] = crack_counts[-50:] if crack_counts else []
|
315 |
severity_counts = Counter(crack_severity_all[-200:]) if crack_severity_all else {}
|
316 |
last_detections["crack_severity_distribution"] = dict(severity_counts)
|
@@ -336,10 +422,22 @@ def monitor_feed() -> Tuple[
|
|
336 |
cv2.putText(frame, f"Frame: {frame_count}", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
337 |
cv2.putText(frame, f"{last_timestamp}", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
338 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
339 |
return (
|
340 |
frame[:, :, ::-1], # Convert BGR to RGB for Gradio
|
341 |
json.dumps(last_detections, indent=2),
|
342 |
"\n".join(log_entries[-10:]),
|
|
|
|
|
|
|
343 |
last_detected_images
|
344 |
)
|
345 |
|
@@ -394,7 +492,12 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
|
|
394 |
with gr.Column(scale=2):
|
395 |
logs_output = gr.Textbox(label="Live Logs", lines=8, interactive=False)
|
396 |
with gr.Column(scale=1):
|
397 |
-
|
|
|
|
|
|
|
|
|
|
|
398 |
|
399 |
with gr.Row():
|
400 |
pause_btn = gr.Button("⏸️ Pause", variant="secondary")
|
@@ -478,16 +581,16 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
|
|
478 |
"""Continuous loop to stream video and process frames."""
|
479 |
while True:
|
480 |
if not video_loaded:
|
481 |
-
yield None, json.dumps({"error": "Video not loaded. Please upload a video file."}, indent=2), "\n".join(log_entries[-10:]), last_detected_images
|
482 |
else:
|
483 |
-
frame, detections, logs, captured = monitor_feed()
|
484 |
if frame is None:
|
485 |
-
yield None, detections, logs, captured
|
486 |
else:
|
487 |
-
yield frame, detections, logs, captured
|
488 |
time.sleep(frame_rate)
|
489 |
|
490 |
-
app.load(streaming_loop, outputs=[video_output, detections_output, logs_output, captured_images])
|
491 |
|
492 |
if __name__ == "__main__":
|
493 |
app.launch(share=False)
|
|
|
5 |
import json
|
6 |
import random
|
7 |
import logging
|
8 |
+
import numpy as np # Moved to top to fix NameError
|
9 |
from datetime import datetime
|
10 |
from collections import Counter
|
11 |
from typing import Any, Dict, List, Optional, Tuple
|
12 |
+
import matplotlib.pyplot as plt # Added for chart generation
|
13 |
|
14 |
# Suppress Ultralytics warning by setting a writable config directory
|
15 |
os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
|
|
|
145 |
logging.info("No service category enabled.")
|
146 |
return None, "No Service Category Enabled"
|
147 |
|
148 |
+
def generate_crack_trend_chart() -> Optional[plt.Figure]:
|
149 |
+
"""
|
150 |
+
Generate a line chart for crack trend over time using Matplotlib.
|
151 |
+
|
152 |
+
Returns:
|
153 |
+
Optional[plt.Figure]: Matplotlib figure object or None if no data.
|
154 |
+
"""
|
155 |
+
if not crack_counts:
|
156 |
+
return None
|
157 |
+
|
158 |
+
data = crack_counts[-50:]
|
159 |
+
labels = list(range(len(data)))
|
160 |
+
|
161 |
+
fig, ax = plt.subplots(figsize=(6, 4))
|
162 |
+
ax.plot(labels, data, color="#FF6347", label="Cracks Over Time")
|
163 |
+
ax.set_title("Crack Trend (Operations Maintenance)")
|
164 |
+
ax.set_xlabel("Frame")
|
165 |
+
ax.set_ylabel("Count")
|
166 |
+
ax.set_ylim(bottom=0)
|
167 |
+
ax.grid(True)
|
168 |
+
ax.legend()
|
169 |
+
plt.tight_layout()
|
170 |
+
return fig
|
171 |
+
|
172 |
+
def generate_crack_severity_chart() -> Optional[plt.Figure]:
|
173 |
+
"""
|
174 |
+
Generate a pie chart for crack severity distribution using Matplotlib.
|
175 |
+
|
176 |
+
Returns:
|
177 |
+
Optional[plt.Figure]: Matplotlib figure object or None if no data.
|
178 |
+
"""
|
179 |
+
if not crack_severity_all:
|
180 |
+
return None
|
181 |
+
|
182 |
+
count = Counter(crack_severity_all[-200:])
|
183 |
+
labels = list(count.keys())
|
184 |
+
sizes = list(count.values())
|
185 |
+
|
186 |
+
fig, ax = plt.subplots(figsize=(6, 4))
|
187 |
+
ax.pie(
|
188 |
+
sizes,
|
189 |
+
labels=labels,
|
190 |
+
colors=["#FF6347", "#4682B4", "#FFD700"],
|
191 |
+
autopct="%1.1f%%",
|
192 |
+
startangle=90
|
193 |
+
)
|
194 |
+
ax.set_title("Crack Severity (Operations Maintenance)")
|
195 |
+
plt.tight_layout()
|
196 |
+
return fig
|
197 |
+
|
198 |
+
def generate_severity_distribution_chart() -> Optional[plt.Figure]:
|
199 |
+
"""
|
200 |
+
Generate a bar chart for crack severity distribution using Matplotlib.
|
201 |
+
|
202 |
+
Returns:
|
203 |
+
Optional[plt.Figure]: Matplotlib figure object or None if no data.
|
204 |
+
"""
|
205 |
+
if not crack_severity_all:
|
206 |
+
return None
|
207 |
+
|
208 |
+
count = Counter(crack_severity_all[-200:])
|
209 |
+
labels = list(count.keys())
|
210 |
+
sizes = list(count.values())
|
211 |
+
|
212 |
+
fig, ax = plt.subplots(figsize=(6, 4))
|
213 |
+
ax.bar(labels, sizes, color=["#FF6347", "#4682B4", "#FFD700"])
|
214 |
+
ax.set_title("Severity Distribution (Operations Maintenance)")
|
215 |
+
ax.set_xlabel("Severity")
|
216 |
+
ax.set_ylabel("Count")
|
217 |
+
ax.set_ylim(bottom=0)
|
218 |
+
plt.tight_layout()
|
219 |
+
return fig
|
220 |
+
|
221 |
def monitor_feed() -> Tuple[
|
222 |
Optional[np.ndarray],
|
223 |
str,
|
224 |
str,
|
225 |
+
Optional[plt.Figure],
|
226 |
+
Optional[plt.Figure],
|
227 |
+
Optional[plt.Figure],
|
228 |
List[str]
|
229 |
]:
|
230 |
"""
|
|
|
235 |
- Processed frame (numpy array or None).
|
236 |
- Detection metrics (JSON string with crack trend and severity data).
|
237 |
- Recent logs (string).
|
238 |
+
- Crack trend chart (Matplotlib figure or None).
|
239 |
+
- Crack severity chart (Matplotlib figure or None).
|
240 |
+
- Severity distribution chart (Matplotlib figure or None).
|
241 |
- List of captured image paths.
|
242 |
"""
|
243 |
global paused, frame_count, last_frame, last_detections, last_timestamp
|
|
|
250 |
None,
|
251 |
json.dumps({"error": "Video not loaded. Please upload a video file."}, indent=2),
|
252 |
"\n".join(log_entries[-10:]),
|
253 |
+
None,
|
254 |
+
None,
|
255 |
+
None,
|
256 |
last_detected_images
|
257 |
)
|
258 |
|
|
|
271 |
None,
|
272 |
json.dumps(last_detections, indent=2),
|
273 |
"\n".join(log_entries[-10:]),
|
274 |
+
None,
|
275 |
+
None,
|
276 |
+
None,
|
277 |
last_detected_images
|
278 |
)
|
279 |
|
|
|
386 |
last_frame = frame.copy()
|
387 |
last_detections = metrics
|
388 |
|
389 |
+
# Track cracks for metrics (for Operations Maintenance only)
|
390 |
+
crack_detected = len([item for item in all_detected_items if item.get("type") == "crack"]) if active_service == "operations_maintenance" else 0
|
391 |
+
if active_service == "operations_maintenance":
|
392 |
crack_severity_all.extend([
|
393 |
item["severity"]
|
394 |
for item in all_detected_items
|
|
|
396 |
])
|
397 |
|
398 |
# Add crack trend and severity to metrics
|
399 |
+
if active_service == "operations_maintenance":
|
400 |
last_detections["crack_count_last_50_frames"] = crack_counts[-50:] if crack_counts else []
|
401 |
severity_counts = Counter(crack_severity_all[-200:]) if crack_severity_all else {}
|
402 |
last_detections["crack_severity_distribution"] = dict(severity_counts)
|
|
|
422 |
cv2.putText(frame, f"Frame: {frame_count}", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
423 |
cv2.putText(frame, f"{last_timestamp}", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
424 |
|
425 |
+
# Generate charts for Operations Maintenance only
|
426 |
+
line_chart = None
|
427 |
+
pie_chart = None
|
428 |
+
bar_chart = None
|
429 |
+
if active_service == "operations_maintenance":
|
430 |
+
line_chart = generate_crack_trend_chart()
|
431 |
+
pie_chart = generate_crack_severity_chart()
|
432 |
+
bar_chart = generate_severity_distribution_chart()
|
433 |
+
|
434 |
return (
|
435 |
frame[:, :, ::-1], # Convert BGR to RGB for Gradio
|
436 |
json.dumps(last_detections, indent=2),
|
437 |
"\n".join(log_entries[-10:]),
|
438 |
+
line_chart,
|
439 |
+
pie_chart,
|
440 |
+
bar_chart,
|
441 |
last_detected_images
|
442 |
)
|
443 |
|
|
|
492 |
with gr.Column(scale=2):
|
493 |
logs_output = gr.Textbox(label="Live Logs", lines=8, interactive=False)
|
494 |
with gr.Column(scale=1):
|
495 |
+
chart_output = gr.Plot(label="Crack Trend (Operations Maintenance Only)")
|
496 |
+
pie_output = gr.Plot(label="Crack Severity (Operations Maintenance Only)")
|
497 |
+
bar_output = gr.Plot(label="Severity Distribution (Operations Maintenance Only)")
|
498 |
+
|
499 |
+
with gr.Row():
|
500 |
+
captured_images = gr.Gallery(label="Detected Frames (Last 100+)", columns=4, rows=25, height="auto")
|
501 |
|
502 |
with gr.Row():
|
503 |
pause_btn = gr.Button("⏸️ Pause", variant="secondary")
|
|
|
581 |
"""Continuous loop to stream video and process frames."""
|
582 |
while True:
|
583 |
if not video_loaded:
|
584 |
+
yield None, json.dumps({"error": "Video not loaded. Please upload a video file."}, indent=2), "\n".join(log_entries[-10:]), None, None, None, last_detected_images
|
585 |
else:
|
586 |
+
frame, detections, logs, line_chart, pie_chart, bar_chart, captured = monitor_feed()
|
587 |
if frame is None:
|
588 |
+
yield None, detections, logs, None, None, None, captured
|
589 |
else:
|
590 |
+
yield frame, detections, logs, line_chart, pie_chart, bar_chart, captured
|
591 |
time.sleep(frame_rate)
|
592 |
|
593 |
+
app.load(streaming_loop, outputs=[video_output, detections_output, logs_output, chart_output, pie_output, bar_output, captured_images])
|
594 |
|
595 |
if __name__ == "__main__":
|
596 |
app.launch(share=False)
|