|
|
""" |
|
|
Vehicle Detection Configuration Module |
|
|
======================================= |
|
|
|
|
|
Manages configuration settings for vehicle detection, tracking, and speed estimation. |
|
|
|
|
|
Authors: |
|
|
- Abhay Gupta (0205CC221005) |
|
|
- Aditi Lakhera (0205CC221011) |
|
|
- Balraj Patel (0205CC221049) |
|
|
- Bhumika Patel (0205CC221050) |
|
|
""" |
|
|
|
|
|
import os |
|
|
from dataclasses import dataclass, field |
|
|
from typing import List, Tuple, Optional |
|
|
import logging |
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
@dataclass |
|
|
class VehicleDetectionConfig: |
|
|
""" |
|
|
Configuration class for vehicle detection and speed estimation system. |
|
|
|
|
|
This class encapsulates all configuration parameters needed for the |
|
|
vehicle detection pipeline, including video paths, model settings, |
|
|
detection zones, and perspective transformation parameters. |
|
|
""" |
|
|
|
|
|
|
|
|
input_video: str = "./data/vehicles.mp4" |
|
|
output_video: str = "./data/vehicles_output.mp4" |
|
|
|
|
|
|
|
|
model_name: str = "yolov8n" |
|
|
model_path: Optional[str] = None |
|
|
confidence_threshold: float = 0.3 |
|
|
iou_threshold: float = 0.7 |
|
|
|
|
|
|
|
|
line_y: int = 480 |
|
|
line_offset: int = 55 |
|
|
crossing_threshold: int = 1 |
|
|
|
|
|
|
|
|
|
|
|
source_points: List[List[int]] = field(default_factory=lambda: [ |
|
|
[450, 300], |
|
|
[860, 300], |
|
|
[1900, 720], |
|
|
[-660, 720] |
|
|
]) |
|
|
|
|
|
|
|
|
target_width_meters: float = 25.0 |
|
|
target_height_meters: float = 100.0 |
|
|
|
|
|
|
|
|
window_name: str = "Vehicle Speed Estimation - Traffic Analysis" |
|
|
display_enabled: bool = False |
|
|
|
|
|
|
|
|
enable_boxes: bool = True |
|
|
enable_labels: bool = True |
|
|
enable_traces: bool = True |
|
|
enable_line_zones: bool = True |
|
|
trace_length: int = 20 |
|
|
|
|
|
|
|
|
speed_history_seconds: int = 1 |
|
|
speed_unit: str = "km/h" |
|
|
|
|
|
def __post_init__(self): |
|
|
"""Validate configuration after initialization.""" |
|
|
self._validate_config() |
|
|
self._setup_model_path() |
|
|
|
|
|
def _validate_config(self) -> None: |
|
|
""" |
|
|
Validate configuration parameters. |
|
|
|
|
|
Raises: |
|
|
ValueError: If configuration parameters are invalid |
|
|
""" |
|
|
|
|
|
if not self.input_video: |
|
|
raise ValueError("Input video path cannot be empty") |
|
|
|
|
|
|
|
|
if not 0.0 <= self.confidence_threshold <= 1.0: |
|
|
raise ValueError(f"Confidence threshold must be between 0 and 1, got {self.confidence_threshold}") |
|
|
|
|
|
if not 0.0 <= self.iou_threshold <= 1.0: |
|
|
raise ValueError(f"IOU threshold must be between 0 and 1, got {self.iou_threshold}") |
|
|
|
|
|
|
|
|
if self.line_y < 0: |
|
|
raise ValueError(f"Line Y position must be positive, got {self.line_y}") |
|
|
|
|
|
if self.line_offset < 0: |
|
|
raise ValueError(f"Line offset must be positive, got {self.line_offset}") |
|
|
|
|
|
|
|
|
if len(self.source_points) != 4: |
|
|
raise ValueError(f"Source points must contain exactly 4 points, got {len(self.source_points)}") |
|
|
|
|
|
for i, point in enumerate(self.source_points): |
|
|
if len(point) != 2: |
|
|
raise ValueError(f"Source point {i} must have 2 coordinates, got {len(point)}") |
|
|
|
|
|
if self.target_width_meters <= 0 or self.target_height_meters <= 0: |
|
|
raise ValueError("Target dimensions must be positive") |
|
|
|
|
|
|
|
|
if self.speed_unit not in ["km/h", "mph", "m/s"]: |
|
|
raise ValueError(f"Invalid speed unit: {self.speed_unit}. Must be 'km/h', 'mph', or 'm/s'") |
|
|
|
|
|
logger.info("Configuration validation successful") |
|
|
|
|
|
def _setup_model_path(self) -> None: |
|
|
"""Set up the model path based on model name.""" |
|
|
if self.model_path is None: |
|
|
|
|
|
model_dir = "./models" |
|
|
potential_paths = [ |
|
|
f"{model_dir}/{self.model_name}.pt", |
|
|
f"{model_dir}/VisDrone_YOLO_x2.pt", |
|
|
self.model_name |
|
|
] |
|
|
|
|
|
for path in potential_paths: |
|
|
if os.path.exists(path): |
|
|
self.model_path = path |
|
|
logger.info(f"Using model from: {path}") |
|
|
return |
|
|
|
|
|
|
|
|
self.model_path = self.model_name |
|
|
logger.info(f"Model will be downloaded: {self.model_name}") |
|
|
|
|
|
@property |
|
|
def target_points(self) -> List[List[float]]: |
|
|
""" |
|
|
Generate target points for perspective transformation. |
|
|
|
|
|
Returns: |
|
|
List of 4 points defining the target perspective in meters |
|
|
""" |
|
|
w, h = self.target_width_meters, self.target_height_meters |
|
|
return [ |
|
|
[0, 0], |
|
|
[w, 0], |
|
|
[w, h], |
|
|
[0, h] |
|
|
] |
|
|
|
|
|
def get_speed_conversion_factor(self) -> float: |
|
|
""" |
|
|
Get conversion factor for speed unit. |
|
|
|
|
|
Returns: |
|
|
Conversion factor from m/s to desired unit |
|
|
""" |
|
|
conversions = { |
|
|
"km/h": 3.6, |
|
|
"mph": 2.23694, |
|
|
"m/s": 1.0 |
|
|
} |
|
|
return conversions[self.speed_unit] |
|
|
|
|
|
def to_dict(self) -> dict: |
|
|
""" |
|
|
Convert configuration to dictionary. |
|
|
|
|
|
Returns: |
|
|
Dictionary representation of configuration |
|
|
""" |
|
|
return { |
|
|
"input_video": self.input_video, |
|
|
"output_video": self.output_video, |
|
|
"model_name": self.model_name, |
|
|
"model_path": self.model_path, |
|
|
"confidence_threshold": self.confidence_threshold, |
|
|
"line_y": self.line_y, |
|
|
"speed_unit": self.speed_unit, |
|
|
} |
|
|
|
|
|
def __repr__(self) -> str: |
|
|
"""String representation of configuration.""" |
|
|
return f"VehicleDetectionConfig(model={self.model_name}, input={self.input_video})" |
|
|
|
|
|
|
|
|
|
|
|
DEFAULT_CONFIG = VehicleDetectionConfig() |
|
|
|
|
|
|
|
|
IN_VIDEO_PATH = DEFAULT_CONFIG.input_video |
|
|
OUT_VIDEO_PATH = DEFAULT_CONFIG.output_video |
|
|
YOLO_MODEL_PATH = DEFAULT_CONFIG.model_path |
|
|
LINE_Y = DEFAULT_CONFIG.line_y |
|
|
SOURCE_POINTS = DEFAULT_CONFIG.source_points |
|
|
TARGET_POINTS = DEFAULT_CONFIG.target_points |
|
|
WINDOW_NAME = DEFAULT_CONFIG.window_name |
|
|
|