nagasurendra commited on
Commit
b763b36
·
verified ·
1 Parent(s): a783c6b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -576
app.py CHANGED
@@ -1,597 +1,102 @@
1
- import cv2
2
- import torch
3
  import gradio as gr
 
4
  import numpy as np
5
- import os
6
- import json
7
- import logging
8
- import matplotlib.pyplot as plt
9
- import csv
10
- import time
11
- from datetime import datetime
12
- from collections import Counter
13
- from typing import List, Dict, Any, Optional
14
  from ultralytics import YOLO
15
- import piexif
16
- import zipfile
17
- import base64
18
-
19
- os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
20
- logging.basicConfig(filename="app.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
21
-
22
- CAPTURED_FRAMES_DIR = "captured_frames"
23
- OUTPUT_DIR = "outputs"
24
- FLIGHT_LOG_DIR = "flight_logs"
25
- os.makedirs(CAPTURED_FRAMES_DIR, exist_ok=True)
26
- os.makedirs(OUTPUT_DIR, exist_ok=True)
27
- os.makedirs(FLIGHT_LOG_DIR, exist_ok=True)
28
- os.chmod(CAPTURED_FRAMES_DIR, 0o777)
29
- os.chmod(OUTPUT_DIR, 0o777)
30
- os.chmod(FLIGHT_LOG_DIR, 0o777)
31
-
32
- log_entries: List[str] = []
33
- detected_counts: List[int] = []
34
- detected_issues: List[str] = []
35
- gps_coordinates: List[List[float]] = []
36
- last_metrics: Dict[str, Any] = {}
37
- frame_count: int = 0
38
- SAVE_IMAGE_INTERVAL = 1
39
- DETECTION_CLASSES = ["Longitudinal", "Pothole", "Transverse"]
40
- MAX_IMAGES = 500
41
-
42
- device = "cuda" if torch.cuda.is_available() else "cpu"
43
- model = YOLO('./data/yolo11n (2).pt').to(device)
44
- if device == "cuda":
45
- model.half()
46
-
47
- def zip_all_outputs(report_path: str, video_path: str, chart_path: str, map_path: str) -> str:
48
- zip_path = os.path.join(OUTPUT_DIR, f"drone_analysis_outputs_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip")
49
- try:
50
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_STORED) as zipf:
51
- if os.path.exists(report_path):
52
- zipf.write(report_path, os.path.basename(report_path))
53
- if os.path.exists(video_path):
54
- zipf.write(video_path, os.path.join("outputs", os.path.basename(video_path)))
55
- if os.path.exists(chart_path):
56
- zipf.write(chart_path, os.path.join("outputs", os.path.basename(chart_path)))
57
- if os.path.exists(map_path):
58
- zipf.write(map_path, os.path.join("outputs", os.path.basename(map_path)))
59
- for file in detected_issues:
60
- if os.path.exists(file):
61
- zipf.write(file, os.path.join("captured_frames", os.path.basename(file)))
62
- for root, _, files in os.walk(FLIGHT_LOG_DIR):
63
- for file in files:
64
- file_path = os.path.join(root, file)
65
- zipf.write(file_path, os.path.join("flight_logs", file))
66
- log_entries.append(f"Created ZIP: {zip_path}")
67
- return zip_path
68
- except Exception as e:
69
- log_entries.append(f"Error: Failed to create ZIP: {str(e)}")
70
- return ""
71
-
72
- def generate_map(gps_coords: List[List[float]], items: List[Dict[str, Any]]) -> str:
73
- map_path = os.path.join(OUTPUT_DIR, f"map_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png")
74
- plt.figure(figsize=(4, 4))
75
- plt.scatter([x[1] for x in gps_coords], [x[0] for x in gps_coords], c='blue', label='GPS Points')
76
- plt.title("Issue Locations Map")
77
- plt.xlabel("Longitude")
78
- plt.ylabel("Latitude")
79
- plt.legend()
80
- plt.savefig(map_path)
81
- plt.close()
82
- return map_path
83
-
84
- def write_geotag(image_path: str, gps_coord: List[float]) -> bool:
85
- try:
86
- lat = abs(gps_coord[0])
87
- lon = abs(gps_coord[1])
88
- lat_ref = "N" if gps_coord[0] >= 0 else "S"
89
- lon_ref = "E" if gps_coord[1] >= 0 else "W"
90
- exif_dict = piexif.load(image_path) if os.path.exists(image_path) else {"GPS": {}}
91
- exif_dict["GPS"] = {
92
- piexif.GPSIFD.GPSLatitudeRef: lat_ref,
93
- piexif.GPSIFD.GPSLatitude: ((int(lat), 1), (0, 1), (0, 1)),
94
- piexif.GPSIFD.GPSLongitudeRef: lon_ref,
95
- piexif.GPSIFD.GPSLongitude: ((int(lon), 1), (0, 1), (0, 1))
96
- }
97
- piexif.insert(piexif.dump(exif_dict), image_path)
98
- return True
99
- except Exception as e:
100
- log_entries.append(f"Error: Failed to geotag {image_path}: {str(e)}")
101
- return False
102
-
103
- def write_flight_log(frame_count: int, gps_coord: List[float], timestamp: str) -> str:
104
- log_path = os.path.join(FLIGHT_LOG_DIR, f"flight_log_{frame_count:06d}.csv")
105
- try:
106
- with open(log_path, 'w', newline='') as csvfile:
107
- writer = csv.writer(csvfile)
108
- writer.writerow(["Frame", "Timestamp", "Latitude", "Longitude", "Speed_ms", "Satellites", "Altitude_m"])
109
- writer.writerow([frame_count, timestamp, gps_coord[0], gps_coord[1], 5.0, 12, 60])
110
- return log_path
111
- except Exception as e:
112
- log_entries.append(f"Error: Failed to write flight log {log_path}: {str(e)}")
113
- return ""
114
-
115
- def check_image_quality(frame: np.ndarray, input_resolution: int) -> bool:
116
- height, width, _ = frame.shape
117
- frame_resolution = width * height
118
- if frame_resolution < 2_073_600:
119
- log_entries.append(f"Frame {frame_count}: Resolution {width}x{height} below 2MP")
120
- return False
121
- if frame_resolution < input_resolution:
122
- log_entries.append(f"Frame {frame_count}: Output resolution below input")
123
- return False
124
- return True
125
-
126
- def update_metrics(detections: List[Dict[str, Any]]) -> Dict[str, Any]:
127
- counts = Counter([det["label"] for det in detections])
128
- return {
129
- "items": [{"type": k, "count": v} for k, v in counts.items()],
130
- "total_detections": len(detections),
131
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
132
- }
133
-
134
- def generate_line_chart() -> Optional[str]:
135
- if not detected_counts:
136
- return None
137
- plt.figure(figsize=(4, 2))
138
- plt.plot(detected_counts[-50:], marker='o', color='#FF8C00')
139
- plt.title("Detections Over Time")
140
- plt.xlabel("Frame")
141
- plt.ylabel("Count")
142
- plt.grid(True)
143
- plt.tight_layout()
144
- chart_path = os.path.join(OUTPUT_DIR, f"chart_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png")
145
- plt.savefig(chart_path)
146
- plt.close()
147
- return chart_path
148
-
149
- def generate_report(
150
- metrics: Dict[str, Any],
151
- detected_issues: List[str],
152
- gps_coordinates: List[List[float]],
153
- all_detections: List[Dict[str, Any]],
154
- frame_count: int,
155
- total_time: float,
156
- output_frames: int,
157
- output_fps: float,
158
- output_duration: float,
159
- detection_frame_count: int,
160
- chart_path: str,
161
- map_path: str,
162
- frame_times: List[float],
163
- resize_times: List[float],
164
- inference_times: List[float],
165
- io_times: List[float]
166
- ) -> str:
167
- log_entries.append("Generating report...")
168
- report_path = os.path.join(OUTPUT_DIR, f"drone_analysis_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html")
169
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
170
- report_content = [
171
- "<!DOCTYPE html>",
172
- "<html lang='en'>",
173
- "<head>",
174
- "<meta charset='UTF-8'>",
175
- "<title>NHAI Drone Survey Analysis Report</title>",
176
- "<style>",
177
- "body { font-family: Arial, sans-serif; margin: 40px; }",
178
- "h1, h2, h3 { color: #333; }",
179
- "ul { margin-left: 20px; }",
180
- "table { border-collapse: collapse; width: 100%; margin: 10px 0; }",
181
- "th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }",
182
- "th { background-color: #f2f2f2; }",
183
- "img { max-width: 600px; height: auto; margin: 10px 0; }",
184
- "p.caption { font-weight: bold; margin: 5px 0; }",
185
- "</style>",
186
- "</head>",
187
- "<body>",
188
- "<h1>NHAI Drone Survey Analysis Report</h1>",
189
- "",
190
- "<h2>Project Details</h2>",
191
- "<ul>",
192
- "<li><strong>Project Name:</strong> NH-44 Delhi-Hyderabad Section (Package XYZ)</li>",
193
- "<li><strong>Highway Section:</strong> Km 100 to Km 150</li>",
194
- "<li><strong>State:</strong> Telangana</li>",
195
- "<li><strong>Region:</strong> South</li>",
196
- f"<li><strong>Survey Date:</strong> {datetime.now().strftime('%Y-%m-%d')}</li>",
197
- "<li><strong>Drone Service Provider:</strong> ABC Drone Services Pvt. Ltd.</li>",
198
- "<li><strong>Technology Service Provider:</strong> XYZ AI Analytics Ltd.</li>",
199
- f"<li><strong>Work Order Reference:</strong> Data Lake WO-{datetime.now().strftime('%Y-%m-%d')}-XYZ</li>",
200
- "<li><strong>Report Prepared By:</strong> Nagasurendra, Data Analyst</li>",
201
- f"<li><strong>Report Date:</strong> {datetime.now().strftime('%Y-%m-%d')}</li>",
202
- "</ul>",
203
- "",
204
- "<h2>1. Introduction</h2>",
205
- "<p>This report consolidates drone survey results for NH-44 (Km 100–150) under Operations & Maintenance, per NHAI Policy Circular No. 18.98/2024, detecting potholes and cracks using YOLOv8 for Monthly Progress Report integration.</p>",
206
- "",
207
- "<h2>2. Drone Survey Metadata</h2>",
208
- "<ul>",
209
- "<li><strong>Drone Speed:</strong> 5 m/s</li>",
210
- "<li><strong>Drone Height:</strong> 60 m</li>",
211
- "<li><strong>Camera Sensor:</strong> RGB, 12 MP</li>",
212
- "<li><strong>Recording Type:</strong> JPEG, 90° nadir</li>",
213
- "<li><strong>Image Overlap:</strong> 85%</li>",
214
- "<li><strong>Flight Pattern:</strong> Single lap, ROW centered</li>",
215
- "<li><strong>Geotagging:</strong> Enabled</li>",
216
- "<li><strong>Satellite Lock:</strong> 12 satellites</li>",
217
- "<li><strong>Terrain Follow Mode:</strong> Enabled</li>",
218
- "</ul>",
219
- "",
220
- "<h2>3. Quality Check Results</h2>",
221
- "<ul>",
222
- "<li><strong>Resolution:</strong> 1920x1080</li>",
223
- "<li><strong>Overlap:</strong> 85%</li>",
224
- "<li><strong>Camera Angle:</strong> 90° nadir</li>",
225
- "<li><strong>Drone Speed:</strong> ≤ 5 m/s</li>",
226
- "<li><strong>Geotagging:</strong> 100% compliant</li>",
227
- "<li><strong>QC Status:</strong> Passed</li>",
228
- "</ul>",
229
- "",
230
- "<h2>4. AI/ML Analytics</h2>",
231
- f"<p><strong>Total Frames Processed:</strong> {frame_count}</p>",
232
- f"<p><strong>Detection Frames:</strong> {detection_frame_count} ({detection_frame_count/frame_count*100:.1f}%)</p>",
233
- f"<p><strong>Total Detections:</strong> {metrics['total_detections']}</p>",
234
- "<p><strong>Breakdown:</strong></p>",
235
- "<ul>"
236
- ]
237
-
238
- for item in metrics.get("items", []):
239
- percentage = (item["count"] / metrics["total_detections"] * 100) if metrics["total_detections"] > 0 else 0
240
- report_content.append(f"<li>{item['type']}: {item['count']} ({percentage:.1f}%)</li>")
241
- report_content.extend([
242
- "</ul>",
243
- f"<p><strong>Processing Time:</strong> {total_time:.1f} seconds</p>",
244
- f"<p><strong>Average Frame Time:</strong> {sum(frame_times)/len(frame_times):.1f} ms</p>" if frame_times else "<p><strong>Average Frame Time:</strong> N/A</p>",
245
- f"<p><strong>Average Resize Time:</strong> {sum(resize_times)/len(resize_times):.1f} ms</p>" if resize_times else "<p><strong>Average Resize Time:</strong> N/A</p>",
246
- f"<p><strong>Average Inference Time:</strong> {sum(inference_times)/len(inference_times):.1f} ms</p>" if inference_times else "<p><strong>Average Inference Time:</strong> N/A</p>",
247
- f"<p><strong>Average I/O Time:</strong> {sum(io_times)/len(io_times):.1f} ms</p>" if io_times else "<p><strong>Average I/O Time:</strong> N/A</p>",
248
- f"<p><strong>Timestamp:</strong> {metrics.get('timestamp', 'N/A')}</p>",
249
- "<p><strong>Summary:</strong> Potholes and cracks detected in high-traffic areas.</p>",
250
- "",
251
- "<h2>5. Output File Structure</h2>",
252
- "<p>ZIP file contains:</p>",
253
- "<ul>",
254
- f"<li><code>drone_analysis_report_{timestamp}.html</code>: This report</li>",
255
- "<li><code>outputs/processed_output.mp4</code>: Processed video with annotations</li>",
256
- f"<li><code>outputs/chart_{timestamp}.png</code>: Detection trend chart</li>",
257
- f"<li><code>outputs/map_{timestamp}.png</code>: Issue locations map</li>",
258
- "<li><code>captured_frames/detected_&lt;frame&gt;.jpg</code>: Geotagged images for detected issues</li>",
259
- "<li><code>flight_logs/flight_log_&lt;frame&gt;.csv</code>: Flight logs matching image frames</li>",
260
- "</ul>",
261
- "<p><strong>Note:</strong> Images and logs share frame numbers (e.g., <code>detected_000001.jpg</code> corresponds to <code>flight_log_000001.csv</code>).</p>",
262
- "",
263
- "<h2>6. Geotagged Images</h2>",
264
- f"<p><strong>Total Images:</strong> {len(detected_issues)}</p>",
265
- f"<p><strong>Storage:</strong> Data Lake <code>/project_xyz/images/{datetime.now().strftime('%Y%m%d')}</code></p>",
266
- "",
267
- "<table>",
268
- "<tr><th>Frame</th><th>Issue Type</th><th>GPS (Lat, Lon)</th><th>Timestamp</th><th>Confidence</th><th>Image Path</th></tr>"
269
- ])
270
-
271
- for detection in all_detections[:100]:
272
- report_content.append(
273
- f"<tr><td>{detection['frame']:06d}</td><td>{detection['label']}</td><td>({detection['gps'][0]:.6f}, {detection['gps'][1]:.6f})</td><td>{detection['timestamp']}</td><td>{detection['conf']:.1f}</td><td>captured_frames/{os.path.basename(detection['path'])}</td></tr>"
274
- )
275
-
276
- report_content.extend([
277
- "</table>",
278
- "",
279
- "<h2>7. Flight Logs</h2>",
280
- f"<p><strong>Total Logs:</strong> {len(detected_issues)}</p>",
281
- f"<p><strong>Storage:</strong> Data Lake <code>/project_xyz/flight_logs/{datetime.now().strftime('%Y%m%d')}</code></p>",
282
- "",
283
- "<table>",
284
- "<tr><th>Frame</th><th>Timestamp</th><th>Latitude</th><th>Longitude</th><th>Speed (m/s)</th><th>Satellites</th><th>Altitude (m)</th><th>Log Path</th></tr>"
285
- ])
286
-
287
- for detection in all_detections[:100]:
288
- log_path = f"flight_logs/flight_log_{detection['frame']:06d}.csv"
289
- report_content.append(
290
- f"<tr><td>{detection['frame']:06d}</td><td>{detection['timestamp']}</td><td>{detection['gps'][0]:.6f}</td><td>{detection['gps'][1]:.6f}</td><td>5.0</td><td>12</td><td>60</td><td>{log_path}</td></tr>"
291
- )
292
-
293
- report_content.extend([
294
- "</table>",
295
- "",
296
- "<h2>8. Processed Video</h2>",
297
- f"<p><strong>Path:</strong> outputs/processed_output.mp4</p>",
298
- f"<p><strong>Frames:</strong> {output_frames}</p>",
299
- f"<p><strong>FPS:</strong> {output_fps:.1f}</p>",
300
- f"<p><strong>Duration:</strong> {output_duration:.1f} seconds</p>",
301
- "",
302
- "<h2>9. Visualizations</h2>",
303
- f"<p><strong>Detection Trend Chart:</strong> outputs/chart_{timestamp}.png</p>",
304
- f"<p><strong>Issue Locations Map:</strong> outputs/map_{timestamp}.png</p>",
305
- "",
306
- "<h2>10. Processing Timestamps</h2>",
307
- f"<p><strong>Total Processing Time:</strong> {total_time:.1f} seconds</p>",
308
- "<p><strong>Log Entries (Last 10):</strong></p>",
309
- "<ul>"
310
- ])
311
-
312
- for entry in log_entries[-10:]:
313
- report_content.append(f"<li>{entry}</li>")
314
-
315
- report_content.extend([
316
- "</ul>",
317
- "",
318
- "<h2>11. Stakeholder Validation</h2>",
319
- "<ul>",
320
- "<li><strong>AE/IE Comments:</strong> [Pending]</li>",
321
- "<li><strong>PD/RO Comments:</strong> [Pending]</li>",
322
- "</ul>",
323
- "",
324
- "<h2>12. Recommendations</h2>",
325
- "<ul>",
326
- "<li>Repair potholes in high-traffic areas.</li>",
327
- "<li>Seal cracks to prevent further degradation.</li>",
328
- "<li>Schedule a follow-up survey.</li>",
329
- "</ul>",
330
- "",
331
- "<h2>13. Data Lake References</h2>",
332
- "<ul>",
333
- f"<li><strong>Images:</strong> <code>/project_xyz/images/{datetime.now().strftime('%Y%m%d')}</code></li>",
334
- f"<li><strong>Flight Logs:</strong> <code>/project_xyz/flight_logs/{datetime.now().strftime('%Y%m%d')}</code></li>",
335
- f"<li><strong>Video:</strong> <code>/project_xyz/videos/processed_output_{timestamp}.mp4</code></li>",
336
- f"<li><strong>DAMS Dashboard:</strong> <code>/project_xyz/dams/{datetime.now().strftime('%Y%m%d')}</code></li>",
337
- "</ul>",
338
- "",
339
- "<h2>14. Captured Images</h2>",
340
- "<p>Below are the embedded images from the captured frames directory showing detected issues:</p>",
341
- ""
342
- ])
343
-
344
- for image_path in detected_issues:
345
- if os.path.exists(image_path):
346
- image_name = os.path.basename(image_path)
347
- try:
348
- with open(image_path, "rb") as image_file:
349
- base64_string = base64.b64encode(image_file.read()).decode('utf-8')
350
- report_content.append(f"<img src='data:image/jpeg;base64,{base64_string}' alt='{image_name}'>")
351
- report_content.append(f"<p class='caption'>Image: {image_name}</p>")
352
- report_content.append("")
353
- except Exception as e:
354
- log_entries.append(f"Error: Failed to encode image {image_name} to base64: {str(e)}")
355
-
356
- report_content.extend([
357
- "</body>",
358
- "</html>"
359
- ])
360
-
361
- try:
362
- with open(report_path, 'w') as f:
363
- f.write("\n".join(report_content))
364
- log_entries.append(f"Report saved at: {report_path}")
365
- return report_path
366
- except Exception as e:
367
- log_entries.append(f"Error: Failed to save report: {str(e)}")
368
- return ""
369
-
370
- def process_video(video, resize_width=1920, resize_height=1080, frame_skip=10):
371
- global frame_count, last_metrics, detected_counts, detected_issues, gps_coordinates, log_entries
372
- frame_count = 0
373
- detected_counts.clear()
374
- detected_issues.clear()
375
- gps_coordinates.clear()
376
- log_entries.clear()
377
- last_metrics = {}
378
-
379
- if video is None:
380
- log_entries.append("Error: No video uploaded")
381
- return None, json.dumps({"error": "No video uploaded"}, indent=2), "\n".join(log_entries), [], None, None, None
382
-
383
- log_entries.append("Starting video processing...")
384
- start_time = time.time()
385
- cap = cv2.VideoCapture(video)
386
  if not cap.isOpened():
387
- log_entries.append("Error: Could not open video file")
388
- return None, json.dumps({"error": "Could not open video file"}, indent=2), "\n".join(log_entries), [], None, None, None
389
 
390
- frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
391
- frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
392
- input_resolution = frame_width * frame_height
393
- fps = cap.get(cv2.CAP_PROP_FPS)
394
- total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
395
- log_entries.append(f"Input video: {frame_width}x{frame_height} at {fps} FPS, {total_frames} frames")
396
 
397
- out_width, out_height = resize_width, resize_height
398
- output_path = os.path.join(OUTPUT_DIR, "processed_output.mp4")
399
- out = cv2.VideoWriter(output_path, cv2.VideoWriter_fourcc(*'XVID'), fps, (out_width, out_height))
400
- if not out.isOpened():
401
- log_entries.append("Error: Failed to initialize video writer")
402
- cap.release()
403
- return None, json.dumps({"error": "Video writer failed"}, indent=2), "\n".join(log_entries), [], None, None, None
404
 
405
- processed_frames = 0
406
- all_detections = []
407
- frame_times = []
408
- inference_times = []
409
- resize_times = []
410
- io_times = []
411
- detection_frame_count = 0
412
- output_frame_count = 0
413
- last_annotated_frame = None
414
- disk_space_threshold = 1024 * 1024 * 1024
415
-
416
- while True:
417
  ret, frame = cap.read()
418
  if not ret:
419
  break
420
- frame_count += 1
421
- if frame_count % frame_skip != 0:
422
- continue
423
- processed_frames += 1
424
- frame_start = time.time()
425
-
426
- if os.statvfs(os.path.dirname(output_path)).f_frsize * os.statvfs(os.path.dirname(output_path)).f_bavail < disk_space_threshold:
427
- log_entries.append("Error: Insufficient disk space")
428
- break
429
-
430
- frame = cv2.resize(frame, (out_width, out_height))
431
- resize_times.append((time.time() - frame_start) * 1000)
432
-
433
- if not check_image_quality(frame, input_resolution):
434
- continue
435
-
436
- inference_start = time.time()
437
- results = model(frame, verbose=False, conf=0.5, iou=0.7)
438
- annotated_frame = results[0].plot()
439
- inference_times.append((time.time() - inference_start) * 1000)
440
-
441
- frame_timestamp = frame_count / fps if fps > 0 else 0
442
- timestamp_str = f"{int(frame_timestamp // 60):02d}:{int(frame_timestamp % 60):02d}"
443
-
444
- gps_coord = [17.385044 + (frame_count * 0.0001), 78.486671 + (frame_count * 0.0001)]
445
- gps_coordinates.append(gps_coord)
446
-
447
- io_start = time.time()
448
- frame_detections = []
449
- for detection in results[0].boxes:
450
- cls = int(detection.cls)
451
- conf = float(detection.conf)
452
- box = detection.xyxy[0].cpu().numpy().astype(int).tolist()
453
- label = model.names[cls]
454
- if label in DETECTION_CLASSES:
455
- detection_data = {
456
- "label": label,
457
- "box": box,
458
- "conf": conf,
459
- "gps": gps_coord,
460
- "timestamp": timestamp_str,
461
- "frame": frame_count,
462
- "path": os.path.join(CAPTURED_FRAMES_DIR, f"detected_{frame_count:06d}.jpg")
463
- }
464
- frame_detections.append(detection_data)
465
- log_entries.append(f"Frame {frame_count} at {timestamp_str}: Detected {label} with confidence {conf:.2f}")
466
-
467
- if frame_detections:
468
- detection_frame_count += 1
469
- if detection_frame_count % SAVE_IMAGE_INTERVAL == 0:
470
- captured_frame_path = os.path.join(CAPTURED_FRAMES_DIR, f"detected_{frame_count:06d}.jpg")
471
- if cv2.imwrite(captured_frame_path, annotated_frame):
472
- if write_geotag(captured_frame_path, gps_coord):
473
- detected_issues.append(captured_frame_path)
474
- if len(detected_issues) > MAX_IMAGES:
475
- os.remove(detected_issues.pop(0))
476
- else:
477
- log_entries.append(f"Frame {frame_count}: Geotagging failed")
478
- else:
479
- log_entries.append(f"Error: Failed to save frame at {captured_frame_path}")
480
- write_flight_log(frame_count, gps_coord, timestamp_str)
481
-
482
- io_times.append((time.time() - io_start) * 1000)
483
-
484
- out.write(annotated_frame)
485
- output_frame_count += 1
486
- last_annotated_frame = annotated_frame
487
- if frame_skip > 1:
488
- for _ in range(frame_skip - 1):
489
- out.write(annotated_frame)
490
- output_frame_count += 1
491
-
492
- detected_counts.append(len(frame_detections))
493
- all_detections.extend(frame_detections)
494
-
495
- frame_times.append((time.time() - frame_start) * 1000)
496
- if len(log_entries) > 50:
497
- log_entries.pop(0)
498
-
499
- if time.time() - start_time > 600:
500
- log_entries.append("Error: Processing timeout after 600 seconds")
501
- break
502
 
503
- while output_frame_count < total_frames and last_annotated_frame is not None:
504
- out.write(last_annotated_frame)
505
- output_frame_count += 1
 
 
 
 
 
506
 
507
- last_metrics = update_metrics(all_detections)
 
 
 
508
 
509
- out.release()
510
  cap.release()
511
 
512
- cap = cv2.VideoCapture(output_path)
513
- if not cap.isOpened():
514
- log_entries.append("Error: Failed to open output video for verification")
515
- output_path = None
516
- else:
517
- output_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
518
- output_fps = cap.get(cv2.CAP_PROP_FPS)
519
- output_duration = output_frames / output_fps if output_fps > 0 else 0
520
- cap.release()
521
- log_entries.append(f"Output video: {output_frames} frames, {output_fps:.2f} FPS, {output_duration:.2f} seconds")
522
-
523
- total_time = time.time() - start_time
524
- log_entries.append(f"Processing completed in {total_time:.2f} seconds")
525
-
526
- chart_path = generate_line_chart()
527
- map_path = generate_map(gps_coordinates[-5:], all_detections)
528
- report_path = generate_report(
529
- last_metrics,
530
- detected_issues,
531
- gps_coordinates,
532
- all_detections,
533
- frame_count,
534
- total_time,
535
- output_frames,
536
- output_fps,
537
- output_duration,
538
- detection_frame_count,
539
- chart_path,
540
- map_path,
541
- frame_times,
542
- resize_times,
543
- inference_times,
544
- io_times
545
- )
546
- output_zip_path = zip_all_outputs(report_path, output_path, chart_path, map_path)
547
-
548
- return (
549
- output_path,
550
- json.dumps(last_metrics, indent=2),
551
- "\n".join(log_entries[-10:]),
552
- detected_issues,
553
- chart_path,
554
- map_path,
555
- output_zip_path
556
- )
557
-
558
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange")) as iface:
559
- gr.Markdown("# NHAI Road Defect Detection Dashboard")
560
  with gr.Row():
561
- with gr.Column(scale=3):
562
  video_input = gr.Video(label="Upload Video")
563
- width_slider = gr.Slider(320, 1920, value=1920, label="Output Width", step=1)
564
- height_slider = gr.Slider(240, 1080, value=1080, label="Output Height", step=1)
565
- skip_slider = gr.Slider(1, 20, value=10, label="Frame Skip", step=1)
566
- process_btn = gr.Button("Process Video", variant="primary")
567
- with gr.Column(scale=1):
568
- metrics_output = gr.Textbox(label="Detection Metrics", lines=5, interactive=False)
569
- with gr.Row():
570
- video_output = gr.Video(label="Processed Video")
571
- issue_gallery = gr.Gallery(label="Detected Issues", columns=4, height="auto", object_fit="contain")
572
- with gr.Row():
573
- chart_output = gr.Image(label="Detection Trend")
574
- map_output = gr.Image(label="Issue Locations Map")
575
- with gr.Row():
576
- logs_output = gr.Textbox(label="Logs", lines=5, interactive=False)
577
- with gr.Row():
578
- gr.Markdown("## Download Results")
579
- with gr.Row():
580
- output_zip_download = gr.File(label="Download All Outputs (ZIP)")
581
-
582
- process_btn.click(
 
 
583
  fn=process_video,
584
- inputs=[video_input, width_slider, height_slider, skip_slider],
585
- outputs=[
586
- video_output,
587
- metrics_output,
588
- logs_output,
589
- issue_gallery,
590
- chart_output,
591
- map_output,
592
- output_zip_download
593
- ]
594
  )
595
 
596
  if __name__ == "__main__":
597
- iface.launch()
 
 
 
1
  import gradio as gr
2
+ import cv2
3
  import numpy as np
 
 
 
 
 
 
 
 
 
4
  from ultralytics import YOLO
5
+ import os
6
+ import tempfile
7
+ from moviepy.editor import ImageSequenceClip
8
+ from PIL import Image
9
+
10
+ # Load both YOLO models
11
+ model_yolo11 = YOLO('./data/yolo11n.pt')
12
+ model_best = YOLO('./data/best.pt')
13
+
14
+ def process_video(video_path, model_name, conf_threshold=0.4):
15
+ """
16
+ Process the input video frame by frame using the selected YOLO model,
17
+ draw bounding boxes, and return the processed video path.
18
+ """
19
+ # Select model based on user input
20
+ model = model_yolo11 if model_name == "YOLO11n" else model_best
21
+
22
+ # Open video capture
23
+ cap = cv2.VideoCapture(video_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  if not cap.isOpened():
25
+ raise ValueError("Could not open video file")
 
26
 
27
+ # Get video properties
28
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
29
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
30
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
 
 
31
 
32
+ # Store processed frames
33
+ processed_frames = []
 
 
 
 
 
34
 
35
+ while cap.isOpened():
 
 
 
 
 
 
 
 
 
 
 
36
  ret, frame = cap.read()
37
  if not ret:
38
  break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ # Perform object detection
41
+ results = model.predict(
42
+ source=frame,
43
+ conf=conf_threshold,
44
+ imgsz=640,
45
+ show_labels=True,
46
+ show_conf=True
47
+ )
48
 
49
+ # Draw bounding boxes on the frame
50
+ for result in results:
51
+ im_array = result.plot() # Plot bounding boxes
52
+ processed_frames.append(im_array[..., ::-1]) # Convert BGR to RGB
53
 
 
54
  cap.release()
55
 
56
+ # Save processed frames to a temporary video file
57
+ temp_video_path = os.path.join(tempfile.gettempdir(), "output.mp4")
58
+ clip = ImageSequenceClip(processed_frames, fps=fps)
59
+ clip.write_videofile(temp_video_path, codec='libx264')
60
+
61
+ return temp_video_path
62
+
63
+ # Define Gradio interface
64
+ with gr.Blocks() as app:
65
+ gr.HTML("""
66
+ <h1 style='text-align: center'>
67
+ Video Object Detection with YOLO Models
68
+ </h1>
69
+ """)
70
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  with gr.Row():
72
+ with gr.Column():
73
  video_input = gr.Video(label="Upload Video")
74
+ model_choice = gr.Dropdown(
75
+ choices=["YOLO11n", "Best Model"],
76
+ label="Select Model",
77
+ value="YOLO11n"
78
+ )
79
+ conf_threshold = gr.Slider(
80
+ label="Confidence Threshold",
81
+ minimum=0.0,
82
+ maximum=1.0,
83
+ step=0.05,
84
+ value=0.4
85
+ )
86
+ process_button = gr.Button("Process Video")
87
+
88
+ with gr.Column():
89
+ video_output = gr.Video(
90
+ label="Processed Video",
91
+ streaming=True,
92
+ autoplay=True
93
+ )
94
+
95
+ process_button.click(
96
  fn=process_video,
97
+ inputs=[video_input, model_choice, conf_threshold],
98
+ outputs=[video_output]
 
 
 
 
 
 
 
 
99
  )
100
 
101
  if __name__ == "__main__":
102
+ app.launch()