Spaces:
Sleeping
Sleeping
| """ | |
| Wrong-Side Driving Detector. | |
| Detects vehicles travelling against the expected flow of traffic in their assigned lane. | |
| """ | |
| import logging | |
| from typing import List, Optional | |
| import numpy as np | |
| from config.settings import ViolationType, VIOLATION_DISPLAY_NAMES, TWO_WHEELER_CLASSES, FOUR_WHEELER_CLASSES | |
| from config.settings import EntityClass | |
| from core.violation_engine import ViolationRecord | |
| from core.scene_graph import SceneGraph | |
| from violations.base import BaseViolationDetector | |
| logger = logging.getLogger(__name__) | |
| class WrongSideViolationDetector(BaseViolationDetector): | |
| """ | |
| Detector for wrong-side driving violations. | |
| """ | |
| violation_type = ViolationType.WRONG_SIDE_DRIVING | |
| def detect( | |
| self, | |
| scene_graph: SceneGraph, | |
| frame: Optional[np.ndarray] = None, | |
| ) -> List[ViolationRecord]: | |
| """ | |
| Scan the scene graph for vehicles driving in the wrong direction. | |
| Args: | |
| scene_graph: Structured representation of the current frame. | |
| frame: Optional raw BGR image. | |
| Returns: | |
| List of Wrong-Side driving violations. | |
| """ | |
| violations: List[ViolationRecord] = [] | |
| import datetime | |
| # Expected directions by lane half (left / right of frame). | |
| # In India, traffic flows left (Left-Hand Driving). We assume: | |
| # - Left lanes (indices < 2) should move NORTH (away from camera). | |
| # - Right lanes (indices >= 2) should move SOUTH (oncoming/towards camera). | |
| expected_left_half = {"NORTH", "NORTHWEST", "NORTHEAST"} | |
| expected_right_half = {"SOUTH", "SOUTHWEST", "SOUTHEAST"} | |
| all_vehicle_classes = ( | |
| TWO_WHEELER_CLASSES | FOUR_WHEELER_CLASSES | {EntityClass.AUTO_RICKSHAW} | |
| ) | |
| for node in scene_graph.nodes.values(): | |
| if node.entity_class not in all_vehicle_classes: | |
| continue | |
| direction = scene_graph.get_direction(node.node_id) | |
| if direction is None or direction == "STATIONARY": | |
| continue | |
| # Skip nodes with negligible movement to avoid false flags from tracking jitter | |
| import math | |
| vector = node.attributes.get("direction_vector") | |
| if vector is not None: | |
| dx, dy = vector | |
| magnitude = math.hypot(dx, dy) | |
| if magnitude < 2.0: | |
| continue | |
| lane_index = node.attributes.get("lane_index") | |
| if lane_index is None: | |
| continue | |
| is_left_half = lane_index < 2 | |
| wrong_side = False | |
| if is_left_half and direction in expected_right_half: | |
| wrong_side = True | |
| elif not is_left_half and direction in expected_left_half: | |
| wrong_side = True | |
| if not wrong_side: | |
| continue | |
| involved = [node.node_id] | |
| plate_node = scene_graph.get_plate_of(node.node_id) | |
| plate_text = "" | |
| if plate_node is not None: | |
| involved.append(plate_node.node_id) | |
| plate_text = plate_node.attributes.get("plate_text", "") | |
| display_name = VIOLATION_DISPLAY_NAMES.get( | |
| self.violation_type, "Wrong-Side Driving" | |
| ) | |
| timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat() | |
| unique_suffix = f"{node.node_id[-3:]}-{direction}" | |
| temp_id = f"VIO-WRONGSIDE-{unique_suffix}" | |
| expected_dir = "SOUTH-bound" if is_left_half else "NORTH-bound" | |
| chain = [ | |
| f"Detected {node.entity_class.value}_{node.node_id} (conf={node.confidence:.2f}) with trajectory data", | |
| f"Computed direction: {direction} in lane {lane_index}", | |
| f"Lane {lane_index} expected {expected_dir} flow, vehicle moving {direction} (against flow)", | |
| f"VIOLATION: {display_name} — {node.entity_class.value}_{node.node_id}", | |
| ] | |
| violations.append( | |
| ViolationRecord( | |
| violation_id=temp_id, | |
| violation_type=self.violation_type, | |
| confidence=node.confidence * 0.8, # Heuristic penalty | |
| involved_nodes=involved, | |
| description=( | |
| f"{display_name}: {node.entity_class.value} moving " | |
| f"{direction} in lane {lane_index} (expected opposite flow)." | |
| ), | |
| bbox=node.bbox, | |
| timestamp=timestamp, | |
| metadata={ | |
| "direction": direction, | |
| "lane_index": lane_index, | |
| "plate_text": plate_text, | |
| "vehicle_type": node.entity_class.value, | |
| }, | |
| reasoning_chain=chain, | |
| ) | |
| ) | |
| return violations | |