lokesh341 commited on
Commit
b957a42
·
1 Parent(s): ef8e64f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +187 -205
app.py CHANGED
@@ -40,7 +40,7 @@ logging.basicConfig(
40
 
41
  # Globals
42
  paused = False
43
- frame_rate = 0.5 # Process every 0.5 seconds for real-time feel
44
  frame_count = 0
45
  log_entries = []
46
  crack_counts = []
@@ -48,15 +48,10 @@ crack_severity_all = []
48
  last_frame = None
49
  last_detections = {}
50
  last_timestamp = ""
51
- last_detected_images = [] # Store up to 100+ crack images
52
  gps_coordinates = []
53
  video_loaded = False
54
- service_toggles = {
55
- "under_construction": False,
56
- "operations_maintenance": False,
57
- "road_safety": False,
58
- "plantation": False
59
- }
60
 
61
  # Constants
62
  DEFAULT_VIDEO_PATH = "sample.mp4"
@@ -85,26 +80,49 @@ def initialize_video(video_file=None):
85
  logging.info(status)
86
  return status
87
 
88
- def toggle_service(service_name, value):
89
  """
90
- Toggle a service category.
 
91
  """
92
- global service_toggles
93
- service_toggles[service_name] = value
94
- status_msg = f"{service_name.replace('_', ' ').title()} Services {'Enabled' if value else 'Disabled'}"
95
- log_entries.append(status_msg)
96
- logging.info(status_msg)
97
- return status_msg
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
  def monitor_feed():
100
  """
101
- Main function to process video frames in real-time, showing only enabled service detections.
 
102
  """
103
  global paused, frame_count, last_frame, last_detections, last_timestamp, gps_coordinates, last_detected_images, video_loaded
104
 
105
  if not video_loaded:
106
  log_entries.append("Cannot start streaming: Video not loaded successfully.")
107
- logging.error("Cannot start streaming: Video not loaded successfully.")
108
  return None, json.dumps({"error": "Video not loaded. Please upload a video file."}, indent=2), "\n".join(log_entries[-10:]), None, None, last_detected_images
109
 
110
  if paused and last_frame is not None:
@@ -113,158 +131,91 @@ def monitor_feed():
113
  else:
114
  try:
115
  frame = get_next_video_frame()
 
 
116
  except RuntimeError as e:
117
  log_entries.append(f"Error: {str(e)}")
118
- logging.error(f"Video Frame Error: {str(e)}")
119
  return None, json.dumps(last_detections, indent=2), "\n".join(log_entries[-10:]), None, None, last_detected_images
120
 
121
- # Reset frame for overlay
122
- original_frame = frame.copy()
123
  all_detected_items = []
124
- display_items = [] # Items to display in the live feed
125
- crack_items = [] # Track crack items separately for charts and gallery
126
 
127
- # Counter for numbered labels, reset per frame
128
- line_counter = 1
129
-
130
- # Process enabled services
131
- if service_toggles["under_construction"]:
132
- try:
133
- earthwork_dets, _ = process_earthwork(original_frame.copy())
134
- culvert_dets, _ = process_culverts(original_frame.copy())
135
- bridge_pier_dets, _ = process_bridge_piers(original_frame.copy())
136
- uc_items = earthwork_dets + culvert_dets + bridge_pier_dets
137
- # Add numbered labels
138
- for item in uc_items:
139
- item["label"] = f"Line {line_counter} - {item.get('type', 'Unknown').capitalize()} (Conf: {item.get('confidence', 0):.2f})"
140
- line_counter += 1
141
- all_detected_items.extend(uc_items)
142
- display_items.extend(uc_items)
143
- except Exception as e:
144
- log_entries.append(f"Under Construction Error: {str(e)}")
145
- logging.error(f"Under Construction Error: {str(e)}")
146
-
147
- if service_toggles["operations_maintenance"]:
148
- try:
149
- crack_dets = detect_cracks_and_objects(original_frame.copy())
150
- pothole_dets, _ = process_potholes(original_frame.copy())
151
- signage_dets, _ = process_signages(original_frame.copy())
152
- om_items = crack_dets + pothole_dets + signage_dets
153
- # Add numbered labels
154
- for item in om_items:
155
- item["label"] = f"Line {line_counter} - {item.get('type', 'Unknown').capitalize()} (Conf: {item.get('confidence', 0):.2f})"
156
- line_counter += 1
157
- all_detected_items.extend(om_items)
158
- display_items.extend(om_items)
159
- # Track cracks for charts and gallery
160
- crack_items = [item for item in om_items if item.get('type') == 'crack']
161
- except Exception as e:
162
- log_entries.append(f"Operations Maintenance Error: {str(e)}")
163
- logging.error(f"Operations Maintenance Error: {str(e)}")
164
-
165
- if service_toggles["road_safety"]:
166
- try:
167
- barrier_dets, _ = process_barriers(original_frame.copy())
168
- lighting_dets, _ = process_lighting(original_frame.copy())
169
- accident_dets, _ = process_accident_spots(original_frame.copy())
170
- rs_items = barrier_dets + lighting_dets + accident_dets
171
- # Add numbered labels
172
- for item in rs_items:
173
- item["label"] = f"Line {line_counter} - {item.get('type', 'Unknown').replace('_', ' ').capitalize()} (Conf: {item.get('confidence', 0):.2f})"
174
- line_counter += 1
175
- all_detected_items.extend(rs_items)
176
- display_items.extend(rs_items)
177
- except Exception as e:
178
- log_entries.append(f"Road Safety Error: {str(e)}")
179
- logging.error(f"Road Safety Error: {str(e)}")
180
-
181
- if service_toggles["plantation"]:
182
- try:
183
- plant_dets, _ = process_plants(original_frame.copy())
184
- health_dets, _ = process_plant_health(original_frame.copy())
185
- missing_dets, _ = process_missing_patches(original_frame.copy())
186
- pl_items = plant_dets + health_dets + missing_dets
187
- # Add numbered labels
188
- for item in pl_items:
189
- label_type = item.get('type', 'Unknown')
190
- if label_type == "plant" and "health" in item:
191
- label_str = f"Line {line_counter} - Plant (Health: {item['health'].capitalize()})"
192
- else:
193
- label_str = f"Line {line_counter} - {label_type.replace('_', ' ').capitalize()}"
194
- if "confidence" in item:
195
- label_str += f" (Conf: {item['confidence']:.2f})"
196
- item["label"] = label_str
197
- line_counter += 1
198
- all_detected_items.extend(pl_items)
199
- display_items.extend(pl_items)
200
- except Exception as e:
201
- log_entries.append(f"Plantation Error: {str(e)}")
202
- logging.error(f"Plantation Error: {str(e)}")
203
 
204
- # Fallback: Run generic detection if no services are enabled
205
- if not any(service_toggles.values()) or not all_detected_items:
206
- try:
207
- generic_dets, _ = process_generic(original_frame.copy())
208
- # Add numbered labels
209
- for item in generic_dets:
210
- item["label"] = f"Line {line_counter} - {item.get('type', 'Unknown').capitalize()} (Conf: {item.get('confidence', 0):.2f})"
211
- line_counter += 1
212
  all_detected_items.extend(generic_dets)
213
- display_items.extend(generic_dets)
214
- except Exception as e:
215
- log_entries.append(f"Generic Detection Error: {str(e)}")
216
- logging.error(f"Generic Detection Error: {str(e)}")
217
 
218
- # Optional: Run shadow detection (only log results for now)
219
- try:
220
- shadow_results = detect_shadows(original_frame.copy())
221
  shadow_dets = shadow_results["detections"]
222
- # Add numbered labels if shadows are to be displayed
223
- if any(service_toggles.values()): # Only append if a service is enabled
224
- for item in shadow_dets:
225
- item["label"] = f"Line {line_counter} - Shadow (Conf: {item.get('confidence', 0):.2f})"
226
- line_counter += 1
227
- all_detected_items.extend(shadow_dets)
228
- display_items.extend(shadow_dets)
229
- except Exception as e:
230
- log_entries.append(f"Shadow Detection Error: {str(e)}")
231
- logging.error(f"Shadow Detection Error: {str(e)}")
232
 
233
- # Optional: Run thermal processing if frame is grayscale (simulated check)
234
- if len(original_frame.shape) == 2:
235
- try:
236
- thermal_results = process_thermal(original_frame.copy())
237
  thermal_dets = thermal_results["detections"]
238
- # Add numbered labels
239
- for item in thermal_dets:
240
- item["label"] = f"Line {line_counter} - Thermal Anomaly (Conf: {item.get('confidence', 0):.2f})"
241
- line_counter += 1
242
  all_detected_items.extend(thermal_dets)
243
- display_items.extend(thermal_dets)
244
- except Exception as e:
245
- log_entries.append(f"Thermal Processing Error: {str(e)}")
246
- logging.error(f"Thermal Processing Error: {str(e)}")
247
 
248
- # Overlay only the display_items on the frame
249
- frame = original_frame.copy()
250
- if display_items:
251
- frame = overlay_boxes(frame, display_items)
252
 
253
  # Save frame with overlays
254
- cv2.imwrite(TEMP_IMAGE_PATH, frame, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
 
 
 
 
 
255
  metrics = update_metrics(all_detected_items)
256
 
257
  # Simulate GPS coordinates
258
  gps_coord = [17.385044 + random.uniform(-0.001, 0.001), 78.486671 + frame_count * 0.0001]
259
  gps_coordinates.append(gps_coord)
260
 
261
- # Save frame if cracks are detected (only if operations_maintenance is enabled)
262
- if service_toggles["operations_maintenance"] and crack_items:
263
- captured_frame_path = os.path.join(CAPTURED_FRAMES_DIR, f"crack_{frame_count}.jpg")
264
- cv2.imwrite(captured_frame_path, frame)
265
- last_detected_images.append(captured_frame_path)
266
- if len(last_detected_images) > 100:
267
- last_detected_images.pop(0)
 
 
 
 
 
268
 
269
  # Combine detections for Salesforce
270
  all_detections = {
@@ -280,29 +231,35 @@ def monitor_feed():
280
  dispatch_to_salesforce(all_detections, all_detections["timestamp"])
281
  except Exception as e:
282
  log_entries.append(f"Salesforce Dispatch Error: {str(e)}")
283
- logging.error(f"Salesforce Dispatch Error: {str(e)}")
284
 
285
  # Save annotated frame
286
- frame_path = os.path.join(OUTPUT_DIR, f"frame_{frame_count:04d}.jpg")
287
- cv2.imwrite(frame_path, frame)
 
 
 
 
288
 
289
  frame_count += 1
290
  last_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
291
  last_frame = frame.copy()
292
  last_detections = metrics
293
 
294
- # Update logs and stats (focus on cracks if operations_maintenance is enabled)
295
- crack_detected = len(crack_items)
296
- if service_toggles["operations_maintenance"]:
297
  crack_severity_all.extend([
298
- item['severity']
299
- for item in crack_items
300
- if 'severity' in item
301
  ])
302
 
303
- log_msg = f"{last_timestamp} - Frame {frame_count} - Cracks: {crack_detected} - Total Displayed Detections: {len(display_items)} - GPS: {gps_coord} - Avg Conf: {metrics['avg_confidence']:.2f}"
304
- log_entries.append(log_msg)
305
- logging.info(log_msg)
 
 
306
  crack_counts.append(crack_detected)
307
 
308
  if len(log_entries) > 100:
@@ -317,10 +274,10 @@ def monitor_feed():
317
  cv2.putText(frame, f"Frame: {frame_count}", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
318
  cv2.putText(frame, f"{last_timestamp}", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
319
 
320
- # Generate charts (only if operations_maintenance is enabled)
321
  line_chart = None
322
  pie_chart = None
323
- if service_toggles["operations_maintenance"]:
324
  line_chart = generate_line_chart()
325
  pie_chart = generate_pie_chart()
326
 
@@ -414,51 +371,79 @@ def generate_pie_chart():
414
  }
415
 
416
  # Gradio UI
417
- with gr.Blocks(theme=gr.themes.Soft()) as app:
418
- gr.Markdown("# 🛡️ NHAI Drone Road Inspection Dashboard")
 
 
 
 
 
419
 
420
  # Video upload section
421
  with gr.Row():
422
- video_input = gr.File(label="Upload Video File (e.g., sample.mp4)", file_types=["video"])
423
- load_button = gr.Button("Load Video")
424
- video_status = gr.Textbox(label="Video Load Status", value="Please upload a video file or ensure 'sample.mp4' exists in the root directory.")
 
 
 
 
 
 
425
 
426
- # Toggles for service categories
427
  with gr.Row():
428
  with gr.Column():
429
- uc_toggle = gr.Checkbox(label="Enable Under Construction Services", value=service_toggles["under_construction"])
430
- uc_status = gr.Textbox(label="Under Construction Status", value="Under Construction Services: Disabled")
431
  with gr.Column():
432
- om_toggle = gr.Checkbox(label="Enable Operations Maintenance Services", value=service_toggles["operations_maintenance"])
433
- om_status = gr.Textbox(label="Operations Maintenance Status", value="Operations Maintenance Services: Disabled")
434
  with gr.Column():
435
- rs_toggle = gr.Checkbox(label="Enable Road Safety Services", value=service_toggles["road_safety"])
436
- rs_status = gr.Textbox(label="Road Safety Status", value="Road Safety Services: Disabled")
437
  with gr.Column():
438
- pl_toggle = gr.Checkbox(label="Enable Plantation Services", value=service_toggles["plantation"])
439
- pl_status = gr.Textbox(label="Plantation Status", value="Plantation Services: Disabled")
440
 
441
  status_text = gr.Markdown("**Status:** 🟢 Ready (Upload a video to start)")
442
 
443
  with gr.Row():
444
  with gr.Column(scale=3):
445
- video_output = gr.Image(label="Live Drone Feed", width=640, height=480)
446
  with gr.Column(scale=1):
447
- detections_output = gr.Textbox(label="Detection Metrics", lines=4)
448
 
449
  with gr.Row():
450
- logs_output = gr.Textbox(label="Live Logs", lines=8)
 
451
  with gr.Column(scale=1):
452
- chart_output = gr.Plot(label="Crack Trend")
453
- pie_output = gr.Plot(label="Crack Severity")
454
 
455
  with gr.Row():
456
- captured_images = gr.Gallery(label="Detected Cracks (Last 100+)", columns=4, rows=25)
457
 
458
  with gr.Row():
459
- pause_btn = gr.Button("⏸️ Pause")
460
- resume_btn = gr.Button("▶️ Resume")
461
- frame_slider = gr.Slider(0.0005, 5, value=0.5, label="Frame Interval (seconds)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462
 
463
  def toggle_pause():
464
  global paused
@@ -483,26 +468,23 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
483
  outputs=[video_status]
484
  )
485
 
486
- uc_toggle.change(
487
- toggle_service,
488
- inputs=[gr.State("under_construction"), uc_toggle],
489
- outputs=[uc_status]
490
- )
491
- om_toggle.change(
492
- toggle_service,
493
- inputs=[gr.State("operations_maintenance"), om_toggle],
494
- outputs=[om_status]
495
- )
496
- rs_toggle.change(
497
- toggle_service,
498
- inputs=[gr.State("road_safety"), rs_toggle],
499
- outputs=[rs_status]
500
- )
501
- pl_toggle.change(
502
- toggle_service,
503
- inputs=[gr.State("plantation"), pl_toggle],
504
- outputs=[pl_status]
505
- )
506
 
507
  pause_btn.click(toggle_pause, outputs=status_text)
508
  resume_btn.click(toggle_resume, outputs=status_text)
 
40
 
41
  # Globals
42
  paused = False
43
+ frame_rate = 0.1 # Faster frame rate for real-time feel
44
  frame_count = 0
45
  log_entries = []
46
  crack_counts = []
 
48
  last_frame = None
49
  last_detections = {}
50
  last_timestamp = ""
51
+ last_detected_images = [] # Store up to 100+ frames with detections
52
  gps_coordinates = []
53
  video_loaded = False
54
+ active_service = None # Track the active service category
 
 
 
 
 
55
 
56
  # Constants
57
  DEFAULT_VIDEO_PATH = "sample.mp4"
 
80
  logging.info(status)
81
  return status
82
 
83
+ def set_active_service(service_name, uc_val, om_val, rs_val, pl_val):
84
  """
85
+ Set the active service category based on toggles.
86
+ Only one service category can be active at a time.
87
  """
88
+ global active_service, service_toggles
89
+ toggles = {
90
+ "under_construction": uc_val,
91
+ "operations_maintenance": om_val,
92
+ "road_safety": rs_val,
93
+ "plantation": pl_val
94
+ }
95
+
96
+ # Ensure only one toggle is active
97
+ active_count = sum(toggles.values())
98
+ if active_count > 1:
99
+ log_entries.append("Error: Only one service category can be active at a time.")
100
+ logging.error("Multiple service categories enabled simultaneously.")
101
+ return None, "Error: Please enable only one service category at a time."
102
+
103
+ # Set active service
104
+ for service, enabled in toggles.items():
105
+ if enabled:
106
+ active_service = service
107
+ log_entries.append(f"{service.replace('_', ' ').title()} Services Enabled")
108
+ logging.info(f"{service} services enabled")
109
+ return active_service, f"{service.replace('_', ' ').title()} Services: Enabled"
110
+
111
+ active_service = None
112
+ log_entries.append("No service category enabled.")
113
+ logging.info("No service category enabled.")
114
+ return None, "No Service Category Enabled"
115
 
116
  def monitor_feed():
117
  """
118
+ Main function to process video frames in real-time.
119
+ Only the active service category processes the frame.
120
  """
121
  global paused, frame_count, last_frame, last_detections, last_timestamp, gps_coordinates, last_detected_images, video_loaded
122
 
123
  if not video_loaded:
124
  log_entries.append("Cannot start streaming: Video not loaded successfully.")
125
+ logging.error("Video not loaded successfully.")
126
  return None, json.dumps({"error": "Video not loaded. Please upload a video file."}, indent=2), "\n".join(log_entries[-10:]), None, None, last_detected_images
127
 
128
  if paused and last_frame is not None:
 
131
  else:
132
  try:
133
  frame = get_next_video_frame()
134
+ if frame is None:
135
+ raise RuntimeError("Failed to retrieve frame from video.")
136
  except RuntimeError as e:
137
  log_entries.append(f"Error: {str(e)}")
138
+ logging.error(f"Frame retrieval error: {str(e)}")
139
  return None, json.dumps(last_detections, indent=2), "\n".join(log_entries[-10:]), None, None, last_detected_images
140
 
141
+ # Initialize detected items list
 
142
  all_detected_items = []
 
 
143
 
144
+ # Process only the active service category
145
+ try:
146
+ if active_service == "under_construction":
147
+ earthwork_dets, frame = process_earthwork(frame)
148
+ culvert_dets, frame = process_culverts(frame)
149
+ bridge_pier_dets, frame = process_bridge_piers(frame)
150
+ all_detected_items.extend(earthwork_dets + culvert_dets + bridge_pier_dets)
151
+
152
+ elif active_service == "operations_maintenance":
153
+ crack_items = detect_cracks_and_objects(frame)
154
+ frame = overlay_boxes(frame, crack_items)
155
+ pothole_dets, frame = process_potholes(frame)
156
+ signage_dets, frame = process_signages(frame)
157
+ all_detected_items.extend(crack_items + pothole_dets + signage_dets)
158
+
159
+ elif active_service == "road_safety":
160
+ barrier_dets, frame = process_barriers(frame)
161
+ lighting_dets, frame = process_lighting(frame)
162
+ accident_dets, frame = process_accident_spots(frame)
163
+ all_detected_items.extend(barrier_dets + lighting_dets + accident_dets)
164
+
165
+ elif active_service == "plantation":
166
+ plant_dets, frame = process_plants(frame)
167
+ health_dets, frame = process_plant_health(frame)
168
+ missing_dets, frame = process_missing_patches(frame)
169
+ all_detected_items.extend(plant_dets + health_dets + missing_dets)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
+ else:
172
+ # Fallback: Run generic detection if no service is active
173
+ generic_dets, frame = process_generic(frame)
 
 
 
 
 
174
  all_detected_items.extend(generic_dets)
 
 
 
 
175
 
176
+ # Optional: Run shadow detection (affects all modes for better accuracy)
177
+ shadow_results = detect_shadows(frame)
 
178
  shadow_dets = shadow_results["detections"]
179
+ frame = shadow_results["frame"]
180
+ all_detected_items.extend(shadow_dets)
 
 
 
 
 
 
 
 
181
 
182
+ # Optional: Run thermal processing if frame is grayscale (simulated check)
183
+ if len(frame.shape) == 2: # Grayscale frame (simulating thermal input)
184
+ thermal_results = process_thermal(frame)
 
185
  thermal_dets = thermal_results["detections"]
186
+ frame = thermal_results["frame"]
 
 
 
187
  all_detected_items.extend(thermal_dets)
 
 
 
 
188
 
189
+ except Exception as e:
190
+ log_entries.append(f"Processing Error: {str(e)}")
191
+ logging.error(f"Processing error in {active_service}: {str(e)}")
192
+ all_detected_items = []
193
 
194
  # Save frame with overlays
195
+ try:
196
+ cv2.imwrite(TEMP_IMAGE_PATH, frame, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
197
+ except Exception as e:
198
+ log_entries.append(f"Error saving temp image: {str(e)}")
199
+ logging.error(f"Error saving temp image: {str(e)}")
200
+
201
  metrics = update_metrics(all_detected_items)
202
 
203
  # Simulate GPS coordinates
204
  gps_coord = [17.385044 + random.uniform(-0.001, 0.001), 78.486671 + frame_count * 0.0001]
205
  gps_coordinates.append(gps_coord)
206
 
207
+ # Save frame if there are detections (e.g., cracks, plants, etc.)
208
+ detection_types = {item.get("type") for item in all_detected_items if "type" in item}
209
+ if detection_types:
210
+ try:
211
+ captured_frame_path = os.path.join(CAPTURED_FRAMES_DIR, f"detected_{frame_count}.jpg")
212
+ cv2.imwrite(captured_frame_path, frame)
213
+ last_detected_images.append(captured_frame_path)
214
+ if len(last_detected_images) > 100:
215
+ last_detected_images.pop(0)
216
+ except Exception as e:
217
+ log_entries.append(f"Error saving captured frame: {str(e)}")
218
+ logging.error(f"Error saving captured frame: {str(e)}")
219
 
220
  # Combine detections for Salesforce
221
  all_detections = {
 
231
  dispatch_to_salesforce(all_detections, all_detections["timestamp"])
232
  except Exception as e:
233
  log_entries.append(f"Salesforce Dispatch Error: {str(e)}")
234
+ logging.error(f"Salesforce dispatch error: {str(e)}")
235
 
236
  # Save annotated frame
237
+ try:
238
+ frame_path = os.path.join(OUTPUT_DIR, f"frame_{frame_count:04d}.jpg")
239
+ cv2.imwrite(frame_path, frame)
240
+ except Exception as e:
241
+ log_entries.append(f"Error saving output frame: {str(e)}")
242
+ logging.error(f"Error saving output frame: {str(e)}")
243
 
244
  frame_count += 1
245
  last_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
246
  last_frame = frame.copy()
247
  last_detections = metrics
248
 
249
+ # Update logs and stats
250
+ crack_detected = len([item for item in all_detected_items if item.get("type") == "crack"]) if active_service == "operations_maintenance" else 0
251
+ if active_service == "operations_maintenance":
252
  crack_severity_all.extend([
253
+ item["severity"]
254
+ for item in all_detected_items
255
+ if item.get("type") == "crack" and "severity" in item
256
  ])
257
 
258
+ log_message = f"{last_timestamp} - Frame {frame_count} - Detections: {len(all_detected_items)} - GPS: {gps_coord} - Avg Conf: {metrics.get('avg_confidence', 0):.2f}"
259
+ if crack_detected:
260
+ log_message += f" - Cracks: {crack_detected}"
261
+ log_entries.append(log_message)
262
+ logging.info(log_message)
263
  crack_counts.append(crack_detected)
264
 
265
  if len(log_entries) > 100:
 
274
  cv2.putText(frame, f"Frame: {frame_count}", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
275
  cv2.putText(frame, f"{last_timestamp}", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
276
 
277
+ # Generate charts (only for operations_maintenance)
278
  line_chart = None
279
  pie_chart = None
280
+ if active_service == "operations_maintenance":
281
  line_chart = generate_line_chart()
282
  pie_chart = generate_pie_chart()
283
 
 
371
  }
372
 
373
  # Gradio UI
374
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green")) as app:
375
+ gr.Markdown(
376
+ """
377
+ # 🛡️ NHAI Drone Road Inspection Dashboard
378
+ Monitor highway conditions in real-time using drone footage. Select a service category to analyze specific aspects of the road.
379
+ """
380
+ )
381
 
382
  # Video upload section
383
  with gr.Row():
384
+ with gr.Column(scale=3):
385
+ video_input = gr.File(label="Upload Video File (e.g., sample.mp4)", file_types=["video"])
386
+ load_button = gr.Button("Load Video", variant="primary")
387
+ with gr.Column(scale=1):
388
+ video_status = gr.Textbox(
389
+ label="Video Load Status",
390
+ value="Please upload a video file or ensure 'sample.mp4' exists in the root directory.",
391
+ interactive=False
392
+ )
393
 
394
+ # Toggles for service categories with status indicators
395
  with gr.Row():
396
  with gr.Column():
397
+ uc_toggle = gr.Checkbox(label="Enable Under Construction Services", value=False)
398
+ uc_status = gr.Textbox(label="Under Construction Status", value="Disabled", interactive=False)
399
  with gr.Column():
400
+ om_toggle = gr.Checkbox(label="Enable Operations Maintenance Services", value=False)
401
+ om_status = gr.Textbox(label="Operations Maintenance Status", value="Disabled", interactive=False)
402
  with gr.Column():
403
+ rs_toggle = gr.Checkbox(label="Enable Road Safety Services", value=False)
404
+ rs_status = gr.Textbox(label="Road Safety Status", value="Disabled", interactive=False)
405
  with gr.Column():
406
+ pl_toggle = gr.Checkbox(label="Enable Plantation Services", value=False)
407
+ pl_status = gr.Textbox(label="Plantation Status", value="Disabled", interactive=False)
408
 
409
  status_text = gr.Markdown("**Status:** 🟢 Ready (Upload a video to start)")
410
 
411
  with gr.Row():
412
  with gr.Column(scale=3):
413
+ video_output = gr.Image(label="Live Drone Feed", width=640, height=480, elem_id="live-feed")
414
  with gr.Column(scale=1):
415
+ detections_output = gr.Textbox(label="Detection Metrics", lines=4, interactive=False)
416
 
417
  with gr.Row():
418
+ with gr.Column(scale=2):
419
+ logs_output = gr.Textbox(label="Live Logs", lines=8, interactive=False)
420
  with gr.Column(scale=1):
421
+ chart_output = gr.Plot(label="Crack Trend (Operations Maintenance Only)")
422
+ pie_output = gr.Plot(label="Crack Severity (Operations Maintenance Only)")
423
 
424
  with gr.Row():
425
+ captured_images = gr.Gallery(label="Detected Frames (Last 100+)", columns=4, rows=25, height="auto")
426
 
427
  with gr.Row():
428
+ pause_btn = gr.Button("⏸️ Pause", variant="secondary")
429
+ resume_btn = gr.Button("▶️ Resume", variant="primary")
430
+ frame_slider = gr.Slider(0.05, 1.0, value=0.1, label="Frame Interval (seconds)", step=0.05)
431
+
432
+ # Add some custom CSS for better UX
433
+ gr.HTML("""
434
+ <style>
435
+ #live-feed {
436
+ border: 2px solid #4682B4;
437
+ border-radius: 10px;
438
+ }
439
+ .gr-button-primary {
440
+ background-color: #4682B4 !important;
441
+ }
442
+ .gr-button-secondary {
443
+ background-color: #FF6347 !important;
444
+ }
445
+ </style>
446
+ """)
447
 
448
  def toggle_pause():
449
  global paused
 
468
  outputs=[video_status]
469
  )
470
 
471
+ # Toggle change events
472
+ def update_toggles(uc_val, om_val, rs_val, pl_val):
473
+ active, status_message = set_active_service("toggle", uc_val, om_val, rs_val, pl_val)
474
+ uc_status_val = "Enabled" if active == "under_construction" else "Disabled"
475
+ om_status_val = "Enabled" if active == "operations_maintenance" else "Disabled"
476
+ rs_status_val = "Enabled" if active == "road_safety" else "Disabled"
477
+ pl_status_val = "Enabled" if active == "plantation" else "Disabled"
478
+ return (
479
+ uc_status_val, om_status_val, rs_status_val, pl_status_val, status_message
480
+ )
481
+
482
+ toggle_inputs = [uc_toggle, om_toggle, rs_toggle, pl_toggle]
483
+ toggle_outputs = [uc_status, om_status, rs_status, pl_status, status_text]
484
+ uc_toggle.change(update_toggles, inputs=toggle_inputs, outputs=toggle_outputs)
485
+ om_toggle.change(update_toggles, inputs=toggle_inputs, outputs=toggle_outputs)
486
+ rs_toggle.change(update_toggles, inputs=toggle_inputs, outputs=toggle_outputs)
487
+ pl_toggle.change(update_toggles, inputs=toggle_inputs, outputs=toggle_outputs)
 
 
 
488
 
489
  pause_btn.click(toggle_pause, outputs=status_text)
490
  resume_btn.click(toggle_resume, outputs=status_text)