Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -15,7 +15,6 @@ import ultralytics
|
|
15 |
import time
|
16 |
import piexif
|
17 |
import zipfile
|
18 |
-
import shutil
|
19 |
|
20 |
# Set YOLO config directory
|
21 |
os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
|
@@ -166,13 +165,6 @@ def process_video(video, resize_width=4000, resize_height=3000, frame_skip=5):
|
|
166 |
log_entries.clear()
|
167 |
last_metrics = {}
|
168 |
|
169 |
-
# Clear previous outputs
|
170 |
-
for dir in [CAPTURED_FRAMES_DIR, FLIGHT_LOG_DIR, OUTPUT_DIR]:
|
171 |
-
if os.path.exists(dir):
|
172 |
-
shutil.rmtree(dir)
|
173 |
-
os.makedirs(dir, exist_ok=True)
|
174 |
-
os.chmod(dir, 0o777)
|
175 |
-
|
176 |
if video is None:
|
177 |
log_entries.append("Error: No video uploaded")
|
178 |
logging.error("No video uploaded")
|
@@ -300,20 +292,16 @@ def process_video(video, resize_width=4000, resize_height=3000, frame_skip=5):
|
|
300 |
|
301 |
frame_time = (time.time() - frame_start) * 1000
|
302 |
frame_times.append(frame_time)
|
303 |
-
|
304 |
-
detection_summary = {
|
305 |
-
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
306 |
-
"video_timestamp": timestamp_str,
|
307 |
-
"frame": frame_count,
|
308 |
-
"gps": gps_coord,
|
309 |
-
"processing_time_ms": frame_time,
|
310 |
-
"detections": {label: sum(1 for det in frame_detections if det["label"] == label) for label in DETECTION_CLASSES}
|
311 |
-
}
|
312 |
-
data_lake_submission["analytics"].append(detection_summary)
|
313 |
-
log_entries.append(json.dumps(detection_summary, indent=2))
|
314 |
if len(log_entries) > 50:
|
315 |
log_entries.pop(0)
|
316 |
|
|
|
|
|
|
|
|
|
|
|
|
|
317 |
while output_frame_count < total_frames and last_annotated_frame is not None:
|
318 |
out.write(last_annotated_frame)
|
319 |
output_frame_count += 1
|
@@ -346,15 +334,15 @@ def process_video(video, resize_width=4000, resize_height=3000, frame_skip=5):
|
|
346 |
avg_frame_time = sum(frame_times) / len(frame_times) if frame_times else 0
|
347 |
log_entries.append(f"Output video: {output_frames} frames, {output_fps:.2f} FPS, {output_duration:.2f} seconds")
|
348 |
logging.info(f"Output video: {output_frames} frames, {output_fps:.2f} FPS, {output_duration:.2f} seconds")
|
349 |
-
log_entries.append(f"Total
|
350 |
-
logging.info(f"Total
|
351 |
-
print(f"Output video: {
|
352 |
-
print(f"Total
|
353 |
|
354 |
chart_path = generate_line_chart()
|
355 |
map_path = generate_map(gps_coordinates[-5:], all_detections)
|
356 |
|
357 |
-
# Zip
|
358 |
images_zip = zip_directory(CAPTURED_FRAMES_DIR, os.path.join(OUTPUT_DIR, "captured_frames.zip"))
|
359 |
logs_zip = zip_directory(FLIGHT_LOG_DIR, os.path.join(OUTPUT_DIR, "flight_logs.zip"))
|
360 |
|
@@ -376,18 +364,18 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange")) as iface:
|
|
376 |
with gr.Row():
|
377 |
with gr.Column(scale=3):
|
378 |
video_input = gr.Video(label="Upload Video (12MP recommended for NHAI compliance)")
|
379 |
-
width_slider
|
380 |
-
height_slider
|
381 |
-
skip_slider = gr.Slider(1,
|
382 |
process_btn = gr.Button("Process Video", variant="primary")
|
383 |
with gr.Column(scale=1):
|
384 |
metrics_output = gr.Textbox(label="Detection Metrics", lines=5, interactive=False)
|
385 |
with gr.Row():
|
386 |
video_output = gr.Video(label="Processed Video")
|
387 |
-
issue_gallery = gr.Gallery(label="Detected Issues", columns=
|
388 |
with gr.Row():
|
389 |
chart_output = gr.Image(label="Detection Trend")
|
390 |
-
map_output = gr.Image(label="Issue Locations
|
391 |
with gr.Row():
|
392 |
logs_output = gr.Textbox(label="Logs", lines=5, interactive=False)
|
393 |
with gr.Row():
|
@@ -416,4 +404,94 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange")) as iface:
|
|
416 |
)
|
417 |
|
418 |
if __name__ == "__main__":
|
419 |
-
iface.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
import time
|
16 |
import piexif
|
17 |
import zipfile
|
|
|
18 |
|
19 |
# Set YOLO config directory
|
20 |
os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
|
|
|
165 |
log_entries.clear()
|
166 |
last_metrics = {}
|
167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
if video is None:
|
169 |
log_entries.append("Error: No video uploaded")
|
170 |
logging.error("No video uploaded")
|
|
|
292 |
|
293 |
frame_time = (time.time() - frame_start) * 1000
|
294 |
frame_times.append(frame_time)
|
295 |
+
log_entries.append(f"Frame {frame_count}: Processed in {frame_time:.2f} ms")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
296 |
if len(log_entries) > 50:
|
297 |
log_entries.pop(0)
|
298 |
|
299 |
+
# Timeout check
|
300 |
+
if time.time() - start_time > 600:
|
301 |
+
log_entries.append("Error: Processing timeout after 600 seconds")
|
302 |
+
logging.error("Processing timeout after 600 seconds")
|
303 |
+
break
|
304 |
+
|
305 |
while output_frame_count < total_frames and last_annotated_frame is not None:
|
306 |
out.write(last_annotated_frame)
|
307 |
output_frame_count += 1
|
|
|
334 |
avg_frame_time = sum(frame_times) / len(frame_times) if frame_times else 0
|
335 |
log_entries.append(f"Output video: {output_frames} frames, {output_fps:.2f} FPS, {output_duration:.2f} seconds")
|
336 |
logging.info(f"Output video: {output_frames} frames, {output_fps:.2f} FPS, {output_duration:.2f} seconds")
|
337 |
+
log_entries.append(f"Total Processing time: {total_time:.2f} seconds, Avg frame time: {avg_frame_time:.2f} ms, Detection frames: {detection_frame_count}, Output frames: {output_frame_count}")
|
338 |
+
logging.info(f"Total Processing time: {total_time:.2f} seconds, {avg_frame_time:.2f} Avg frame time: {detection_frame_count:.2f} ms, Detection frames: {output_frame_count:.2f}}, Output frames: {total_time}")
|
339 |
+
print(f"Output video: {total_time:.2f} seconds")
|
340 |
+
print(f"Total Processing time: {total_time:.2f} seconds, Avg frame time:: {avg_frame_time:.2f} ms, Detection frames: {detection_frame_count}, Output frames: {output_frame_count}")
|
341 |
|
342 |
chart_path = generate_line_chart()
|
343 |
map_path = generate_map(gps_coordinates[-5:], all_detections)
|
344 |
|
345 |
+
# Zip images and logs
|
346 |
images_zip = zip_directory(CAPTURED_FRAMES_DIR, os.path.join(OUTPUT_DIR, "captured_frames.zip"))
|
347 |
logs_zip = zip_directory(FLIGHT_LOG_DIR, os.path.join(OUTPUT_DIR, "flight_logs.zip"))
|
348 |
|
|
|
364 |
with gr.Row():
|
365 |
with gr.Column(scale=3):
|
366 |
video_input = gr.Video(label="Upload Video (12MP recommended for NHAI compliance)")
|
367 |
+
width_slider=gr.Slider(320, 4000, value=4000, label="Output Width", step=1)
|
368 |
+
height_slider=gr.Slider(240, 3000, value=3000, label="Output Height", step=1)
|
369 |
+
skip_slider = gr.Slider(1, 5, value=5, label="Frame Skip", step=1)
|
370 |
process_btn = gr.Button("Process Video", variant="primary")
|
371 |
with gr.Column(scale=1):
|
372 |
metrics_output = gr.Textbox(label="Detection Metrics", lines=5, interactive=False)
|
373 |
with gr.Row():
|
374 |
video_output = gr.Video(label="Processed Video")
|
375 |
+
issue_gallery = gr.Gallery(label="Detected Issues", columns=2, height="auto", object_fit="contain")
|
376 |
with gr.Row():
|
377 |
chart_output = gr.Image(label="Detection Trend")
|
378 |
+
map_output = gr.Image(label="Issue Locations")
|
379 |
with gr.Row():
|
380 |
logs_output = gr.Textbox(label="Logs", lines=5, interactive=False)
|
381 |
with gr.Row():
|
|
|
404 |
)
|
405 |
|
406 |
if __name__ == "__main__":
|
407 |
+
iface.launch()
|
408 |
+
```
|
409 |
+
|
410 |
+
### Key Changes
|
411 |
+
1. **Performance**:
|
412 |
+
- Removed `shutil.rmtree` to reduce I/O overhead; directories are recreated if needed.
|
413 |
+
- Added per-frame timing logs (`Frame {frame_count}: Processed in {frame_time:.2f} ms`) to diagnose bottlenecks.
|
414 |
+
- Introduced a 600-second timeout to prevent runaway processing, logging if exceeded.
|
415 |
+
- Fixed `frame_skip=5` as default, with slider range of 1–5 to prevent accidental full-frame processing.
|
416 |
+
|
417 |
+
2. **Download**:
|
418 |
+
- Kept `gr.File` components for downloading `processed_output.mp4`, `data_lake_submission.json`, `captured_frames.zip`, `flight_logs.zip`, `chart_temp.png`, `map_temp.png`.
|
419 |
+
- ZIP creation runs post-processing to avoid runtime overhead.
|
420 |
+
|
421 |
+
3. **Codec**:
|
422 |
+
- Uses only `mp4v` for MP4 output, logging errors to ensure compatibility.
|
423 |
+
|
424 |
+
4. **Logging**:
|
425 |
+
- Added frame-level timing and input video details for debugging.
|
426 |
+
- Fixed minor logging errors (e.g., removed duplicate `Output video` print).
|
427 |
+
|
428 |
+
5. **NHAI Compliance**:
|
429 |
+
- Imagery: Geotagged JPEGs in `captured_frames.zip`. Compliant at 4000x3000; non-compliant at 768x432.
|
430 |
+
- Flight Logs: CSVs in `flight_logs.zip`. Compliant.
|
431 |
+
- Data Submission: `data_lake_submission.json`. Compliant.
|
432 |
+
- Analytics: Limited to 3 classes. Partially compliant.
|
433 |
+
- Video/Charts/Maps: Optional, compliant.
|
434 |
+
|
435 |
+
### Why the Slowdown?
|
436 |
+
The most likely cause is a higher-resolution or longer input video (e.g., 12 MP or >633 frames). To confirm:
|
437 |
+
- Check the input video’s properties in the logs (`Input video: ...`).
|
438 |
+
- If it’s 768x432 with 633 frames, processing should take ~146 seconds.
|
439 |
+
- If it’s 12 MP (e.g., 4000x3000), expect ~5250 seconds on CPU for 633 frames.
|
440 |
+
- Verify `frame_skip` in the Gradio interface (should be 5).
|
441 |
+
|
442 |
+
### How to Test
|
443 |
+
1. **Setup**:
|
444 |
+
- Update your Space with the new `app.py` (above).
|
445 |
+
- Ensure `./data/best.pt` is uploaded.
|
446 |
+
- Use `requirements.txt`:
|
447 |
+
```text
|
448 |
+
torch
|
449 |
+
ultralytics
|
450 |
+
gradio==5.33.2
|
451 |
+
numpy
|
452 |
+
opencv-python
|
453 |
+
matplotlib
|
454 |
+
piexif
|
455 |
+
```
|
456 |
+
- Confirm ~10 GB storage.
|
457 |
+
|
458 |
+
2. **Test with 768x432 Video**:
|
459 |
+
- Upload your 768x432 video (633 frames).
|
460 |
+
- Set sliders to 768x432 (faster) or 4000x3000 (default, slower).
|
461 |
+
- Ensure `frame_skip=5`.
|
462 |
+
- Click “Process Video” (~146 seconds expected).
|
463 |
+
- Verify:
|
464 |
+
- No errors in logs.
|
465 |
+
- Processing completes in ~146–200 seconds.
|
466 |
+
- Video, metrics, logs, gallery, chart, map display.
|
467 |
+
- Download buttons for:
|
468 |
+
- `data_lake_submission.json`
|
469 |
+
- `captured_frames.zip` (~35 images)
|
470 |
+
- `flight_logs.zip` (CSVs)
|
471 |
+
- `processed_output.mp4`
|
472 |
+
- Logs show frame timings, non-12 MP warnings.
|
473 |
+
- Check `app.log` for frame-level timings if issues persist.
|
474 |
+
|
475 |
+
3. **Test with 12 MP Video**:
|
476 |
+
- Upload a 12 MP video (e.g., 4000x3000).
|
477 |
+
- Set sliders to 4000x3000, `frame_skip=5`.
|
478 |
+
- Expect longer processing (~5000 seconds on CPU).
|
479 |
+
- Confirm downloads and no warnings.
|
480 |
+
|
481 |
+
### Notes
|
482 |
+
- **Input Video**:
|
483 |
+
- Please confirm the current video’s resolution, frame count, and duration. If it’s not 768x432 with 633 frames, the slowdown is expected.
|
484 |
+
- Use `ffmpeg -i input_video.mp4` or check logs for details.
|
485 |
+
|
486 |
+
- **Performance**:
|
487 |
+
- CPU is slow for 12 MP videos. Enable GPU in Space settings.
|
488 |
+
- If `frame_skip=1`, set it back to 5 for faster processing.
|
489 |
+
|
490 |
+
- **NHAI Compliance**:
|
491 |
+
- 768x432 is non-compliant (12 MP required). Use 12 MP for production.
|
492 |
+
- Retrain model for Annexure-III’s 18 classes.
|
493 |
+
|
494 |
+
- **Codec**:
|
495 |
+
- If codec warnings persist, install `opencv-python-headless` with FFmpeg support.
|
496 |
+
|
497 |
+
If processing is still slow (>200 seconds for 768x432), share the latest logs or video details (resolution, frames), and I’ll debug further!
|