Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -165,142 +165,179 @@ def generate_report(
|
|
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')}.
|
169 |
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
170 |
report_content = [
|
171 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
"",
|
173 |
-
"
|
174 |
-
"
|
175 |
-
"-
|
176 |
-
"
|
177 |
-
"
|
178 |
-
|
179 |
-
"
|
180 |
-
"
|
181 |
-
|
182 |
-
"
|
183 |
-
|
|
|
|
|
184 |
"",
|
185 |
-
"
|
186 |
-
"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
|
187 |
"",
|
188 |
-
"
|
189 |
-
"
|
190 |
-
"
|
191 |
-
"
|
192 |
-
"
|
193 |
-
"
|
194 |
-
"
|
195 |
-
"
|
196 |
-
"
|
197 |
-
"
|
|
|
|
|
198 |
"",
|
199 |
-
"
|
200 |
-
"
|
201 |
-
"
|
202 |
-
"
|
203 |
-
"
|
204 |
-
"
|
205 |
-
"
|
|
|
|
|
206 |
"",
|
207 |
-
"
|
208 |
-
f"
|
209 |
-
f"
|
210 |
-
f"
|
211 |
-
"
|
|
|
212 |
]
|
213 |
|
214 |
for item in metrics.get("items", []):
|
215 |
percentage = (item["count"] / metrics["total_detections"] * 100) if metrics["total_detections"] > 0 else 0
|
216 |
-
report_content.append(f"
|
217 |
report_content.extend([
|
218 |
-
|
219 |
-
f"
|
220 |
-
f"
|
221 |
-
f"
|
222 |
-
f"
|
223 |
-
f"
|
224 |
-
"
|
|
|
225 |
"",
|
226 |
-
"
|
227 |
-
"
|
228 |
-
"
|
229 |
-
"
|
230 |
-
"
|
231 |
-
"
|
232 |
-
"
|
233 |
-
"
|
234 |
-
"
|
|
|
|
|
235 |
"",
|
236 |
-
"
|
237 |
-
f"
|
238 |
-
f"
|
239 |
"",
|
240 |
-
"
|
241 |
-
"
|
242 |
])
|
243 |
|
244 |
for detection in all_detections[:100]:
|
245 |
report_content.append(
|
246 |
-
f"
|
247 |
)
|
248 |
|
249 |
report_content.extend([
|
|
|
250 |
"",
|
251 |
-
"
|
252 |
-
f"
|
253 |
-
f"
|
254 |
"",
|
255 |
-
"
|
256 |
-
"
|
257 |
])
|
258 |
|
259 |
for detection in all_detections[:100]:
|
260 |
log_path = f"flight_logs/flight_log_{detection['frame']:06d}.csv"
|
261 |
report_content.append(
|
262 |
-
f"
|
263 |
)
|
264 |
|
265 |
report_content.extend([
|
|
|
266 |
"",
|
267 |
-
"
|
268 |
-
f"
|
269 |
-
f"
|
270 |
-
f"
|
271 |
-
f"
|
272 |
"",
|
273 |
-
"
|
274 |
-
f"
|
275 |
-
f"
|
276 |
"",
|
277 |
-
"
|
278 |
-
f"
|
279 |
-
"
|
|
|
280 |
])
|
281 |
|
282 |
for entry in log_entries[-10:]:
|
283 |
-
report_content.append(f"
|
284 |
|
285 |
report_content.extend([
|
|
|
286 |
"",
|
287 |
-
"
|
288 |
-
"
|
289 |
-
"
|
|
|
|
|
290 |
"",
|
291 |
-
"
|
292 |
-
"
|
293 |
-
"
|
294 |
-
"
|
|
|
|
|
295 |
"",
|
296 |
-
"
|
297 |
-
|
298 |
-
f"
|
299 |
-
f"
|
300 |
-
f"
|
|
|
|
|
301 |
"",
|
302 |
-
"
|
303 |
-
"Below are the embedded images from the captured frames directory showing detected issues
|
304 |
""
|
305 |
])
|
306 |
|
@@ -310,12 +347,17 @@ def generate_report(
|
|
310 |
try:
|
311 |
with open(image_path, "rb") as image_file:
|
312 |
base64_string = base64.b64encode(image_file.read()).decode('utf-8')
|
313 |
-
report_content.append(f"
|
314 |
-
report_content.append(f"
|
315 |
report_content.append("")
|
316 |
except Exception as e:
|
317 |
log_entries.append(f"Error: Failed to encode image {image_name} to base64: {str(e)}")
|
318 |
|
|
|
|
|
|
|
|
|
|
|
319 |
try:
|
320 |
with open(report_path, 'w') as f:
|
321 |
f.write("\n".join(report_content))
|
|
|
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_<frame>.jpg</code>: Geotagged images for detected issues</li>",
|
259 |
+
"<li><code>flight_logs/flight_log_<frame>.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 |
|
|
|
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))
|