Auto-Calibration with openptv warmup¶
openptv warmup measures a small window of your sequence, tunes the tracking
search parameters (dvxmin/dvxmax/.../dacc) from what it actually finds,
picks between the two directly-supported tracking engines, and (optionally)
writes the result back into your parameters.yaml — so you don't have to
guess dvxmax/dacc by hand or run a manual parameter sweep.
It is a standalone step you run before tracking, as many times as you
like. It never runs automatically as part of openptv track.
Prerequisites¶
- A
parameters.yamlexperiment directory with calibration (cal/) already done. - The sequence step already run (
res/rt_is.#correspondences andimg/camN.#_targetspresent, or written into the run's zarr store) — warmup measures existing correspondences, it does not detect particles itself. - A zarr store for the run (
res/run.zarr), the way a normal sequence run produces one. See Batch Processing anddocs/zarr-hdf5-storage.mdfor how the store gets created.
Basic usage¶
This is a dry run — it prints a report but does not touch your YAML.
Add --write once you're happy with the result:
Options¶
| Flag | Default | Meaning |
|---|---|---|
yaml_file |
(required) | Path to the experiment's parameters.yaml |
--frames N |
25 | Size of the measurement window, starting at the sequence's first frame |
--max-cycles N |
3 | Max tuning cycles (each cycle re-measures and re-tunes) |
--write |
off | Persist the chosen tracker + tuned params into the YAML |
What it actually does¶
For each cycle, on the frame window:
- Runs forward and backward tracking (
Tracker.full_forward()+full_backward()) on a scratch linkage group — your real tracking output is untouched. - Runs
enforce_reciprocity(does every forward link have a matching backward link?) as a ground-truth-free quality signal — this works the same way on real data as on synthetic data, since it never needs to know the true particle identities. - Reads the displacement distribution of the confirmed (reciprocal) links
and uses its 99th percentile to set the velocity search box
(
dvxmin/dvxmax/.../dvzmax/dacc) for the next cycle. - After the cycles settle, it runs the two directly-supported engines
(
priority_segment_3d= track3d,full_multipass= trackcorr forward+backward) with the tuned parameters and picks whichever produces longer average trajectories.
Example output¶
$ uv run openptv warmup parameters.yaml --frames 8 --max-cycles 2
Warmup window: frames 10001-10008 (2 cycle(s))
Chosen tracker: priority_segment_3d (engine scores: {'priority_segment_3d': 6.51, 'full_multipass': 3.29})
Forward/backward agreement: 100.0%
Empirical noise estimate: 2.920 mm
Tuned track params:
dvxmin: -13.284303841747132
dvxmax: 13.284303841747132
dvymin: -13.284303841747132
dvymax: 13.284303841747132
dvzmin: -13.284303841747132
dvzmax: 13.284303841747132
dangle: 120.0
dacc: 13.284303841747132
add: 1
Dry run (pass --write to persist into the YAML)
With --write, the same run updates parameters.yaml's track: block
(dvxmin/dvxmax/.../dacc/angle) and plugins.selected_tracking, so a
plain openptv track afterward picks up the tuned config automatically —
openptv track has no warmup-awareness of its own, it just reads whatever
config is in the YAML.
Reading the report¶
- Chosen tracker — whichever of the two engines produced longer average
trajectories on the window with the tuned parameters.
engine scoresare the mean trajectory length for each. - Forward/backward agreement — the fraction of forward links confirmed by a matching backward link. This is a self-consistency measure, not a correctness measure against ground truth — see the caveat below.
- Empirical noise estimate — the standard deviation of confirmed-link displacements, in mm. A rough proxy for how much position noise the setup has, useful for sanity-checking whether your calibration/detection is behaving as expected for this rig.
- Tuned track params — what gets written with
--write.
Known limitation: check the result before trusting it¶
Benchmarking against real ground truth (scripts/benchmark_stage_improvements.py,
see docs/plans/2026-08-15-tracking-quality-overhaul.md's Stage 1 write-up)
found that warmup's current tuning margin can overshoot on data that was
already reasonably configured: it widens the search box using
p99(confirmed displacements) × 3.0, a margin calibrated against a
deliberately under-tuned case. On several measured scenes this widened an
already-good default and lowered precision while raising the
acceleration-kurtosis physics metric (a sign of more spurious link swaps),
even though "Forward/backward agreement" still read 100%.
In practice: if your parameters.yaml already has a reasonable
dvxmax/dacc (e.g. carried over from a prior calibration run, or set by
hand from the expected flow speed), run warmup as a dry run first, and
compare its suggested dvxmax/dacc against your current values before
--writeing. Warmup is most useful when your current search box is
clearly too loose or too tight for the flow (the scenario it was
originally built to fix — see the plan doc's commit 07f1fc1 reference for
the motivating real-world case), not as a blind "always run this" step yet.
A fix for the margin is tracked as Stage 5 part 2, item 1 in the plan doc.
See also¶
- Batch Processing — running the sequence step that warmup measures
- Sequence & Tracking Plugins —
track.corrective_passesand othertrack:YAML options warmup can tune docs/plans/2026-08-15-tracking-quality-overhaul.md— the full design rationale, what warmup deliberately does not do yet (GMM prediction, the other tracker engines), and measured benchmark results