Python API

May 30, 2026 · View on GitHub

Use boxmot for the high-level workflow facade, and explicit modules such as boxmot.detectors, boxmot.reid, and boxmot.trackers.tracker_zoo when you want lower-level control.

High-level facade

Use Boxmot when you want the Python equivalent of the CLI with minimal boilerplate:

from boxmot import Boxmot

boxmot = Boxmot(detector="yolov8n", reid="lmbn_n_duke", tracker="boosttrack")
run = boxmot.track(source="video.mp4", save=True)
print(run)

cache = Boxmot().generate(benchmark="mot17-mini")
print(cache.cache_dir)

metrics = boxmot.val(benchmark="mot17-mini")
print(metrics)

tuned = boxmot.tune(benchmark="mot17-mini", n_trials=2)
print(tuned)

ReID lifecycle workflows are available from the same facade:

from boxmot import Boxmot

api = Boxmot()
train_result = api.train(
    model="mobilenetv2_x1_0",
    dataset="market1501",
    data_dir="assets/reid-mini",
    device="cpu",
    epochs=5,
    batch_size=16,
)

metrics = api.eval_reid(
    weights=train_result.weights_path,
    model="mobilenetv2_x1_0",
    dataset="market1501",
    data_dir="assets/reid-mini",
    device="cpu",
)
print(metrics)

The same facade also exposes research(...) for GEPA-backed benchmark optimization, train(...) and eval_reid(...) for ReID model lifecycle workflows, and export(...) for ReID conversion workflows.

Use .summary, .timings, .delta_summary, or .to_dict() on returned results when you need structured data instead of the human-readable report.

Native C++ backends

Use tracker_backend="cpp" or an inline tracker spec such as "bytetrack:cpp" when the selected tracker has a native backend:

from boxmot import Boxmot

native_track = Boxmot(detector="yolov8n", tracker="bytetrack:cpp")
run = native_track.track(source="video.mp4")

native_eval = Boxmot(tracker="ocsort")
metrics = native_eval.val(benchmark="mot17", split="ablation", tracker_backend="cpp")

Native C++ backends are currently registered for botsort, bytetrack, ocsort, occluboost, and sfsort.

Streaming frame results

When you want per-frame access to tracks, detections, and embeddings, iterate the results yourself instead of passing show=True or save=True:

from boxmot import Boxmot

model = Boxmot(detector="yolov8l.pt", reid="lmbn_n_duke.pt", tracker="occluboost")
results = model.track(source=0)

for frame_result in results:
    tracks = frame_result.tracks          # (M, 8) TrackResults array
    ids    = frame_result.tracks.id       # (M,) track IDs
    confs  = frame_result.tracks.conf     # (M,) confidences
    boxes  = frame_result.tracks.xyxy     # (M, 4) bounding boxes
    dets   = frame_result.detections      # (M, 6) matched detections, aligned to tracks
    embs   = frame_result.embeddings      # (M, D) matched embeddings, aligned to tracks

    print(f"Frame {frame_result.frame_idx}: {len(ids)} tracks")

    frame_result.save_csv("tracks.csv")   # append tracks to CSV
    frame_result.save_vid("output.mp4")   # append frame to video (auto-detects FPS)

    if not frame_result.show():           # display frame, quit on 'q'
        break

frame_result.close_vid()                  # finalize the video file

!!! note "Detections and embeddings are track-aligned" frame_result.detections[i] and frame_result.embeddings[i] correspond to frame_result.tracks[i]. Coasting tracks (no matched detection) have zero-filled rows. Use frame_result.tracks.det_ind to check which tracks are coasting (-1).

!!! warning "Avoid show=True / save=True when iterating" Passing show=True or save=True to model.track(...) consumes the stream internally. The returned object will be exhausted, so your for loop gets nothing. Handle display and saving yourself inside the loop as shown above.

Composable runtime

If you need more control, compose the detector, ReID runtime, and tracker explicitly:

from boxmot import track
from boxmot.reid import ReID
from boxmot.trackers import StrongSort
from boxmot.detectors import Detector

detector = Detector("yolov8n.pt", device="cpu")
reid = ReID("osnet_x0_25_msmt17.pt", device="cpu")
tracker = StrongSort(reid_weights="osnet_x0_25_msmt17.pt", device="cpu", half=False)

results = track("video.mp4", detector, reid, tracker, verbose=False)
print(results.summary())

Importing trackers directly

Every tracker class is exported from boxmot.trackers, so you can import any of them into your own project:

from boxmot.trackers import (
    BoostTrack,
    BotSort,
    ByteTrack,
    DeepOcSort,
    HybridSort,
    OccluBoost,
    OcSort,
    SFSORT,
    StrongSort,
)

Using the tracker factory

The create_tracker factory builds a tracker from its string name and loads its default YAML config automatically:

from boxmot.trackers.tracker_zoo import create_tracker

# Motion-only tracker (no ReID model needed)
tracker = create_tracker("bytetrack")

# ReID-aware tracker — pass weights so the factory builds the ReID backend
tracker = create_tracker(
    "botsort",
    reid_weights="osnet_x0_25_msmt17.pt",
    device="cpu",
    half=False,
)

Instantiating a tracker class directly

Import the class and pass parameters yourself for full control:

import numpy as np
from boxmot.trackers import ByteTrack

tracker = ByteTrack(
    track_high_thresh=0.6,
    track_low_thresh=0.1,
    track_buffer=30,
)

# Feed detections frame-by-frame
# dets: (N, 6) array with columns [x1, y1, x2, y2, conf, cls]
# img:  the current frame as a numpy array (H, W, 3)
tracks = tracker.update(dets, img)

For ReID-aware trackers, supply a ReID model:

from boxmot.trackers import OccluBoost
from boxmot.reid.core import ReID

reid = ReID(weights="osnet_x0_25_msmt17.pt", device="cpu", half=False)

tracker = OccluBoost(reid_model=reid.model)

tracks = tracker.update(dets, img)

# tracks is a TrackResults array (M, 8) with columns:
# [x1, y1, x2, y2, id, conf, cls, det_ind]
print(tracks.id)    # track IDs
print(tracks.xyxy)  # bounding boxes
print(tracks.conf)  # confidences

Available trackers

Import nameString keyUses ReID
ByteTrackbytetrackNo
BotSortbotsortYes
StrongSortstrongsortYes
OcSortocsortNo
DeepOcSortdeepocsortYes
HybridSorthybridsortYes
BoostTrackboosttrackYes
OccluBoostoccluboostYes
SFSORTsfsortNo

!!! tip "Custom config overrides" Pass tracker_config to create_tracker to load a non-default YAML, or pass evolve_param_dict with a plain dict of parameters to skip YAML entirely:

```python
from boxmot.trackers.tracker_zoo import create_tracker

tracker = create_tracker(
    "ocsort",
    evolve_param_dict={"det_thresh": 0.3, "iou_thresh": 0.2, "max_age": 50},
)
```

Reference pages

  • High-level APIBoxmot facade, track(...), evaluate(...), and result objects
  • Low-level APIDetector, ReID, and the tracker factory