Botsort Strategy
app.infrastructure.tracking.botsort_strategyIncremental BoT-SORT-style tracking with conservative appearance updates.
See Also
TrackingWorker— dispatches frames to this strategyReIDExtractorProtocol— supplies optional appearance features
Motion uses the constant-velocity state [cx, cy, w, h, vx, vy, vw, vh],
not aspect ratio. High-confidence boxes are associated by spatially gated
minimum IoU/appearance cost, then by an unambiguous wide-radius Re-ID pass.
Low-confidence boxes only recover existing tracks through motion. No
camera-motion compensation or learned model is needed.
Layer: Infrastructure. Context: Implements concrete processing and I/O behind application-facing contracts.
BotSortStrategy
Track boxes incrementally, with optional real appearance descriptors.
| ATTRIBUTE | DESCRIPTION |
|---|---|
uses_appearance |
Whether the strategy consumes appearance vectors.
TYPE:
|
embeddings must align with the original frame_boxes order; None or
zero vectors mean appearance is unavailable. Explicit embeddings take
precedence over extraction from RGB frame pixels. Without either, the
strategy operates on motion/IoU alone, never geometry-derived fake Re-ID.
Tracks start only from scores >= max(0.5, min_confidence). Appearance EMA (90% history by default) is initialized/updated only on isolated high-score detections whose IoU with every other input box is strictly below 0.25. Coasting ends at max_age or when linearly decayed confidence falls below min_confidence. Frame indices must be nonnegative and strictly increasing.
Re-ID requires cosine distance <= 0.25 and a >= 0.08 margin over competing tracks AND detections, within a radius scaled by box size, elapsed time, and track velocity. This recovery pass resets stale velocity after relocation.
Note
The persistent motion state is [cx, cy, w, h, vx, vy, vw, vh] and
appearance prototypes are updated only from isolated high-confidence
observations.
Example
strategy = BotSortStrategy(min_confidence=0.2)
Layer: Infrastructure. Context: Implements concrete processing or I/O behind application-facing contracts.
uses_appearance: bool = True
class-attribute
instance-attribute
__init__(min_iou: float = 0.3, min_confidence: float = 0.1, confidence_decay: float = 0.05, *, max_age: int = 30, ema_alpha: float = 0.9, appearance_extractor: ReIDExtractorProtocol | None = None) -> None
| PARAMETER | DESCRIPTION |
|---|---|
min_iou
|
Minimum overlap required for local spatial association.
TYPE:
|
min_confidence
|
Minimum confidence retained while coasting and used as the lower threshold for low-confidence recovery.
TYPE:
|
confidence_decay
|
Confidence penalty per missed frame.
TYPE:
|
max_age
|
Maximum number of missed frames before a track expires.
TYPE:
|
ema_alpha
|
Weight of the previous appearance prototype.
TYPE:
|
appearance_extractor
|
Optional replacement for the built-in extractor.
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If a threshold, decay, age, or EMA weight is invalid. |
Note: The default appearance extractor is created lazily only when RGB frames are supplied without explicit embeddings.
Example
strategy = BotSortStrategy(ema_alpha=0.9)
track(source_data: ListOfBoxesByFrameIndexAsDict, total_frames: int = 0, *, frames: Mapping[int, np.ndarray] | Sequence[np.ndarray] | None = None, embeddings_by_frame: Mapping[int, Sequence[np.ndarray | None]] | None = None) -> ListOfBoxesByFrameIndexAsDict
Track a fresh batch by calling update_frame in chronological order.
| PARAMETER | DESCRIPTION |
|---|---|
source_data
|
Sparse per-frame detections. |
total_frames
|
Number of frames; zero infers it from
TYPE:
|
frames
|
Optional RGB frames keyed by index or supplied as a sequence.
TYPE:
|
embeddings_by_frame
|
Optional per-frame, box-aligned feature vectors.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ListOfBoxesByFrameIndexAsDict
|
Track boxes keyed by frame index. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If frame counts or source-data indices are invalid. |
Note
Each batch resets IDs and incremental state. Positive total_frames
includes trailing empty frames.
Example
tracked = strategy.track(detections, total_frames=120)
update_frame(frame_idx: int, frame_boxes: Sequence[BBoxViewModel], frame: np.ndarray | None = None, embeddings: Sequence[np.ndarray | None] | None = None) -> list[BBoxViewModel]
Advance to one frame and return observed and bounded coasted boxes.
| PARAMETER | DESCRIPTION |
|---|---|
frame_idx
|
Nonnegative, strictly increasing frame index.
TYPE:
|
frame_boxes
|
Detections in the original input order.
TYPE:
|
frame
|
Optional RGB frame for extractor fallback.
TYPE:
|
embeddings
|
Optional vectors aligned with
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[BBoxViewModel]
|
Tracking and coasted boxes for this frame. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If indices, boxes, embeddings, or embedding sizes are invalid. |
Note
Skipped frame indices age and predict tracks by the actual elapsed time.
Low scores below min_confidence are ignored, but remain part of
the same-frame appearance-isolation check.
Example
boxes = strategy.update_frame(0, detections, embeddings=features)