CLI tool
cheind/py-motmetrics avatar
cheind/py-motmetrics

py-motmetrics, one call to score a tracker against MOTChallenge ground truth

:bar_chart: Benchmark multiple object trackers (MOT) in Python

1,487 stars261 forksPythonMIT

At a glance

What is it?
py-motmetrics is a Python package that evaluates multiple object tracking results with MOTChallenge-aligned CLEAR MOT, Identity and HOTA metrics, reading MOT16, VATIC and UA-DETRAC files automatically. The one function call is the whole interface, and the lower level pieces underneath are what you reach for when you need control.
Who is it for?
py-motmetrics suits researchers who already have tracker output in MOTChallenge format and want a number they can compare against published results, and it suits teams that want HOTA and CLEAR MOT in one place with a pandas frame behind it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 98 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The metric set matches the papers, and the README names the differences

py-motmetrics is a Python package for evaluating multiple object tracking results, and the metrics it implements are the ones the tracking literature actually reports: MOTChallenge-aligned CLEAR MOT, Identity and HOTA-related measures, covering MOTA, MOTP, IDF1, precision, recall and track quality counts.

The references section pins the lineage to specific papers, which is how you should read the implementation. The CLEAR MOT metrics come from Bernardin and Stiefelhagen in 2008, the MOT16 benchmark from Milan and colleagues in 2016, the HybridBoosted tracker paper by Li and colleagues in 2009, and the multi-target, multi-camera performance measures paper by Ristani and colleagues from 2016.

The README is candid about where this implementation departs from the devkit, and those two differences matter if you are comparing numbers. FAR is not listed directly, but you can compute it as false positives per frame. And MOTChallenge reports MOTP as a percentage while py-motmetrics reports the average distance, with the conversion given as (1 - MOTP) * 100 for MOTChallenge-style MOTP. A number taken from this package and a number taken from the official devkit will differ in form unless you apply that transform, and a paper table will not always make it obvious which convention was used.

There is also a parity check against TrackEval, in the file motmetrics/tests/test_trackeval_parity.py, which is the reference to look at if you need to convince a reviewer the numbers line up.

One function call for files, folders and three input formats

The public API is small enough to describe in one paragraph. For MOTChallenge-style text files you call evaluate_motchallenge with a ground truth path and a prediction path, and it returns a summary object that prints as a MOTChallenge-style table while keeping the underlying pandas data available:

python
import motmetrics as mm

summary = mm.evaluate_motchallenge("path/to/gt.txt", "path/to/pred.txt")
print(summary)

The summary is not just a printed table. Individual metrics are attributes, so summary.mota, summary.idf1 and summary.hota each give a value, and summary.df.to_csv("metrics.csv") writes the frame out for a report or a spreadsheet.

The same function handles folder evaluation, where the layout is a sequence directory per entry:

python
summary = mm.evaluate_motchallenge("path/to/gt_root", "path/to/preds_root")
print(summary)

with the expected layout being gt_root/<SEQUENCE>/gt/gt.txt on the ground truth side and preds_root/<SEQUENCE>.txt on the prediction side. Pointing the same function at two files or two roots is a deliberate design choice, since the per-sequence metrics then get aggregated the same way.

Format detection is automatic by default, because evaluate_motchallenge uses fmt="auto", and the auto mode recognises MOTChallenge text, VATIC text and UA-DETRAC .mat and .xml files. Where a text file is ambiguous, you pass the format explicitly, for example mm.evaluate_motchallenge(gt, pred, fmt=mm.io.Format.MOT16). A command line evaluator exists too, for people who would rather not write the three lines of Python:

bash
python -m motmetrics.apps.eval_motchallenge path/to/gt_root path/to/preds_root

Installing, and the lower level pieces the summary hides

Installation is a single pip command, and the supported Python range is wide:

bash
pip install motmetrics

The README states that Python 3.8 through 3.14 is supported, and pyproject.toml agrees, with requires-python set to >=3.8,<3.15 and classifiers listed for every version in that range. The runtime dependencies are numpy at 1.23.2 or newer, pandas at 1.5.0 or newer and scipy at 1.9.3 or newer, so the package is a thin layer over the scientific Python stack rather than a self-contained implementation.

For development, the README gives a uv based setup, where a virtual environment is created and the dev dependency group installed:

bash
uv venv
uv pip install --group dev

Below the summary API sit the pieces worth knowing about, and the README lists them as the useful lower level components. mm.MOTAccumulator stores the frame-level matching events, which is where the actual assignment of detections to tracks happens. mm.distances holds the distance helpers, including IoU and Euclidean matrices. mm.io.loadtxt with fmt="auto" is the reader you would call directly if you want the data without the metrics. mm.metrics.create() returns a MetricsHost, which is the mechanism for selecting which metrics you want rather than taking the default set. And two comparison utilities exist, mm.utils.compare_to_groundtruth for working directly with loaded dataframes, and mm.utils.compare_to_groundtruth_reweighting for custom HOTA-style multi-threshold workflows.

The default summary includes the commonly reported CLEAR, Identity and HOTA metrics, and mm.list_metrics_markdown() prints the full registry of what is available:

python
import motmetrics as mm

print(mm.list_metrics_markdown())

The package name on PyPI is not the repository name

A practical detail catches people out. The GitHub repository is cheind/py-motmetrics, but the import name and the distribution name are both motmetrics, and the README installs it with pip install motmetrics. The package metadata in pyproject.toml confirms it, with name set to motmetrics and version 1.4.0, and the description reading "Metrics for multiple object tracker benchmarking."

The author list is worth a note too, since the project carries three names: Christoph Heindl, Jack Valmadre and Mikel Broström. The project also has a Zenodo DOI badge, which means releases are archived with a persistent identifier, so a paper can cite a specific version of the evaluation code rather than the repository at large.

The repository root is small: the motmetrics/ package directory, a pyproject.toml, a Dockerfile, a Release.md, and a seqmaps/ directory. The package data configuration shows that the distribution ships data files, with.mat,.txt and.xml files under data/ and.png files under etc/, which is how the format readers and any bundled reference material travel with the wheel.

The Dockerfile at the root is an old development convenience rather than a deployment artifact. It starts from ubuntu:latest, installs python3-pip and vim, symlinks python3 to python in /usr/local/bin, installs numpy and scipy, then pins pandas to the 0.21.0 release, copies the source in and installs the package in editable mode. The pandas pin is well behind the current requirement of 1.5.0 or newer, so treat that image as a historical artifact and install from pip instead.

Where the package stops, and what a different tool would do

The boundaries are clear enough to name.

The first is input format. The readers cover MOTChallenge text, VATIC text and UA-DETRAC .mat and .xml files, and there is no plug-in point described for a tracker that writes its own log format. A tracker emitting JSON, a custom CSV or a database table needs a conversion step before this package can score it, and that conversion becomes part of your evaluation pipeline and part of your risk.

The second is multi-camera work. The repository has a seqmaps/ directory, which is the sequence mapping material used when a benchmark spans several cameras and you need to relate identities across views, but the metric code and the documentation do not present sequence mapping as part of the main API. If your question is how well a tracker keeps one identity across two cameras, you are using a different part of the MOTChallenge tooling.

The third is that this package scores, it does not track. There is no detector, no tracker and no data pipeline here, so a team that wants an end-to-end evaluation harness still has to produce the prediction files. Compared with a full MOT evaluation framework, the approach is narrower and simpler: py-motmetrics is the scoring layer, written to match a known reference so you can trust the number, with the parity test against TrackEval as the guarantee. The trade is that you get metrics and no tooling around them.

For provenance, the project is MIT licensed, and the newest tagged release is v1.4.0 from 2024-10-30 while the last push was on 2026-07-17, so development continues on the develop branch between releases.

Editorial conclusion

py-motmetrics suits researchers who already have tracker output in MOTChallenge format and want a number they can compare against published results, and it suits teams that want HOTA and CLEAR MOT in one place with a pandas frame behind it. It is the wrong tool if your tracker emits its own bespoke log format, because the readers cover MOTChallenge text, VATIC text and UA-DETRAC, and it is the wrong tool if you need per-camera or sequence-map analysis across a multi-camera benchmark, because the sequence mapping support lives in the separate seqmaps/ directory rather than in the metric code. Before adopting it, check the MOTP convention against the papers you compare to, since the package reports an average distance where MOTChallenge reports a percentage and the README gives the conversion, and pin a version, since the last release v1.4.0 dates from 2024-10-30 while the last push was on 2026-07-17.

Frequently asked questions

How do I install py-motmetrics and what Python versions are supported?

Run pip install motmetrics, and note that the distribution is named motmetrics even though the repository is called py-motmetrics. The README states that Python 3.8 through 3.14 is supported, and pyproject.toml sets requires-python to >=3.8,<3.15.

How do I evaluate a tracker with py-motmetrics?

Call mm.evaluate_motchallenge with the ground truth and prediction paths, either two files or two root folders, then print the summary. The summary exposes mota, idf1 and hota as attributes and summary.df for the underlying pandas frame, and formats are detected automatically unless you pass fmt=mm.io.Format.MOT16 for an ambiguous text file.

Which file formats can py-motmetrics read?

The default fmt="auto" mode detects MOTChallenge text, VATIC text and UA-DETRAC .mat and .xml files. The lower level reader mm.io.loadtxt with fmt="auto" does the same detection when you want the data without the metrics.

Why does my MOTP number differ from the one in the MOTChallenge devkit?

MOTChallenge reports MOTP as a percentage while py-motmetrics reports the average distance. The README gives the conversion as (1 - MOTP) * 100 for MOTChallenge-style MOTP, and notes that FAR is not listed directly but can be computed as false positives per frame.

Can I use py-motmetrics outside a standard MOTChallenge setup?

Yes, through the lower level API. mm.MOTAccumulator stores frame-level matching events, mm.metrics.create() returns a MetricsHost for custom metric selection, and mm.utils.compare_to_groundtruth_reweighting supports custom HOTA-style multi-threshold workflows. Sequence mapping for multi-camera benchmarks lives in the separate seqmaps/ directory.

Official sources

  1. cheind/py-motmetrics on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cheind-py-motmetrics.svg)](https://hysenlabs.com/projects/cheind-py-motmetrics)

Was this useful?