Changelog
All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
🚀 Added
trackers benchmark mcbytesubcommand — runs McByte over complete MOT17, DanceTrack, SportsMOT, or SoccerNet-tracking benchmark test sets and writes MOTChallenge-format results, with flags for dataset selection (--dataset), per-dataset detection/image roots (--dataset_roots), device, output location, CMC method/downscale, isolated mask matching, skip-existing, and partial-result retention (#541, #543).trackers inspectcommand group — visual validation commands for the mask stack and the tracker that uses it:inspect sam(box-prompted mask generation),inspect cutie(mask propagation),inspect mask-manager(mask lifecycle over a frame range, driven either from command-line boxes or from a MOT ground-truth file), andinspect mcbyte(locked-IoU baseline against full mask-conditioned McByte). Each writes annotated per-frame images into a timestamped run directory (#543).
🔄 Deprecated
trackers eval --tracker/--tracker_dirrenamed to--predictions/--predictions_dir— old spellings still parse and emitFutureWarning, will be removed in v2.10.0. Use--predictions/--predictions_dirinstead (#541).
🌱 Changed
- McByte CMC now defaults to
cmc_downscale=6— this aggregate-performance default halves median CMC latency versus factor2on the complete 45-clip, 1280x720 SportsMOT validation split and passes the dataset-level mean/median quality criterion. The benchmark used ground-truth detections with masks disabled; 9/45 clips regressed under the previous strict per-clip gate. Passcmc_downscale=2to preserve the previous conservative behavior. GenericCMCConfigandBoTSORTTrackerremain at2. - Mask stack moved from
trackers.core.mcbyte.maskstotrackers.core.masks— SAM mask generation, Cutie propagation, andMaskManagerreference no tracker and are not McByte-specific, so they now live beside the trackers rather than inside one. Import fromtrackers.core.masksinstead (#543).
🔧 Fixed
- OC-SORT now returns low-confidence detections with
tracker_id=-1— detections belowhigh_conf_det_thresholdwere previously dropped silently instead of being emitted, unlikeSORTTracker/ByteTrackTracker.update()now returns one row per input detection, matching the documented contract. Output-contract change: callers may now see additionaltracker_id == -1rows (#566).
2.6.0 — 2026-08-03
🚀 Added
McByteTracker— new mask-conditioned ByteTrack tracker combining mask-conditioned association with SAM box-mask generation and Cutie mask propagation; exported fromtrackers(McByteMaskConfigalso exported) and CLI-discoverable. Newtrackers[mask]extra installstorch,torchvision,rf-segment-anything, andrf-cutie[inference](rf-cutienow ships on PyPI instead of git). Device selection defaults to"auto"(CUDA → MPS → CPU); Cutie streaming behaviour is tunable viacutie_max_internal_size(480),cutie_mem_every(10), andcutie_use_long_term(True), withcutie_channels_last/cutie_compileand AMP left off by default;minimum_mask_creation_frames(3) defers SAM/Cutie encoding for short-lived tracklets. Frames are expected in RGB (BaseTracker's other trackers expect BGR). Mask checkpoints load withweights_only=True(CWE-502 guard, never shipped unsafe) (#388, #418, #441, #452, #459, #481, #491, #508, #513, #519, #520, #521, #522, #523, #524, #525, #526, #529, #532).- Optional
timestamp=onBaseTracker.update()— all six trackers convert elapsed wall-clock seconds into Kalman frame units and prune lost tracks on a seconds budget when timestamps are supplied; omittingtimestamppreserves fixed-rate behaviour (#446). KalmanMotionModelintrackers.utils.motion_models— supplies the KalmanFandQfor a givenframe_step;Fis a trivial constant-velocity matrix (constant_velocity_F) whileScalableProcessNoiseholds the tunedQ, used at the nominal step and DWNA-scaled on timestamp gaps.
⚠️ Breaking Changes
- Invalid lost-track buffer settings now raise
ValueError—lost_track_buffermust be non-negative andframe_ratemust be finite and positive forSORTTracker,ByteTrackTracker,OCSORTTracker,BoTSORTTracker, andCBIoUTracker(which forwards its constructor args toBoTSORTTracker). Explicitlost_track_buffer=0remains valid and means no missed-frame grace period; negative buffers and invalid frame rates previously initialized but produced nonsensical lifecycle behavior (#420). - Confirmed tracks now survive one additional missed frame — all trackers changed from exclusive (
time_since_update < maximum_frames_without_update) to inclusive (<=) boundary semantics to match OC-SORT's previous behavior. Users comparing metric results across this version should expect small IDSW/HOTA shifts (#420).
🌱 Changed
- Improved tracker performance (bit-identical output) — CMC's sparse optical-flow status-mask filtering vectorized (~24x faster for that operation);
KalmanMotionModelnow caches its transition/noise matrices and defers DWNA calibration (~38% lower BoT-SORT/CBIoU predict cost); predicted boxes cached across association stages in BoT-SORT/CBIoU; Kalman-state-estimator copies trimmed and OC-SORT association hygiene improved (#522, #527, #528). - Direct dependency constraint
pydeprecate>=0.7.0raised to>=0.8.0; build backend migrated fromhatchlingtosetuptools(src-layout discovery +py.typedsupport) (#492).
🔧 Fixed
- Positive low-FPS lost-track buffers no longer collapse to zero frames — all trackers now scale positive
lost_track_buffervalues withceil(...)and keep confirmed tracks alive through exactly the scaled number of missed frames, matching OC-SORT's previous inclusive boundary semantics (#420). - BoT-SORT / CBIoU: instant-activated first-frame tracks no longer dropped on a single miss — sticky maturity now triggers once a real
tracker_idis assigned (tracker_id != -1), matching ByteTrack; fixes a default-config ID switch when a frame-1 object is missed once (#478 BoT-SORT, #504 CBIoU). - ByteTrack / BoT-SORT / CBIoU now return unmatched detections between the two confidence thresholds — detections below
track_activation_thresholdbut abovehigh_conf_det_thresholdare now emitted withtracker_id=-1instead of silently dropped, matching the documentedupdate()contract. Output-contract change: callers may now see additionaltracker_id == -1rows (#475). - Signed IoU variants clamped to
[0, 1]before score fusion — the CIoU floor (~-1.5 from the aspect penalty) no longer produces negative fused similarities; the clamp is a no-op for GIoU/DIoU (#476). - OC-SORT: Kalman scale kept positive across frame-step gaps —
clamp_velocityno longer allows scale to collapse negative over long timestamp gaps (#509). - CMC: handle mid-stream frame-resolution changes in optical-flow motion estimation — adds a re-sync/resize path when the input resolution changes between frames (#505).
- CMC: fixed
cv2.resizecrash on tiny downscaled images — dimensions are now clamped tomax(1, ...), covering 1x1, 1x3, and 3x1 edge cases (#488). xcycsr_to_xyxy: prevent zero-division on zero-aspect boxes — anr1 != 0guard now yields height0instead ofinf(#485).- Video output resized on mid-stream resolution change —
VideoOutputnow resizes frames to match the writer's configured size (INTER_AREAon downscale only) (#514). - Package builds fixed for
srclayout — correctedsetuptoolspackage discovery and typed-package (py.typed) support, plus release CI artifact handling and publish triggers (#492).
🔒 Security
- Hardened ZIP extraction against Zip Slip / path traversal (CWE-22) — archive members with absolute paths, traversal segments, empty names, or Windows-style backslash paths are now rejected; validated archives are contained so they cannot write outside the destination directory, including path-swap protection (#495).
2.5.0 — 2026-06-22
🚀 Added
- Pluggable IoU variants —
iou=parameter on all four trackers (SORTTracker,ByteTrackTracker,OCSORTTracker,BoTSORTTracker) accepts anyBaseIoUsubclass. Built-in variants:IoU(standard),GIoU,DIoU,CIoU,BIoU(Buffered IoU) (#403). BaseIoUABC intrackers.utils.iou— defines thecompute(boxes_1, boxes_2)contract; subclass and override_computeto implement a custom similarity metric (#403).normalize_for_fusiononBaseIoU— signed variants (GIoU, DIoU, CIoU) override this to shift[-1, 1]→[0, 1]before BoT-SORT score fusion, preventing ranking inversion (#403).CBIoUTracker— Cascaded-Buffered IoU tracker (Yang et al., WACV 2023). Two-stage matching with independently tunablebuffer_ratio_first/buffer_ratio_secondbuffer scales; inherits ByteTrack-style low-confidence second pass from BoT-SORT (#417).py.typedmarker — PEP 561 compliance; IDEs and type checkers now recognise the package as typed without--ignore-missing-imports.
🔄 Deprecated
SORTTracker.trackers— deprecated alias for.tracks; emitsFutureWarningsince v2.5, will be removed in v3.0. Usetracker.tracksinstead.trackers.core.botsort.cmcmodule —CMCmoved totrackers.utils.cmc; old path re-exports all symbols withDeprecationWarninguntil v3.0. Migrate:from trackers.utils.cmc import CMCorfrom trackers import CMC(#414).BoTSORTTracker.apply_cmc_batch— useCMC.apply_batch(H, tracker.tracks)directly. Will be removed in v3.0 (#414).CMCTMethodtype alias — kept as a back-compat alias forCMCMethod; will be removed in v3.0. Migrate toCMCMethod(#414).CMC.apply_to_xyxyrenamed toCMC.warp_xyxy_corners— old name kept as a deprecated wrapper that forwards to the new name; will be removed in v3.0. Update call sites toCMC.warp_xyxy_corners(#414).
⚠️ Breaking Changes
- Internal tracklet ID counters removed — track IDs are now allocated by each tracker instance instead of the class-level counters on each
*Trackletsubclass (e.g.BoTSORTTracklet.get_next_tracker_id()). Internal tracklet subclassers should allocate IDs in tracker code and assigntracklet.tracker_iddirectly. Useself._allocate_tracker_id()(inherited fromBaseTracker) as the replacement allocator when implementing a custom tracker subclass.
🌱 Changed
CMC,CMCConfig,CMCMethodmoved totrackers.utils.cmcand re-exported from top-leveltrackerspackage — import directly withfrom trackers import CMC; oldtrackers.core.botsort.cmcpath kept as a deprecated shim (#414).CMC.warp_xyxy_corners—apply_to_xyxyrenamed towarp_xyxy_corners; old name kept as a deprecated wrapper until v3.0 (#414).CMC.apply_batchhomogeneity guard — now raisesTypeErrorimmediately when the tracklet list contains mixed state-estimator types, preventing silent state corruption (#414).BoTSORTTracklet.apply_cmcdelegates toCMC.apply_batch— per-track and batch paths now share identical code; behaviour is unchanged (#414).Tunergainsenqueue_defaults,fixed_params,images_dir,seed—enqueue_defaults=True(default) evaluates a baseline trial using each param's__init__default before Optuna samples;fixed_paramsholds selected params constant across all trials;images_direnables frame loading for CMC-enabled trackers;seedmakes TPE sampling reproducible (#427).
🔧 Fixed
- Clarified in docs that
SORTTrackeritself is not deprecated — only the.trackersalias is. - BoT-SORT score fusion with signed IoU —
_fuse_scoremultiplied raw negative IoU values by confidence, inverting track ranking for GIoU/DIoU/CIoU;normalize_for_fusionnow normalises similarity before fusion (#403). - Non-finite box coordinates crash
linear_sum_assignment—BaseIoU.computenow raisesValueErrorwith a clear message for NaN/inf inputs instead of propagating invalid entries into SciPy (#403). - OC-SORT Observation-Centric Recovery now uses standard
IoUper the paper, independent of the configurediou=variant (#403). - Eager division warnings on zero-area boxes — IoU helper switched from
np.where(eager) tonp.divide(..., where=...)(lazy), suppressingRuntimeWarningunder strict NumPy error settings (#403). - CLI argparse crash on
BaseIoUparameter —iou=is now excluded from argparse auto-discovery; the variant must be set programmatically (#403). - ByteTrack tracked nothing when detections lacked confidence scores — the default-fill changed from
np.zerostonp.ones, matching SORT / OC-SORT / BoT-SORT behaviour, so detectors that emitsv.Detectionswithoutconfidencenow produce tracks instead of empty results (#415). - Tracker instances no longer share track ID counters — resetting one tracker instance no longer resets another instance's ID allocator, preventing duplicate live IDs in multi-camera, class-specific, or parallel tracker workflows.
- HOTA per-frame alpha loop vectorized — removes the inner Python loop; large evaluations run significantly faster with no change to numeric output (#462).
- MOT evaluation distractor handling — ground-truth preprocessing now applies distractor class filtering consistent with TrackEval, correcting reported metrics on MOT17 and similar datasets (#466).
2.4.0 — 2026-05-06
🚀 Added
- BoT-SORT tracker (
BoTSORTTracker) — new tracker with optional camera motion compensation (CMC), configurable methods (orb,sift,sparseOptFlow,ecc), and ByteTrack-style score-fused association (#386). tracked_objectsproperty onBaseTrackerand all concrete trackers — exposes every alive track with its Kalman-predicted bounding box, including occluded or detector-missed tracks.update()return value is unchanged for backward compatibility (#373, resolves #105).Tunerclass (trackers.tune.Tuner) — Optuna-based hyperparameter optimisation driven by each tracker's newsearch_spaceClassVar. Supports HOTA / MOTA / IDF1 objectives over MOT-format ground-truth and pre-computed detections (#301).trackers tuneCLI subcommand — wiresTunerinto the CLI; selects tracker, ground-truth directory, detections directory, objective, and--n-trials(#374).load_mot_fileis now public — was_load_mot_file. Now exported fromtrackers.io.motfor use in custom tuning and evaluation scripts (#301, #374).xyxy_to_xywhandxywh_to_xyxyconverters added totrackers.utils.convertersfor center-width-height format support (#310, #386).frameparameter onBaseTracker.update()—update(detections, frame=None). Required by BoT-SORT when CMC is enabled; ignored (withUserWarning) by SORT, ByteTrack, OC-SORT. ThetrackCLI passes the current frame automatically (#386).- Swappable Kalman state estimators —
BaseStateEstimator,XCYCSRStateEstimator,XCYCWHStateEstimator,XYXYStateEstimatorintrackers.utils.state_representations; trackers can opt in viastate_estimator_class=(#310). TrackletProtocolstructural type intrackers.core.base— formalises the contract every tracklet stored inBaseTracker.tracksmust satisfy.search_spaceClassVar on every tracker — declarative hyperparameter spaces consumed byTuner, validated for unknown keys and bad types.- Modern Python 3.10+ type hints across the public surface (#302).
- Documentation:
docs/trackers/botsort.mduser guide,docs/learn/state-estimators.md, expanded comparison page with DanceTrack section.
⚠️ Breaking Changes
SORTTracker.update()no longer mutates its inputsv.Detections— previously assignedtracker_idon the caller's object and returned that same instance; now returns a fresh indexed copy, matching ByteTrack and OC-SORT (#360). Callers that relied on aliasing the input post-update must readtracker_idfrom the returned object.- Per-frame spawn order is now deterministic across SORT, ByteTrack, and OC-SORT — IDs assigned to detections that spawn in the same frame no longer depend on CPython set iteration order (#361). IDs from a recorded run are reproducible across machines but may differ from a v2.3.0 baseline.
- Internal tracklet update contract changed (subclassers of internal
*Trackletclasses only — callers of the publicTracker.update()API are unaffected) — internal tracklet classes (notablyOCSORTTracklet) no longer acceptupdate(None)for unmatched tracks; missed-association logic now lives inpredict()and_get_alive_tracklets. Subclasses that overrode tracklet update behaviour must move that logic intopredict()(#383, follow-up to #376).
🌱 Changed
- Refactored Kalman filter out of tracklet classes — every tracker now shares a single Kalman implementation backed by
BaseStateEstimator. Tracklet classes (SORTTracklet,ByteTrackTracklet,OCSORTTracklet,BoTSORTTracklet) handle association and lifecycle only (#310). - ByteTrack tracklets now count
number_of_successful_consecutive_updatesinstead of totalnumber_of_updates, matching the original ByteTrack reference (#310). - Eval submodule uses lazy
__getattr__forevaluate_mot_sequenceandevaluate_mot_sequencesto avoid circular imports. - Documentation: rewrote landing page, install guide, evaluate guide, ByteTrack page, comparison page; added DanceTrack default tuned numbers.
🔧 Fixed
- ByteTrack: prune unmatched tracks correctly after the Kalman refactor —
time_since_updateadvances on unmatched tracks and_get_alive_trackletsexpires them afterlost_track_bufferempty frames (#376). - Documentation index ByteTrack correction (#371).
2.3.0 — 2026-03-16
🚀 Added
- OC-SORT tracker (
OCSORTTracker) — complete implementation with swappable state estimators (XCYCSRStateEstimator,XYXYStateEstimator), direction-consistency batch calculations, full tracklet lifecycle management, API docs, and unit tests; registered in CLI and public API (#207). trackers downloadCLI subcommand — downloads MOT17 and SportsMOT benchmark datasets to a persistent local cache (~/.cache/trackers) with MD5 verification and Rich-styled progress output; backed by type-safeDataset,DatasetSplit, andDatasetAssetenums (#262).- Integration tests with TrackEval — regression tests for SORT, ByteTrack, and OC-SORT against oracle detections from SportsMOT and DanceTrack; evaluates HOTA, MOTA, IDF1, and IDSW in CI (#298).
- Parameter-tuned benchmark results — tracker comparison page redesigned with tabbed Default / Tuned layout; includes grid-search configs for SportsMOT, SoccerNet, MOT17, and DanceTrack as copyable YAML blocks (#309).
- DanceTrack default parameters — SORT and ByteTrack ship tuned defaults for DanceTrack out of the box (#299).
🌱 Changed
- Coordinate converter hot-path optimisation —
xcycsr_to_xyxyandxyxy_to_xcycsrrestructured for the single-box case, reducing per-frame overhead in tight tracking loops (#296). - Documentation rewrite — landing page, install guide, and evaluate pages comprehensively rewritten; tracker comparison page expanded with dataset videos, paper links, and a DanceTrack section (#322).
- Release and stable branching strategy adopted — repository now follows a
release/stablebranching model (#275).
🔧 Fixed
- Evaluation distractor filtering corrected on the comparison numbers (#322).
- PyPI publish action pinned to verified SHA —
pypa/gh-action-pypi-publishcorrected to the actual v1.13.0 commit SHA (#294).
2.2.0 — 2026-02-18
🚀 Added
- Evaluation metrics — HOTA, CLEAR (MOTA / MOTP / IDSW / MT / PT / ML), and Identity (IDF1 / IDP / IDR) metric implementations in
trackers.eval;evaluate_mot_sequenceandevaluate_mot_sequencespublic API (#210, #212, #223, #224, #226). trackers evalCLI subcommand — runs a tracker over a MOT-format ground-truth directory and prints HOTA / MOTA / IDF1 results; configurable via JSON tracker arguments (#215).- MOT format I/O —
load_mot_fileandsave_mot_fileintrackers.io.motfor reading and writing MOT-format.txtannotation files (#214). MotionAwareTraceAnnotatorwith camera motion compensation — applies homography-based CMC to keep trace paths stable on moving-camera footage (#263).- Tracker auto-registration —
BaseTracker.__init_subclass__now registers every subclass and extracts parameter metadata from__init__docstrings, enabling CLI auto-discovery without a hard-coded tracker list (TrackerInfo,ParameterInfo) (#230). - Benchmark documentation — evaluation metrics (HOTA, MOTA, IDF1) for SORT and ByteTrack published to the docs site (#193).
- Example notebooks — links to runnable Colab notebooks added to docs index (#199).
🌱 Changed
- Apache 2.0 license headers added to all source files via a new pre-commit hook.
- Ruff security rules migrated to S (bandit) —
banditpre-commit hook replaced by Ruff's built-inSrule set (#188). - Dependency trim — removed unused optional extras; install footprint reduced (#192).
2.1.0 — 2026-01-28
🚀 Added
- ByteTrack tracker (
ByteTrackTracker) — two-stage low-score / high-score association with a ByteTrack-specific Kalman box tracker; full API docs and unit tests; registered in CLI and public API (#174).
⚠️ Breaking Changes
- DeepSort removed —
DeepSortTrackerand all associated REID infrastructure removed from the package, docs, tests, and CI workflows. Projects using DeepSort must pin to<2.1.0. - Python 3.9 dropped — minimum supported version is now Python 3.10; type annotations updated to use built-in generics (
list[...],dict[...]) throughout the public API (#200).
🔧 Fixed
- Documentation build — pinned
mkdocstrings-python<2.0.0to resolve docs generation failure.