vernier
Fast, parity-preserving evaluation for object detection, instance / panoptic / semantic segmentation, boundary IoU, OKS keypoints, LVIS federated, LRP / oLRP error decomposition, and detection-family calibration (ECE / MCE / reliability). Rust core, Python frontend, optional CLI.
60-second example
Post training, if your predictions are already serialized to JSON (CI gate, post-training inspection):
from pathlib import Path
from vernier.instance import Bbox, CocoDataset, Evaluator
gt_bytes = Path("instances_val2017.json").read_bytes()
dt_bytes = Path("detections.json").read_bytes()
dataset = CocoDataset.from_json(gt_bytes)
summary = Evaluator(iou=Bbox()).evaluate(dataset, dt_bytes)
for line in summary.pretty_lines():
print(line)
In a training loop, vernier supports overlapping eval with the data loading and inference. The matching kernel runs on a worker thread, so submit(...) returns immediately and the main thread keeps moving. Passing a CocoDataset reuses the parsed-once GT and its per-kernel derivation cache across every epoch (ADR-0020). On a dedicated validation pass (no trainer competing for cores), pass num_threads=N to parallelise the matching kernel inside the worker (ADR-0047):
from pathlib import Path
from vernier.instance import Bbox, CocoDataset, Evaluator
gt = CocoDataset.from_json(Path("instances_val2017.json").read_bytes())
evaluator = Evaluator(iou=Bbox())
with evaluator.background(gt, num_threads=8) as bg: # default: single core
for images, targets in val_loader:
# torchvision detection API shape: list[dict] of length batch_size,
# each with "boxes" (N,4 xywh), "scores" (N,), "labels" (N,) as
# torch.Tensor. vernier consumes any DLPack-producing array library
# (torch, jax, cupy, numpy) zero-copy.
predictions = model(images)
bg.submit([
{"image_id": int(t["image_id"]), **p}
for t, p in zip(targets, predictions)
])
summary = bg.finalize()
print("AP =", summary.stats[0])
Both end in the same 12-line pycocotools-shaped Summary;
docs/tutorials/first-evaluation.md
walks each end-to-end.
Benchmarks
| Workload | vernier median | Speedup vs alternatives |
|---|---|---|
| Instance — bbox AP (val2017) | 354 ms | 1.6× hotcoco · 4.7× faster-coco-eval · 16.0× pycocotools |
| Instance — segm AP (val2017) | 968 ms | 1.4× hotcoco · 3.5× faster-coco-eval · 6.7× pycocotools |
| Instance — boundary AP (val2017) | 3.2 s | 16.7× faster-coco-eval · 19.5× boundary-iou-api |
| Instance — keypoints AP (val2017, OKS) | 136 ms | 1.6× hotcoco · 5.7× faster-coco-eval · 16.9× pycocotools |
| Panoptic — PQ (val2017) | 10.6 s | 3.3× panopticapi |
| Semantic — mIoU (val2017) | 2.9 s | 14.0× mmsegmentation |
| Instance — LVIS bbox AP (v1 val, perfect-DT) | 2.6 s | 1.4× hotcoco · 73.1× lvis-api · 10× lower peak RSS (1.45 GiB vs 15.01 GiB) |
| Instance — bbox AP (Objects365 val, 1.06M dets) † | 8.8 s | 1.7× hotcoco · 41.9× pycocotools · faster-coco-eval did not finish (OOM at ~30 GiB) |
Thread scaling (num_threads, each cell pinned to that many CPUs —
nt=8 is every library's out-of-the-box configuration on this 8-vCPU host):
| Workload | nt=1 |
nt=2 |
nt=4 |
nt=8 |
vs hotcoco / faster-coco-eval at nt=8 |
|---|---|---|---|---|---|
| bbox (val2017) | 354 ms | 267 ms | 229 ms | 226 ms | 1.6× hotcoco · 6.5× faster-coco-eval |
| segm (val2017) | 983 ms | 569 ms | 375 ms | 319 ms | 1.7× hotcoco · 11.0× faster-coco-eval |
| boundary (val2017) | 3.20 s | 1.70 s | 937 ms | 790 ms | 21.5× faster-coco-eval |
| keypoints (val2017) | 136 ms | 118 ms | 109 ms | 104 ms | 1.5× hotcoco · 7.3× faster-coco-eval |
| bbox (Objects365) | 8.8 s | — | — | 4.7 s | 1.7× hotcoco |
faster-coco-eval ≥1.8 and hotcoco are multi-threaded too, so every column compares equal CPU budgets. Its parallelism pays off on boundary IoU but is flat on segm and keypoints, which is why vernier's lead widens with cores there.
† The Objects365 row is harness mode dev (one measurement rep per impl, no IQR gate) — pycocotools alone needs ~6 minutes per rep at that size. Every other row is release mode.
Median total-stage wall time on a KVM VPS (AMD EPYC-Milan, 4 cores ×
2 threads = 8 logical CPUs, x86_64 — not a bare-metal Milan box),
harness mode release (N=10 measurement reps + 2 warmup, randomised
impl order, 5% relative-IQR gate per impl), build profile = cargo
release defaults (opt-level=3, lto=thin, codegen-units=1, no
target-cpu) — same as the PyPI wheel. Every impl gets one CPU:
faster-coco-eval ≥1.8, hotcoco and mmsegmentation are multi-threaded by
default, so each runner is pinned to the cell's CPU budget and each
result records its CPU/wall ratio as evidence (ADR-0049). vernier
scales with num_threads — the thread-scaling tables on the benchmarks
page cover 1/2/4/8. Full per-cell breakdown
(including IQRs), peak and eval-delta memory, and methodology in
docs/benchmarks.md; per-library comparison of
when to pick which in docs/comparison.md.
Baselines pinned for these numbers —
pycocotools==2.0.11,
hotcoco==1.0.1,
faster-coco-eval==1.8.0,
panopticapi @ 7bb4655,
boundary-iou-api @ 37d2558,
mmsegmentation @ c685fe6 (vendored),
lvis-api @ 031ac21
(PyPI lvis==0.5.3).
All cells were measured at HEAD e361050ef582 (machine fingerprint
59aab88b17f4). That is a different host from the 2026-05 snapshot's
37652a58e939, and the CPU budget above changed what a "single-thread"
cell means, so absolute numbers are not comparable with earlier
snapshots — ratios within this one are. Each baseline is locked in its own uv-managed venv per
ADR-0017.
Install
pip install vernier # Python wheel
cargo add vernier # Rust library (all paradigms)
cargo install vernier-cli # `vernier` CLI binary
Wheels ship for linux x86_64 / aarch64 (glibc + musl), macOS x86_64 / arm64, and windows x64.
On the Rust side, vernier is a facade
(ADR-0048): it re-exports the
paradigm crates under one dependency and one module map —
vernier::{instance, mask, panoptic, semantic, partial} — mirroring the
Python namespace. It holds no code of its own, so depending on a leaf
crate directly (cargo add vernier-core for bbox / segm / boundary /
keypoints AP alone) is equally supported; the three optional paradigms
can also be trimmed in place with default-features = false. Note the
asymmetry: cargo add vernier gets the library, cargo install vernier-cli gets the binary — the CLI stays out of the library's
dependency tree so clap never lands in a consumer's build.
Status & validation
Pre-1.0; public API is unstable. See docs/adr/ for the design decisions shaping it.
pycocotools==2.0.11 is the de-facto reference for COCO evaluation — slow, unmaintained, and full of edge-case quirks. Faster reimplementations exist, but each silently fixes some quirks and not others, so you discover the divergences empirically. vernier takes a third path:
- Auditable parity. Every divergence from pycocotools is filed in the quirks survey under
ADR-0002 as either
strict(bit-equal output, even when vernier's implementation is structurally different) orcorrected(opt-in opinionated fix). Strict is the default; corrected fixes are itemized so you always know when your numbers diverge from a reference run. A drop-in shim (vernier.patch_pycocotools()) keeps existing pycocotools-based scripts working with one line. - Rust core, Python frontend. The matching kernel is pure Rust with runtime SIMD dispatch; the FFI layer is data conversion only. The CLI ships as a static binary, so CI pipelines call vernier without provisioning a Python interpreter.
- One toolkit instead of five. bbox / segm / boundary / keypoints
AP, panoptic PQ, semantic mIoU, LVIS federated, oLRP error
decomposition, and detection-family calibration all live behind
one Python API and one CLI — folded over a single matching pass.
Per-paradigm migration guides under
docs/migrate/show how to replacepycocotools,faster-coco-eval,panopticapi,lvis-api, andmmsegmentationone at a time. - Scenario slicing + cross-run aggregation. A partition manifest
(
weather,time_of_day, …) feedsvernier eval --manifestfor per-slice headline metrics andvernier aggregatefor cross-run corruption tables (mPC / rPC) — one matching pass, N slices (ADR-0046).
Per-paradigm parity status:
| Paradigm / metric | Oracle | Parity tier | Open caveat |
|---|---|---|---|
| Instance bbox / segm / keypoints AP | pycocotools==2.0.11 |
strict bit-equal | none |
| Instance boundary IoU | boundary-iou-api |
strict bit-equal | none |
Segm + boundary TIDE thresholds (t_b) |
none yet | corrected-only | ADR-0022 still proposed; defaults extrapolated, not measured |
| Panoptic PQ | panopticapi (single-core path) |
strict bit-equal | none |
| Panoptic boundary PQ | bowenc0221/boundary-iou-api (single-core path, same SHA as the instance vendor) |
strict bit-equal | ADR-0025 §Z1/Z2 amendment; Cityscapes panoptic (Z3) deferred |
| Semantic mIoU / FWIoU / pAcc / mAcc | mmseg.IoUMetric vendored at v1.2.2 (ADR-0036, still proposed); cityscapesScripts + ADE20K cross-impl bench externally blocked |
strict bit-equal on the four per-class u64 marginals at val2017 scale | ADR-0028; ADE20K-scale bench gated on license-cleared cache |
| LVIS federated AP | lvis-api (vendored at 031ac21f, ORACLE_LVIS_COMMIT_SHA) |
strict bit-equal on the (T, R, K, A) precision tensor at full LVIS v1 val |
bench paradigm wired; segm cell waits on evaluate_segm_grid_with_dataset |
| LRP / oLRP error decomposition (instance bbox / segm / boundary / keypoints) | pure-NumPy oracle (ADR-0043) | strict against the oracle within 1e-9; kemaloksuz/LRP-Error tripwire vendored opt-in |
panoptic LRP is a typed NotImplementedError stub — panoptic predictions carry no per-segment scores (ADR follow-up) |
| Detection-family calibration — ECE / MCE / reliability (instance bbox / segm / boundary / keypoints) | clean-room NumPy oracle (ADR-0018) with isolated P1–P10 quirks survey | strict bit-equal against the oracle (16/16 parity tests) | panoptic (Shape 2) and semantic (Shape 3) calibration deferred on data-model prerequisites; Clopper-Pearson CI documented Phase-2 |
Three-tier parity model: ADR-0002;
per-library comparison: docs/comparison.md.
Three evaluation paradigms
They have different data models, different matching rules, and different parity oracles:
vernier.instance— detections with scores → bbox / segm / boundary / keypoints AP.vernier.panoptic— RGB-encoded panoptic PNGs +segments_infoJSON → PQ.vernier.semantic— single-channel class-id label maps → mIoU / FWIoU / pAcc / mAcc.
See Three paradigms.
Documentation
- Tutorials —
docs/tutorials/ - Migration guides (from pycocotools, faster-coco-eval,
panopticapi, lvis-api, mmsegmentation) —
docs/migrate/ - How-to —
docs/how-to/ - Reference —
docs/reference/ - Design / ADRs —
docs/adr/ - Comparison vs pycocotools / faster-coco-eval / panopticapi /
boundary-iou-api / lvis-api / mmsegmentation —
docs/comparison.md
Contributing
Local checks: just lint && just test && just audit. The full
contributor workflow (ADR lifecycle, vendoring policy, code style) is
in CONTRIBUTING.md. Repository layout and
common just recipes are in CLAUDE.md.
License
Dual-licensed under Apache-2.0 or MIT at your option.
Third-party code
vernier vendors a small number of test-only reference implementations
to support parity testing. None of this code is included in published
wheels or linked into the Rust binary. See
THIRD_PARTY_NOTICES.md for the full
inventory and license attributions.
Metadata
Release files for vernier 0.3.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.3.0.tar.gz | 688.0 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| vernier-0.3.0-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| vernier-0.3.0-cp310-abi3-musllinux_1_2_x86_64.whl | CPython 3.10 | abi3 | Linux musl 1.2+ x86-64 | Details |
| vernier-0.3.0-cp310-abi3-musllinux_1_2_aarch64.whl | CPython 3.10 | abi3 | Linux musl 1.2+ ARM64 | Details |
| vernier-0.3.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.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| vernier-0.3.0-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
| vernier-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 15.9 MB
Release files / vernier-0.3.0.tar.gz
| Download URL | vernier-0.3.0.tar.gz |
|---|---|
| Size | 688.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a45162065810d535bc3635939b06870fb89023133f01915fc176cdafd6d226f6
|
|
BLAKE2b-256 checksum How to use checksums |
e48bbb49fca47b05be74b377a9558e472517e6ef4adcbd0267f968f2e6abd42d
|
| 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 16, 2026.
Transparency logRelease files / vernier-0.3.0-cp310-abi3-win_amd64.whl
| Download URL | vernier-0.3.0-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
5eccc8f12d0cbcb918caac392015c4b9f5d9e0ca8b313d38c15e2b59abfad348
|
|
BLAKE2b-256 checksum How to use checksums |
434d91c3bd27dfb0c8494cb39b663212cfb54f542b9a7ed765477b5e1c844fec
|
| 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 16, 2026.
Transparency logRelease files / vernier-0.3.0-cp310-abi3-musllinux_1_2_x86_64.whl
| Download URL | vernier-0.3.0-cp310-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 2.4 MB |
| Tags | CPython 3.10 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
3dbcb8d34646bb545bb2f184c50fd908d19537951a0354442117cd88ceeee9d6
|
|
BLAKE2b-256 checksum How to use checksums |
13d1560dd48e5349c87c3f254149a63e4188ad05f2eac5303f9bcb5677af13a1
|
| 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 16, 2026.
Transparency logRelease files / vernier-0.3.0-cp310-abi3-musllinux_1_2_aarch64.whl
| Download URL | vernier-0.3.0-cp310-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | CPython 3.10 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
9be89a4a135f764c3c18fd0262c9265ac3b7f01e87351a55dc6705f16a66a01c
|
|
BLAKE2b-256 checksum How to use checksums |
ed854bd214560efb893708ef7491e870354cff4e06cc7878f80b665d480852ba
|
| 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 16, 2026.
Transparency logRelease files / vernier-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | vernier-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
9563a97d6ca0a095f18e38eecf9a29164e95302af8119a956af80dba2708a828
|
|
BLAKE2b-256 checksum How to use checksums |
5400654f0532b69be2589882b79f411f1892e22daa0d2608610b508d79361c7c
|
| 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 16, 2026.
Transparency logRelease files / vernier-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | vernier-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 2.0 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
fb219e2bd4b887828d736bf9d9b06e0f2b7b15ab3e4253c604da02b827753228
|
|
BLAKE2b-256 checksum How to use checksums |
0342e5df88d8682347f8553d22856eede1bf2d6450c47e72429e8cef0c800ceb
|
| 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 16, 2026.
Transparency logRelease files / vernier-0.3.0-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | vernier-0.3.0-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 2.0 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
a8c59f30b386d76056c58f3b01eed8dcb21b4a1c4c543453b32c975f680f26ad
|
|
BLAKE2b-256 checksum How to use checksums |
f78b6b1722c12758b1d51d3fdbb950c0150609036d6fe13434a3333c82380655
|
| 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 16, 2026.
Transparency logRelease files / vernier-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | vernier-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 2.1 MB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
299e80b60df21d3ec6635ef725a99db007b697b02f9085ff9b883702d463766c
|
|
BLAKE2b-256 checksum How to use checksums |
ddca7be729f0918671da51022db52874d4c1400282251a62ef059abeb976eba4
|
| 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 16, 2026.
Transparency log