| | import os |
| | import sys |
| | import numpy as np |
| | from typing import List |
| |
|
| | |
| | sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'ByteTrack')) |
| |
|
| | import yolox |
| | from yolox.tracker.byte_tracker import BYTETracker, STrack |
| | from onemetric.cv.utils.iou import box_iou_batch |
| | from dataclasses import dataclass |
| | from supervision import Detections |
| |
|
| | @dataclass(frozen=True) |
| | class BYTETrackerArgs: |
| | track_thresh: float = 0.25 |
| | track_buffer: int = 30 |
| | match_thresh: float = 0.8 |
| | aspect_ratio_thresh: float = 3.0 |
| | min_box_area: float = 1.0 |
| | mot20: bool = False |
| |
|
| | |
| | def detections2boxes(detections: Detections) -> np.ndarray: |
| | return np.hstack(( |
| | detections.xyxy, |
| | detections.confidence[:, np.newaxis] |
| | )) |
| |
|
| | |
| | def tracks2boxes(tracks: List[STrack]) -> np.ndarray: |
| | return np.array([ |
| | track.tlbr |
| | for track |
| | in tracks |
| | ], dtype=float) |
| |
|
| | |
| | def match_detections_with_tracks( |
| | detections: Detections, |
| | tracks: List[STrack] |
| | ) -> Detections: |
| | if not np.any(detections.xyxy) or len(tracks) == 0: |
| | return np.empty((0,)) |
| |
|
| | tracks_boxes = tracks2boxes(tracks=tracks) |
| | iou = box_iou_batch(tracks_boxes, detections.xyxy) |
| | track2detection = np.argmax(iou, axis=1) |
| |
|
| | tracker_ids = [None] * len(detections) |
| |
|
| | for tracker_index, detection_index in enumerate(track2detection): |
| | if iou[tracker_index, detection_index] != 0: |
| | tracker_ids[detection_index] = tracks[tracker_index].track_id |
| |
|
| | return tracker_ids |
| |
|
| | |
| | import cv2 |
| |
|
| | class ViewTransformer: |
| | """ |
| | Transforms image coordinates to real-world coordinates using perspective transformation. |
| | Used for calculating vehicle speeds in real-world units (meters, km/h). |
| | """ |
| | def __init__(self, source: np.ndarray, target: np.ndarray): |
| | """ |
| | Initialize the perspective transformer. |
| | |
| | Args: |
| | source: 4 points in image coordinates forming a quadrilateral |
| | target: 4 points in real-world coordinates (meters) |
| | """ |
| | source = source.astype(np.float32) |
| | target = target.astype(np.float32) |
| | self.m = cv2.getPerspectiveTransform(source, target) |
| | |
| | def transform_points(self, points: np.ndarray) -> np.ndarray: |
| | """ |
| | Transform points from image coordinates to real-world coordinates. |
| | |
| | Args: |
| | points: Array of shape (N, 2) with [x, y] coordinates |
| | |
| | Returns: |
| | Transformed points in real-world coordinates |
| | """ |
| | if len(points) == 0: |
| | return np.array([]) |
| | reshaped_points = points.reshape(-1, 1, 2).astype(np.float32) |
| | transformed_points = cv2.perspectiveTransform(reshaped_points, self.m) |
| | return transformed_points.reshape(-1, 2) |