Skip to content

trackforge / trackers / deep_ocsort


Module deep_ocsort

Deep OC-SORT: Observation-Centric SORT with appearance

This module implements the Deep OC-SORT algorithm.

Deep OC-SORT: Multi-Pedestrian Tracking by Adaptive Re-Identification Gerard Maggiolino, Adnan Ahmad, Jinkun Cao, Kris Kitani arXiv:2302.11813

Algorithm overview

Deep OC-SORT extends OC-SORT by adding an appearance term to the association:

  • OCM adds a velocity direction-consistency bonus to the IoU before matching.
  • ORU replays interpolated observations to correct the Kalman filter after a track is re-associated following a gap.
  • Appearance association blends a cosine distance to each track's feature gallery with the motion cost. The appearance weight scales with detector confidence (dynamic appearance) and is gated by max_cosine_distance. With appearance_weight = 0 the association reduces to plain OC-SORT.
  • Camera motion compensation (CMC) warps track predictions by a caller-supplied affine transform before association, for moving-camera footage. The transform is shared common::cmc infrastructure reused by other trackers.

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 filter
  • utils::geometry - iou_batch, tlwh_to_xyah, xyah_to_tlwh
  • utils::assignment - greedy_match
  • trackers::common - KalmanTrack, TrackState, and CameraMotion (CMC)
  • trackers::deepsort - NearestNeighborDistanceMetric for the cosine feature gallery

Parameters

Parameter Default Description
max_age 30 Frames a lost track survives before deletion
min_hits 3 Consecutive matches required to confirm a track
iou_threshold 0.3 Minimum IoU to associate a detection with a track
delta_t 3 Observation window (frames) used to compute velocity (OCM)
inertia 0.2 Weight of the direction-consistency cost bonus (OCM)
appearance_weight 0.5 Blend weight of the appearance cost, scaled by det. score
max_cosine_distance 0.2 Maximum cosine distance for the appearance term to apply
nn_budget 100 Maximum appearance features stored per track

Rust API

```rust,ignore use trackforge::trackers::deep_ocsort::DeepOcSort;

// extractor implements the AppearanceExtractor trait (plug in any Re-ID model). let mut tracker = DeepOcSort::new(extractor, 30, 3, 0.3, 3, 0.2, 0.5, 0.2, 100); let tracks = tracker.update(&image, detections)?; for t in tracks { println!("ID: {}, Box: {:?}", t.track_id, t.tlwh); }

## Python API

```python
from trackforge import DEEPOCSORT

tracker = DEEPOCSORT(
    max_age=30,
    min_hits=3,
    iou_threshold=0.3,
    delta_t=3,
    inertia=0.2,
    appearance_weight=0.5,
    max_cosine_distance=0.2,
    nn_budget=100,
)

detections = [([100.0, 100.0, 50.0, 100.0], 0.9, 0)]
embeddings = [[0.1, 0.2, 0.3]]  # one appearance vector per detection
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: GerardMaggiolino/Deep-OC-SORT.

Citation

@inproceedings{maggiolino2023deepocsort,
  title={Deep OC-SORT: Multi-Pedestrian Tracking by Adaptive Re-Identification},
  author={Maggiolino, Gerard and Ahmad, Adnan and Cao, Jinkun and Kitani, Kris},
  booktitle={IEEE International Conference on Image Processing (ICIP)},
  year={2023}
}

Quick Reference

Item Kind Description
DeepOcSortParams struct Settings for DeepOcSort.

Types

DeepOcSortTrack

struct DeepOcSortTrack {
    pub tlwh: [f32; 4],
    pub score: f32,
    pub class_id: i64,
    pub track_id: u64,
    pub state: crate::trackers::common::TrackState,
    pub hits: usize,
    pub hit_streak: usize,
    pub time_since_update: usize,
    pub age: usize,
    pub det_ind: Option<usize>,
    // [REDACTED: Private Fields]
}

A tracked object with an observation history.

Shared by OC-SORT and Deep OC-SORT (each re-exports it under its own name).

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 monotonically increasing track identifier.
state crate::trackers::common::TrackState Current lifecycle state.
hits usize Total number of detection matches over the track lifetime.
hit_streak usize Consecutive detection matches without interruption (resets on a missed frame).
time_since_update usize Frames elapsed since the last detection match.
age usize Total frames since track creation.
det_ind Option<usize> Index of the detection this track was most recently matched to (in the current frame's detection list), or None when unmatched this frame.

Implementations

fn is_confirmed(&self) -> bool

Whether the track has been confirmed and is returned to callers.

Trait Implementations

impl Clone for ObsTrack

fn clone(&self) -> ObsTrack

impl Debug for ObsTrack

fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result

fn to_subset(&self) -> Option<SS>

fn is_in_subset(&self) -> bool

fn to_subset_unchecked(&self) -> SS

fn from_subset(element: &SS) -> SP

DeepOcSortTracker

struct DeepOcSortTracker {
    pub tracks: Vec<crate::trackers::common::ObsTrack>,
    // [REDACTED: Private Fields]
}

Deep OC-SORT tracker core.

Runs the OC-SORT motion association (IoU with an OCM direction bonus, plus an ORU re-update on re-association) and blends in an appearance cost from a cosine feature gallery. The appearance weight scales with detector confidence (dynamic appearance) and is gated by max_cosine_distance. With appearance_weight = 0 the association reduces to plain OC-SORT.

Implementations

fn new(max_age: usize, min_hits: usize, iou_threshold: f32, delta_t: usize, inertia: f32, appearance_weight: f32, max_cosine_distance: f32, metric: NearestNeighborDistanceMetric) -> Self

fn update(&mut self, detections: &[([f32; 4], f32, i64)], embeddings: &[Vec<f32>]) -> Vec<DeepOcSortTrack>

Update the tracker with the current frame's detections and embeddings.

embeddings is parallel to detections. Pass an empty slice to run without

appearance (pure OC-SORT). Returns confirmed tracks matched this frame.

fn update_with_camera_motion(&mut self, detections: &[([f32; 4], f32, i64)], embeddings: &[Vec<f32>], camera_motion: &CameraMotion) -> Vec<DeepOcSortTrack>

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

fn to_subset(&self) -> Option<SS>

fn is_in_subset(&self) -> bool

fn to_subset_unchecked(&self) -> SS

fn from_subset(element: &SS) -> SP

DeepOcSortParams

struct DeepOcSortParams {
    pub common: crate::trackers::common::CommonParams,
    pub iou_threshold: f32,
    pub delta_t: usize,
    pub inertia: f32,
    pub appearance_weight: f32,
    pub max_cosine_distance: f32,
    pub nn_budget: usize,
}

Settings for DeepOcSort.

Shared lifecycle fields live in CommonParams; the rest are Deep OC-SORT specific. This is OC-SORT plus an appearance term, so it carries the OC-SORT motion fields and the Re-ID fields together. Build it with default.

Fields

Name Type Description
common crate::trackers::common::CommonParams Shared lifecycle settings, max_age and min_hits.
iou_threshold f32 Smallest IoU overlap that still counts as the same object. This is a minimum IoU, so higher is stricter.
delta_t usize How many frames back the tracker looks to estimate an object's direction of travel from its real past positions. Larger is steadier but slower to react to turns.
inertia f32 How strongly a track's recent direction of travel is trusted when matching, in the range zero to one. Zero turns the direction term off.
appearance_weight f32 How much the appearance match counts against the motion match when scoring a pair, in the range zero to one. Zero ignores appearance and falls back to plain OC-SORT; higher leans on Re-ID to hold ids through occlusion.
max_cosine_distance f32 How different two appearance embeddings may be and still count as the same object, as cosine distance. Lower demands a closer appearance match.
nn_budget usize How many past appearance embeddings to keep per track for Re-ID. When the gallery is full the oldest is dropped.

Trait Implementations

impl Clone for DeepOcSortParams

fn clone(&self) -> DeepOcSortParams

impl Copy for DeepOcSortParams
impl Debug for DeepOcSortParams

fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result

impl Default for DeepOcSortParams

fn default() -> Self

impl PartialEq for DeepOcSortParams

fn eq(&self, other: &DeepOcSortParams) -> bool

fn to_subset(&self) -> Option<SS>

fn is_in_subset(&self) -> bool

fn to_subset_unchecked(&self) -> SS

fn from_subset(element: &SS) -> SP