Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -26,6 +26,7 @@ from services.operations_maintenance.signage_check import process_signages
|
|
| 26 |
from services.road_safety.barrier_check import process_barriers
|
| 27 |
from services.road_safety.lighting_check import process_lighting
|
| 28 |
from services.road_safety.accident_spot_check import process_accident_spots
|
|
|
|
| 29 |
# Plantation services
|
| 30 |
from services.plantation.plant_count import process_plants
|
| 31 |
from services.plantation.plant_health import process_plant_health
|
|
@@ -40,7 +41,7 @@ logging.basicConfig(
|
|
| 40 |
|
| 41 |
# Globals
|
| 42 |
paused = False
|
| 43 |
-
frame_rate = 0.1
|
| 44 |
frame_count = 0
|
| 45 |
log_entries = []
|
| 46 |
crack_counts = []
|
|
@@ -48,10 +49,10 @@ crack_severity_all = []
|
|
| 48 |
last_frame = None
|
| 49 |
last_detections = {}
|
| 50 |
last_timestamp = ""
|
| 51 |
-
last_detected_images = []
|
| 52 |
gps_coordinates = []
|
| 53 |
video_loaded = False
|
| 54 |
-
active_service = None
|
| 55 |
|
| 56 |
# Constants
|
| 57 |
DEFAULT_VIDEO_PATH = "sample.mp4"
|
|
@@ -62,9 +63,6 @@ os.makedirs(CAPTURED_FRAMES_DIR, exist_ok=True)
|
|
| 62 |
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 63 |
|
| 64 |
def initialize_video(video_file=None):
|
| 65 |
-
"""
|
| 66 |
-
Initialize the video with the provided file or default path.
|
| 67 |
-
"""
|
| 68 |
global video_loaded, log_entries
|
| 69 |
release_video()
|
| 70 |
video_path = DEFAULT_VIDEO_PATH
|
|
@@ -81,11 +79,7 @@ def initialize_video(video_file=None):
|
|
| 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,
|
|
@@ -93,14 +87,12 @@ def set_active_service(service_name, uc_val, om_val, rs_val, pl_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
|
|
@@ -114,16 +106,12 @@ def set_active_service(service_name, uc_val, om_val, rs_val, pl_val):
|
|
| 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:
|
| 129 |
frame = last_frame.copy()
|
|
@@ -136,12 +124,10 @@ def monitor_feed():
|
|
| 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)
|
|
@@ -160,7 +146,8 @@ def monitor_feed():
|
|
| 160 |
barrier_dets, frame = process_barriers(frame)
|
| 161 |
lighting_dets, frame = process_lighting(frame)
|
| 162 |
accident_dets, frame = process_accident_spots(frame)
|
| 163 |
-
|
|
|
|
| 164 |
|
| 165 |
elif active_service == "plantation":
|
| 166 |
plant_dets, frame = process_plants(frame)
|
|
@@ -169,18 +156,15 @@ def monitor_feed():
|
|
| 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 |
-
|
| 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"]
|
|
@@ -191,7 +175,6 @@ def monitor_feed():
|
|
| 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:
|
|
@@ -200,11 +183,9 @@ def monitor_feed():
|
|
| 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:
|
|
@@ -217,7 +198,6 @@ def monitor_feed():
|
|
| 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 = {
|
| 222 |
"items": all_detected_items,
|
| 223 |
"metrics": metrics,
|
|
@@ -226,14 +206,12 @@ def monitor_feed():
|
|
| 226 |
"gps_coordinates": gps_coord
|
| 227 |
}
|
| 228 |
|
| 229 |
-
# Dispatch to Salesforce
|
| 230 |
try:
|
| 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)
|
|
@@ -246,9 +224,8 @@ def monitor_feed():
|
|
| 246 |
last_frame = frame.copy()
|
| 247 |
last_detections = metrics
|
| 248 |
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
if active_service == "operations_maintenance":
|
| 252 |
crack_severity_all.extend([
|
| 253 |
item["severity"]
|
| 254 |
for item in all_detected_items
|
|
@@ -269,28 +246,25 @@ def monitor_feed():
|
|
| 269 |
if len(crack_severity_all) > 500:
|
| 270 |
crack_severity_all.pop(0)
|
| 271 |
|
| 272 |
-
# Add frame count and timestamp to display
|
| 273 |
frame = cv2.resize(last_frame, (640, 480))
|
| 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 |
-
|
|
|
|
| 281 |
line_chart = generate_line_chart()
|
| 282 |
pie_chart = generate_pie_chart()
|
|
|
|
| 283 |
|
| 284 |
-
return frame[:, :, ::-1], json.dumps(last_detections, indent=2), "\n".join(log_entries[-10:]), line_chart, pie_chart, last_detected_images
|
| 285 |
|
| 286 |
def generate_line_chart():
|
| 287 |
-
"""
|
| 288 |
-
Generate a line chart for crack counts over time using Chart.js.
|
| 289 |
-
"""
|
| 290 |
if not crack_counts:
|
| 291 |
return None
|
| 292 |
|
| 293 |
-
data = crack_counts[-50:]
|
| 294 |
labels = list(range(len(data)))
|
| 295 |
|
| 296 |
return {
|
|
@@ -300,7 +274,7 @@ def generate_line_chart():
|
|
| 300 |
"datasets": [{
|
| 301 |
"label": "Cracks Over Time",
|
| 302 |
"data": data,
|
| 303 |
-
"borderColor": "#FF6347",
|
| 304 |
"backgroundColor": "rgba(255, 99, 71, 0.2)",
|
| 305 |
"fill": True,
|
| 306 |
"tension": 0.4
|
|
@@ -311,7 +285,7 @@ def generate_line_chart():
|
|
| 311 |
"plugins": {
|
| 312 |
"title": {
|
| 313 |
"display": True,
|
| 314 |
-
"text": "
|
| 315 |
}
|
| 316 |
},
|
| 317 |
"scales": {
|
|
@@ -333,13 +307,10 @@ def generate_line_chart():
|
|
| 333 |
}
|
| 334 |
|
| 335 |
def generate_pie_chart():
|
| 336 |
-
"""
|
| 337 |
-
Generate a pie chart for crack severity distribution using Chart.js.
|
| 338 |
-
"""
|
| 339 |
if not crack_severity_all:
|
| 340 |
return None
|
| 341 |
|
| 342 |
-
count = Counter(crack_severity_all[-200:])
|
| 343 |
labels = list(count.keys())
|
| 344 |
sizes = list(count.values())
|
| 345 |
|
|
@@ -350,9 +321,9 @@ def generate_pie_chart():
|
|
| 350 |
"datasets": [{
|
| 351 |
"data": sizes,
|
| 352 |
"backgroundColor": [
|
| 353 |
-
"#FF6347",
|
| 354 |
-
"#4682B4",
|
| 355 |
-
"#FFD700"
|
| 356 |
]
|
| 357 |
}]
|
| 358 |
},
|
|
@@ -361,7 +332,7 @@ def generate_pie_chart():
|
|
| 361 |
"plugins": {
|
| 362 |
"title": {
|
| 363 |
"display": True,
|
| 364 |
-
"text": "Crack Severity
|
| 365 |
},
|
| 366 |
"legend": {
|
| 367 |
"position": "top"
|
|
@@ -370,6 +341,57 @@ def generate_pie_chart():
|
|
| 370 |
}
|
| 371 |
}
|
| 372 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
# Gradio UI
|
| 374 |
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green")) as app:
|
| 375 |
gr.Markdown(
|
|
@@ -379,7 +401,6 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
|
|
| 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"])
|
|
@@ -391,7 +412,6 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
|
|
| 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)
|
|
@@ -418,8 +438,9 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
|
|
| 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
|
| 422 |
-
pie_output = gr.Plot(label="Crack Severity (Operations Maintenance
|
|
|
|
| 423 |
|
| 424 |
with gr.Row():
|
| 425 |
captured_images = gr.Gallery(label="Detected Frames (Last 100+)", columns=4, rows=25, height="auto")
|
|
@@ -429,7 +450,6 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
|
|
| 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 {
|
|
@@ -459,7 +479,6 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
|
|
| 459 |
global frame_rate
|
| 460 |
frame_rate = val
|
| 461 |
|
| 462 |
-
# Initialize video on app load
|
| 463 |
video_status.value = initialize_video()
|
| 464 |
|
| 465 |
load_button.click(
|
|
@@ -468,7 +487,6 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
|
|
| 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"
|
|
@@ -493,16 +511,16 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"))
|
|
| 493 |
def streaming_loop():
|
| 494 |
while True:
|
| 495 |
if not video_loaded:
|
| 496 |
-
yield None, json.dumps({"error": "Video not loaded. Please upload a video file."}, indent=2), "\n".join(log_entries[-10:]), None, None, last_detected_images
|
| 497 |
else:
|
| 498 |
-
frame, detections, logs, line_chart, pie_chart, captured = monitor_feed()
|
| 499 |
if frame is None:
|
| 500 |
-
yield None, detections, logs, line_chart, pie_chart, captured
|
| 501 |
else:
|
| 502 |
-
yield frame, detections, logs, line_chart, pie_chart, captured
|
| 503 |
time.sleep(frame_rate)
|
| 504 |
|
| 505 |
-
app.load(streaming_loop, outputs=[video_output, detections_output, logs_output, chart_output, pie_output, captured_images])
|
| 506 |
|
| 507 |
if __name__ == "__main__":
|
| 508 |
app.launch(share=True)
|
|
|
|
| 26 |
from services.road_safety.barrier_check import process_barriers
|
| 27 |
from services.road_safety.lighting_check import process_lighting
|
| 28 |
from services.road_safety.accident_spot_check import process_accident_spots
|
| 29 |
+
from services.road_safety.pothole_crack_detection import detect_potholes_and_cracks
|
| 30 |
# Plantation services
|
| 31 |
from services.plantation.plant_count import process_plants
|
| 32 |
from services.plantation.plant_health import process_plant_health
|
|
|
|
| 41 |
|
| 42 |
# Globals
|
| 43 |
paused = False
|
| 44 |
+
frame_rate = 0.1
|
| 45 |
frame_count = 0
|
| 46 |
log_entries = []
|
| 47 |
crack_counts = []
|
|
|
|
| 49 |
last_frame = None
|
| 50 |
last_detections = {}
|
| 51 |
last_timestamp = ""
|
| 52 |
+
last_detected_images = []
|
| 53 |
gps_coordinates = []
|
| 54 |
video_loaded = False
|
| 55 |
+
active_service = None
|
| 56 |
|
| 57 |
# Constants
|
| 58 |
DEFAULT_VIDEO_PATH = "sample.mp4"
|
|
|
|
| 63 |
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 64 |
|
| 65 |
def initialize_video(video_file=None):
|
|
|
|
|
|
|
|
|
|
| 66 |
global video_loaded, log_entries
|
| 67 |
release_video()
|
| 68 |
video_path = DEFAULT_VIDEO_PATH
|
|
|
|
| 79 |
return status
|
| 80 |
|
| 81 |
def set_active_service(service_name, uc_val, om_val, rs_val, pl_val):
|
| 82 |
+
global active_service
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
toggles = {
|
| 84 |
"under_construction": uc_val,
|
| 85 |
"operations_maintenance": om_val,
|
|
|
|
| 87 |
"plantation": pl_val
|
| 88 |
}
|
| 89 |
|
|
|
|
| 90 |
active_count = sum(toggles.values())
|
| 91 |
if active_count > 1:
|
| 92 |
log_entries.append("Error: Only one service category can be active at a time.")
|
| 93 |
logging.error("Multiple service categories enabled simultaneously.")
|
| 94 |
return None, "Error: Please enable only one service category at a time."
|
| 95 |
|
|
|
|
| 96 |
for service, enabled in toggles.items():
|
| 97 |
if enabled:
|
| 98 |
active_service = service
|
|
|
|
| 106 |
return None, "No Service Category Enabled"
|
| 107 |
|
| 108 |
def monitor_feed():
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
global paused, frame_count, last_frame, last_detections, last_timestamp, gps_coordinates, last_detected_images, video_loaded
|
| 110 |
|
| 111 |
if not video_loaded:
|
| 112 |
log_entries.append("Cannot start streaming: Video not loaded successfully.")
|
| 113 |
logging.error("Video not loaded successfully.")
|
| 114 |
+
return 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
|
| 115 |
|
| 116 |
if paused and last_frame is not None:
|
| 117 |
frame = last_frame.copy()
|
|
|
|
| 124 |
except RuntimeError as e:
|
| 125 |
log_entries.append(f"Error: {str(e)}")
|
| 126 |
logging.error(f"Frame retrieval error: {str(e)}")
|
| 127 |
+
return None, json.dumps(last_detections, indent=2), "\n".join(log_entries[-10:]), None, None, None, last_detected_images
|
| 128 |
|
|
|
|
| 129 |
all_detected_items = []
|
| 130 |
|
|
|
|
| 131 |
try:
|
| 132 |
if active_service == "under_construction":
|
| 133 |
earthwork_dets, frame = process_earthwork(frame)
|
|
|
|
| 146 |
barrier_dets, frame = process_barriers(frame)
|
| 147 |
lighting_dets, frame = process_lighting(frame)
|
| 148 |
accident_dets, frame = process_accident_spots(frame)
|
| 149 |
+
pothole_crack_dets, frame = detect_potholes_and_cracks(frame)
|
| 150 |
+
all_detected_items.extend(barrier_dets + lighting_dets + accident_dets + pothole_crack_dets)
|
| 151 |
|
| 152 |
elif active_service == "plantation":
|
| 153 |
plant_dets, frame = process_plants(frame)
|
|
|
|
| 156 |
all_detected_items.extend(plant_dets + health_dets + missing_dets)
|
| 157 |
|
| 158 |
else:
|
|
|
|
| 159 |
generic_dets, frame = process_generic(frame)
|
| 160 |
all_detected_items.extend(generic_dets)
|
| 161 |
|
|
|
|
| 162 |
shadow_results = detect_shadows(frame)
|
| 163 |
shadow_dets = shadow_results["detections"]
|
| 164 |
frame = shadow_results["frame"]
|
| 165 |
all_detected_items.extend(shadow_dets)
|
| 166 |
|
| 167 |
+
if len(frame.shape) == 2:
|
|
|
|
| 168 |
thermal_results = process_thermal(frame)
|
| 169 |
thermal_dets = thermal_results["detections"]
|
| 170 |
frame = thermal_results["frame"]
|
|
|
|
| 175 |
logging.error(f"Processing error in {active_service}: {str(e)}")
|
| 176 |
all_detected_items = []
|
| 177 |
|
|
|
|
| 178 |
try:
|
| 179 |
cv2.imwrite(TEMP_IMAGE_PATH, frame, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
|
| 180 |
except Exception as e:
|
|
|
|
| 183 |
|
| 184 |
metrics = update_metrics(all_detected_items)
|
| 185 |
|
|
|
|
| 186 |
gps_coord = [17.385044 + random.uniform(-0.001, 0.001), 78.486671 + frame_count * 0.0001]
|
| 187 |
gps_coordinates.append(gps_coord)
|
| 188 |
|
|
|
|
| 189 |
detection_types = {item.get("type") for item in all_detected_items if "type" in item}
|
| 190 |
if detection_types:
|
| 191 |
try:
|
|
|
|
| 198 |
log_entries.append(f"Error saving captured frame: {str(e)}")
|
| 199 |
logging.error(f"Error saving captured frame: {str(e)}")
|
| 200 |
|
|
|
|
| 201 |
all_detections = {
|
| 202 |
"items": all_detected_items,
|
| 203 |
"metrics": metrics,
|
|
|
|
| 206 |
"gps_coordinates": gps_coord
|
| 207 |
}
|
| 208 |
|
|
|
|
| 209 |
try:
|
| 210 |
dispatch_to_salesforce(all_detections, all_detections["timestamp"])
|
| 211 |
except Exception as e:
|
| 212 |
log_entries.append(f"Salesforce Dispatch Error: {str(e)}")
|
| 213 |
logging.error(f"Salesforce dispatch error: {str(e)}")
|
| 214 |
|
|
|
|
| 215 |
try:
|
| 216 |
frame_path = os.path.join(OUTPUT_DIR, f"frame_{frame_count:04d}.jpg")
|
| 217 |
cv2.imwrite(frame_path, frame)
|
|
|
|
| 224 |
last_frame = frame.copy()
|
| 225 |
last_detections = metrics
|
| 226 |
|
| 227 |
+
crack_detected = len([item for item in all_detected_items if item.get("type") == "crack"]) if active_service in ["operations_maintenance", "road_safety"] else 0
|
| 228 |
+
if active_service in ["operations_maintenance", "road_safety"]:
|
|
|
|
| 229 |
crack_severity_all.extend([
|
| 230 |
item["severity"]
|
| 231 |
for item in all_detected_items
|
|
|
|
| 246 |
if len(crack_severity_all) > 500:
|
| 247 |
crack_severity_all.pop(0)
|
| 248 |
|
|
|
|
| 249 |
frame = cv2.resize(last_frame, (640, 480))
|
| 250 |
cv2.putText(frame, f"Frame: {frame_count}", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
| 251 |
cv2.putText(frame, f"{last_timestamp}", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
| 252 |
|
|
|
|
| 253 |
line_chart = None
|
| 254 |
pie_chart = None
|
| 255 |
+
bar_chart = None
|
| 256 |
+
if active_service in ["operations_maintenance", "road_safety"]:
|
| 257 |
line_chart = generate_line_chart()
|
| 258 |
pie_chart = generate_pie_chart()
|
| 259 |
+
bar_chart = generate_bar_chart()
|
| 260 |
|
| 261 |
+
return frame[:, :, ::-1], json.dumps(last_detections, indent=2), "\n".join(log_entries[-10:]), line_chart, pie_chart, bar_chart, last_detected_images
|
| 262 |
|
| 263 |
def generate_line_chart():
|
|
|
|
|
|
|
|
|
|
| 264 |
if not crack_counts:
|
| 265 |
return None
|
| 266 |
|
| 267 |
+
data = crack_counts[-50:]
|
| 268 |
labels = list(range(len(data)))
|
| 269 |
|
| 270 |
return {
|
|
|
|
| 274 |
"datasets": [{
|
| 275 |
"label": "Cracks Over Time",
|
| 276 |
"data": data,
|
| 277 |
+
"borderColor": "#FF6347",
|
| 278 |
"backgroundColor": "rgba(255, 99, 71, 0.2)",
|
| 279 |
"fill": True,
|
| 280 |
"tension": 0.4
|
|
|
|
| 285 |
"plugins": {
|
| 286 |
"title": {
|
| 287 |
"display": True,
|
| 288 |
+
"text": "Crack Trend"
|
| 289 |
}
|
| 290 |
},
|
| 291 |
"scales": {
|
|
|
|
| 307 |
}
|
| 308 |
|
| 309 |
def generate_pie_chart():
|
|
|
|
|
|
|
|
|
|
| 310 |
if not crack_severity_all:
|
| 311 |
return None
|
| 312 |
|
| 313 |
+
count = Counter(crack_severity_all[-200:])
|
| 314 |
labels = list(count.keys())
|
| 315 |
sizes = list(count.values())
|
| 316 |
|
|
|
|
| 321 |
"datasets": [{
|
| 322 |
"data": sizes,
|
| 323 |
"backgroundColor": [
|
| 324 |
+
"#FF6347",
|
| 325 |
+
"#4682B4",
|
| 326 |
+
"#FFD700"
|
| 327 |
]
|
| 328 |
}]
|
| 329 |
},
|
|
|
|
| 332 |
"plugins": {
|
| 333 |
"title": {
|
| 334 |
"display": True,
|
| 335 |
+
"text": "Crack Severity"
|
| 336 |
},
|
| 337 |
"legend": {
|
| 338 |
"position": "top"
|
|
|
|
| 341 |
}
|
| 342 |
}
|
| 343 |
|
| 344 |
+
def generate_bar_chart():
|
| 345 |
+
if not crack_severity_all:
|
| 346 |
+
return None
|
| 347 |
+
|
| 348 |
+
count = Counter(crack_severity_all[-200:])
|
| 349 |
+
labels = list(count.keys())
|
| 350 |
+
sizes = list(count.values())
|
| 351 |
+
|
| 352 |
+
return {
|
| 353 |
+
"type": "bar",
|
| 354 |
+
"data": {
|
| 355 |
+
"labels": labels,
|
| 356 |
+
"datasets": [{
|
| 357 |
+
"label": "Severity Distribution",
|
| 358 |
+
"data": sizes,
|
| 359 |
+
"backgroundColor": [
|
| 360 |
+
"#FF6347",
|
| 361 |
+
"#4682B4",
|
| 362 |
+
"#FFD700"
|
| 363 |
+
]
|
| 364 |
+
}]
|
| 365 |
+
},
|
| 366 |
+
"options": {
|
| 367 |
+
"responsive": True,
|
| 368 |
+
"plugins": {
|
| 369 |
+
"title": {
|
| 370 |
+
"display": True,
|
| 371 |
+
"text": "Severity Distribution"
|
| 372 |
+
},
|
| 373 |
+
"legend": {
|
| 374 |
+
"display": False
|
| 375 |
+
}
|
| 376 |
+
},
|
| 377 |
+
"scales": {
|
| 378 |
+
"y": {
|
| 379 |
+
"beginAtZero": True,
|
| 380 |
+
"title": {
|
| 381 |
+
"display": True,
|
| 382 |
+
"text": "Count"
|
| 383 |
+
}
|
| 384 |
+
},
|
| 385 |
+
"x": {
|
| 386 |
+
"title": {
|
| 387 |
+
"display": True,
|
| 388 |
+
"text": "Severity"
|
| 389 |
+
}
|
| 390 |
+
}
|
| 391 |
+
}
|
| 392 |
+
}
|
| 393 |
+
}
|
| 394 |
+
|
| 395 |
# Gradio UI
|
| 396 |
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green")) as app:
|
| 397 |
gr.Markdown(
|
|
|
|
| 401 |
"""
|
| 402 |
)
|
| 403 |
|
|
|
|
| 404 |
with gr.Row():
|
| 405 |
with gr.Column(scale=3):
|
| 406 |
video_input = gr.File(label="Upload Video File (e.g., sample.mp4)", file_types=["video"])
|
|
|
|
| 412 |
interactive=False
|
| 413 |
)
|
| 414 |
|
|
|
|
| 415 |
with gr.Row():
|
| 416 |
with gr.Column():
|
| 417 |
uc_toggle = gr.Checkbox(label="Enable Under Construction Services", value=False)
|
|
|
|
| 438 |
with gr.Column(scale=2):
|
| 439 |
logs_output = gr.Textbox(label="Live Logs", lines=8, interactive=False)
|
| 440 |
with gr.Column(scale=1):
|
| 441 |
+
chart_output = gr.Plot(label="Crack Trend (Operations Maintenance & Road Safety)")
|
| 442 |
+
pie_output = gr.Plot(label="Crack Severity (Operations Maintenance & Road Safety)")
|
| 443 |
+
bar_output = gr.Plot(label="Severity Distribution (Operations Maintenance & Road Safety)")
|
| 444 |
|
| 445 |
with gr.Row():
|
| 446 |
captured_images = gr.Gallery(label="Detected Frames (Last 100+)", columns=4, rows=25, height="auto")
|
|
|
|
| 450 |
resume_btn = gr.Button("▶️ Resume", variant="primary")
|
| 451 |
frame_slider = gr.Slider(0.05, 1.0, value=0.1, label="Frame Interval (seconds)", step=0.05)
|
| 452 |
|
|
|
|
| 453 |
gr.HTML("""
|
| 454 |
<style>
|
| 455 |
#live-feed {
|
|
|
|
| 479 |
global frame_rate
|
| 480 |
frame_rate = val
|
| 481 |
|
|
|
|
| 482 |
video_status.value = initialize_video()
|
| 483 |
|
| 484 |
load_button.click(
|
|
|
|
| 487 |
outputs=[video_status]
|
| 488 |
)
|
| 489 |
|
|
|
|
| 490 |
def update_toggles(uc_val, om_val, rs_val, pl_val):
|
| 491 |
active, status_message = set_active_service("toggle", uc_val, om_val, rs_val, pl_val)
|
| 492 |
uc_status_val = "Enabled" if active == "under_construction" else "Disabled"
|
|
|
|
| 511 |
def streaming_loop():
|
| 512 |
while True:
|
| 513 |
if not video_loaded:
|
| 514 |
+
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
|
| 515 |
else:
|
| 516 |
+
frame, detections, logs, line_chart, pie_chart, bar_chart, captured = monitor_feed()
|
| 517 |
if frame is None:
|
| 518 |
+
yield None, detections, logs, line_chart, pie_chart, bar_chart, captured
|
| 519 |
else:
|
| 520 |
+
yield frame, detections, logs, line_chart, pie_chart, bar_chart, captured
|
| 521 |
time.sleep(frame_rate)
|
| 522 |
|
| 523 |
+
app.load(streaming_loop, outputs=[video_output, detections_output, logs_output, chart_output, pie_output, bar_output, captured_images])
|
| 524 |
|
| 525 |
if __name__ == "__main__":
|
| 526 |
app.launch(share=True)
|