motrics
An extremely fast MOT and HOTA metrics library, written in Rust — CLEAR (MOTA/MOTP), Identity (IDF1), and HOTA, with an ergonomic Python API.
MOT17-train, wall time, from a live CI run — see Benchmarks.
Highlights
- ⚡ Extremely fast — Rust core, ~7–9× faster than TrackEval and ~12–16× faster than py-motmetrics on real MOT17 data.
- 🎯 Numerically validated — exact parity with TrackEval on CLEAR, Identity, and HOTA, checked in CI.
- 🔄 Drop-in migration — swap one import to replace py-motmetrics; evaluate a MOTChallenge benchmark without installing TrackEval.
- 🐍 Ergonomic, typed Python API — PEP 561,
numpythe only required runtime dependency. - 🔢 Flexible box input —
xyxyorxywh, and a zero-copy read path for contiguous NumPy arrays.
Install
pip install motrics
Prebuilt wheels for Linux, macOS, and Windows (Python 3.10+). Building from source instead? See CONTRIBUTING.md for the dev setup.
Quickstart
import motrics
# Parse MOTChallenge ground truth and tracker results.
gt = motrics.load_motchallenge("seq/gt/gt.txt")
pred = motrics.load_motchallenge("seq/res.txt", min_confidence=0.5)
# Align onto a shared frame timeline, bundle each side, then evaluate.
gt_ids, gt_boxes, pred_ids, pred_boxes = motrics.align_frames(gt, pred)
result = motrics.evaluate(
motrics.Frames(ids=gt_ids, boxes=gt_boxes),
motrics.Frames(ids=pred_ids, boxes=pred_boxes),
)
print(result.clear.mota, result.identity.idf1, result.hota.hota)
- Only need one metric?
compute_clear/compute_identity/compute_hotatake the same four arguments directly, noFramesneeded. - Boxes:
xyxyby default,box_format="xywh"for the alternative; NumPy(N, 4)arrays accepted too. - Want TrackEval's exact reported numbers? Use
load_motchallenge_gt+preprocess_motchallengeinstead ofload_motchallenge+align_frames.
Migrating from py-motmetrics or TrackEval
Swap one import — the rest of your code is unchanged.
py-motmetrics
# before
import motmetrics as mm
# after — same code, motrics underneath
import motrics.compat.motmetrics as mm
acc = mm.MOTAccumulator(auto_id=True)
for gt_ids, gt_boxes, pred_ids, pred_boxes in sequence:
dists = mm.distances.iou_matrix(gt_boxes, pred_boxes, max_iou=0.5)
acc.update(gt_ids, pred_ids, dists)
summary = mm.metrics.create().compute(acc, metrics=mm.metrics.SUPPORTED, name="acc")
pip install motrics[compat] (pulls in pandas, needed only for this subpackage).
| ✅ Supported | mota, motp, idf1, idp, idr, recall, precision, num_false_positives, num_misses, num_switches, num_unique_objects |
| ❌ Not yet | Per-trajectory metrics (mostly-tracked, fragmentations, transfer/ascend/migrate) — raises NotImplementedError naming what's missing |
See python/motrics/compat/motmetrics/
for what else differs (e.g. no events/mot_events DataFrame).
TrackEval
# before
import trackeval
# after — same code, motrics underneath
import motrics.compat.trackeval as trackeval
eval_config = trackeval.Evaluator.get_default_eval_config()
evaluator = trackeval.Evaluator(eval_config)
dataset_config = trackeval.datasets.MotChallenge2DBox.get_default_dataset_config()
dataset_config["GT_FOLDER"] = "data/gt/mot_challenge/"
dataset_config["TRACKERS_FOLDER"] = "data/trackers/mot_challenge/"
dataset_list = [trackeval.datasets.MotChallenge2DBox(dataset_config)]
metrics_list = [trackeval.metrics.HOTA(), trackeval.metrics.CLEAR(), trackeval.metrics.Identity()]
results, messages = evaluator.evaluate(dataset_list, metrics_list)
print(results["MotChallenge2DBox"]["my_tracker"]["COMBINED_SEQ"]["pedestrian"]["CLEAR"]["MOTA"])
Same class names, config keys, directory/seqmap conventions, and result shape
as real TrackEval — no trackeval/scipy install required, only numpy (a
core dependency already).
| ✅ Supported | HOTA, Identity, CLEAR's MOTA/MOTP — bit-exact vs real TrackEval |
| ❌ Not yet | Parallel evaluation · BREAK_ON_ERROR config · printing/plotting · zipped input · DO_PREPROC=False · MOT15 · extra CLEAR fields (MT/PT/ML/Frag/etc.) · IDEucl/JAndF/TrackMAP/VACE |
See python/motrics/compat/trackeval/
for the full list of what differs from real TrackEval.
Metric name map — TrackEval / py-motmetrics / motrics' native API
Using motrics' own API directly (faster than the compat layer — no
per-frame Python bookkeeping)? Here's how the field names line up:
| Concept | TrackEval | py-motmetrics | motrics (native) |
|---|---|---|---|
| Matched detections (incl. switches) | CLR_TP |
num_detections |
ClearMetrics.num_matches |
| False positives | CLR_FP |
num_false_positives |
ClearMetrics.num_false_positives |
| Misses | CLR_FN |
num_misses |
ClearMetrics.num_misses |
| Identity switches | IDSW |
num_switches |
ClearMetrics.num_switches |
| MOTA / MOTP | MOTA / MOTP |
mota / motp |
ClearMetrics.mota / .motp |
| Identity TP / FP / FN | IDTP/IDFP/IDFN |
idtp/idfp/idfn |
IdentityMetrics.idtp/.idfp/.idfn |
| IDF1 / IDP / IDR | IDF1/IDP/IDR |
idf1/idp/idr |
IdentityMetrics.idf1/.idp/.idr |
| HOTA / DetA / AssA / LocA | HOTA/DetA/AssA/LocA |
— (not in motmetrics) | HotaMetrics.hota/.deta/.assa/.loca |
Benchmarks
On real MOT17 data, release build, end-to-end from raw boxes (chart at the top of this README):
| motrics vs… | Metrics | Speedup |
|---|---|---|
| TrackEval | CLEAR + Identity + HOTA | ~7–9× |
| py-motmetrics | CLEAR + Identity | ~12–16× |
Numbers are illustrative and machine-dependent — see the CI benchmark comment
on any PR for a live measurement. See
benchmarks/README.md for methodology and how to run
it yourself.
Roadmap
- Project scaffolding (build, lint, packaging, CI)
- Published to PyPI (
pip install motrics), automated tag-and-release on everyCargo.tomlversion bump (see.github/workflows/release-tag.yml) - Bounding-box IoU + assignment (Hungarian/greedy) primitives
- CLEAR metrics (MOTA, MOTP, ID switches, FP/FN)
- Identity metrics (IDF1 / IDP / IDR)
- HOTA (DetA, AssA, alpha sweep)
- MOTChallenge ingest + integration tests
- TrackEval numeric parity tests (CLEAR / Identity / HOTA)
- Benchmark & parity infrastructure vs TrackEval and py-motmetrics,
on real MOTChallenge data, validated in CI.
- Zero-copy NumPy input path (see "broaden core inputs" below).
- Replace TrackEval / py-motmetrics, not just benchmark against them:
- Precomputed-similarity core inputs (
compute_clear_from_similarity,compute_identity_from_similarity) — the piececompat.motmetricsneeded, and the first slice of "broaden core inputs" below. -
motrics.compat.motmetrics— a drop-inMOTAccumulatorreplacement. - Migration guide + metric-name map (see above).
- MOTChallenge ingest with TrackEval-parity preprocessing
(
load_motchallenge_gt+preprocess_motchallenge: distractor-class removal, pedestrian-only, "do not consider" rows dropped) — validated against TrackEval's ownget_preprocessed_seq_data, and now what the real-data benchmark uses. The enabling piece forcompat.trackeval. -
motrics.compat.trackeval— a drop-in for TrackEval'sEvaluator/datasets.MotChallenge2DBox/metrics.{HOTA,CLEAR,Identity}(same class names, config keys, and result shape); see above for what's out of scope (parallel eval, fullCLEARfield set, other metrics). - Broaden core inputs further —
box_format="xywh"alongside the defaultxyxy, and a zero-copy read path for contiguous(N, 4)float64 NumPy arrays, oncompute_clear/compute_identity/compute_hota/iou_matrix/match_boxes.numpyis now the one required runtime dependency of the core.
- Precomputed-similarity core inputs (
- Ergonomic native API —
Framesbundles one side's ids/boxes (ground truth or predictions) so the common case isn't four parallel lists retyped per metric;evaluate()takes twoFramesand returns CLEAR + Identity + HOTA together, computing the gt/pred similarity matrix once and sharing it across all three (compute_clear/compute_identity/compute_hotacalled separately each build their own). The flatcompute_clear/compute_identity/compute_hotafunctions are unchanged, for single-metric use.- Streaming accumulator —
update()per frame,compute()at the end, the shape both py-motmetrics and torchmetrics use, for online evaluation or sequences too large to hold fully in memory. Deferred: HOTA's alpha sweep is naturally a whole-sequence batch computation, so incrementalizing it correctly is real design work, not a thin wrapper around the existing core — worth doing once the dataset-adapter layer below has settledFramesas the shape adapters produce, not before.
- Streaming accumulator —
- Pluggable dataset-adapter layer — one metric core, one small adapter per
benchmark (ingest + preprocessing + similarity), added incrementally:
- DanceTrack — no adapter code needed. Its
gt.txt/results format is byte-for-byte MOTChallenge's (fixed class=1/consider=1 columns), and TrackEval evaluates it via plainMotChallenge2DBoxwith no DanceTrack-specific preprocessing branch.load_motchallenge_gt+preprocess_motchallengealready handle it — confirmed by a round-trip test against TrackEval's real preprocessing and metrics. - KITTI 2D-box —
load_kitti/load_kitti_gt+preprocess_kittireplicate TrackEval'sKitti2DBoxpreprocessing (per-class evaluation,person/vandistractors, occlusion/truncation thresholds, min-height andDontCare-region filtering for unmatched predictions), validated against TrackEval's ownget_preprocessed_seq_data. - Mask-IoU similarity kernel (KITTI-MOTS, BDD-MOTS, DAVIS) —
Mask,mask_iou/mask_iou_matrix/mask_area/mask_decode/mask_encode/mask_merge/mask_to_bbox, a from-scratch Rust port of pycocotools' RLE codec, a direct run-based intersection/union sweep (no dense-array decode), and themerge/toBboxprimitives TrackEval's real KITTI-MOTS/MOTSChallenge/RobMOTS adapters need for ignore-region unioning and size-based filtering. Includes theis_crowd/IoA semantics TrackEval's mask datasets use, spelledis_crowdconsistently on bothmask_iouandmask_iou_matrix(matching this library's own naming convention, rather than pycocotools'iscrowd). Accepts pycocotools' own RLE dicts directly ({"size": [h, w], "counts": ...}, compressedstr/bytesor already-decoded run lengths — nopycocotoolsinstall required), validated byte-for-byte and numerically against a realpycocotoolsbuild. This is the similarity kernel (plus the two extra primitives those adapters specifically need) the mask-based dataset adapters below build on. - KITTI-MOTS —
load_kitti_mots/load_kitti_mots_gt+preprocess_kitti_motsreplicate TrackEval'sKittiMOTSpreprocessing (per-class evaluation, gt/prediction matching by mask IoU, ignore-region-covered unmatched predictions dropped — no distractor classes or occlusion/truncation thresholds here, unlike KITTI 2D-box), validated against TrackEval's ownget_preprocessed_seq_data. Since there's no corecompute_*mask overload, preprocessing returns ids plus a precomputed similarity matrix for thecompute_*_from_similarityfunctions. Also addsmatch_masks, a mask-IoU sibling tomatch_boxessharing the same Hungarian/greedy assignment core. - BDD-MOTS / DAVIS — same mask kernel, different dataset-specific preprocessing rules.
- 3D similarity kernel (KITTI-3D) — same as above, separate core work.
- DanceTrack — no adapter code needed. Its
Contributing
See CONTRIBUTING.md for the development setup, tooling, and checks to run before opening a PR.
License
MIT © 2026 Kevin Serrano
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file motrics-0.2.0.tar.gz.
File metadata
- Download URL: motrics-0.2.0.tar.gz
- Upload date:
- Size: 229.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f581a4029d8bc435d191b06d35ee2e3fba0678824605b5095561a0cdc62dfbc
|
|
| MD5 |
62b2a90c3a04791032a48fa3a14a554b
|
|
| BLAKE2b-256 |
8cf20f7d46c3baf77320425496053f038e6b403f7c091de01fc98590990c2469
|
File details
Details for the file motrics-0.2.0-cp310-abi3-win_amd64.whl.
File metadata
- Download URL: motrics-0.2.0-cp310-abi3-win_amd64.whl
- Upload date:
- Size: 330.0 kB
- Tags: CPython 3.10+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e57284a5714cd6d3fa1163c3e82a6baea98d256ae7845af547f1a5137446d67c
|
|
| MD5 |
edc3164b939d8d7eb99f4f245ffdf9be
|
|
| BLAKE2b-256 |
d476d9def5c1e6ef92a2fd2be177692cfb5a7bc67cad1321576a6bd5214bfc3c
|
File details
Details for the file motrics-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: motrics-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 452.2 kB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78d45ac08f339575ca268b43110b7bc40175fe92d4939b4e999062f6ff663c00
|
|
| MD5 |
f2241579fb8bc34f5ac6cd0d016c2c5b
|
|
| BLAKE2b-256 |
0470b1fa3c84a7c167a4d8aaaab39ac9b4258fd617b4156f6e0134c0c83d90cb
|
File details
Details for the file motrics-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: motrics-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 439.0 kB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3411e9cc41e92a5438b6e1e6a16cb5ae7ea9ffa849ee7c2ccfadaa5a7e579a06
|
|
| MD5 |
962deb90267ec62013eb8b22b546140b
|
|
| BLAKE2b-256 |
9b2e0907d28be55666d0e2161fdf710cf7cfbe8f9b79a248470e4cb7bfc55fa9
|
File details
Details for the file motrics-0.2.0-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: motrics-0.2.0-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 413.4 kB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e42f69a8767f2f0f0b9e10361b732b822f84e0b345b7fb96f7e7454e0bc6e040
|
|
| MD5 |
8cd448497d34c38119ccfe8f733ab329
|
|
| BLAKE2b-256 |
7eae10c478c69a3db9dcb84a087eaa64b2f3c48e2786d1b32263aa1b213eea05
|
File details
Details for the file motrics-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: motrics-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 423.6 kB
- Tags: CPython 3.10+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
672abf96ee29f9c19649c07bb1f5310777abfe9921b38b135b05cfe1050399ac
|
|
| MD5 |
e7cfee2c987a25ae4f4191eba18c5e28
|
|
| BLAKE2b-256 |
9701e3481644b7f49b8dbc826008fc99b7a02ab8ccf331d3227f5b2298b0406e
|