| | """ |
| | Frame Annotation Module |
| | ======================== |
| | |
| | Provides comprehensive video frame annotation capabilities for vehicle detection |
| | visualization including bounding boxes, labels, trajectories, and counting zones. |
| | |
| | Authors: |
| | - Abhay Gupta (0205CC221005) |
| | - Aditi Lakhera (0205CC221011) |
| | - Balraj Patel (0205CC221049) |
| | - Bhumika Patel (0205CC221050) |
| | """ |
| |
|
| | import numpy as np |
| | import supervision as sv |
| | from typing import List, Optional, Tuple |
| | import logging |
| |
|
| | logger = logging.getLogger(__name__) |
| |
|
| |
|
| | class FrameAnnotator: |
| | """ |
| | Comprehensive frame annotation system for vehicle detection visualization. |
| | |
| | This class manages multiple annotation layers including bounding boxes, |
| | labels, object trajectories, counting lines, and detection zones. |
| | """ |
| | |
| | def __init__( |
| | self, |
| | video_resolution: Tuple[int, int], |
| | show_boxes: bool = True, |
| | show_labels: bool = True, |
| | show_traces: bool = True, |
| | show_line_zones: bool = True, |
| | trace_length: int = 20, |
| | zone_polygon: Optional[np.ndarray] = None |
| | ): |
| | """ |
| | Initialize the frame annotator. |
| | |
| | Args: |
| | video_resolution: Video resolution as (width, height) |
| | show_boxes: Enable bounding box annotations |
| | show_labels: Enable label annotations |
| | show_traces: Enable trajectory trace annotations |
| | show_line_zones: Enable line zone annotations |
| | trace_length: Maximum number of points in trajectory trace |
| | zone_polygon: Optional polygon defining detection zone |
| | """ |
| | self.resolution = video_resolution |
| | self.show_boxes = show_boxes |
| | self.show_labels = show_labels |
| | self.show_traces = show_traces |
| | self.show_line_zones = show_line_zones |
| | self.zone_polygon = zone_polygon |
| | |
| | |
| | self.line_thickness = sv.calculate_optimal_line_thickness(video_resolution) |
| | self.text_scale = sv.calculate_optimal_text_scale(video_resolution) |
| | |
| | logger.info(f"Initializing annotator for {video_resolution[0]}x{video_resolution[1]}") |
| | logger.info(f"Line thickness: {self.line_thickness}, Text scale: {self.text_scale}") |
| | |
| | |
| | self._setup_annotators(trace_length) |
| | |
| | def _setup_annotators(self, trace_length: int) -> None: |
| | """ |
| | Set up individual annotation components. |
| | |
| | Args: |
| | trace_length: Maximum trajectory trace length |
| | """ |
| | |
| | if self.show_boxes: |
| | self.box_drawer = sv.BoxAnnotator( |
| | thickness=self.line_thickness, |
| | color_lookup=sv.ColorLookup.TRACK |
| | ) |
| | logger.debug("Box annotator initialized") |
| | else: |
| | self.box_drawer = None |
| | |
| | |
| | if self.show_labels: |
| | self.label_drawer = sv.LabelAnnotator( |
| | text_thickness=self.line_thickness, |
| | text_scale=self.text_scale, |
| | text_position=sv.Position.BOTTOM_CENTER, |
| | color_lookup=sv.ColorLookup.TRACK |
| | ) |
| | logger.debug("Label annotator initialized") |
| | else: |
| | self.label_drawer = None |
| | |
| | |
| | if self.show_traces: |
| | self.trace_drawer = sv.TraceAnnotator( |
| | thickness=self.line_thickness, |
| | trace_length=trace_length, |
| | position=sv.Position.BOTTOM_CENTER, |
| | color_lookup=sv.ColorLookup.TRACK |
| | ) |
| | logger.debug(f"Trace annotator initialized with length {trace_length}") |
| | else: |
| | self.trace_drawer = None |
| | |
| | |
| | if self.show_line_zones: |
| | self.line_zone_drawer = sv.LineZoneAnnotator( |
| | thickness=self.line_thickness, |
| | text_thickness=self.line_thickness, |
| | text_scale=self.text_scale, |
| | color=sv.Color.WHITE, |
| | text_color=sv.Color.BLACK, |
| | display_in_count=True, |
| | display_out_count=True |
| | ) |
| | logger.debug("Line zone annotator initialized") |
| | else: |
| | self.line_zone_drawer = None |
| | |
| | |
| | if self.zone_polygon is not None: |
| | try: |
| | zone = sv.PolygonZone(polygon=self.zone_polygon) |
| | self.polygon_drawer = sv.PolygonZoneAnnotator( |
| | zone=zone, |
| | color=sv.Color.GREEN, |
| | thickness=self.line_thickness, |
| | display_in_zone_count=False |
| | ) |
| | logger.debug("Polygon zone annotator initialized") |
| | except Exception as e: |
| | logger.warning(f"Failed to initialize polygon zone: {e}") |
| | self.polygon_drawer = None |
| | else: |
| | self.polygon_drawer = None |
| | |
| | def draw_annotations( |
| | self, |
| | frame: np.ndarray, |
| | detections: sv.Detections, |
| | labels: Optional[List[str]] = None, |
| | line_zones: Optional[List[sv.LineZone]] = None |
| | ) -> np.ndarray: |
| | """ |
| | Apply all enabled annotations to a video frame. |
| | |
| | Args: |
| | frame: Input video frame (BGR format) |
| | detections: Detection results to annotate |
| | labels: Optional list of labels for each detection |
| | line_zones: Optional list of line zones for counting visualization |
| | |
| | Returns: |
| | Annotated frame |
| | """ |
| | |
| | annotated = frame.copy() |
| | |
| | try: |
| | |
| | if self.polygon_drawer is not None: |
| | annotated = self.polygon_drawer.annotate(annotated) |
| | |
| | |
| | if self.box_drawer is not None and len(detections) > 0: |
| | annotated = self.box_drawer.annotate( |
| | scene=annotated, |
| | detections=detections |
| | ) |
| | |
| | |
| | if self.trace_drawer is not None and len(detections) > 0: |
| | annotated = self.trace_drawer.annotate( |
| | scene=annotated, |
| | detections=detections |
| | ) |
| | |
| | |
| | if self.label_drawer is not None and len(detections) > 0: |
| | annotated = self.label_drawer.annotate( |
| | scene=annotated, |
| | detections=detections, |
| | labels=labels |
| | ) |
| | |
| | |
| | if self.line_zone_drawer is not None and line_zones: |
| | for zone in line_zones: |
| | annotated = self.line_zone_drawer.annotate( |
| | frame=annotated, |
| | line_counter=zone |
| | ) |
| | |
| | return annotated |
| | |
| | except Exception as e: |
| | logger.error(f"Error during annotation: {e}") |
| | |
| | return frame |
| | |
| | def update_settings( |
| | self, |
| | show_boxes: Optional[bool] = None, |
| | show_labels: Optional[bool] = None, |
| | show_traces: Optional[bool] = None, |
| | show_line_zones: Optional[bool] = None |
| | ) -> None: |
| | """ |
| | Update annotation settings dynamically. |
| | |
| | Args: |
| | show_boxes: Enable/disable bounding boxes |
| | show_labels: Enable/disable labels |
| | show_traces: Enable/disable traces |
| | show_line_zones: Enable/disable line zones |
| | """ |
| | if show_boxes is not None: |
| | self.show_boxes = show_boxes |
| | if show_labels is not None: |
| | self.show_labels = show_labels |
| | if show_traces is not None: |
| | self.show_traces = show_traces |
| | if show_line_zones is not None: |
| | self.show_line_zones = show_line_zones |
| | |
| | logger.info(f"Annotation settings updated: boxes={self.show_boxes}, " |
| | f"labels={self.show_labels}, traces={self.show_traces}, " |
| | f"zones={self.show_line_zones}") |
| |
|