vernier
Fast, auditable evaluation for 2D vision models. Detection, instance and panoptic segmentation, semantic segmentation, keypoints, and LVIS, all in one package with a Rust core, a Python API, and a standalone CLI.
- Bit-exact with
pycocotools==2.0.11,panopticapi,lvis-apiandboundary-iou-apiin strict mode. Every upstream quirk has a documented disposition (quirks survey). - Drop-in for
pycocotools.cocoeval.COCOeval: change one import, or none. Same constructor, sameparamsmutations, sameeval/evalImgs/ious/statsread back off the instance (what carries over). - 3.7–19× faster than faster-coco-eval and pycocotools at equal CPU budget (benchmarks).
- Built for real pipelines: training-loop evaluation, multi-rank gathering, per-image tables, error decomposition, calibration, scenario slicing.
Install
pip install vernier # Python ≥ 3.10, abi3 wheels
cargo binstall vernier-cli # standalone `vernier` binary, no Python needed
cargo add vernier # Rust library
Quickstart: the COCO API you already know
vernier.COCOeval has the same constructor, the same
evaluate() / accumulate() / summarize() sequence, and the same .stats
as pycocotools. It defaults to parity_mode="strict", so the output is
bit-identical (see Status & validation for what
that is measured against).
from pycocotools.coco import COCO
from vernier import COCOeval # was: from pycocotools.cocoeval import COCOeval
coco_gt = COCO("instances_val2017.json")
coco_dt = coco_gt.loadRes("detections.json")
E = COCOeval(coco_gt, coco_dt, iouType="bbox") # "segm" | "keypoints" | "boundary"
E.evaluate()
E.accumulate()
E.summarize()
Code you can't edit (mmdetection, detectron2, ultralytics, …): patch
the symbol before anything imports pycocotools.cocoeval.
import vernier
unpatch = vernier.patch_pycocotools() # pycocotools.cocoeval.COCOeval -> vernier
run_existing_eval()
unpatch()
The patch is explicit, reversible, and never happens on import. A context
manager (vernier.adapters.patched_pycocotools) and a one-fixture pytest
recipe are in the pycocotools migration guide.
TorchMetrics' MeanAveragePrecision runs under the patch unchanged,
including class_metrics=True and extended_summary=True
(tests/python/test_compat_torchmetrics.py asserts the patched and
unpatched results are equal). Two params fields are the exception and
raise rather than diverge quietly: imgIds subsetting, and areaRng
(custom area ranges are a native-Evaluator feature, ADR-0040). The
migration guide
has the field-by-field table.
Recommended: the native API
The shim exists for migration. New code should use the native Evaluator:
immutable configuration, typed results, no pycocotools dependency, and
access to everything below.
from pathlib import Path
from vernier.instance import Bbox, CocoDataset, Evaluator
gt = CocoDataset.from_json(Path("instances_val2017.json").read_bytes())
dt = Path("detections.json").read_bytes()
evaluator = Evaluator(iou=Bbox(), parity_mode="strict")
summary = evaluator.evaluate(gt, dt, num_threads=8)
print("\n".join(summary.pretty_lines())) # the familiar 12-line table
ap = summary.stats[0]
The native
Evaluatordefaults toparity_mode="corrected", which applies the itemized fixes to upstream bugs. Pass"strict"when your numbers must match published pycocotools results.
Inside a training loop, evaluate on a background worker and feed it tensors directly (torch, JAX, CuPy, NumPy via DLPack, zero-copy):
with evaluator.background(gt) as bg:
for images, targets in val_loader:
preds = model(images)
bg.submit([{"image_id": int(t["image_id"]), **p} for t, p in zip(targets, preds)])
summary = bg.finalize()
Per-image and per-class diagnostics as Polars DataFrames
(pip install "vernier[tables]"):
result = evaluator.evaluate(gt, dt, tables="all")
result.per_class
Paradigms
Pick the submodule that matches your model's output. They have different data models and matching rules, so they are separate evaluators rather than one class with a mode switch (why).
| Submodule | Input | Metrics |
|---|---|---|
vernier.instance |
Scored detections: boxes, masks, keypoints | AP / AR (bbox, segm, boundary, OKS), LVIS federated AP |
vernier.panoptic |
Panoptic PNGs + segments_info |
PQ / SQ / RQ, boundary PQ |
vernier.semantic |
Class-id label maps | mIoU, FWIoU, pixel accuracy, mean accuracy |
Beyond the COCO API
| Need | Feature | Guide |
|---|---|---|
| Evaluate without blocking training | Evaluator.background(...) |
how-to |
| Evaluate across DDP ranks | evaluate_to_partial / from_partials |
how-to |
| Find which images/classes regressed | tables="all" |
how-to |
| Explain an AP gap | TIDE error decomposition, oLRP | tutorial |
| Check score calibration | ECE / MCE / reliability (calibration=True) |
how-to |
| Metrics per weather, time of day, … | Manifest slicing, vernier aggregate (mPC / rPC) |
how-to |
| Non-standard IoU / recall / area grids | iou_thresholds=, recall_thresholds=, area_ranges= |
how-to |
CLI
A static binary for CI gates and robotics replay pipelines. Output is byte-deterministic (sorted keys, no timestamps), so artifacts diff cleanly.
vernier eval --gt gt.json --dt dt.json --iou-type bbox # pycocotools-identical stdout
vernier eval --gt gt.json --dt dt.json --iou-type segm --emit json=result.json --threads 8
Exit codes: 0 success, 1 evaluation error, 2 invalid arguments.
Full reference: crates/vernier-cli.
Status & validation
Every row is checked by a parity harness that runs the reference
implementation and vernier on the same inputs. "Bit-exact" means
parity_mode="strict", against the reference as published on PyPI
— vernier's own output is identical on x86-64 and ARM. (A pycocotools
you recompile from source can round bbox IoU 1-2 ULP differently on
ARM; see ADR-0056
and the migration guide.)
| Metric | Reference | Parity | Notes |
|---|---|---|---|
| bbox / segm / keypoints AP | pycocotools==2.0.11 |
bit-exact | |
| Boundary AP | boundary-iou-api |
bit-exact | |
| LVIS federated AP | lvis-api 0.5.3 |
bit-exact | full v1 val, bbox |
| Panoptic PQ, boundary PQ | panopticapi (single-core path) |
bit-exact | Cityscapes panoptic deferred |
| Semantic mIoU / FWIoU / pAcc / mAcc | mmseg.IoUMetric v1.2.2 (vendored) |
bit-exact on class marginals | ADR-0036 proposed; ADE20K-scale check pending |
| oLRP | clean-room NumPy oracle | ≤ 1e-9 | panoptic not supported |
| Calibration (ECE / MCE) | clean-room NumPy oracle | bit-exact | detection family only |
| TIDE thresholds (segm, boundary) | none | corrected only | ADR-0022 proposed |
Parity model: ADR-0002.
Library-by-library comparison: docs/comparison.md.
Performance
Median wall time at a one-CPU budget (CPU/wall is 1.00 in every cell). COCO val2017 throughout, except the LVIS row, which is LVIS v1 val. Speedup is the other library's time divided by vernier's.
| Workload | vernier | vs pycocotools | vs faster-coco-eval | vs hotcoco |
|---|---|---|---|---|
| bbox AP | 305 ms | 18.7× | 5.6× | 1.9× |
| segm AP | 931 ms | 7.2× | 3.7× | 1.5× |
| keypoints AP | 137 ms | 16.8× | 5.7× | 1.6× |
| boundary AP | 3.15 s | 19.8× ¹ | 16.9× | — |
| Panoptic PQ | 10.5 s | 3.3× ² | — | — |
| Semantic mIoU | 2.87 s | 14.1× ³ | — | — |
| LVIS v1 bbox AP | 2.50 s | 81.0× ⁴ | — | 1.6× |
¹ boundary-iou-api · ² panopticapi · ³ mmsegmentation · ⁴ lvis-api, with 13.6× lower peak memory (1.11 vs 15.08 GiB)
Peak RSS on the COCO cells is 200 MiB, against 274 MiB (hotcoco), 569 MiB (pycocotools) and 691 MiB (faster-coco-eval).
Thread scaling (num_threads, 8-vCPU host)
faster-coco-eval ≥ 1.8 and hotcoco are multi-threaded by default, so each column compares equal CPU budgets.
| Workload | 1 | 2 | 4 | 8 | vs hotcoco @ 8 | vs faster-coco-eval @ 8 |
|---|---|---|---|---|---|---|
| bbox | 306 ms | 221 ms | 146 ms | 131 ms | 2.8× | 11.4× |
| segm | 926 ms | 536 ms | 304 ms | 239 ms | 2.3× | 14.6× |
| boundary | 3.18 s | 1.68 s | 866 ms | 711 ms | — | 23.8× |
| keypoints | 139 ms | 90 ms | 60 ms | 52 ms | 3.1× | 14.7× |
| bbox, Objects365 (1.06M dets) † | 6.6 s | — | — | 3.2 s | 2.6× | OOM at ~30 GiB |
Strict-mode results are bit-identical across thread counts. † Single-rep dev run; pycocotools takes ~6 min per rep at this size. The faster-coco-eval OOM is carried forward from the previous round rather than re-measured — reproducing a 30 GiB OOM destabilises the host mid-round.
AMD EPYC-Milan KVM VPS (4 cores x 2 threads = 8 logical CPUs), harness
mode release (N=10 measurement reps + 2 warmup, randomised impl order,
5% relative-IQR gate), against hotcoco==1.0.1,
faster-coco-eval==1.8.0 and pycocotools==2.0.11. Full methodology,
every baseline pin and the complete results:
docs/benchmarks.md.
Documentation
- Tutorials: first evaluation, training-loop integration
- Migration guides: pycocotools, faster-coco-eval, panopticapi, lvis-api, mmsegmentation
- How-to guides · Reference · Design decisions (ADRs)
Contributing
just lint && just test && just audit
See CONTRIBUTING.md for the ADR workflow, vendoring
policy, and code style.
License
Licensed under either of Apache-2.0 or MIT
at your option. Test-only reference implementations used for parity checks
are listed in THIRD_PARTY_NOTICES.md. They are not
shipped in wheels or binaries.
Metadata
Release files for vernier 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vernier-0.4.0.tar.gz | 787.4 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| vernier-0.4.0-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| vernier-0.4.0-cp310-abi3-musllinux_1_2_x86_64.whl | CPython 3.10 | abi3 | Linux musl 1.2+ x86-64 | Details |
| vernier-0.4.0-cp310-abi3-musllinux_1_2_aarch64.whl | CPython 3.10 | abi3 | Linux musl 1.2+ ARM64 | Details |
| vernier-0.4.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| vernier-0.4.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| vernier-0.4.0-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
| vernier-0.4.0-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 16.8 MB
Release files / vernier-0.4.0.tar.gz
| Download URL | vernier-0.4.0.tar.gz |
|---|---|
| Size | 787.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ad6e78ae73f2b6dcb9aa694687f33c2788ae62b45810cbc7fd15b75d4a1eef43
|
|
BLAKE2b-256 checksum How to use checksums |
9cc84b128db8aad2111cc2deee4cfdf1e1ca0c6d922d1249b387b043c94593b0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / vernier-0.4.0-cp310-abi3-win_amd64.whl
| Download URL | vernier-0.4.0-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 2.4 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
b07126e1238e243e97cc2be489e127f846183758f87baff355b31cd6122962b1
|
|
BLAKE2b-256 checksum How to use checksums |
203747b61f43be094e13b2088ba2e70f100a6bc9395c9d8af99743fdb73e45e3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / vernier-0.4.0-cp310-abi3-musllinux_1_2_x86_64.whl
| Download URL | vernier-0.4.0-cp310-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 2.5 MB |
| Tags | CPython 3.10 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
ab99e4e8a0696348bf24e24d2ad1b84a6063843807766ac42616d97b2c0f776e
|
|
BLAKE2b-256 checksum How to use checksums |
eb2c316b28d3ab745752856020dd1ebee0f10ee9b48c936887ac63b3440a7f58
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / vernier-0.4.0-cp310-abi3-musllinux_1_2_aarch64.whl
| Download URL | vernier-0.4.0-cp310-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 2.3 MB |
| Tags | CPython 3.10 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
9108cb9a4f0e67cc5eaec8e6719bb6a9b0ed25402afd7e295a685260a873fb3e
|
|
BLAKE2b-256 checksum How to use checksums |
c8e3d329be41638bcac087d0fa4bc15564dea16a4bd9cb6f06b831dce23615a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / vernier-0.4.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | vernier-0.4.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 2.3 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
cb764de34317c890ac687914ac39ad2e944db66c80a33b7b7f55cf5f897ec785
|
|
BLAKE2b-256 checksum How to use checksums |
695e315c283c8ea93ec930d1a57c6732b42c4e6806be0032f037ba97c5d5aef8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / vernier-0.4.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | vernier-0.4.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
3bf35907a987b1756dfb7f1acd5aea18634f890bc3de9f9cde20227be3d32f60
|
|
BLAKE2b-256 checksum How to use checksums |
4b8db2c52ed47c2f89cd54419f3b7e6955b45f3983e7036e5c747955c7006e63
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / vernier-0.4.0-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | vernier-0.4.0-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 2.1 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
111e47fe625e139b666b890e77e8b0619843ee26b26573a67ab95e9120e8fc01
|
|
BLAKE2b-256 checksum How to use checksums |
86cf4915627ce86e3e9852acc649a53712a4ae692d73f0b3b6afcacc16a17d2f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / vernier-0.4.0-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | vernier-0.4.0-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
6b899f452b9d2d3dc8030c2d4379b0d9096d1d61fa2de43835d265bb9f0d5c9a
|
|
BLAKE2b-256 checksum How to use checksums |
7b4302d4c927e34ff0735ab52abefdc55793d2fb920700dc52e9c5a84a82e917
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency log