Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -162,249 +162,217 @@ def generate_report(
|
|
| 162 |
inference_times: List[float],
|
| 163 |
io_times: List[float]
|
| 164 |
) -> str:
|
| 165 |
-
|
| 166 |
-
|
|
|
|
|
|
|
| 167 |
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
| 168 |
|
| 169 |
-
#
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
for item in metrics.get("items", []):
|
| 172 |
percentage = (item["count"] / metrics["total_detections"] * 100) if metrics["total_detections"] > 0 else 0
|
| 173 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
|
| 175 |
-
# Prepare image rows with embedded images
|
| 176 |
-
image_rows = []
|
| 177 |
for detection in all_detections[:100]:
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
f"{detection['frame']:06d} & {detection['label']} & "
|
| 181 |
-
f"({detection['gps'][0]:.6f}, {detection['gps'][1]:.6f}) & "
|
| 182 |
-
f"{detection['timestamp']} & {detection['conf']:.2f} & "
|
| 183 |
-
f"\\includegraphics[width=4cm, height=3cm, keepaspectratio]{{{image_basename}}} \\\\"
|
| 184 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
|
| 186 |
-
# Prepare flight log rows
|
| 187 |
-
log_rows = []
|
| 188 |
for detection in all_detections[:100]:
|
| 189 |
log_path = f"flight_logs/flight_log_{detection['frame']:06d}.csv"
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
f"{detection['gps'][0]:.6f} & {detection['gps'][1]:.6f} & "
|
| 193 |
-
f"5.0 & 12 & 60 & \\texttt{{{log_path}}} \\\\"
|
| 194 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
|
| 196 |
-
# Prepare log entries
|
| 197 |
-
log_entries_str = []
|
| 198 |
for entry in log_entries[-10:]:
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
\
|
| 210 |
-
\
|
| 211 |
-
\
|
| 212 |
-
\
|
| 213 |
-
\
|
| 214 |
-
\
|
| 215 |
-
\
|
| 216 |
-
\
|
| 217 |
-
\
|
| 218 |
-
\
|
| 219 |
-
|
| 220 |
-
\setlength{{\parskip}}{{0.5em}}
|
| 221 |
-
\usepackage{{noto}}
|
| 222 |
-
\graphicspath{{{{./captured_frames/}}}}
|
| 223 |
-
|
| 224 |
-
\begin{{document}}
|
| 225 |
-
|
| 226 |
-
% Title and project details
|
| 227 |
-
\section*{{NHAI Drone Survey Analysis Report}}
|
| 228 |
-
|
| 229 |
-
\subsection*{{Project Details}}
|
| 230 |
-
\begin{{itemize}}
|
| 231 |
-
\item \textbf{{Project Name:}} NH-44 Delhi-Hyderabad Section (Package XYZ)
|
| 232 |
-
\item \textbf{{Highway Section:}} Km 100 to Km 150
|
| 233 |
-
\item \textbf{{State:}} Telangana
|
| 234 |
-
\item \textbf{{Region:}} South
|
| 235 |
-
\item \textbf{{Survey Date:}} \today
|
| 236 |
-
\item \textbf{{Drone Service Provider:}} ABC Drone Services Pvt. Ltd.
|
| 237 |
-
\item \textbf{{Technology Service Provider:}} XYZ AI Analytics Ltd.
|
| 238 |
-
\item \textbf{{Work Order Reference:}} Data Lake WO-\today-XYZ
|
| 239 |
-
\item \textbf{{Report Prepared By:}} Nagasurendra, Data Analyst
|
| 240 |
-
\item \textbf{{Report Date:}} \today
|
| 241 |
-
\end{{itemize}}
|
| 242 |
-
|
| 243 |
-
\section{{Introduction}}
|
| 244 |
-
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.
|
| 245 |
-
|
| 246 |
-
\section{{Drone Survey Metadata}}
|
| 247 |
-
\begin{{itemize}}
|
| 248 |
-
\item \textbf{{Drone Speed:}} 5 m/s
|
| 249 |
-
\item \textbf{{Drone Height:}} 60 m
|
| 250 |
-
\item \textbf{{Camera Sensor:}} RGB, 12 MP
|
| 251 |
-
\item \textbf{{Recording Type:}} JPEG, 90° nadir
|
| 252 |
-
\item \textbf{{Image Overlap:}} 85\%
|
| 253 |
-
\item \textbf{{Flight Pattern:}} Single lap, ROW centered
|
| 254 |
-
\item \textbf{{Geotagging:}} Enabled
|
| 255 |
-
\item \textbf{{Satellite Lock:}} 12 satellites
|
| 256 |
-
\item \textbf{{Terrain Follow Mode:}} Enabled
|
| 257 |
-
\end{{itemize}}
|
| 258 |
-
|
| 259 |
-
\section{{Quality Check Results}}
|
| 260 |
-
\begin{{itemize}}
|
| 261 |
-
\item \textbf{{Resolution:}} 4000x3000 (12 MP)
|
| 262 |
-
\item \textbf{{Overlap:}} 85\%
|
| 263 |
-
\item \textbf{{Camera Angle:}} 90° nadir
|
| 264 |
-
\item \textbf{{Drone Speed:}} $\leq$ 5 m/s
|
| 265 |
-
\item \textbf{{Geotagging:}} 100\% compliant
|
| 266 |
-
\item \textbf{{QC Status:}} Passed
|
| 267 |
-
\end{{itemize}}
|
| 268 |
-
|
| 269 |
-
\section{{AI/ML Analytics}}
|
| 270 |
-
\begin{{itemize}}
|
| 271 |
-
\item \textbf{{Total Frames Processed:}} {frame_count}
|
| 272 |
-
\item \textbf{{Detection Frames:}} {detection_frame_count} ({detection_percentage:.2f}\%)
|
| 273 |
-
\item \textbf{{Total Detections:}} {total_detections}
|
| 274 |
-
\item \textbf{{Breakdown:}}
|
| 275 |
-
\begin{{itemize}}
|
| 276 |
-
{items}
|
| 277 |
-
\end{{itemize}}
|
| 278 |
-
\item \textbf{{Processing Time:}} {total_time:.2f} seconds
|
| 279 |
-
\item \textbf{{Average Frame Time:}} {avg_frame_time:.2f} ms
|
| 280 |
-
\item \textbf{{Average Resize Time:}} {avg_resize_time:.2f} ms
|
| 281 |
-
\item \textbf{{Average Inference Time:}} {avg_inference_time:.2f} ms
|
| 282 |
-
\item \textbf{{Average I/O Time:}} {avg_io_time:.2f} ms
|
| 283 |
-
\item \textbf{{Timestamp:}} {timestamp_str}
|
| 284 |
-
\item \textbf{{Summary:}} Potholes and cracks detected in high-traffic segments.
|
| 285 |
-
\end{{itemize}}
|
| 286 |
-
|
| 287 |
-
\section{{Output File Structure}}
|
| 288 |
-
\begin{{itemize}}
|
| 289 |
-
\item ZIP file contains:
|
| 290 |
-
\begin{{itemize}}
|
| 291 |
-
\item \texttt{{drone_analysis_report_{timestamp}.pdf}}: This report
|
| 292 |
-
\item \texttt{{outputs/processed_output.mp4}}: Processed video with annotations
|
| 293 |
-
\item \texttt{{outputs/chart_{timestamp}.png}}: Detection trend chart
|
| 294 |
-
\item \texttt{{outputs/map_{timestamp}.png}}: Issue locations map
|
| 295 |
-
\item \texttt{{captured_frames/detected_<frame>.jpg}}: Geotagged images for detected issues
|
| 296 |
-
\item \texttt{{flight_logs/flight_log_<frame>.csv}}: Flight logs matching image frames
|
| 297 |
-
\end{{itemize}}
|
| 298 |
-
\item \textbf{{Note:}} Images and logs share frame numbers (e.g., \texttt{{detected_000001.jpg}} corresponds to \texttt{{flight_log_000001.csv}}).
|
| 299 |
-
\end{{itemize}}
|
| 300 |
-
|
| 301 |
-
\section{{Geotagged Images}}
|
| 302 |
-
\begin{{itemize}}
|
| 303 |
-
\item \textbf{{Total Images:}} {total_images}
|
| 304 |
-
\item \textbf{{Storage:}} Data Lake \texttt{{/project_xyz/images/{date_str}}}
|
| 305 |
-
\end{{itemize}}
|
| 306 |
-
|
| 307 |
-
\begin{{longtable}}{{c l c c c p{{4cm}}}}
|
| 308 |
-
\toprule
|
| 309 |
-
\textbf{{Frame}} & \textbf{{Issue Type}} & \textbf{{GPS (Lat, Lon)}} & \textbf{{Timestamp}} & \textbf{{Confidence}} & \textbf{{Image}} \\
|
| 310 |
-
\midrule
|
| 311 |
-
\endhead
|
| 312 |
-
{image_rows}
|
| 313 |
-
\bottomrule
|
| 314 |
-
\end{{longtable}}
|
| 315 |
-
|
| 316 |
-
\section{{Flight Logs}}
|
| 317 |
-
\begin{{itemize}}
|
| 318 |
-
\item \textbf{{Total Logs:}} {total_logs}
|
| 319 |
-
\item \textbf{{Storage:}} Data Lake \texttt{{/project_xyz/flight_logs/{date_str}}}
|
| 320 |
-
\end{{itemize}}
|
| 321 |
-
|
| 322 |
-
\begin{{longtable}}{{c c c c c c c l}}
|
| 323 |
-
\toprule
|
| 324 |
-
\textbf{{Frame}} & \textbf{{Timestamp}} & \textbf{{Latitude}} & \textbf{{Longitude}} & \textbf{{Speed (m/s)}} & \textbf{{Satellites}} & \textbf{{Altitude (m)}} & \textbf{{Log Path}} \\
|
| 325 |
-
\midrule
|
| 326 |
-
\endhead
|
| 327 |
-
{log_rows}
|
| 328 |
-
\bottomrule
|
| 329 |
-
\end{{longtable}}
|
| 330 |
-
|
| 331 |
-
\section{{Processed Video}}
|
| 332 |
-
\begin{{itemize}}
|
| 333 |
-
\item \textbf{{Path:}} \texttt{{outputs/processed_output.mp4}}
|
| 334 |
-
\item \textbf{{Frames:}} {output_frames}
|
| 335 |
-
\item \textbf{{FPS:}} {output_fps:.2f}
|
| 336 |
-
\item \textbf{{Duration:}} {output_duration:.2f} seconds
|
| 337 |
-
\end{{itemize}}
|
| 338 |
-
|
| 339 |
-
\section{{Visualizations}}
|
| 340 |
-
\begin{{itemize}}
|
| 341 |
-
\item \textbf{{Detection Trend Chart:}} \texttt{{outputs/chart_{timestamp}.png}}
|
| 342 |
-
\item \textbf{{Issue Locations Map:}} \texttt{{outputs/map_{timestamp}.png}}
|
| 343 |
-
\end{{itemize}}
|
| 344 |
-
|
| 345 |
-
\section{{Processing Timestamps}}
|
| 346 |
-
\begin{{itemize}}
|
| 347 |
-
\item \textbf{{Total Processing Time:}} {total_time:.2f} seconds
|
| 348 |
-
\item \textbf{{Log Entries (Last 10):}}
|
| 349 |
-
\begin{{itemize}}
|
| 350 |
-
{log_entries}
|
| 351 |
-
\end{{itemize}}
|
| 352 |
-
\end{{itemize}}
|
| 353 |
-
|
| 354 |
-
\section{{Stakeholder Validation}}
|
| 355 |
-
\begin{{itemize}}
|
| 356 |
-
\item \textbf{{AE/IE Comments:}} [Pending]
|
| 357 |
-
\item \textbf{{PD/RO Comments:}} [Pending]
|
| 358 |
-
\end{{itemize}}
|
| 359 |
-
|
| 360 |
-
\section{{Recommendations}}
|
| 361 |
-
\begin{{itemize}}
|
| 362 |
-
\item Repair potholes in high-traffic segments.
|
| 363 |
-
\item Seal cracks to prevent degradation.
|
| 364 |
-
\item Schedule follow-up survey.
|
| 365 |
-
\end{{itemize}}
|
| 366 |
-
|
| 367 |
-
\section{{Data Lake References}}
|
| 368 |
-
\begin{{itemize}}
|
| 369 |
-
\item \textbf{{Images:}} \texttt{{/project_xyz/images/{date_str}}}
|
| 370 |
-
\item \textbf{{Flight Logs:}} \texttt{{/project_xyz/flight_logs/{date_str}}}
|
| 371 |
-
\item \textbf{{Video:}} \texttt{{/project_xyz/videos/processed_output_{date_str_no_dash}.mp4}}
|
| 372 |
-
\item \textbf{{DAMS Dashboard:}} \texttt{{/project_xyz/dams/{date_str}}}
|
| 373 |
-
\end{{itemize}}
|
| 374 |
-
|
| 375 |
-
\end{{document}}
|
| 376 |
-
"""
|
| 377 |
-
|
| 378 |
-
# Format the LaTeX template
|
| 379 |
-
report_content = latex_template.format(
|
| 380 |
-
frame_count=frame_count,
|
| 381 |
-
detection_frame_count=detection_frame_count,
|
| 382 |
-
detection_percentage=(detection_frame_count / frame_count * 100) if frame_count > 0 else 0,
|
| 383 |
-
total_detections=metrics['total_detections'],
|
| 384 |
-
items="\n ".join(items),
|
| 385 |
-
total_time=total_time,
|
| 386 |
-
avg_frame_time=avg_frame_time,
|
| 387 |
-
avg_resize_time=avg_resize_time,
|
| 388 |
-
avg_inference_time=avg_inference_time,
|
| 389 |
-
avg_io_time=avg_io_time,
|
| 390 |
-
timestamp_str=metrics.get('timestamp', 'N/A'),
|
| 391 |
-
timestamp=timestamp,
|
| 392 |
-
total_images=len(detected_issues),
|
| 393 |
-
total_logs=len(detected_issues),
|
| 394 |
-
date_str=datetime.now().strftime('%Y-%m-%d'),
|
| 395 |
-
date_str_no_dash=datetime.now().strftime('%Y%m%d'),
|
| 396 |
-
image_rows="\n ".join(image_rows),
|
| 397 |
-
log_rows="\n ".join(log_rows),
|
| 398 |
-
log_entries="\n ".join(log_entries_str)
|
| 399 |
-
)
|
| 400 |
|
| 401 |
try:
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
return report_path
|
| 406 |
except Exception as e:
|
| 407 |
-
log_entries.append(f"Error: Failed to
|
| 408 |
return ""
|
| 409 |
|
| 410 |
def process_video(video, resize_width=4000, resize_height=3000, frame_skip=5):
|
|
|
|
| 162 |
inference_times: List[float],
|
| 163 |
io_times: List[float]
|
| 164 |
) -> str:
|
| 165 |
+
# Generating LaTeX-based PDF report
|
| 166 |
+
log_entries.append("Generating LaTeX report...")
|
| 167 |
+
report_path = os.path.join(OUTPUT_DIR, f"drone_analysis_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf")
|
| 168 |
+
tex_path = os.path.join(OUTPUT_DIR, f"drone_analysis_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.tex")
|
| 169 |
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
| 170 |
|
| 171 |
+
# LaTeX document content
|
| 172 |
+
report_content = [
|
| 173 |
+
r"\documentclass[a4paper,12pt]{article}",
|
| 174 |
+
r"\usepackage[utf8]{inputenc}",
|
| 175 |
+
r"\usepackage[T1]{fontenc}",
|
| 176 |
+
r"\usepackage{geometry}",
|
| 177 |
+
r"\geometry{margin=1in}",
|
| 178 |
+
r"\usepackage{graphicx}",
|
| 179 |
+
r"\graphicspath{{./}}",
|
| 180 |
+
r"\usepackage{booktabs}",
|
| 181 |
+
r"\usepackage{longtable}",
|
| 182 |
+
r"\usepackage{enumitem}",
|
| 183 |
+
r"\usepackage{hyperref}",
|
| 184 |
+
r"\hypersetup{colorlinks=true,linkcolor=blue,filecolor=blue,urlcolor=blue}",
|
| 185 |
+
r"\usepackage{amsmath}",
|
| 186 |
+
r"\usepackage{caption}",
|
| 187 |
+
r"\usepackage{pdfpages}",
|
| 188 |
+
r"\usepackage{times}", # Using Times font
|
| 189 |
+
r"\begin{document}",
|
| 190 |
+
r"\title{NHAI Drone Survey Analysis Report}",
|
| 191 |
+
r"\author{Nagasurendra, Data Analyst}",
|
| 192 |
+
r"\date{\today}",
|
| 193 |
+
r"\maketitle",
|
| 194 |
+
r"\tableofcontents",
|
| 195 |
+
r"\newpage",
|
| 196 |
+
r"\section{Project Details}",
|
| 197 |
+
r"\begin{itemize}",
|
| 198 |
+
r"\item \textbf{Project Name}: NH-44 Delhi-Hyderabad Section (Package XYZ)",
|
| 199 |
+
r"\item \textbf{Highway Section}: Km 100 to Km 150",
|
| 200 |
+
r"\item \textbf{State}: Telangana",
|
| 201 |
+
r"\item \textbf{Region}: South",
|
| 202 |
+
rf"\item \textbf{Survey Date}: {datetime.now().strftime('%Y-%m-%d')}",
|
| 203 |
+
r"\item \textbf{Drone Service Provider}: ABC Drone Services Pvt. Ltd.",
|
| 204 |
+
r"\item \textbf{Technology Service Provider}: XYZ AI Analytics Ltd.",
|
| 205 |
+
rf"\item \textbf{Work Order Reference}: Data Lake WO-{datetime.now().strftime('%Y-%m-%d')}-XYZ",
|
| 206 |
+
r"\item \textbf{Report Prepared By}: Nagasurendra, Data Analyst",
|
| 207 |
+
rf"\item \textbf{Report Date}: {datetime.now().strftime('%Y-%m-%d')}",
|
| 208 |
+
r"\end{itemize}",
|
| 209 |
+
r"\section{Introduction}",
|
| 210 |
+
r"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.",
|
| 211 |
+
r"\section{Drone Survey Metadata}",
|
| 212 |
+
r"\begin{itemize}",
|
| 213 |
+
r"\item \textbf{Drone Speed}: 5 m/s",
|
| 214 |
+
r"\item \textbf{Drone Height}: 60 m",
|
| 215 |
+
r"\item \textbf{Camera Sensor}: RGB, 12 MP",
|
| 216 |
+
r"\item \textbf{Recording Type}: JPEG, 90$^\circ$ nadir",
|
| 217 |
+
r"\item \textbf{Image Overlap}: 85\%",
|
| 218 |
+
r"\item \textbf{Flight Pattern}: Single lap, ROW centered",
|
| 219 |
+
r"\item \textbf{Geotagging}: Enabled",
|
| 220 |
+
r"\item \textbf{Satellite Lock}: 12 satellites",
|
| 221 |
+
r"\item \textbf{Terrain Follow Mode}: Enabled",
|
| 222 |
+
r"\end{itemize}",
|
| 223 |
+
r"\section{Quality Check Results}",
|
| 224 |
+
r"\begin{itemize}",
|
| 225 |
+
r"\item \textbf{Resolution}: 4000x3000 (12 MP)",
|
| 226 |
+
r"\item \textbf{Overlap}: 85\%",
|
| 227 |
+
r"\item \textbf{Camera Angle}: 90$^\circ$ nadir",
|
| 228 |
+
r"\item \textbf{Drone Speed}: $\leq$ 5 m/s",
|
| 229 |
+
r"\item \textbf{Geotagging}: 100\% compliant",
|
| 230 |
+
r"\item \textbf{QC Status}: Passed",
|
| 231 |
+
r"\end{itemize}",
|
| 232 |
+
r"\section{AI/ML Analytics}",
|
| 233 |
+
rf"Total Frames Processed: {frame_count}\par",
|
| 234 |
+
rf"Detection Frames: {detection_frame_count} ({detection_frame_count/frame_count*100:.2f}\%)\par",
|
| 235 |
+
rf"Total Detections: {metrics['total_detections']}\par",
|
| 236 |
+
r"\textbf{Breakdown:}",
|
| 237 |
+
r"\begin{itemize}"
|
| 238 |
+
]
|
| 239 |
+
|
| 240 |
for item in metrics.get("items", []):
|
| 241 |
percentage = (item["count"] / metrics["total_detections"] * 100) if metrics["total_detections"] > 0 else 0
|
| 242 |
+
report_content.append(rf"\item {item['type']}: {item['count']} ({percentage:.2f}\%)")
|
| 243 |
+
report_content.extend([
|
| 244 |
+
r"\end{itemize}",
|
| 245 |
+
rf"Processing Time: {total_time:.2f} seconds\par",
|
| 246 |
+
rf"Average Frame Time: {sum(frame_times)/len(frame_times):.2f} ms" if frame_times else r"Average Frame Time: N/A\par",
|
| 247 |
+
rf"Average Resize Time: {sum(resize_times)/len(resize_times):.2f} ms" if resize_times else r"Average Resize Time: N/A\par",
|
| 248 |
+
rf"Average Inference Time: {sum(inference_times)/len(inference_times):.2f} ms" if inference_times else r"Average Inference Time: N/A\par",
|
| 249 |
+
rf"Average I/O Time: {sum(io_times)/len(io_times):.2f} ms" if io_times else r"Average I/O Time: N/A\par",
|
| 250 |
+
rf"Timestamp: {metrics.get('timestamp', 'N/A')}\par",
|
| 251 |
+
r"Summary: Potholes and cracks detected in high-traffic segments.\par",
|
| 252 |
+
r"\section{Output File Structure}",
|
| 253 |
+
r"\begin{itemize}",
|
| 254 |
+
rf"\item \texttt{{drone\_analysis\_report\_<{timestamp}>.pdf}}: This report",
|
| 255 |
+
r"\item \texttt{outputs/processed\_output.mp4}: Processed video with annotations",
|
| 256 |
+
rf"\item \texttt{{outputs/chart\_<{timestamp}>.png}}: Detection trend chart",
|
| 257 |
+
rf"\item \texttt{{outputs/map\_<{timestamp}>.png}}: Issue locations map",
|
| 258 |
+
r"\item \texttt{captured\_frames/detected\_<frame>.jpg}: Geotagged images for detected issues",
|
| 259 |
+
r"\item \texttt{flight\_logs/flight\_log\_<frame>.csv}: Flight logs matching image frames",
|
| 260 |
+
r"\end{itemize}",
|
| 261 |
+
r"\textbf{Note}: Images and logs share frame numbers (e.g., \texttt{detected\_000001.jpg} corresponds to \texttt{flight\_log\_000001.csv}).",
|
| 262 |
+
r"\section{Geotagged Images}",
|
| 263 |
+
rf"Total Images: {len(detected_issues)}\par",
|
| 264 |
+
rf"Storage: Data Lake \texttt{{/project\_xyz/images/{datetime.now().strftime('%Y-%m-%d')}}}\par",
|
| 265 |
+
r"\begin{longtable}{|c|l|l|l|l|l|}",
|
| 266 |
+
r"\hline",
|
| 267 |
+
r"\textbf{Frame} & \textbf{Issue Type} & \textbf{GPS (Lat, Lon)} & \textbf{Timestamp} & \textbf{Confidence} & \textbf{Image Path} \\ \hline",
|
| 268 |
+
r"\endhead"
|
| 269 |
+
])
|
| 270 |
|
|
|
|
|
|
|
| 271 |
for detection in all_detections[:100]:
|
| 272 |
+
report_content.append(
|
| 273 |
+
rf"{detection['frame']:06d} & {detection['label']} & ({detection['gps'][0]:.6f}, {detection['gps'][1]:.6f}) & {detection['timestamp']} & {detection['conf']:.2f} & \texttt{{{os.path.basename(detection['path'])}}} \\ \hline"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
)
|
| 275 |
+
report_content.append(r"\end{longtable}")
|
| 276 |
+
|
| 277 |
+
# Embedding geotagged images
|
| 278 |
+
report_content.append(r"\subsection{Geotagged Images Display}")
|
| 279 |
+
for detection in all_detections[:100]:
|
| 280 |
+
image_path = detection['path']
|
| 281 |
+
if os.path.exists(image_path):
|
| 282 |
+
report_content.append(rf"\begin{{figure}}[h]")
|
| 283 |
+
report_content.append(rf"\centering")
|
| 284 |
+
report_content.append(rf"\includegraphics[width=0.8\textwidth]{{{image_path}}}")
|
| 285 |
+
report_content.append(rf"\caption{{Frame {detection['frame']:06d}: {detection['label']} at ({detection['gps'][0]:.6f}, {detection['gps'][1]:.6f}), Confidence: {detection['conf']:.2f}}}")
|
| 286 |
+
report_content.append(rf"\end{{figure}}")
|
| 287 |
+
|
| 288 |
+
report_content.extend([
|
| 289 |
+
r"\section{Flight Logs}",
|
| 290 |
+
rf"Total Logs: {len(detected_issues)}\par",
|
| 291 |
+
rf"Storage: Data Lake \texttt{{/project\_xyz/flight\_logs/{datetime.now().strftime('%Y-%m-%d')}}}\par",
|
| 292 |
+
r"\begin{longtable}{|c|l|l|l|l|l|l|l|}",
|
| 293 |
+
r"\hline",
|
| 294 |
+
r"\textbf{Frame} & \textbf{Timestamp} & \textbf{Latitude} & \textbf{Longitude} & \textbf{Speed (m/s)} & \textbf{Satellites} & \textbf{Altitude (m)} & \textbf{Log Path} \\ \hline",
|
| 295 |
+
r"\endhead"
|
| 296 |
+
])
|
| 297 |
|
|
|
|
|
|
|
| 298 |
for detection in all_detections[:100]:
|
| 299 |
log_path = f"flight_logs/flight_log_{detection['frame']:06d}.csv"
|
| 300 |
+
report_content.append(
|
| 301 |
+
rf"{detection['frame']:06d} & {detection['timestamp']} & {detection['gps'][0]:.6f} & {detection['gps'][1]:.6f} & 5.0 & 12 & 60 & \texttt{{{os.path.basename(log_path)}}} \\ \hline"
|
|
|
|
|
|
|
| 302 |
)
|
| 303 |
+
report_content.extend([
|
| 304 |
+
r"\end{longtable}",
|
| 305 |
+
r"\section{Processed Video}",
|
| 306 |
+
rf"Path: \texttt{{outputs/processed\_output.mp4}}\par",
|
| 307 |
+
rf"Frames: {output_frames}\par",
|
| 308 |
+
rf"FPS: {output_fps:.2f}\par",
|
| 309 |
+
rf"Duration: {output_duration:.2f} seconds\par",
|
| 310 |
+
r"\section{Visualizations}",
|
| 311 |
+
rf"Detection Trend Chart: \texttt{{outputs/chart\_<{timestamp}>.png}}\par",
|
| 312 |
+
rf"Issue Locations Map: \texttt{{outputs/map\_<{timestamp}>.png}}\par"
|
| 313 |
+
])
|
| 314 |
+
|
| 315 |
+
# Including chart and map images
|
| 316 |
+
if chart_path and os.path.exists(chart_path):
|
| 317 |
+
report_content.extend([
|
| 318 |
+
r"\begin{figure}[h]",
|
| 319 |
+
r"\centering",
|
| 320 |
+
rf"\includegraphics[width=0.8\textwidth]{{{chart_path}}}",
|
| 321 |
+
r"\caption{Detection Trend Chart}",
|
| 322 |
+
r"\end{figure}"
|
| 323 |
+
])
|
| 324 |
+
if map_path and os.path.exists(map_path):
|
| 325 |
+
report_content.extend([
|
| 326 |
+
r"\begin{figure}[h]",
|
| 327 |
+
r"\centering",
|
| 328 |
+
rf"\includegraphics[width=0.8\textwidth]{{{map_path}}}",
|
| 329 |
+
r"\caption{Issue Locations Map}",
|
| 330 |
+
r"\end{figure}"
|
| 331 |
+
])
|
| 332 |
+
|
| 333 |
+
report_content.extend([
|
| 334 |
+
r"\section{Processing Timestamps}",
|
| 335 |
+
rf"Total Processing Time: {total_time:.2f} seconds\par",
|
| 336 |
+
r"\textbf{Log Entries (Last 10):}",
|
| 337 |
+
r"\begin{itemize}"
|
| 338 |
+
])
|
| 339 |
|
|
|
|
|
|
|
| 340 |
for entry in log_entries[-10:]:
|
| 341 |
+
report_content.append(rf"\item {entry}")
|
| 342 |
+
report_content.extend([
|
| 343 |
+
r"\end{itemize}",
|
| 344 |
+
r"\section{Stakeholder Validation}",
|
| 345 |
+
r"\begin{itemize}",
|
| 346 |
+
r"\item \textbf{AE/IE Comments}: Pending",
|
| 347 |
+
r"\item \textbf{PD/RO Comments}: Pending",
|
| 348 |
+
r"\end{itemize}",
|
| 349 |
+
r"\section{Recommendations}",
|
| 350 |
+
r"\begin{itemize}",
|
| 351 |
+
r"\item Repair potholes in high-traffic segments.",
|
| 352 |
+
r"\item Seal cracks to prevent degradation.",
|
| 353 |
+
r"\item Schedule follow-up survey.",
|
| 354 |
+
r"\end{itemize}",
|
| 355 |
+
r"\section{Data Lake References}",
|
| 356 |
+
rf"Images: \texttt{{/project\_xyz/images/{datetime.now().strftime('%Y-%m-%d')}}}\par",
|
| 357 |
+
rf"Flight Logs: \texttt{{/project\_xyz/flight\_logs/{datetime.now().strftime('%Y-%m-%d')}}}\par",
|
| 358 |
+
rf"Video: \texttt{{/project\_xyz/videos/processed\_output_{datetime.now().strftime('%Y%m%d')}.mp4}}\par",
|
| 359 |
+
rf"DAMS Dashboard: \texttt{{/project\_xyz/dams/{datetime.now().strftime('%Y-%m-%d')}}}\par",
|
| 360 |
+
r"\end{document}"
|
| 361 |
+
])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
|
| 363 |
try:
|
| 364 |
+
# Writing LaTeX file
|
| 365 |
+
with open(tex_path, 'w') as f:
|
| 366 |
+
f.write("\n".join(report_content))
|
| 367 |
+
log_entries.append(f"LaTeX report saved: {tex_path}")
|
| 368 |
+
|
| 369 |
+
# Compiling LaTeX to PDF using latexmk
|
| 370 |
+
import subprocess
|
| 371 |
+
subprocess.run(["latexmk", "-pdf", "-interaction=nonstopmode", tex_path], cwd=OUTPUT_DIR, check=True)
|
| 372 |
+
log_entries.append(f"PDF report generated: {report_path}")
|
| 373 |
return report_path
|
| 374 |
except Exception as e:
|
| 375 |
+
log_entries.append(f"Error: Failed to generate report: {str(e)}")
|
| 376 |
return ""
|
| 377 |
|
| 378 |
def process_video(video, resize_width=4000, resize_height=3000, frame_skip=5):
|