How to Save and Load Tracker States in BoxMOT for Resuming Tracking Sessions
You can save and load tracker states in BoxMOT by serializing the internal tracker object, active tracks, and ReID model weights using pickle, then restoring them to resume interrupted tracking sessions without losing track identities or Kalman filter history.
BoxMOT is a popular multi-object tracking library that integrates detection and Re-identification (ReID) models. While the repository does not ship with a built-in checkpointing mechanism for the tracking pipeline, its modular architecture makes it straightforward to implement persistence for long-running video sequences. By capturing the internal state of trackers like StrongSort or ByteTrack, you can save progress at arbitrary frames and resume later exactly where you left off.
Understanding Tracker State Architecture in BoxMOT
Before implementing save and load functionality, you need to understand which attributes constitute the tracker's state. Concrete trackers in BoxMOT inherit from BaseTracker and maintain several critical data structures during inference.
Key Components to Persist
The essential state components that must be serialized include:
self.tracker– The underlying SORT-style tracker instance that holds Kalman filter states and the list of activeTrackobjects.self.active_tracks– A list maintaining currently active track IDs for general tracking scenarios.self.per_class_active_tracks– Per-class bookkeeping structures used when tracking multiple object categories simultaneously.self.frame_count– The current frame index to maintain temporal consistency.- ReID model weights – If the tracker uses appearance features, the learned weights in
self.modelmust be captured viastate_dict().
In boxmot/trackers/strongsort/strongsort.py, these attributes are initialized in the __init__ method and updated during the update() cycle (lines 41-63).
Where State Lives in the Code
The state resides in specific files across the repository:
boxmot/trackers/basetracker.py– Defines the common base class that all trackers inherit from, making it the ideal location to add persistence helpers.boxmot/trackers/strongsort/strongsort.py– Concrete implementation showing howself.trackerstores the actual tracking logic and Kalman filters.boxmot/trackers/tracker_zoo.py– Contains thecreate_tracker()factory function used to instantiate trackers with specific configurations.
Implementing Save and Load Methods in BaseTracker
To enable save and load functionality across all tracker types (StrongSort, OCSort, ByteTrack, etc.), add helper methods to the BaseTracker class in boxmot/trackers/basetracker.py.
Adding State Serialization Methods
Insert the following methods into the BaseTracker class definition:
import pickle
from pathlib import Path
from typing import Any, Dict
class BaseTracker(VisualizationMixin):
# … existing __init__ and methods …
def _state_dict(self) -> Dict[str, Any]:
"""
Return a dictionary that fully describes the current tracker state.
Sub-classes can extend this method if they keep extra attributes.
"""
return {
"tracker": self.tracker,
"active_tracks": self.active_tracks,
"per_class_active_tracks": self.per_class_active_tracks,
"frame_count": self.frame_count,
"reid_state": getattr(self, "model", None).state_dict()
if hasattr(self, "model") else None,
}
def save_state(self, path: str | Path) -> None:
"""
Serialise the tracker state to *path* so that a later session can resume.
"""
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "wb") as f:
pickle.dump(self._state_dict(), f, protocol=pickle.HIGHEST_PROTOCOL)
def load_state(self, path: str | Path) -> None:
"""
Load a previously saved state from *path* and inject it back into the tracker.
The tracker must have been instantiated with the same constructor arguments
as when it was saved.
"""
path = Path(path)
if not path.is_file():
raise FileNotFoundError(f"Tracker state file not found: {path}")
with open(path, "rb") as f:
state = pickle.load(f)
self.tracker = state["tracker"]
self.active_tracks = state["active_tracks"]
self.per_class_active_tracks = state["per_class_active_tracks"]
self.frame_count = state["frame_count"]
if hasattr(self, "model") and state["reid_state"] is not None:
self.model.load_state_dict(state["reid_state"])
These methods use Python's pickle module to serialize the entire tracker state, including Kalman filter instances and NumPy arrays, to a binary file.
Practical Usage for Resuming Tracking Sessions
Once the helper methods are implemented, you can integrate them into your tracking workflow to enable session resumption.
Saving State Periodically
To create checkpoints during a long video sequence, call save_state() at regular intervals within your tracking loop:
# Inside your main tracking loop, after each frame:
if args.save_state_every > 0 and frame_idx % args.save_state_every == 0:
tracker = predictor.trackers[0] # assuming batch-size = 1
tracker.save_state("checkpoints/tracker_state.pkl")
This preserves track IDs, Kalman filter means and covariances, and the ReID model's current weights.
Loading State on Startup
To resume from a saved checkpoint, load the state after tracker instantiation but before processing frames. Modify your entry point to accept a --state argument:
from boxmot.engine.tracker import on_predict_start
from functools import partial
import argparse
import pathlib
parser = argparse.ArgumentParser()
parser.add_argument("--tracking-method", default="strongsort")
parser.add_argument("--reid-model", type=pathlib.Path, required=True)
parser.add_argument("--source", required=True)
parser.add_argument("--state", type=pathlib.Path,
help="Path to a previously saved tracker state")
args = parser.parse_args()
def inject_state(predictor, _):
"""Callback to restore tracker state after creation."""
if args.state and args.state.exists() and predictor.trackers:
predictor.trackers[0].load_state(args.state)
# Register the injection callback to run after tracker creation
# This hooks into the BoxMOT pipeline in boxmot/engine/tracker.py
original_callback = on_predict_start
def wrapped_on_predict_start(predictor, *args, **kwargs):
original_callback(predictor, *args, **kwargs)
inject_state(predictor, None)
# Replace the callback in your pipeline setup
on_predict_start = wrapped_on_predict_start
Integration with the BoxMOT Pipeline
The trackers are instantiated via create_tracker() in boxmot/trackers/tracker_zoo.py and attached to the predictor in boxmot/engine/tracker.py. When resuming, you must instantiate the tracker with identical constructor arguments (tracking method, ReID model, device, etc.) as used during the original session.
For multi-GPU or batch processing scenarios, iterate over predictor.trackers and call save_state() or load_state() for each tracker instance. The pattern remains consistent: serialize each tracker's internal state individually.
Summary
- BoxMOT does not include built-in checkpointing, but you can add save and load functionality by extending
BaseTrackerinboxmot/trackers/basetracker.py. - Serialize the core state including
self.tracker, active track lists, frame count, and ReID model weights usingpickle. - Restore by instantiating a tracker with identical arguments, then call
load_state()to inject the saved attributes. - Resume tracking seamlessly by hooking into the
on_predict_startcallback inboxmot/engine/tracker.pyto load state before processing begins.
Frequently Asked Questions
Can I use JSON instead of pickle to save tracker states?
No, you should use pickle or torch.save instead of JSON. The tracker state contains complex Python objects like Kalman filter instances, NumPy arrays, and PyTorch tensors that cannot be serialized to JSON without significant custom encoding. Pickle handles these object types natively while preserving the exact state of the tracking algorithms.
Will resuming from a checkpoint produce identical tracking results?
Yes, if you restore the state correctly using the methods described above, the tracker will resume with identical track IDs, Kalman filter covariances, and appearance features. However, you must ensure the tracker is instantiated with the same configuration parameters (detection threshold, ReID model, etc.) as the original session, and the input video must resume from the exact frame where the checkpoint was created.
How do I handle multiple video streams or batch tracking?
For batch processing with multiple trackers, iterate over the predictor.trackers list and call save_state() or load_state() for each tracker individually. Store each state file with a unique identifier corresponding to the video stream or batch index. When resuming, match each state file to its respective tracker instance in the list.
Is it safe to load tracker states from untrusted sources?
No, loading pickle files from untrusted sources poses security risks because pickle can execute arbitrary code during deserialization. Only load tracker state files that you created yourself or received from trusted sources. For production environments requiring untrusted checkpoints, implement additional validation or use a safer serialization format restricted to specific numeric tensor values.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →