🧹 more cleanup - remove more unused code, documentation improvements

This commit is contained in:
SkalskiP 2023-08-07 14:39:25 +02:00
parent ba880402ad
commit db2eede55b
3 changed files with 37 additions and 142 deletions

View File

@ -2,7 +2,7 @@ from typing import List, Tuple
import numpy as np
from supervision import Detections
from supervision.detection.core import Detections
from supervision.tracker.byte_tracker import matching
from supervision.tracker.byte_tracker.basetrack import BaseTrack, TrackState
from supervision.tracker.byte_tracker.kalman_filter import KalmanFilter
@ -143,16 +143,13 @@ class STrack(BaseTrack):
return "OT_{}_({}-{})".format(self.track_id, self.start_frame, self.end_frame)
# converts Detections into format that can be consumed by match_detections_with_tracks function
def detections2boxes(detections: Detections) -> np.ndarray:
"""
Convert Detections into a format that can be consumed by the match_detections_with_tracks function.
Parameters:
detections (Detections): An object representing the detected bounding boxes.
Convert Supervision Detections to numpy tensors for further computation.
Args:
detections (Detections): Detections/Targets in the format of sv.Detections.
Returns:
np.ndarray: An array containing the bounding boxes' coordinates (xyxy) and their corresponding confidences.
(np.ndarray): Detections as numpy tensors as in `(x_min, y_min, x_max, y_max, confidence, class_id)` order.
"""
return np.hstack(
(
@ -201,8 +198,31 @@ class ByteTrack:
detections: The new detections to update with.
Returns:
Detection: supervision detection result with track id.
Examples:
Example:
```python
>>> import supervision as sv
>>> from ultralytics import YOLO
>>> model = YOLO(...)
>>> byte_tracker = sv.ByteTrack()
>>> annotator = sv.BoxAnnotator()
def callback(frame: np.ndarray, index: int) -> np.ndarray:
results = model(frame)[0]
detections = sv.Detections.from_yolov8(results)
detections = byte_tracker.update_from_detections(detections=detections)
labels = [
f"#{tracker_id} {model.model.names[class_id]} {confidence:0.2f}"
for _, _, confidence, class_id, tracker_id
in detections
]
return annotator.annotate(scene=frame.copy(), detections=detections, labels=labels)
sv.process_video(
source_path='...',
target_path='...',
callback=callback
)
```
"""
@ -225,7 +245,6 @@ class ByteTrack:
Parameters:
output_results: The new detections to update with.
Updates the strack with the provided results and frame info.
Returns:
Track_id: track id

View File

@ -1,32 +1,12 @@
from typing import List, Optional, Tuple
from typing import List, Tuple
import numpy as np
import scipy
from scipy.optimize import linear_sum_assignment
from scipy.spatial.distance import cdist
from supervision.detection.utils import box_iou_batch
from supervision.tracker.byte_tracker import kalman_filter
def merge_matches(m1, m2, shape) -> Tuple[List, tuple, tuple]:
O, P, Q = shape
m1 = np.asarray(m1)
m2 = np.asarray(m2)
M1 = scipy.sparse.coo_matrix((np.ones(len(m1)), (m1[:, 0], m1[:, 1])), shape=(O, P))
M2 = scipy.sparse.coo_matrix((np.ones(len(m2)), (m2[:, 0], m2[:, 1])), shape=(P, Q))
mask = M1 * M2
match = mask.nonzero()
match = list(zip(match[0], match[1]))
unmatched_O = tuple(set(range(O)) - set([i for i, j in match]))
unmatched_Q = tuple(set(range(Q)) - set([j for i, j in match]))
return match, unmatched_O, unmatched_Q
def _indices_to_matches(
def indices_to_matches(
cost_matrix: np.ndarray, indices: np.ndarray, thresh: float
) -> Tuple[np.ndarray, tuple, tuple]:
matched_cost = cost_matrix[tuple(zip(*indices))]
@ -58,7 +38,7 @@ def linear_assignment(
row_ind, col_ind = linear_sum_assignment(cost_matrix)
indices = np.column_stack((row_ind, col_ind))
return _indices_to_matches(cost_matrix, indices, thresh)
return indices_to_matches(cost_matrix, indices, thresh)
def iou_distance(atracks: List, btracks: List) -> np.ndarray:
@ -87,110 +67,6 @@ def iou_distance(atracks: List, btracks: List) -> np.ndarray:
return cost_matrix
def v_iou_distance(atracks: List, btracks: List) -> np.ndarray:
"""
Compute cost based on IoU
:type atracks: list[STrack]
:type btracks: list[STrack]
:rtype cost_matrix np.ndarray
"""
if (len(atracks) > 0 and isinstance(atracks[0], np.ndarray)) or (
len(btracks) > 0 and isinstance(btracks[0], np.ndarray)
):
atlbrs = atracks
btlbrs = btracks
else:
atlbrs = [track.tlwh_to_tlbr(track.pred_bbox) for track in atracks]
btlbrs = [track.tlwh_to_tlbr(track.pred_bbox) for track in btracks]
_ious = box_iou_batch(np.asarray(atlbrs), np.asarray(btlbrs))
cost_matrix = 1 - _ious
return cost_matrix
def embedding_distance(tracks: List, detections: List, metric="cosine") -> np.ndarray:
"""
:param tracks: list[STrack]
:param detections: list[BaseTrack]
:param metric:
:return: cost_matrix np.ndarray
"""
cost_matrix = np.zeros((len(tracks), len(detections)), dtype=np.float32)
if cost_matrix.size == 0:
return cost_matrix
det_features = np.asarray(
[track.curr_feat for track in detections], dtype=np.float32
)
track_features = np.asarray(
[track.smooth_feat for track in tracks], dtype=np.float32
)
cost_matrix = np.maximum(
0.0, cdist(track_features, det_features, metric)
) # Nomalized features
return cost_matrix
def gate_cost_matrix(
kf,
cost_matrix: np.ndarray,
tracks: List,
detections: np.ndarray,
only_position=False,
) -> np.ndarray:
if cost_matrix.size == 0:
return cost_matrix
gating_dim = 2 if only_position else 4
gating_threshold = kalman_filter.chi2inv95[gating_dim]
measurements = np.asarray([det.to_xyah() for det in detections])
for row, track in enumerate(tracks):
gating_distance = kf.gating_distance(
track.mean, track.covariance, measurements, only_position
)
cost_matrix[row, gating_distance > gating_threshold] = np.inf
return cost_matrix
def fuse_motion(
kf,
cost_matrix: np.ndarray,
tracks: List,
detections: np.ndarray,
only_position=False,
lambda_=0.98,
) -> np.ndarray:
if cost_matrix.size == 0:
return cost_matrix
gating_dim = 2 if only_position else 4
gating_threshold = kalman_filter.chi2inv95[gating_dim]
measurements = np.asarray([det.to_xyah() for det in detections])
for row, track in enumerate(tracks):
gating_distance = kf.gating_distance(
track.mean, track.covariance, measurements, only_position, metric="maha"
)
cost_matrix[row, gating_distance > gating_threshold] = np.inf
cost_matrix[row] = lambda_ * cost_matrix[row] + (1 - lambda_) * gating_distance
return cost_matrix
def fuse_iou(
cost_matrix: np.ndarray, tracks: List, detections: np.ndarray
) -> np.ndarray:
if cost_matrix.size == 0:
return cost_matrix
reid_sim = 1 - cost_matrix
iou_dist = iou_distance(tracks, detections)
iou_sim = 1 - iou_dist
fuse_sim = reid_sim * (1 + iou_sim) / 2
det_scores = np.array([det.score for det in detections])
det_scores = np.expand_dims(det_scores, axis=0).repeat(cost_matrix.shape[0], axis=0)
# fuse_sim = fuse_sim * (1 + det_scores) / 2
fuse_cost = 1 - fuse_sim
return fuse_cost
def fuse_score(cost_matrix: np.ndarray, detections: List) -> np.ndarray:
if cost_matrix.size == 0:
return cost_matrix

View File

@ -163,15 +163,15 @@ def process_video(
Examples:
```python
>>> from supervision import process_video
>>> import supervision as sv
>>> def process_frame(scene: np.ndarray) -> np.ndarray:
>>> def callback(scene: np.ndarray, index: int) -> np.ndarray:
... ...
>>> process_video(
... source_path='source_video.mp4',
... target_path='target_video.mp4',
... callback=process_frame
... source_path='...',
... target_path='...',
... callback=callback
... )
```
"""