trackforge / trackers / botsort
Module botsort
BoT-SORT: Robust associations multi-object tracking
This module implements the BoT-SORT algorithm.
BoT-SORT: Robust Associations Multi-Pedestrian Tracking Nir Aharon, Roy Orfaig, Ben-Zion Bobrovsky arXiv:2206.14651
Algorithm overview
BoT-SORT builds on ByteTrack's two-stage association and adds two pieces:
- Camera motion compensation (CMC) warps each track's Kalman prediction by a
caller-supplied affine transform before association, so tracking survives panning and
zooming cameras. The transform is the shared
common::cmcinfrastructure. - Appearance fusion combines a cosine distance to each track's appearance embedding
with the IoU distance in the high-confidence stage. Appearance is used only when it is
confident (below
appearance_thresh) and the pair is spatially close (IoU distance belowproximity_thresh); the fused cost is the smaller of the two. Each track keeps an exponential moving average of its embeddings. With no embeddings the association reduces to ByteTrack with camera motion.
The two-stage cascade is unchanged from ByteTrack: high-confidence detections are matched first (on the fused cost), then low-confidence detections recover fragmented tracks on IoU alone.
This is a clean-room implementation. The tracker applies a camera-motion transform but does not estimate it: the caller supplies the affine (for example from image registration), keeping the core free of heavy computer-vision dependencies.
Builds on
utils::kalman- the shared 8-dimensional Kalman filterutils::geometry-iou_batch,tlwh_to_xyahutils::assignment-greedy_match,iou_matchtrackers::common-KalmanTrackandCameraMotion(CMC)trackers::byte_track- theTrackStatelifecycle shared with ByteTrack
Parameters
| Parameter | Default | Description |
|---|---|---|
track_thresh |
0.5 | Confidence split between high- and low-score detections |
track_buffer |
30 | Frames a lost track is kept alive before removal |
match_thresh |
0.8 | Maximum cost for a first-stage (high-confidence) match |
det_thresh |
0.6 | Minimum score to start a new track |
proximity_thresh |
0.5 | IoU-distance gate above which appearance is ignored |
appearance_thresh |
0.25 | Cosine-distance gate above which appearance is ignored |
Rust API
```rust,ignore use trackforge::trackers::botsort::BotSort;
let mut tracker = BotSort::new(0.5, 30, 0.8, 0.6, 0.5, 0.25);
let detections = vec![([100.0, 100.0, 50.0, 100.0], 0.9, 0)]; let embeddings = vec![vec![0.1, 0.2, 0.3]]; // one appearance vector per detection let tracks = tracker.update(detections, &embeddings); for t in tracks { println!("ID: {}, Box: {:?}", t.track_id, t.tlwh); }
## Python API
```python
from trackforge import BOTSORT
tracker = BOTSORT(
track_thresh=0.5,
track_buffer=30,
match_thresh=0.8,
det_thresh=0.6,
proximity_thresh=0.5,
appearance_thresh=0.25,
)
detections = [([100.0, 100.0, 50.0, 100.0], 0.9, 0)]
embeddings = [[0.1, 0.2, 0.3]] # one appearance vector per detection; omit for motion only
tracks = tracker.update(detections, embeddings)
Moving camera: pass a [a, b, tx, c, d, ty] affine mapping the previous frame
to the current one (estimate it however you like, e.g. with OpenCV).
camera_motion = [1.0, 0.0, 12.0, 0.0, 1.0, -4.0]
tracks = tracker.update(detections, embeddings, camera_motion)
for track_id, tlwh, score, class_id, det_ind in tracks:
print(f"ID: {track_id}, Box: {tlwh}")
Credit
Clean-room Rust implementation of the algorithm described in the paper above. Original reference implementation: NirAharon/BoT-SORT.
Citation
@article{aharon2022botsort,
title={BoT-SORT: Robust Associations Multi-Pedestrian Tracking},
author={Aharon, Nir and Orfaig, Roy and Bobrovsky, Ben-Zion},
journal={arXiv preprint arXiv:2206.14651},
year={2022}
}
Quick Reference
| Item | Kind | Description |
|---|---|---|
BotSort |
struct | BoT-SORT tracker. |
BotSortParams |
struct | Settings for [BotSort]. |
BotTrack |
struct | A single tracked object managed by BoT-SORT. |
Types
BotSort
BoT-SORT tracker.
Extends ByteTrack's two-stage cascade with camera motion compensation and an
appearance-fused first stage. Camera motion is supplied by the caller as an
affine transform (see CameraMotion); appearance embeddings are optional and,
when present, fused into the high-confidence association by taking the smaller of
the IoU distance and the gated cosine distance.
Example
use trackforge::trackers::botsort::BotSort;
// track_thresh=0.5, track_buffer=30, match_thresh=0.8, det_thresh=0.6,
// proximity_thresh=0.5, appearance_thresh=0.25
let mut tracker = BotSort::new(0.5, 30, 0.8, 0.6, 0.5, 0.25);
let detections = vec![([100.0, 100.0, 50.0, 100.0], 0.9, 0)];
let tracks = tracker.update(detections, &[]);
for t in &tracks {
println!("ID: {}, Box: {:?}", t.track_id, t.tlwh);
}
Implementations
fn new(track_thresh: f32, track_buffer: usize, match_thresh: f32, det_thresh: f32, proximity_thresh: f32, appearance_thresh: f32) -> Self
Create a new BoT-SORT tracker.
# Arguments
| Argument | Description |
|---|---|
track_thresh |
Confidence split between high- and low-score detections (default: 0.5). |
track_buffer |
Frames a lost track is kept alive (default: 30). |
match_thresh |
Maximum cost for a first-stage match (default: 0.8). |
det_thresh |
Minimum score to start a new track (default: 0.6). |
proximity_thresh |
IoU-distance gate above which appearance is ignored (default: 0.5). |
appearance_thresh |
Cosine-distance gate above which appearance is ignored (default: 0.25). |
Create a BoT-SORT tracker from a BotSortParams.
BoT-SORT activates a track on its first high confidence match, so
params.common.min_hits has no effect; only common.max_age is used.
fn update(&mut self, detections: Vec<([f32; 4], f32, i64)>, embeddings: &[Vec<f32>]) -> Vec<BotTrack>
Update the tracker with the current frame's detections and embeddings.
embeddings is parallel to detections; pass an empty slice to track on
motion only. Returns the activated tracks for this frame.
fn update_with_camera_motion(&mut self, detections: Vec<([f32; 4], f32, i64)>, embeddings: &[Vec<f32>], camera_motion: &CameraMotion) -> Vec<BotTrack>
Update the tracker, first warping track predictions by camera_motion.
camera_motion maps the previous frame's coordinates into the current frame
(see CameraMotion); pass [CameraMotion::identity] for a static camera.
Trait Implementations
BotSortParams
struct BotSortParams {
pub common: crate::trackers::common::CommonParams,
pub track_thresh: f32,
pub match_thresh: f32,
pub det_thresh: f32,
pub second_match_thresh: f32,
pub proximity_thresh: f32,
pub appearance_thresh: f32,
}
Settings for BotSort.
BoT-SORT is ByteTrack plus camera motion and an optional appearance term, so its
params look like ByteTrack's plus two Re-ID gates. Shared lifecycle fields live in
CommonParams; BoT-SORT maps its track buffer onto common.max_age and, like
ByteTrack, activates on the first match so common.min_hits has no effect. Build
it with default.
Fields
| Name | Type | Description |
|---|---|---|
common |
crate::trackers::common::CommonParams |
Shared lifecycle settings. common.max_age is the track buffer length. |
track_thresh |
f32 |
Score above which a detection is treated as high confidence and matched first. |
match_thresh |
f32 |
First stage match cutoff, a maximum IoU distance of one minus IoU. Lower is stricter. |
det_thresh |
f32 |
Smallest score an unmatched high confidence detection needs to start a new track. |
second_match_thresh |
f32 |
Second stage match cutoff for recovering objects from low confidence detections, a maximum IoU distance. Reference value 0.5. |
proximity_thresh |
f32 |
How much boxes must overlap before appearance is allowed to influence the match, as a maximum IoU distance. If a track and a detection are farther apart than this, only motion is used and appearance is ignored. |
appearance_thresh |
f32 |
How close two appearance embeddings must be for Re-ID to help the match, as a maximum cosine distance. Above this the appearance term is dropped and the match falls back to motion. |
Trait Implementations
BotTrack
struct BotTrack {
pub tlwh: [f32; 4],
pub score: f32,
pub class_id: i64,
pub track_id: u64,
pub state: crate::trackers::byte_track::TrackState,
pub is_activated: bool,
pub frame_id: usize,
pub start_frame: usize,
pub tracklet_len: usize,
pub det_ind: Option<usize>,
// [REDACTED: Private Fields]
}
A single tracked object managed by BoT-SORT.
Carries the shared KalmanTrack state, the ByteTrack-style lifecycle used by
the two-stage cascade, and a smoothed appearance embedding (exponential moving
average) used for the appearance-fused association.
Fields
| Name | Type | Description |
|---|---|---|
tlwh |
[f32; 4] |
Bounding box in TLWH (top-left x, top-left y, width, height) format. |
score |
f32 |
Detection confidence of the most recent match. |
class_id |
i64 |
Class label of the most recent match. |
track_id |
u64 |
Unique track identifier (0 until the track is activated). |
state |
crate::trackers::byte_track::TrackState |
Lifecycle state (New, Tracked, Lost, Removed). |
is_activated |
bool |
Whether the track is confirmed and returned to callers. |
frame_id |
usize |
Frame id of the most recent update. |
start_frame |
usize |
Frame id at which the track started. |
tracklet_len |
usize |
Number of consecutive frames the track has been followed. |
det_ind |
Option<usize> |
Index of the source detection in the frame's input list (when available). |
Trait Implementations