|
|
""" |
|
|
Vehicle Speed Estimation and Counting - Gradio Interface |
|
|
========================================================= |
|
|
|
|
|
A real-time vehicle detection, tracking, counting, and speed estimation system |
|
|
using YOLO object detection and perspective transformation techniques. |
|
|
|
|
|
Authors: |
|
|
- Abhay Gupta (0205CC221005) |
|
|
- Aditi Lakhera (0205CC221011) |
|
|
- Balraj Patel (0205CC221049) |
|
|
- Bhumika Patel (0205CC221050) |
|
|
|
|
|
This application provides an interactive web interface for analyzing traffic videos |
|
|
and estimating vehicle speeds using computer vision techniques. |
|
|
""" |
|
|
|
|
|
import os |
|
|
import sys |
|
|
import tempfile |
|
|
import logging |
|
|
from pathlib import Path |
|
|
from typing import Optional, Tuple |
|
|
|
|
|
import gradio as gr |
|
|
import cv2 |
|
|
import numpy as np |
|
|
|
|
|
|
|
|
logging.basicConfig( |
|
|
level=logging.INFO, |
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' |
|
|
) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
try: |
|
|
import spaces |
|
|
ZEROGPU_AVAILABLE = True |
|
|
logger.info("β
ZeroGPU support enabled - GPU acceleration available") |
|
|
except ImportError: |
|
|
ZEROGPU_AVAILABLE = False |
|
|
logger.info("βΉοΈ ZeroGPU not available - will use CPU (slower but functional)") |
|
|
|
|
|
class spaces: |
|
|
@staticmethod |
|
|
def GPU(func): |
|
|
"""Dummy decorator when ZeroGPU is not available""" |
|
|
return func |
|
|
|
|
|
|
|
|
try: |
|
|
from main import process_video |
|
|
from config import VehicleDetectionConfig |
|
|
except ImportError as e: |
|
|
logger.error(f"Failed to import required modules: {e}") |
|
|
raise |
|
|
|
|
|
|
|
|
def validate_video_file(video_path: str) -> Tuple[bool, str]: |
|
|
""" |
|
|
Validate uploaded video file. |
|
|
|
|
|
Args: |
|
|
video_path: Path to video file |
|
|
|
|
|
Returns: |
|
|
Tuple of (is_valid, error_message) |
|
|
""" |
|
|
if not video_path: |
|
|
return False, "No video file provided" |
|
|
|
|
|
if not os.path.exists(video_path): |
|
|
return False, f"Video file not found: {video_path}" |
|
|
|
|
|
|
|
|
file_size_mb = os.path.getsize(video_path) / (1024 * 1024) |
|
|
if file_size_mb > 100: |
|
|
return False, f"Video file too large ({file_size_mb:.1f}MB). Maximum size is 100MB" |
|
|
|
|
|
|
|
|
try: |
|
|
cap = cv2.VideoCapture(video_path) |
|
|
if not cap.isOpened(): |
|
|
return False, "Unable to open video file. Please ensure it's a valid video format" |
|
|
|
|
|
|
|
|
ret, _ = cap.read() |
|
|
cap.release() |
|
|
|
|
|
if not ret: |
|
|
return False, "Video file appears to be empty or corrupted" |
|
|
|
|
|
return True, "" |
|
|
except Exception as e: |
|
|
return False, f"Error validating video: {str(e)}" |
|
|
|
|
|
|
|
|
@spaces.GPU |
|
|
def estimate_vehicle_speed( |
|
|
video_file: str, |
|
|
model_choice: str, |
|
|
line_position: int, |
|
|
confidence_threshold: float, |
|
|
progress=gr.Progress() |
|
|
) -> Tuple[Optional[str], str]: |
|
|
""" |
|
|
Process video and estimate vehicle speeds. |
|
|
|
|
|
This function is decorated with @spaces.GPU to enable automatic GPU |
|
|
acceleration when running on ZeroGPU Spaces. If GPU is not available, |
|
|
it automatically falls back to CPU processing. |
|
|
|
|
|
Args: |
|
|
video_file: Path to uploaded video |
|
|
model_choice: YOLO model selection |
|
|
line_position: Y-coordinate for counting line |
|
|
confidence_threshold: Detection confidence threshold |
|
|
progress: Gradio progress tracker |
|
|
|
|
|
Returns: |
|
|
Tuple of (output_video_path, statistics_text) |
|
|
""" |
|
|
try: |
|
|
|
|
|
progress(0, desc="Validating video file...") |
|
|
is_valid, error_msg = validate_video_file(video_file) |
|
|
if not is_valid: |
|
|
logger.error(f"Video validation failed: {error_msg}") |
|
|
return None, f"β Error: {error_msg}" |
|
|
|
|
|
|
|
|
output_path = tempfile.mktemp(suffix='.mp4') |
|
|
|
|
|
|
|
|
progress(0.1, desc="Configuring detection parameters...") |
|
|
config = VehicleDetectionConfig( |
|
|
input_video=video_file, |
|
|
output_video=output_path, |
|
|
model_name=model_choice, |
|
|
line_y=line_position, |
|
|
confidence_threshold=confidence_threshold |
|
|
) |
|
|
|
|
|
|
|
|
progress(0.2, desc="Processing video (this may take a few minutes)...") |
|
|
logger.info(f"Starting video processing: {video_file}") |
|
|
|
|
|
try: |
|
|
stats = process_video( |
|
|
config=config, |
|
|
progress_callback=lambda p: progress(0.2 + p * 0.7, desc=f"Processing... {int(p*100)}%") |
|
|
) |
|
|
|
|
|
progress(0.95, desc="Finalizing output...") |
|
|
|
|
|
|
|
|
stats_text = f""" |
|
|
## π Processing Results |
|
|
|
|
|
### Vehicle Count Statistics |
|
|
- **Total Vehicles Detected:** {stats['total_count']} |
|
|
- **Vehicles Entering (In):** {stats['in_count']} |
|
|
- **Vehicles Exiting (Out):** {stats['out_count']} |
|
|
|
|
|
### Speed Analysis |
|
|
- **Average Speed:** {stats['avg_speed']:.1f} km/h |
|
|
- **Maximum Speed:** {stats['max_speed']:.1f} km/h |
|
|
- **Minimum Speed:** {stats['min_speed']:.1f} km/h |
|
|
|
|
|
### Processing Information |
|
|
- **Frames Processed:** {stats['frames_processed']} |
|
|
- **Processing Time:** {stats['processing_time']:.2f} seconds |
|
|
- **Model Used:** {model_choice} |
|
|
- **Detection Confidence:** {confidence_threshold:.2f} |
|
|
|
|
|
β
**Processing completed successfully!** |
|
|
""" |
|
|
|
|
|
progress(1.0, desc="Complete!") |
|
|
logger.info("Video processing completed successfully") |
|
|
return output_path, stats_text |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Error during video processing: {e}", exc_info=True) |
|
|
return None, f"β **Processing Error:** {str(e)}\n\nPlease try with different settings or a different video." |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Unexpected error in estimate_vehicle_speed: {e}", exc_info=True) |
|
|
return None, f"β **Unexpected Error:** {str(e)}" |
|
|
|
|
|
|
|
|
def create_demo_interface() -> gr.Blocks: |
|
|
""" |
|
|
Create Gradio interface for vehicle speed estimation. |
|
|
|
|
|
Returns: |
|
|
Gradio Blocks interface |
|
|
""" |
|
|
with gr.Blocks( |
|
|
title="Vehicle Speed Estimation & Counting" |
|
|
) as demo: |
|
|
|
|
|
gr.Markdown(""" |
|
|
# π Vehicle Speed Estimation & Counting System |
|
|
|
|
|
An intelligent traffic analysis system that detects, tracks, counts, and estimates the speed of vehicles in video footage using advanced computer vision techniques. |
|
|
|
|
|
### π― Features |
|
|
- **Real-time Vehicle Detection** using YOLO |
|
|
- **Multi-Object Tracking** with ByteTrack |
|
|
- **Accurate Speed Estimation** via perspective transformation |
|
|
- **Vehicle Counting** with configurable detection zones |
|
|
|
|
|
### π₯ Developed By |
|
|
- **Abhay Gupta** (0205CC221005) |
|
|
- **Aditi Lakhera** (0205CC221011) |
|
|
- **Balraj Patel** (0205CC221049) |
|
|
- **Bhumika Patel** (0205CC221050) |
|
|
|
|
|
--- |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### π€ Input Configuration") |
|
|
|
|
|
video_input = gr.Video( |
|
|
label="Upload Traffic Video", |
|
|
format="mp4" |
|
|
) |
|
|
|
|
|
with gr.Accordion("βοΈ Advanced Settings", open=False): |
|
|
model_choice = gr.Dropdown( |
|
|
choices=["yolov8n", "yolov8s", "yolov8m", "yolov8l"], |
|
|
value="yolov8n", |
|
|
label="YOLO Model", |
|
|
info="Larger models are more accurate but slower" |
|
|
) |
|
|
|
|
|
line_position = gr.Slider( |
|
|
minimum=100, |
|
|
maximum=1000, |
|
|
value=480, |
|
|
step=10, |
|
|
label="Counting Line Position (Y-coordinate)", |
|
|
info="Vertical position of the vehicle counting line" |
|
|
) |
|
|
|
|
|
confidence_threshold = gr.Slider( |
|
|
minimum=0.1, |
|
|
maximum=0.9, |
|
|
value=0.3, |
|
|
step=0.05, |
|
|
label="Detection Confidence Threshold", |
|
|
info="Higher values reduce false positives" |
|
|
) |
|
|
|
|
|
process_btn = gr.Button( |
|
|
"π Process Video", |
|
|
variant="primary", |
|
|
size="lg" |
|
|
) |
|
|
|
|
|
gr.Markdown(""" |
|
|
### π Instructions |
|
|
1. Upload a traffic video (MP4 format, max 100MB) |
|
|
2. Adjust settings if needed (optional) |
|
|
3. Click "Process Video" and wait for results |
|
|
4. Download the annotated video with speed estimates |
|
|
|
|
|
### π‘ Tips |
|
|
- Use videos with clear vehicle visibility |
|
|
- Ensure consistent camera angle |
|
|
- Better lighting improves detection accuracy |
|
|
""") |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### π₯ Output Results") |
|
|
|
|
|
video_output = gr.Video( |
|
|
label="Processed Video with Annotations" |
|
|
) |
|
|
|
|
|
stats_output = gr.Markdown( |
|
|
label="Statistics", |
|
|
value="*Processing results will appear here...*" |
|
|
) |
|
|
|
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
### π¬ Example Videos |
|
|
Upload your own traffic video or use sample footage to test the system. |
|
|
""") |
|
|
|
|
|
gr.Examples( |
|
|
examples=[ |
|
|
["./data/vehicles.mp4", "yolov8n", 480, 0.3], |
|
|
], |
|
|
inputs=[video_input, model_choice, line_position, confidence_threshold], |
|
|
outputs=[video_output, stats_output], |
|
|
fn=estimate_vehicle_speed, |
|
|
cache_examples=False, |
|
|
label="Sample Videos" |
|
|
) |
|
|
|
|
|
|
|
|
process_btn.click( |
|
|
fn=estimate_vehicle_speed, |
|
|
inputs=[video_input, model_choice, line_position, confidence_threshold], |
|
|
outputs=[video_output, stats_output] |
|
|
) |
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
### π¬ Technical Details |
|
|
|
|
|
This system uses: |
|
|
- **YOLO (You Only Look Once)** for real-time object detection |
|
|
- **ByteTrack** for multi-object tracking across frames |
|
|
- **Perspective Transformation** for accurate speed calculation |
|
|
- **OpenCV** for video processing and computer vision operations |
|
|
|
|
|
### π References |
|
|
- [Ultralytics YOLO](https://github.com/ultralytics/ultralytics) |
|
|
- [Supervision Library](https://github.com/roboflow/supervision) |
|
|
- [OpenCV](https://opencv.org/) |
|
|
|
|
|
### π License |
|
|
MIT License - See LICENSE file for details |
|
|
""") |
|
|
|
|
|
return demo |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
try: |
|
|
logger.info("Starting Vehicle Speed Estimation application...") |
|
|
demo = create_demo_interface() |
|
|
demo.launch( |
|
|
server_name="0.0.0.0", |
|
|
server_port=7860, |
|
|
share=False |
|
|
) |
|
|
except Exception as e: |
|
|
logger.error(f"Failed to launch application: {e}", exc_info=True) |
|
|
sys.exit(1) |
|
|
|