Skip to main content

motrics

An extremely fast MOT and HOTA metrics library, written in Rust — CLEAR (MOTA/MOTP), Identity (IDF1), and HOTA, with an ergonomic Python API.

CI codecov PyPI License Python 3.10+ Ruff

Bar chart: motrics computes CLEAR+Identity+HOTA in 770ms vs TrackEval's 5930ms (7.7x faster), and CLEAR+Identity in 443ms vs py-motmetrics' 6211ms (14.0x faster).

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, numpy the only required runtime dependency.
  • 🔢 Flexible box inputxyxy or xywh, 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_hota take the same four arguments directly, no Frames needed.
  • Boxes: xyxy by default, box_format="xywh" for the alternative; NumPy (N, 4) arrays accepted too.
  • Want TrackEval's exact reported numbers? Use load_motchallenge_gt + preprocess_motchallenge instead of load_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 every Cargo.toml version 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 piece compat.motmetrics needed, and the first slice of "broaden core inputs" below.
    • motrics.compat.motmetrics — a drop-in MOTAccumulator replacement.
    • 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 own get_preprocessed_seq_data, and now what the real-data benchmark uses. The enabling piece for compat.trackeval.
    • motrics.compat.trackeval — a drop-in for TrackEval's Evaluator/datasets.MotChallenge2DBox/metrics.{HOTA,CLEAR,Identity} (same class names, config keys, and result shape); see above for what's out of scope (parallel eval, full CLEAR field set, other metrics).
    • Broaden core inputs further — box_format="xywh" alongside the default xyxy, and a zero-copy read path for contiguous (N, 4) float64 NumPy arrays, on compute_clear/compute_identity/ compute_hota/iou_matrix/match_boxes. numpy is now the one required runtime dependency of the core.
  • Ergonomic native API — Frames bundles one side's ids/boxes (ground truth or predictions) so the common case isn't four parallel lists retyped per metric; evaluate() takes two Frames and returns CLEAR + Identity + HOTA together, computing the gt/pred similarity matrix once and sharing it across all three (compute_clear/compute_identity/ compute_hota called separately each build their own). The flat compute_clear/compute_identity/compute_hota functions 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 settled Frames as the shape adapters produce, not before.
  • 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 plain MotChallenge2DBox with no DanceTrack-specific preprocessing branch. load_motchallenge_gt + preprocess_motchallenge already handle it — confirmed by a round-trip test against TrackEval's real preprocessing and metrics.
    • KITTI 2D-box — load_kitti/load_kitti_gt + preprocess_kitti replicate TrackEval's Kitti2DBox preprocessing (per-class evaluation, person/van distractors, occlusion/truncation thresholds, min-height and DontCare-region filtering for unmatched predictions), validated against TrackEval's own get_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 the merge/toBbox primitives TrackEval's real KITTI-MOTS/MOTSChallenge/RobMOTS adapters need for ignore-region unioning and size-based filtering. Includes the is_crowd/IoA semantics TrackEval's mask datasets use, spelled is_crowd consistently on both mask_iou and mask_iou_matrix (matching this library's own naming convention, rather than pycocotools' iscrowd). Accepts pycocotools' own RLE dicts directly ({"size": [h, w], "counts": ...}, compressed str/bytes or already-decoded run lengths — no pycocotools install required), validated byte-for-byte and numerically against a real pycocotools build. 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_mots replicate TrackEval's KittiMOTS preprocessing (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 own get_preprocessed_seq_data. Since there's no core compute_* mask overload, preprocessing returns ids plus a precomputed similarity matrix for the compute_*_from_similarity functions. Also adds match_masks, a mask-IoU sibling to match_boxes sharing 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.

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

motrics-0.2.0.tar.gz (229.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

motrics-0.2.0-cp310-abi3-win_amd64.whl (330.0 kB view details)

Uploaded CPython 3.10+Windows x86-64

motrics-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (452.2 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

motrics-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (439.0 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

motrics-0.2.0-cp310-abi3-macosx_11_0_arm64.whl (413.4 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

motrics-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl (423.6 kB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

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

Hashes for motrics-0.2.0.tar.gz
Algorithm Hash digest
SHA256 4f581a4029d8bc435d191b06d35ee2e3fba0678824605b5095561a0cdc62dfbc
MD5 62b2a90c3a04791032a48fa3a14a554b
BLAKE2b-256 8cf20f7d46c3baf77320425496053f038e6b403f7c091de01fc98590990c2469

See more details on using hashes here.

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

Hashes for motrics-0.2.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 e57284a5714cd6d3fa1163c3e82a6baea98d256ae7845af547f1a5137446d67c
MD5 edc3164b939d8d7eb99f4f245ffdf9be
BLAKE2b-256 d476d9def5c1e6ef92a2fd2be177692cfb5a7bc67cad1321576a6bd5214bfc3c

See more details on using hashes here.

File details

Details for the file motrics-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for motrics-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 78d45ac08f339575ca268b43110b7bc40175fe92d4939b4e999062f6ff663c00
MD5 f2241579fb8bc34f5ac6cd0d016c2c5b
BLAKE2b-256 0470b1fa3c84a7c167a4d8aaaab39ac9b4258fd617b4156f6e0134c0c83d90cb

See more details on using hashes here.

File details

Details for the file motrics-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for motrics-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 3411e9cc41e92a5438b6e1e6a16cb5ae7ea9ffa849ee7c2ccfadaa5a7e579a06
MD5 962deb90267ec62013eb8b22b546140b
BLAKE2b-256 9b2e0907d28be55666d0e2161fdf710cf7cfbe8f9b79a248470e4cb7bfc55fa9

See more details on using hashes here.

File details

Details for the file motrics-0.2.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for motrics-0.2.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e42f69a8767f2f0f0b9e10361b732b822f84e0b345b7fb96f7e7454e0bc6e040
MD5 8cd448497d34c38119ccfe8f733ab329
BLAKE2b-256 7eae10c478c69a3db9dcb84a087eaa64b2f3c48e2786d1b32263aa1b213eea05

See more details on using hashes here.

File details

Details for the file motrics-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for motrics-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 672abf96ee29f9c19649c07bb1f5310777abfe9921b38b135b05cfe1050399ac
MD5 e7cfee2c987a25ae4f4191eba18c5e28
BLAKE2b-256 9701e3481644b7f49b8dbc826008fc99b7a02ab8ccf331d3227f5b2298b0406e

See more details on using hashes here.

Release history Release notifications | RSS feed

0.3.0

6 files

This release

0.2.0 This release

6 files

0.1.0

6 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page