lokesh341 commited on
Commit
2e67668
·
1 Parent(s): 7698a6c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +124 -205
app.py CHANGED
@@ -5,12 +5,11 @@ import time
5
  import json
6
  import random
7
  import logging
8
- import numpy as np
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
13
- from matplotlib import font_manager
14
 
15
  # Suppress Ultralytics warning by setting a writable config directory
16
  os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
@@ -21,15 +20,16 @@ try:
21
  from services.detection_service import process_frame as process_generic
22
  from services.metrics_service import update_metrics
23
  from services.overlay_service import overlay_boxes
24
- from services.salesforce_dispatcher import dispatch_to_salesforce
25
- from services.shadow_detection import detect_shadows
26
  from services.thermal_service import process_thermal
 
27
  # Under Construction services
28
  from services.under_construction.earthwork_detection import process_earthwork
29
  from services.under_construction.culvert_check import process_culverts
30
  from services.under_construction.bridge_pier_check import process_bridge_piers
31
  # Operations Maintenance services
32
- from services.operations_maintenance.crack_detection import detect_cracks_and_objects
33
  from services.operations_maintenance.pothole_detection import process_potholes
34
  from services.operations_maintenance.signage_check import process_signages
35
  # Road Safety services
@@ -54,16 +54,16 @@ logging.basicConfig(
54
 
55
  # Global variables
56
  paused: bool = False
57
- frame_rate: float = 0.05 # Reduced for faster processing
58
  frame_count: int = 0
59
  log_entries: List[str] = []
60
  crack_counts: List[int] = []
61
  crack_severity_all: List[str] = []
62
  last_frame: Optional[np.ndarray] = None
63
- last_detections: Dict[str, Any] = {}
64
  last_timestamp: str = ""
65
- last_detected_images: List[str] = []
66
- detected_image_set: set = set() # To avoid duplicates
67
  gps_coordinates: List[List[float]] = []
68
  video_loaded: bool = False
69
  active_service: Optional[str] = None
@@ -86,11 +86,19 @@ def initialize_video(video_file: Optional[Any] = None) -> str:
86
  log_entries.append(f"Using uploaded video: {video_path}")
87
  logging.info(f"Using uploaded video: {video_path}")
88
 
89
- status = preload_video(video_path)
90
- video_loaded = "Error" not in status
91
- log_entries.append(status)
92
- logging.info(status)
93
- return status
 
 
 
 
 
 
 
 
94
 
95
  def set_active_service(
96
  service_name: str,
@@ -125,87 +133,47 @@ def set_active_service(
125
  logging.info("No service category enabled.")
126
  return None, "No Service Category Enabled"
127
 
128
- def generate_crack_trend_chart() -> Optional[plt.Figure]:
129
  if not crack_counts:
130
  return None
131
-
132
- data = crack_counts[-50:]
133
- labels = list(range(len(data)))
134
-
135
- fig, ax = plt.subplots(figsize=(6, 4), facecolor='#f0f4f8')
136
- ax.plot(labels, data, color="#FF6347", linewidth=2.5, label="Cracks Over Time", marker='o', markersize=5)
137
- ax.set_title("Crack Trend (Operations Maintenance)", fontsize=14, pad=15, fontweight='bold', color='#333333')
138
- ax.set_xlabel("Frame", fontsize=12, color='#333333')
139
- ax.set_ylabel("Count", fontsize=12, color='#333333')
140
- ax.set_ylim(bottom=0)
141
- ax.grid(True, linestyle='--', alpha=0.7)
142
- ax.set_facecolor('#ffffff')
143
- ax.legend(frameon=True, facecolor='#ffffff', edgecolor='#333333')
144
- for spine in ax.spines.values():
145
- spine.set_edgecolor('#333333')
146
- plt.tight_layout()
147
- return fig
148
-
149
- def generate_crack_severity_chart() -> Optional[plt.Figure]:
150
  if not crack_severity_all:
151
  return None
152
-
153
  count = Counter(crack_severity_all[-200:])
154
- labels = list(count.keys())
155
- sizes = list(count.values())
156
-
157
- fig, ax = plt.subplots(figsize=(6, 4), facecolor='#f0f4f8')
158
- colors = ['#FF6347', '#4682B4', '#FFD700']
159
- wedges, texts, autotexts = ax.pie(
160
- sizes,
161
- labels=labels,
162
- colors=colors,
163
- autopct='%1.1f%%',
164
- startangle=90,
165
- shadow=True,
166
- textprops={'fontsize': 10, 'color': '#333333'}
167
- )
168
- ax.set_title("Crack Severity (Operations Maintenance)", fontsize=14, pad=15, fontweight='bold', color='#333333')
169
- for w in wedges:
170
- w.set_edgecolor('#333333')
171
- plt.tight_layout()
172
- return fig
173
-
174
- def generate_severity_distribution_chart() -> Optional[plt.Figure]:
175
- if not crack_severity_all:
176
- return None
177
-
178
- count = Counter(crack_severity_all[-200:])
179
- labels = list(count.keys())
180
- sizes = list(count.values())
181
-
182
- fig, ax = plt.subplots(figsize=(6, 4), facecolor='#f0f4f8')
183
- colors = ['#FF6347', '#4682B4', '#FFD700']
184
- bars = ax.bar(labels, sizes, color=colors, edgecolor='#333333')
185
- ax.set_title("Severity Distribution (Operations Maintenance)", fontsize=14, pad=15, fontweight='bold', color='#333333')
186
- ax.set_xlabel("Severity", fontsize=12, color='#333333')
187
- ax.set_ylabel("Count", fontsize=12, color='#333333')
188
- ax.set_ylim(bottom=0)
189
- ax.set_facecolor('#ffffff')
190
- for bar in bars:
191
- height = bar.get_height()
192
- ax.text(bar.get_x() + bar.get_width()/2, height, f'{int(height)}', ha='center', va='bottom', fontsize=10, color='#333333')
193
- for spine in ax.spines.values():
194
- spine.set_edgecolor('#333333')
195
- plt.tight_layout()
196
- return fig
197
 
198
  def monitor_feed() -> Tuple[
199
  Optional[np.ndarray],
200
  str,
201
  str,
202
- Optional[plt.Figure],
203
- Optional[plt.Figure],
204
- Optional[plt.Figure],
205
- List[str]
206
  ]:
207
- global paused, frame_count, last_frame, last_detections, last_timestamp
208
- global gps_coordinates, last_detected_images, video_loaded, detected_image_set
209
 
210
  if not video_loaded:
211
  log_entries.append("Cannot start streaming: Video not loaded successfully.")
@@ -214,15 +182,15 @@ def monitor_feed() -> Tuple[
214
  None,
215
  json.dumps({"error": "Video not loaded. Please upload a video file."}, indent=2),
216
  "\n".join(log_entries[-10:]),
 
 
217
  None,
218
- None,
219
- None,
220
- last_detected_images
221
  )
222
 
223
  if paused and last_frame is not None:
224
  frame = last_frame.copy()
225
- detections = last_detections.copy()
226
  else:
227
  try:
228
  frame = get_next_video_frame()
@@ -233,15 +201,17 @@ def monitor_feed() -> Tuple[
233
  logging.error(f"Frame retrieval error: {str(e)}")
234
  return (
235
  None,
236
- json.dumps(last_detections, indent=2),
237
  "\n".join(log_entries[-10:]),
 
 
238
  None,
239
- None,
240
- None,
241
- last_detected_images
242
  )
243
 
244
  all_detected_items: List[Dict[str, Any]] = []
 
 
245
 
246
  try:
247
  # Process frame based on active service
@@ -250,56 +220,48 @@ def monitor_feed() -> Tuple[
250
  culvert_dets, frame = process_culverts(frame)
251
  bridge_pier_dets, frame = process_bridge_piers(frame)
252
  all_detected_items.extend(earthwork_dets + culvert_dets + bridge_pier_dets)
253
-
254
  elif active_service == "operations_maintenance":
255
- crack_items = detect_cracks_and_objects(frame)
256
- frame = overlay_boxes(frame, crack_items)
257
  pothole_dets, frame = process_potholes(frame)
258
  signage_dets, frame = process_signages(frame)
259
- all_detected_items.extend(crack_items + pothole_dets + signage_dets)
260
-
261
  elif active_service == "road_safety":
262
  barrier_dets, frame = process_barriers(frame)
263
  lighting_dets, frame = process_lighting(frame)
264
  accident_dets, frame = process_accident_spots(frame)
265
  pothole_crack_dets, frame = detect_potholes_and_cracks(frame)
266
  all_detected_items.extend(barrier_dets + lighting_dets + accident_dets + pothole_crack_dets)
267
-
268
  elif active_service == "plantation":
269
  plant_dets, frame = process_plants(frame)
270
  health_dets, frame = process_plant_health(frame)
271
  missing_dets, frame = process_missing_patches(frame)
272
  all_detected_items.extend(plant_dets + health_dets + missing_dets)
273
-
274
  else:
275
  generic_dets, frame = process_generic(frame)
276
  all_detected_items.extend(generic_dets)
277
 
278
  # Apply shadow detection
279
- shadow_results = detect_shadows(frame)
280
- shadow_dets = shadow_results["detections"]
281
- frame = shadow_results["frame"]
282
- all_detected_items.extend(shadow_dets)
283
 
284
- # Skip thermal processing to improve speed (unless grayscale frame is detected)
285
  if len(frame.shape) == 2:
286
  thermal_results = process_thermal(frame)
287
  thermal_dets = thermal_results["detections"]
288
  frame = thermal_results["frame"]
289
  all_detected_items.extend(thermal_dets)
 
 
 
 
 
 
 
290
 
291
  except Exception as e:
292
  log_entries.append(f"Processing Error: {str(e)}")
293
  logging.error(f"Processing error in {active_service}: {str(e)}")
294
  all_detected_items = []
295
 
296
- # Save temporary image for display (lower quality for speed)
297
- try:
298
- cv2.imwrite(TEMP_IMAGE_PATH, frame, [int(cv2.IMWRITE_JPEG_QUALITY), 80])
299
- except Exception as e:
300
- log_entries.append(f"Error saving temp image: {str(e)}")
301
- logging.error(f"Error saving temp image: {str(e)}")
302
-
303
  # Update detection metrics
304
  metrics = update_metrics(all_detected_items)
305
 
@@ -307,42 +269,44 @@ def monitor_feed() -> Tuple[
307
  gps_coord = [17.385044 + random.uniform(-0.001, 0.001), 78.486671 + frame_count * 0.0001]
308
  gps_coordinates.append(gps_coord)
309
 
310
- # Save frame if detections are present (avoid duplicates)
311
  detection_types = {item.get("type") for item in all_detected_items if "type" in item}
312
  if detection_types:
313
  try:
314
- # Create a unique identifier for the frame based on detections
315
- detection_key = tuple(sorted([(item["type"], tuple(item["coordinates"])) for item in all_detected_items]))
316
- detection_key_str = str(detection_key)
317
- if detection_key_str not in detected_image_set:
318
- captured_frame_path = os.path.join(CAPTURED_FRAMES_DIR, f"detected_{frame_count}.jpg")
319
- cv2.imwrite(captured_frame_path, frame)
320
- last_detected_images.insert(0, captured_frame_path) # Add to the beginning (newest first)
321
- detected_image_set.add(detection_key_str)
322
- if len(last_detected_images) > 100:
323
- old_image = last_detected_images.pop()
324
- detected_image_set.discard(str(old_image))
325
  except Exception as e:
326
  log_entries.append(f"Error saving captured frame: {str(e)}")
327
  logging.error(f"Error saving captured frame: {str(e)}")
328
 
329
  # Prepare data for Salesforce dispatch
330
  all_detections = {
331
- "items": all_detected_items,
332
  "metrics": metrics,
333
  "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
334
  "frame_count": frame_count,
335
- "gps_coordinates": gps_coord
 
 
336
  }
337
 
338
  # Dispatch to Salesforce
339
  try:
340
- dispatch_to_salesforce(all_detections, all_detections["timestamp"])
341
  except Exception as e:
342
  log_entries.append(f"Salesforce Dispatch Error: {str(e)}")
343
  logging.error(f"Salesforce dispatch error: {str(e)}")
344
 
345
- # Save processed frame to output directory
346
  try:
347
  frame_path = os.path.join(OUTPUT_DIR, f"frame_{frame_count:04d}.jpg")
348
  cv2.imwrite(frame_path, frame)
@@ -354,30 +318,23 @@ def monitor_feed() -> Tuple[
354
  frame_count += 1
355
  last_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
356
  last_frame = frame.copy()
357
- last_detections = metrics
358
 
359
- # Track cracks for metrics (Operations Maintenance only)
360
- crack_detected = len([item for item in all_detected_items if item.get("type") == "crack"]) if active_service == "operations_maintenance" else 0
361
- if active_service == "operations_maintenance":
 
362
  crack_severity_all.extend([
363
  item["severity"]
364
  for item in all_detected_items
365
- if item.get("type") == "crack" and "severity" in item
366
  ])
367
-
368
- # Add crack trend and severity to metrics
369
- if active_service == "operations_maintenance":
370
- last_detections["crack_count_last_50_frames"] = crack_counts[-50:] if crack_counts else []
371
- severity_counts = Counter(crack_severity_all[-200:]) if crack_severity_all else {}
372
- last_detections["crack_severity_distribution"] = dict(severity_counts)
373
 
374
  # Log frame processing details
375
- log_message = f"{last_timestamp} - Frame {frame_count} - Detections: {len(all_detected_items)} - GPS: {gps_coord} - Avg Conf: {metrics.get('avg_confidence', 0):.2f}"
376
- if crack_detected:
377
- log_message += f" - Cracks: {crack_detected}"
378
  log_entries.append(log_message)
379
  logging.info(log_message)
380
- crack_counts.append(crack_detected)
381
 
382
  # Limit the size of logs and crack data
383
  if len(log_entries) > 100:
@@ -387,28 +344,22 @@ def monitor_feed() -> Tuple[
387
  if len(crack_severity_all) > 500:
388
  crack_severity_all.pop(0)
389
 
390
- # Resize frame for display (smaller size for speed)
391
- frame = cv2.resize(last_frame, (320, 240))
392
- cv2.putText(frame, f"Frame: {frame_count}", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
393
- cv2.putText(frame, f"{last_timestamp}", (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
394
 
395
- # Generate charts for Operations Maintenance only
396
- line_chart = None
397
- pie_chart = None
398
- bar_chart = None
399
- if active_service == "operations_maintenance":
400
- line_chart = generate_crack_trend_chart()
401
- pie_chart = generate_crack_severity_chart()
402
- bar_chart = generate_severity_distribution_chart()
403
 
404
  return (
405
  frame[:, :, ::-1], # Convert BGR to RGB for Gradio
406
- json.dumps(last_detections, indent=2),
407
  "\n".join(log_entries[-10:]),
408
- line_chart,
409
- pie_chart,
410
- bar_chart,
411
- last_detected_images
412
  )
413
 
414
  # Gradio UI setup
@@ -449,74 +400,42 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
449
 
450
  with gr.Row():
451
  with gr.Column(scale=3):
452
- video_output = gr.Image(label="Live Drone Feed", width=320, height=240, elem_id="live-feed")
453
  with gr.Column(scale=1):
454
- detections_output = gr.Textbox(
455
  label="Detection Metrics",
456
  lines=10,
457
  interactive=False,
458
- placeholder="Detection metrics, crack trends, and severity distribution will appear here."
459
  )
460
 
461
  with gr.Row():
462
  with gr.Column(scale=2):
463
  logs_output = gr.Textbox(label="Live Logs", lines=8, interactive=False)
464
  with gr.Column(scale=1):
465
- chart_output = gr.Plot(label="Crack Trend (Operations Maintenance Only)")
466
- pie_output = gr.Plot(label="Crack Severity (Operations Maintenance Only)")
467
- bar_output = gr.Plot(label="Severity Distribution (Operations Maintenance Only)")
468
 
469
  with gr.Row():
470
- captured_images = gr.Gallery(
471
- label="Detected Frames (Last 100+)",
472
- columns=4,
473
- rows=5,
474
- height="auto",
475
- object_fit="contain",
476
- preview=True
477
- )
478
 
479
  with gr.Row():
480
  pause_btn = gr.Button("⏸️ Pause", variant="secondary")
481
  resume_btn = gr.Button("▶️ Resume", variant="primary")
482
- frame_slider = gr.Slider(0.01, 1.0, value=0.05, label="Frame Interval (seconds)", step=0.01)
483
 
484
  gr.HTML("""
485
  <style>
486
  #live-feed {
487
  border: 2px solid #4682B4;
488
  border-radius: 10px;
489
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
490
  }
491
  .gr-button-primary {
492
  background-color: #4682B4 !important;
493
- border-radius: 5px;
494
- transition: background-color 0.3s ease;
495
- }
496
- .gr-button-primary:hover {
497
- background-color: #5a9bd4 !important;
498
  }
499
  .gr-button-secondary {
500
  background-color: #FF6347 !important;
501
- border-radius: 5px;
502
- transition: background-color 0.3s ease;
503
- }
504
- .gr-button-secondary:hover {
505
- background-color: #ff826b !important;
506
- }
507
- .gr-plot {
508
- background-color: #f0f4f8;
509
- border-radius: 10px;
510
- padding: 10px;
511
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
512
- }
513
- .gr-gallery img {
514
- border: 1px solid #4682B4;
515
- border-radius: 5px;
516
- transition: transform 0.3s ease;
517
- }
518
- .gr-gallery img:hover {
519
- transform: scale(1.05);
520
  }
521
  </style>
522
  """)
@@ -567,16 +486,16 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
567
  def streaming_loop():
568
  while True:
569
  if not video_loaded:
570
- 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
571
  else:
572
- frame, detections, logs, line_chart, pie_chart, bar_chart, captured = monitor_feed()
573
  if frame is None:
574
- yield None, detections, logs, None, None, None, captured
575
  else:
576
- yield frame, detections, logs, line_chart, pie_chart, bar_chart, captured
577
  time.sleep(frame_rate)
578
 
579
- app.load(streaming_loop, outputs=[video_output, detections_output, logs_output, chart_output, pie_output, bar_output, captured_images])
580
 
581
  if __name__ == "__main__":
582
  app.launch(share=False)
 
5
  import json
6
  import random
7
  import logging
8
+ import matplotlib.pyplot as plt
9
  from datetime import datetime
10
  from collections import Counter
11
  from typing import Any, Dict, List, Optional, Tuple
12
+ import numpy as np
 
13
 
14
  # Suppress Ultralytics warning by setting a writable config directory
15
  os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
 
20
  from services.detection_service import process_frame as process_generic
21
  from services.metrics_service import update_metrics
22
  from services.overlay_service import overlay_boxes
23
+ from services.salesforce_dispatcher import send_to_salesforce
24
+ from services.shadow_detection import detect_shadow_coverage
25
  from services.thermal_service import process_thermal
26
+ from services.map_service import generate_map
27
  # Under Construction services
28
  from services.under_construction.earthwork_detection import process_earthwork
29
  from services.under_construction.culvert_check import process_culverts
30
  from services.under_construction.bridge_pier_check import process_bridge_piers
31
  # Operations Maintenance services
32
+ from services.operations_maintenance.crack_detection import detect_cracks_and_holes
33
  from services.operations_maintenance.pothole_detection import process_potholes
34
  from services.operations_maintenance.signage_check import process_signages
35
  # Road Safety services
 
54
 
55
  # Global variables
56
  paused: bool = False
57
+ frame_rate: float = 0.3
58
  frame_count: int = 0
59
  log_entries: List[str] = []
60
  crack_counts: List[int] = []
61
  crack_severity_all: List[str] = []
62
  last_frame: Optional[np.ndarray] = None
63
+ last_metrics: Dict[str, Any] = {}
64
  last_timestamp: str = ""
65
+ last_detected_cracks: List[str] = []
66
+ last_detected_holes: List[str] = []
67
  gps_coordinates: List[List[float]] = []
68
  video_loaded: bool = False
69
  active_service: Optional[str] = None
 
86
  log_entries.append(f"Using uploaded video: {video_path}")
87
  logging.info(f"Using uploaded video: {video_path}")
88
 
89
+ try:
90
+ preload_video()
91
+ video_loaded = True
92
+ status = f"Successfully loaded video: {video_path}"
93
+ log_entries.append(status)
94
+ logging.info(status)
95
+ return status
96
+ except Exception as e:
97
+ video_loaded = False
98
+ status = f"Error loading video: {str(e)}"
99
+ log_entries.append(status)
100
+ logging.error(status)
101
+ return status
102
 
103
  def set_active_service(
104
  service_name: str,
 
133
  logging.info("No service category enabled.")
134
  return None, "No Service Category Enabled"
135
 
136
+ def generate_line_chart() -> Optional[str]:
137
  if not crack_counts:
138
  return None
139
+ fig, ax = plt.subplots(figsize=(4, 2))
140
+ ax.plot(crack_counts[-50:], marker='o', color='#4682B4')
141
+ ax.set_title("Cracks/Holes Over Time")
142
+ ax.set_xlabel("Frame")
143
+ ax.set_ylabel("Count")
144
+ ax.grid(True)
145
+ fig.tight_layout()
146
+ chart_path = "chart_temp.png"
147
+ fig.savefig(chart_path)
148
+ plt.close(fig)
149
+ return chart_path
150
+
151
+ def generate_pie_chart() -> Optional[str]:
 
 
 
 
 
 
152
  if not crack_severity_all:
153
  return None
154
+ fig, ax = plt.subplots(figsize=(4, 2))
155
  count = Counter(crack_severity_all[-200:])
156
+ labels, sizes = zip(*count.items())
157
+ colors = ['#FF6347', '#FFA500', '#32CD32', '#800080']
158
+ ax.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=140, colors=colors[:len(labels)])
159
+ ax.axis('equal')
160
+ fig.tight_layout()
161
+ pie_path = "pie_temp.png"
162
+ fig.savefig(pie_path)
163
+ plt.close(fig)
164
+ return pie_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
  def monitor_feed() -> Tuple[
167
  Optional[np.ndarray],
168
  str,
169
  str,
170
+ List[str],
171
+ List[str],
172
+ Optional[str],
173
+ Optional[str]
174
  ]:
175
+ global paused, frame_count, last_frame, last_metrics, last_timestamp
176
+ global gps_coordinates, last_detected_cracks, last_detected_holes, video_loaded
177
 
178
  if not video_loaded:
179
  log_entries.append("Cannot start streaming: Video not loaded successfully.")
 
182
  None,
183
  json.dumps({"error": "Video not loaded. Please upload a video file."}, indent=2),
184
  "\n".join(log_entries[-10:]),
185
+ last_detected_cracks,
186
+ last_detected_holes,
187
  None,
188
+ None
 
 
189
  )
190
 
191
  if paused and last_frame is not None:
192
  frame = last_frame.copy()
193
+ metrics = last_metrics.copy()
194
  else:
195
  try:
196
  frame = get_next_video_frame()
 
201
  logging.error(f"Frame retrieval error: {str(e)}")
202
  return (
203
  None,
204
+ json.dumps(last_metrics, indent=2),
205
  "\n".join(log_entries[-10:]),
206
+ last_detected_cracks,
207
+ last_detected_holes,
208
  None,
209
+ None
 
 
210
  )
211
 
212
  all_detected_items: List[Dict[str, Any]] = []
213
+ shadow_issue = False
214
+ thermal_flag = False
215
 
216
  try:
217
  # Process frame based on active service
 
220
  culvert_dets, frame = process_culverts(frame)
221
  bridge_pier_dets, frame = process_bridge_piers(frame)
222
  all_detected_items.extend(earthwork_dets + culvert_dets + bridge_pier_dets)
 
223
  elif active_service == "operations_maintenance":
224
+ crack_hole_dets, frame = detect_cracks_and_holes(frame)
 
225
  pothole_dets, frame = process_potholes(frame)
226
  signage_dets, frame = process_signages(frame)
227
+ all_detected_items.extend(crack_hole_dets + pothole_dets + signage_dets)
 
228
  elif active_service == "road_safety":
229
  barrier_dets, frame = process_barriers(frame)
230
  lighting_dets, frame = process_lighting(frame)
231
  accident_dets, frame = process_accident_spots(frame)
232
  pothole_crack_dets, frame = detect_potholes_and_cracks(frame)
233
  all_detected_items.extend(barrier_dets + lighting_dets + accident_dets + pothole_crack_dets)
 
234
  elif active_service == "plantation":
235
  plant_dets, frame = process_plants(frame)
236
  health_dets, frame = process_plant_health(frame)
237
  missing_dets, frame = process_missing_patches(frame)
238
  all_detected_items.extend(plant_dets + health_dets + missing_dets)
 
239
  else:
240
  generic_dets, frame = process_generic(frame)
241
  all_detected_items.extend(generic_dets)
242
 
243
  # Apply shadow detection
244
+ shadow_issue = detect_shadow_coverage(TEMP_IMAGE_PATH)
 
 
 
245
 
246
+ # Apply thermal processing if frame is grayscale
247
  if len(frame.shape) == 2:
248
  thermal_results = process_thermal(frame)
249
  thermal_dets = thermal_results["detections"]
250
  frame = thermal_results["frame"]
251
  all_detected_items.extend(thermal_dets)
252
+ thermal_flag = bool(thermal_dets)
253
+
254
+ # Overlay detections
255
+ frame = overlay_boxes(frame, all_detected_items)
256
+
257
+ # Save temporary image
258
+ cv2.imwrite(TEMP_IMAGE_PATH, frame, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
259
 
260
  except Exception as e:
261
  log_entries.append(f"Processing Error: {str(e)}")
262
  logging.error(f"Processing error in {active_service}: {str(e)}")
263
  all_detected_items = []
264
 
 
 
 
 
 
 
 
265
  # Update detection metrics
266
  metrics = update_metrics(all_detected_items)
267
 
 
269
  gps_coord = [17.385044 + random.uniform(-0.001, 0.001), 78.486671 + frame_count * 0.0001]
270
  gps_coordinates.append(gps_coord)
271
 
272
+ # Save frame if detections are present
273
  detection_types = {item.get("type") for item in all_detected_items if "type" in item}
274
  if detection_types:
275
  try:
276
+ captured_frame_path = os.path.join(CAPTURED_FRAMES_DIR, f"detected_{frame_count}.jpg")
277
+ cv2.imwrite(captured_frame_path, frame)
278
+ for item in all_detected_items:
279
+ if item.get("type") == "crack":
280
+ last_detected_cracks.append(captured_frame_path)
281
+ if len(last_detected_cracks) > 100:
282
+ last_detected_cracks.pop(0)
283
+ elif item.get("type") == "hole":
284
+ last_detected_holes.append(captured_frame_path)
285
+ if len(last_detected_holes) > 100:
286
+ last_detected_holes.pop(0)
287
  except Exception as e:
288
  log_entries.append(f"Error saving captured frame: {str(e)}")
289
  logging.error(f"Error saving captured frame: {str(e)}")
290
 
291
  # Prepare data for Salesforce dispatch
292
  all_detections = {
293
+ "detections": all_detected_items,
294
  "metrics": metrics,
295
  "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
296
  "frame_count": frame_count,
297
+ "gps_coordinates": gps_coord,
298
+ "shadow_issue": shadow_issue,
299
+ "thermal": thermal_flag
300
  }
301
 
302
  # Dispatch to Salesforce
303
  try:
304
+ send_to_salesforce(all_detections)
305
  except Exception as e:
306
  log_entries.append(f"Salesforce Dispatch Error: {str(e)}")
307
  logging.error(f"Salesforce dispatch error: {str(e)}")
308
 
309
+ # Save processed frame
310
  try:
311
  frame_path = os.path.join(OUTPUT_DIR, f"frame_{frame_count:04d}.jpg")
312
  cv2.imwrite(frame_path, frame)
 
318
  frame_count += 1
319
  last_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
320
  last_frame = frame.copy()
321
+ last_metrics = metrics
322
 
323
+ # Track cracks/holes for metrics
324
+ crack_detected = len([item for item in all_detected_items if item.get("type") == "crack"])
325
+ hole_detected = len([item for item in all_detected_items if item.get("type") == "hole"])
326
+ if active_service in ["operations_maintenance", "road_safety"]:
327
  crack_severity_all.extend([
328
  item["severity"]
329
  for item in all_detected_items
330
+ if item.get("type") in ["crack", "hole"] and "severity" in item
331
  ])
332
+ crack_counts.append(crack_detected + hole_detected)
 
 
 
 
 
333
 
334
  # Log frame processing details
335
+ log_message = f"{last_timestamp} - Frame {frame_count} - Cracks: {crack_detected} - Holes: {hole_detected} - GPS: {gps_coord}"
 
 
336
  log_entries.append(log_message)
337
  logging.info(log_message)
 
338
 
339
  # Limit the size of logs and crack data
340
  if len(log_entries) > 100:
 
344
  if len(crack_severity_all) > 500:
345
  crack_severity_all.pop(0)
346
 
347
+ # Resize frame and add metadata for display
348
+ frame = cv2.resize(last_frame, (640, 480))
349
+ cv2.putText(frame, f"Frame: {frame_count}", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
350
+ cv2.putText(frame, f"{last_timestamp}", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
351
 
352
+ # Generate map
353
+ map_path = generate_map(gps_coordinates[-5:], [item for item in last_metrics.get("items", []) if item.get("type") in ["crack", "hole"]])
 
 
 
 
 
 
354
 
355
  return (
356
  frame[:, :, ::-1], # Convert BGR to RGB for Gradio
357
+ json.dumps(last_metrics, indent=2),
358
  "\n".join(log_entries[-10:]),
359
+ last_detected_cracks,
360
+ last_detected_holes,
361
+ generate_line_chart(),
362
+ map_path
363
  )
364
 
365
  # Gradio UI setup
 
400
 
401
  with gr.Row():
402
  with gr.Column(scale=3):
403
+ video_output = gr.Image(label="Live Drone Feed", width=640, height=480, elem_id="live-feed")
404
  with gr.Column(scale=1):
405
+ metrics_output = gr.Textbox(
406
  label="Detection Metrics",
407
  lines=10,
408
  interactive=False,
409
+ placeholder="Detection metrics, crack/hole counts will appear here."
410
  )
411
 
412
  with gr.Row():
413
  with gr.Column(scale=2):
414
  logs_output = gr.Textbox(label="Live Logs", lines=8, interactive=False)
415
  with gr.Column(scale=1):
416
+ crack_images = gr.Gallery(label="Detected Cracks (Last 100+)", columns=4, rows=13, height="auto")
417
+ hole_images = gr.Gallery(label="Detected Holes (Last 100+)", columns=4, rows=13, height="auto")
 
418
 
419
  with gr.Row():
420
+ chart_output = gr.Image(label="Crack/Hole Trend")
421
+ map_output = gr.Image(label="Crack/Hole Locations Map")
 
 
 
 
 
 
422
 
423
  with gr.Row():
424
  pause_btn = gr.Button("⏸️ Pause", variant="secondary")
425
  resume_btn = gr.Button("▶️ Resume", variant="primary")
426
+ frame_slider = gr.Slider(0.05, 1.0, value=0.3, label="Frame Interval (seconds)", step=0.05)
427
 
428
  gr.HTML("""
429
  <style>
430
  #live-feed {
431
  border: 2px solid #4682B4;
432
  border-radius: 10px;
 
433
  }
434
  .gr-button-primary {
435
  background-color: #4682B4 !important;
 
 
 
 
 
436
  }
437
  .gr-button-secondary {
438
  background-color: #FF6347 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  }
440
  </style>
441
  """)
 
486
  def streaming_loop():
487
  while True:
488
  if not video_loaded:
489
+ yield None, json.dumps({"error": "Video not loaded. Please upload a video file."}, indent=2), "\n".join(log_entries[-10:]), last_detected_cracks, last_detected_holes, None, None
490
  else:
491
+ frame, metrics, logs, cracks, holes, chart, map_path = monitor_feed()
492
  if frame is None:
493
+ yield None, metrics, logs, cracks, holes, chart, map_path
494
  else:
495
+ yield frame, metrics, logs, cracks, holes, chart, map_path
496
  time.sleep(frame_rate)
497
 
498
+ app.load(streaming_loop, outputs=[video_output, metrics_output, logs_output, crack_images, hole_images, chart_output, map_output])
499
 
500
  if __name__ == "__main__":
501
  app.launch(share=False)