lokesh341 commited on
Commit
725fc1c
·
verified ·
1 Parent(s): ccef288

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +93 -85
app.py CHANGED
@@ -3,15 +3,19 @@ import gradio as gr
3
  import cv2
4
  import time
5
  import json
 
6
  import logging
7
  import matplotlib.pyplot as plt
8
  import shutil
9
  from datetime import datetime
 
10
  from typing import Any, Dict, List, Optional, Tuple
11
  import numpy as np
12
 
 
13
  os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
14
 
 
15
  try:
16
  from services.video_service import get_next_video_frame, reset_video_index, preload_video, release_video
17
  from services.metrics_service import update_metrics
@@ -20,15 +24,21 @@ try:
20
  from services.thermal_service import process_thermal
21
  from services.map_service import generate_map
22
  from services.under_construction.earthwork_detection import process_earthwork
23
- from services.under_construction.culvert_detection import process_culverts
24
- from services.under_construction.bridge_pier_detection import process_bridge_piers
25
  except ImportError as e:
26
  print(f"Failed to import service modules: {str(e)}")
27
  logging.error(f"Import error: {str(e)}")
28
  exit(1)
29
 
30
- logging.basicConfig(filename="app.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
 
 
 
 
 
31
 
 
32
  paused: bool = False
33
  frame_rate: float = 0.3
34
  frame_count: int = 0
@@ -44,25 +54,22 @@ active_service: Optional[str] = None
44
  is_video: bool = True
45
  static_image: Optional[np.ndarray] = None
46
  enabled_services: List[str] = []
47
- video_writer: Optional[cv2.VideoWriter] = None
48
- output_video_path: str = ""
49
 
 
50
  DEFAULT_VIDEO_PATH = "sample.mp4"
51
  TEMP_IMAGE_PATH = os.path.abspath("temp.jpg")
52
  CAPTURED_FRAMES_DIR = os.path.abspath("captured_frames")
53
  OUTPUT_DIR = os.path.abspath("outputs")
54
  TEMP_MEDIA_DIR = os.path.abspath("temp_media")
55
 
 
56
  for directory in [CAPTURED_FRAMES_DIR, OUTPUT_DIR, TEMP_MEDIA_DIR]:
57
  os.makedirs(directory, exist_ok=True)
58
  os.chmod(directory, 0o777)
59
 
60
  def initialize_media(media_file: Optional[Any] = None) -> str:
61
- global media_loaded, is_video, static_image, log_entries, frame_count, video_writer, output_video_path
62
  release_video()
63
- if video_writer is not None:
64
- video_writer.release()
65
- video_writer = None
66
  static_image = None
67
  frame_count = 0
68
 
@@ -103,16 +110,6 @@ def initialize_media(media_file: Optional[Any] = None) -> str:
103
  try:
104
  if file_extension in (".mp4", ".avi"):
105
  is_video = True
106
- cap = cv2.VideoCapture(media_path)
107
- fps = cap.get(cv2.CAP_PROP_FPS)
108
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
109
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
110
- cap.release()
111
-
112
- output_video_path = os.path.join(OUTPUT_DIR, f"processed_under_construction_{int(time.time())}.mp4")
113
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
114
- video_writer = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))
115
-
116
  preload_video(media_path)
117
  media_loaded = True
118
  status = f"Successfully loaded video: {media_path}"
@@ -184,12 +181,11 @@ def monitor_feed() -> Tuple[
184
  str,
185
  List[str],
186
  Optional[str],
187
- Optional[str],
188
  Optional[str]
189
  ]:
190
  global paused, frame_count, last_frame, last_metrics, last_timestamp
191
- global gps_coordinates, detected_issues, media_loaded, video_writer
192
- global is_video, static_image, enabled_services, output_video_path
193
 
194
  if not media_loaded:
195
  log_entries.append("Cannot start processing: Media not loaded successfully.")
@@ -200,7 +196,6 @@ def monitor_feed() -> Tuple[
200
  "\n".join(log_entries[-10:]),
201
  detected_issues,
202
  None,
203
- None,
204
  None
205
  )
206
 
@@ -210,13 +205,14 @@ def monitor_feed() -> Tuple[
210
  else:
211
  max_retries = 3
212
  start_time = time.time()
 
213
  for attempt in range(max_retries):
214
  try:
215
  if is_video:
216
  frame = get_next_video_frame()
217
- if frame is None:
218
- log_entries.append(f"Frame retrieval failed on attempt {attempt + 1}, resetting video.")
219
- logging.warning(f"Frame retrieval failed on attempt {attempt + 1}, resetting video.")
220
  reset_video_index()
221
  continue
222
  break
@@ -233,10 +229,9 @@ def monitor_feed() -> Tuple[
233
  "\n".join(log_entries[-10:]),
234
  detected_issues,
235
  None,
236
- None,
237
  None
238
  )
239
- else:
240
  log_entries.append("Failed to retrieve frame after maximum retries.")
241
  logging.error("Failed to retrieve frame after maximum retries.")
242
  return (
@@ -245,10 +240,23 @@ def monitor_feed() -> Tuple[
245
  "\n".join(log_entries[-10:]),
246
  detected_issues,
247
  None,
248
- None,
249
  None
250
  )
251
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  detection_frame = cv2.resize(frame, (512, 320))
253
  all_detected_items: List[Dict[str, Any]] = []
254
  shadow_issue = False
@@ -258,8 +266,8 @@ def monitor_feed() -> Tuple[
258
  if "under_construction" in enabled_services:
259
  earthwork_dets, detection_frame = process_earthwork(detection_frame)
260
  culvert_dets, detection_frame = process_culverts(detection_frame)
261
- pier_dets, detection_frame = process_bridge_piers(detection_frame)
262
- all_detected_items.extend(earthwork_dets + culvert_dets + pier_dets)
263
 
264
  try:
265
  cv2.imwrite(TEMP_IMAGE_PATH, detection_frame)
@@ -294,22 +302,20 @@ def monitor_feed() -> Tuple[
294
  if not box:
295
  continue
296
  x_min, y_min, x_max, y_max = box
 
297
  dtype = item.get("type", "")
298
 
299
  if dtype == "earthwork":
300
- color = (255, 105, 180) # Pink
301
- label = "Earthwork"
302
  elif dtype == "culvert":
303
- color = (0, 128, 128) # Teal
304
- label = "Culvert"
305
  elif dtype == "bridge_pier":
306
- color = (255, 127, 127) # Coral
307
- label = "Bridge Pier"
308
  else:
309
  continue
310
 
311
- cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), color, 3)
312
- (text_w, text_h), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)
313
  label_background = frame[y_min - text_h - 15:y_min - 5, x_min:x_min + text_w + 10]
314
  if label_background.size > 0:
315
  overlay = label_background.copy()
@@ -317,7 +323,7 @@ def monitor_feed() -> Tuple[
317
  alpha = 0.5
318
  cv2.addWeighted(overlay, alpha, label_background, 1 - alpha, 0, label_background)
319
  cv2.putText(frame, label, (x_min + 5, y_min - 10),
320
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
321
 
322
  try:
323
  cv2.imwrite(TEMP_IMAGE_PATH, frame, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
@@ -331,7 +337,7 @@ def monitor_feed() -> Tuple[
331
  all_detected_items = []
332
 
333
  metrics = update_metrics(all_detected_items)
334
- gps_coord = [17.385044, 78.486671] # Fixed GPS for consistency
335
  gps_coordinates.append(gps_coord)
336
 
337
  for item in all_detected_items:
@@ -345,10 +351,9 @@ def monitor_feed() -> Tuple[
345
  if not success:
346
  raise RuntimeError(f"Failed to save captured frame: {captured_frame_path}")
347
  for item in all_detected_items:
348
- if item.get("type") in ["earthwork", "culvert", "bridge_pier"]:
349
- detected_issues.append(captured_frame_path)
350
- if len(detected_issues) > 100:
351
- detected_issues.pop(0)
352
  except Exception as e:
353
  log_entries.append(f"Error saving captured frame: {str(e)}")
354
  logging.error(f"Error saving captured frame: {str(e)}")
@@ -369,12 +374,14 @@ def monitor_feed() -> Tuple[
369
  log_entries.append(f"Salesforce Dispatch Error: {str(e)}")
370
  logging.error(f"Salesforce dispatch error: {str(e)}")
371
 
372
- if is_video and video_writer is not None:
373
- try:
374
- video_writer.write(frame)
375
- except Exception as e:
376
- log_entries.append(f"Error writing frame to video: {str(e)}")
377
- logging.error(f"Error writing frame to video: {str(e)}")
 
 
378
 
379
  frame_count += 1
380
  last_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -383,16 +390,16 @@ def monitor_feed() -> Tuple[
383
 
384
  earthwork_detected = len([item for item in all_detected_items if item.get("type") == "earthwork"])
385
  culvert_detected = len([item for item in all_detected_items if item.get("type") == "culvert"])
386
- pier_detected = len([item for item in all_detected_items if item.get("type") == "bridge_pier"])
387
- detected_counts.append(earthwork_detected + culvert_detected + pier_detected)
388
 
389
  processing_time = time.time() - start_time
390
  detection_summary = {
391
  "timestamp": last_timestamp,
392
  "frame": frame_count,
393
- "earthwork": earthwork_detected,
394
  "culverts": culvert_detected,
395
- "bridge_piers": pier_detected,
396
  "gps": gps_coord,
397
  "processing_time_ms": processing_time * 1000
398
  }
@@ -412,30 +419,22 @@ def monitor_feed() -> Tuple[
412
  map_items = [item for item in last_metrics.get("items", []) if item.get("type") in ["earthwork", "culvert", "bridge_pier"]]
413
  map_path = generate_map(gps_coordinates[-5:], map_items)
414
 
415
- download_link = output_video_path if is_video and os.path.exists(output_video_path) else None
416
-
417
  return (
418
  frame[:, :, ::-1],
419
  json.dumps(last_metrics, indent=2),
420
  "\n".join(log_entries[-10:]),
421
  detected_issues,
422
  generate_line_chart(),
423
- map_path,
424
- download_link
425
  )
426
 
427
- def finalize_video() -> str:
428
- global video_writer
429
- if video_writer is not None:
430
- video_writer.release()
431
- video_writer = None
432
- log_entries.append("Processed video saved successfully.")
433
- logging.info("Processed video saved successfully.")
434
- return "Video processing completed."
435
- return "No video to finalize."
436
-
437
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange", secondary_hue="amber")) as app:
438
- gr.Markdown("# 🏗️ Under Construction Inspection Dashboard\nMonitor construction sites in real-time using drone footage or static images.")
 
 
 
 
 
439
 
440
  with gr.Row():
441
  with gr.Column(scale=3):
@@ -476,21 +475,26 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange", secondary_hue="amber")
476
  chart_output = gr.Image(label="Detection Trend")
477
  map_output = gr.Image(label="Issue Locations Map")
478
 
479
- with gr.Row():
480
- video_download = gr.File(label="Download Processed Video", interactive=False)
481
-
482
  with gr.Row():
483
  pause_btn = gr.Button("⏸️ Pause", variant="secondary")
484
  resume_btn = gr.Button("▶️ Resume", variant="primary")
485
- stop_btn = gr.Button("⏹️ Stop and Save Video", variant="secondary")
486
  frame_slider = gr.Slider(0.05, 1.0, value=0.3, label="Frame Interval (seconds)", step=0.05)
487
 
488
  gr.HTML("""
489
  <style>
490
- body { background-color: #FFDAB9 !important; }
491
- #live-feed { border: 2px solid #FF8C00; border-radius: 10px; }
492
- .gr-button-primary { background-color: #FF8C00 !important; }
493
- .gr-button-secondary { background-color: #FF6347 !important; }
 
 
 
 
 
 
 
 
 
494
  </style>
495
  """)
496
 
@@ -510,7 +514,11 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange", secondary_hue="amber")
510
 
511
  media_status.value = initialize_media()
512
 
513
- load_button.click(initialize_media, inputs=[media_input], outputs=[media_status])
 
 
 
 
514
 
515
  def update_toggles(uc_val: bool) -> Tuple[str, str]:
516
  active, status_message = set_active_service(uc_val)
@@ -518,26 +526,26 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange", secondary_hue="amber")
518
  return uc_status_val, status_message
519
 
520
  uc_toggle.change(update_toggles, inputs=[uc_toggle], outputs=[uc_status, status_text])
 
521
  pause_btn.click(toggle_pause, outputs=status_text)
522
  resume_btn.click(toggle_resume, outputs=status_text)
523
- stop_btn.click(finalize_video, outputs=status_text)
524
  frame_slider.change(set_frame_rate, inputs=[frame_slider])
525
 
526
  def streaming_loop():
527
  while True:
528
  if not media_loaded:
529
- yield None, json.dumps({"error": "Media not loaded. Please upload a video or image file."}, indent=2), "\n".join(log_entries[-10:]), detected_issues, None, None, None
530
  else:
531
- frame, metrics, logs, issues, chart, map_path, download_link = monitor_feed()
532
  if frame is None:
533
- yield None, metrics, logs, issues, chart, map_path, download_link
534
  else:
535
- yield frame, metrics, logs, issues, chart, map_path, download_link
536
  if not is_video:
537
  break
538
  time.sleep(frame_rate)
539
 
540
- app.load(streaming_loop, outputs=[media_output, metrics_output, logs_output, issue_images, chart_output, map_output, video_download])
541
 
542
  if __name__ == "__main__":
543
  app.launch(share=True)
 
3
  import cv2
4
  import time
5
  import json
6
+ import random
7
  import logging
8
  import matplotlib.pyplot as plt
9
  import shutil
10
  from datetime import datetime
11
+ from collections import Counter
12
  from typing import Any, Dict, List, Optional, Tuple
13
  import numpy as np
14
 
15
+ # Suppress Ultralytics warning by setting a writable config directory
16
  os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
17
 
18
+ # Import service modules
19
  try:
20
  from services.video_service import get_next_video_frame, reset_video_index, preload_video, release_video
21
  from services.metrics_service import update_metrics
 
24
  from services.thermal_service import process_thermal
25
  from services.map_service import generate_map
26
  from services.under_construction.earthwork_detection import process_earthwork
27
+ from services.under_construction.culvert_check import process_culverts
28
+ from services.under_construction.bridge_pier_check import process_bridge_piers
29
  except ImportError as e:
30
  print(f"Failed to import service modules: {str(e)}")
31
  logging.error(f"Import error: {str(e)}")
32
  exit(1)
33
 
34
+ # Configure logging
35
+ logging.basicConfig(
36
+ filename="app.log",
37
+ level=logging.INFO,
38
+ format="%(asctime)s - %(levelname)s - %(message)s"
39
+ )
40
 
41
+ # Global variables
42
  paused: bool = False
43
  frame_rate: float = 0.3
44
  frame_count: int = 0
 
54
  is_video: bool = True
55
  static_image: Optional[np.ndarray] = None
56
  enabled_services: List[str] = []
 
 
57
 
58
+ # Constants
59
  DEFAULT_VIDEO_PATH = "sample.mp4"
60
  TEMP_IMAGE_PATH = os.path.abspath("temp.jpg")
61
  CAPTURED_FRAMES_DIR = os.path.abspath("captured_frames")
62
  OUTPUT_DIR = os.path.abspath("outputs")
63
  TEMP_MEDIA_DIR = os.path.abspath("temp_media")
64
 
65
+ # Ensure directories exist with write permissions
66
  for directory in [CAPTURED_FRAMES_DIR, OUTPUT_DIR, TEMP_MEDIA_DIR]:
67
  os.makedirs(directory, exist_ok=True)
68
  os.chmod(directory, 0o777)
69
 
70
  def initialize_media(media_file: Optional[Any] = None) -> str:
71
+ global media_loaded, is_video, static_image, log_entries, frame_count
72
  release_video()
 
 
 
73
  static_image = None
74
  frame_count = 0
75
 
 
110
  try:
111
  if file_extension in (".mp4", ".avi"):
112
  is_video = True
 
 
 
 
 
 
 
 
 
 
113
  preload_video(media_path)
114
  media_loaded = True
115
  status = f"Successfully loaded video: {media_path}"
 
181
  str,
182
  List[str],
183
  Optional[str],
 
184
  Optional[str]
185
  ]:
186
  global paused, frame_count, last_frame, last_metrics, last_timestamp
187
+ global gps_coordinates, detected_issues, media_loaded
188
+ global is_video, static_image, enabled_services
189
 
190
  if not media_loaded:
191
  log_entries.append("Cannot start processing: Media not loaded successfully.")
 
196
  "\n".join(log_entries[-10:]),
197
  detected_issues,
198
  None,
 
199
  None
200
  )
201
 
 
205
  else:
206
  max_retries = 3
207
  start_time = time.time()
208
+ frame = None
209
  for attempt in range(max_retries):
210
  try:
211
  if is_video:
212
  frame = get_next_video_frame()
213
+ if frame is None or frame.size == 0:
214
+ log_entries.append(f"Empty frame on attempt {attempt + 1}, resetting video.")
215
+ logging.warning(f"Empty frame on attempt {attempt + 1}, resetting video.")
216
  reset_video_index()
217
  continue
218
  break
 
229
  "\n".join(log_entries[-10:]),
230
  detected_issues,
231
  None,
 
232
  None
233
  )
234
+ if frame is None:
235
  log_entries.append("Failed to retrieve frame after maximum retries.")
236
  logging.error("Failed to retrieve frame after maximum retries.")
237
  return (
 
240
  "\n".join(log_entries[-10:]),
241
  detected_issues,
242
  None,
 
243
  None
244
  )
245
 
246
+ # Skip frame if processing is too slow
247
+ processing_time = time.time() - start_time
248
+ if is_video and processing_time > frame_rate:
249
+ log_entries.append(f"Processing too slow ({processing_time*1000:.0f}ms > {frame_rate*1000:.0f}ms), skipping frame.")
250
+ logging.warning(f"Processing too slow, skipping frame.")
251
+ return (
252
+ last_frame[:, :, ::-1] if last_frame is not None else None,
253
+ json.dumps(last_metrics, indent=2),
254
+ "\n".join(log_entries[-10:]),
255
+ detected_issues,
256
+ generate_line_chart(),
257
+ map_path if 'map_path' in locals() else None
258
+ )
259
+
260
  detection_frame = cv2.resize(frame, (512, 320))
261
  all_detected_items: List[Dict[str, Any]] = []
262
  shadow_issue = False
 
266
  if "under_construction" in enabled_services:
267
  earthwork_dets, detection_frame = process_earthwork(detection_frame)
268
  culvert_dets, detection_frame = process_culverts(detection_frame)
269
+ bridge_pier_dets, detection_frame = process_bridge_piers(detection_frame)
270
+ all_detected_items.extend(earthwork_dets + culvert_dets + bridge_pier_dets)
271
 
272
  try:
273
  cv2.imwrite(TEMP_IMAGE_PATH, detection_frame)
 
302
  if not box:
303
  continue
304
  x_min, y_min, x_max, y_max = box
305
+ label = item.get("label", "")
306
  dtype = item.get("type", "")
307
 
308
  if dtype == "earthwork":
309
+ color = (255, 20, 147) # Bright Pink
 
310
  elif dtype == "culvert":
311
+ color = (0, 255, 255) # Bright Teal
 
312
  elif dtype == "bridge_pier":
313
+ color = (255, 99, 71) # Bright Coral
 
314
  else:
315
  continue
316
 
317
+ cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), color, 4)
318
+ (text_w, text_h), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.7, 2)
319
  label_background = frame[y_min - text_h - 15:y_min - 5, x_min:x_min + text_w + 10]
320
  if label_background.size > 0:
321
  overlay = label_background.copy()
 
323
  alpha = 0.5
324
  cv2.addWeighted(overlay, alpha, label_background, 1 - alpha, 0, label_background)
325
  cv2.putText(frame, label, (x_min + 5, y_min - 10),
326
+ cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
327
 
328
  try:
329
  cv2.imwrite(TEMP_IMAGE_PATH, frame, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
 
337
  all_detected_items = []
338
 
339
  metrics = update_metrics(all_detected_items)
340
+ gps_coord = [17.385044 + random.uniform(-0.001, 0.001), 78.486671 + frame_count * 0.0001]
341
  gps_coordinates.append(gps_coord)
342
 
343
  for item in all_detected_items:
 
351
  if not success:
352
  raise RuntimeError(f"Failed to save captured frame: {captured_frame_path}")
353
  for item in all_detected_items:
354
+ detected_issues.append(captured_frame_path)
355
+ if len(detected_issues) > 100:
356
+ detected_issues.pop(0)
 
357
  except Exception as e:
358
  log_entries.append(f"Error saving captured frame: {str(e)}")
359
  logging.error(f"Error saving captured frame: {str(e)}")
 
374
  log_entries.append(f"Salesforce Dispatch Error: {str(e)}")
375
  logging.error(f"Salesforce dispatch error: {str(e)}")
376
 
377
+ try:
378
+ frame_path = os.path.join(OUTPUT_DIR, f"frame_{frame_count:04d}.jpg")
379
+ success = cv2.imwrite(frame_path, frame, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
380
+ if not success:
381
+ raise RuntimeError(f"Failed to save output frame: {frame_path}")
382
+ except Exception as e:
383
+ log_entries.append(f"Error saving output frame: {str(e)}")
384
+ logging.error(f"Error saving output frame: {str(e)}")
385
 
386
  frame_count += 1
387
  last_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
390
 
391
  earthwork_detected = len([item for item in all_detected_items if item.get("type") == "earthwork"])
392
  culvert_detected = len([item for item in all_detected_items if item.get("type") == "culvert"])
393
+ bridge_pier_detected = len([item for item in all_detected_items if item.get("type") == "bridge_pier"])
394
+ detected_counts.append(earthwork_detected + culvert_detected + bridge_pier_detected)
395
 
396
  processing_time = time.time() - start_time
397
  detection_summary = {
398
  "timestamp": last_timestamp,
399
  "frame": frame_count,
400
+ "earthworks": earthwork_detected,
401
  "culverts": culvert_detected,
402
+ "bridge_piers": bridge_pier_detected,
403
  "gps": gps_coord,
404
  "processing_time_ms": processing_time * 1000
405
  }
 
419
  map_items = [item for item in last_metrics.get("items", []) if item.get("type") in ["earthwork", "culvert", "bridge_pier"]]
420
  map_path = generate_map(gps_coordinates[-5:], map_items)
421
 
 
 
422
  return (
423
  frame[:, :, ::-1],
424
  json.dumps(last_metrics, indent=2),
425
  "\n".join(log_entries[-10:]),
426
  detected_issues,
427
  generate_line_chart(),
428
+ map_path
 
429
  )
430
 
 
 
 
 
 
 
 
 
 
 
431
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange", secondary_hue="amber")) as app:
432
+ gr.Markdown(
433
+ """
434
+ # 🛠️ Under Construction Inspection Dashboard
435
+ Monitor under construction elements in real-time using drone footage or static images.
436
+ """
437
+ )
438
 
439
  with gr.Row():
440
  with gr.Column(scale=3):
 
475
  chart_output = gr.Image(label="Detection Trend")
476
  map_output = gr.Image(label="Issue Locations Map")
477
 
 
 
 
478
  with gr.Row():
479
  pause_btn = gr.Button("⏸️ Pause", variant="secondary")
480
  resume_btn = gr.Button("▶️ Resume", variant="primary")
 
481
  frame_slider = gr.Slider(0.05, 1.0, value=0.3, label="Frame Interval (seconds)", step=0.05)
482
 
483
  gr.HTML("""
484
  <style>
485
+ body {
486
+ background-color: #FFDAB9 !important;
487
+ }
488
+ #live-feed {
489
+ border: 2px solid #FF8C00;
490
+ border-radius: 10px;
491
+ }
492
+ .gr-button-primary {
493
+ background-color: #FF8C00 !important;
494
+ }
495
+ .gr-button-secondary {
496
+ background-color: #FF6347 !important;
497
+ }
498
  </style>
499
  """)
500
 
 
514
 
515
  media_status.value = initialize_media()
516
 
517
+ load_button.click(
518
+ initialize_media,
519
+ inputs=[media_input],
520
+ outputs=[media_status]
521
+ )
522
 
523
  def update_toggles(uc_val: bool) -> Tuple[str, str]:
524
  active, status_message = set_active_service(uc_val)
 
526
  return uc_status_val, status_message
527
 
528
  uc_toggle.change(update_toggles, inputs=[uc_toggle], outputs=[uc_status, status_text])
529
+
530
  pause_btn.click(toggle_pause, outputs=status_text)
531
  resume_btn.click(toggle_resume, outputs=status_text)
 
532
  frame_slider.change(set_frame_rate, inputs=[frame_slider])
533
 
534
  def streaming_loop():
535
  while True:
536
  if not media_loaded:
537
+ yield None, json.dumps({"error": "Media not loaded. Please upload a video or image file."}, indent=2), "\n".join(log_entries[-10:]), detected_issues, None, None
538
  else:
539
+ frame, metrics, logs, issues, chart, map_path = monitor_feed()
540
  if frame is None:
541
+ yield None, metrics, logs, issues, chart, map_path
542
  else:
543
+ yield frame, metrics, logs, issues, chart, map_path
544
  if not is_video:
545
  break
546
  time.sleep(frame_rate)
547
 
548
+ app.load(streaming_loop, outputs=[media_output, metrics_output, logs_output, issue_images, chart_output, map_output])
549
 
550
  if __name__ == "__main__":
551
  app.launch(share=True)