piv_peak_locking — PIV assess op

• Data kinds: flow2d → table

• Call: import fullseye as fs; fs.ledger.piv_peak_locking(flow, bins=20) (to call the implementation directly, import pivops; pivops.piv_peak_locking(flow, bins=20); from the registry, opspiv.get("piv_peak_locking"))

Usage

Strength of peak locking: how far the distribution of the fractional parts deviates from uniform.

Subpixel estimation has a bias that pulls the fractional part of the true displacement towards 0 or 0.5 when it is near there (a textbook systematic error of PIV, arising because the shape of the correlation peak differs from the shape of the fitted function). Unless the flow is uniform, the fractional part should be close to uniformly distributed, and the deviation from that is measured.

The index c0 is a chi-square-type deviation independent of the sample size:

sum((n_i - n_bar)^2 / n_bar) / (bins - 1). Sampling from a uniform distribution gives an expected value of 1, so around 1 it is indistinguishable from uniform; the larger, the more biased.

★ However, this index assumes that "the fractional part of the true displacement is uniform". In fields with few windows or slowly varying displacement, c0 of the ground truth itself also becomes large (measured: 82 for the ground truth of a linear ramp —— the window grid has only 19 columns, so the fractional part only appears discretely). Therefore read c0 compared with the ground truth of the same input.

The decisive diagnosis is something else —— vary the fractional part of a uniform translation from 0 to 0.9 and see whether the estimates lie on the diagonal. Measured (win=32, density 0.02):

========== ================= ==================

method RMS of frac. err. max abs. error

========== ================= ==================

gauss3 0.0037 px 0.0088 px

parabolic 0.0104 px 0.0151 px

centroid 0.2259 px 0.3701 px

========== ================= ==================

centroid answers 0.02 for a true 0.1 and 0.98 for 0.9 —— the textbook S-curve pulling towards integers. It confirms that what should appear did appear, and is not a defect of the implementation (that is why all 3 are kept).

Args:

flow: (2, h, w).

bins: number of classes of the histogram of the fractional part.

Returns:

dict: c0 (index for both components combined), hist ((2, bins)),

frac_mean / frac_std, bins.

Detailed usage guide

• piv_displacement family guide

References (sample data, literature)

• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).

• Operator provenance and references — the sources of the research/methods this op family came from.

• The canonical algorithm (author, year) and its uses are named in the family usage guide above.

Runnable examples (verified samples that actually call this op)

• piv_field_analysis_tour — py -3.11 examples/piv_field_analysis_tour.py

• poc_battery_electrode_breathing — py -3.11 examples/poc_battery_electrode_breathing.py

Ops the type connects to (they accept table as input)

—

Same category (assess)

piv_sample_at_windows · piv_error_stats · piv_time_statistics


*Provenance: pivops.py — PIV operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.