crt_displacement — RESIDUE image op

• Data kinds: image2d × image2d → table

• Call: import fullseye as fs; fs.ledger.crt_displacement(image0, image1, periods=(5.0, 7.0, 9.0, 11.0, 13.0, 16.0), sigma_px=40.0, max_disp=40.0, axis='x', refine=2) -> 'dict' (to call the implementation directly, import residue; residue.crt_displacement(image0, image1, periods=(5.0, 7.0, 9.0, 11.0, 13.0, 16.0), sigma_px=40.0, max_disp=40.0, axis='x', refine=2) -> 'dict'; from the registry, opsresidue.get("crt_displacement"))

Usage

Local displacement from band phases at several wavelengths, unwrapped by CRT -> dict.

A band of wavelength p measures the local shift only modulo p, so any single-band phase method breaks at |d| = p/2 (motionmag.phase_displacement reports this as wrap_limit_px). Bands at *coprime* wavelengths each break there too, but together they fix d over the least common multiple — 693 px for 7/9/11 — so the limit becomes max_disp, the search half-width.

image1 is image0 with its content moved by d(y, x) along axis ("x" = columns, "y" = rows; positive towards increasing index) — image1 = np.roll(image0, +d) gives +d. Note that filters_freq.phase_correlation_fft(image0, image1) uses the *opposite* sign (it returns -d); flip one when swapping methods. For each wavelength a Gaussian band-pass (analytic, one-sided, spatial std sigma_px) gives a local phase and a *local* wavelength (measured, not nominal — see _band_measure); their phase differences are residues of d; amplitude products are the weights; every pixel is solved by the phasor-agreement search

of residue_crt. refine iterations then warp image1 back by the current estimate and add the (now small, wrap-free) weighted residual.

Returns {"d", "weight", "margin", "residual", "valid", "wrap_limit_single_px"}. valid = enough contrast in the bands *and* a margin above 0.05; a pixel that fails is still returned but should not be trusted.

Where it applies: textured regions that each move (nearly) rigidly by a large amount — measured on two regions of the public scikit-image textures moving +20 / -15 px, pixels farther than 1.5 sigma_px from the region boundary are off by more than 1 px on 2 % (grass) / 5 % (gravel) / 6 % (brick), where the best pyramid Lucas-Kanade (3-6 levels) is off on 49 / 50 / 67 %.

Where it does not: within ~1.5 sigma_px of a motion boundary; where the displacement changes inside the window (a 6 px field with gradient 0.07 is already off on half the pixels — a band narrow enough to keep the local frequency stable needs a window too wide to follow the field); flat regions (weight and valid say where); occlusions. A single global translation is better served by phase correlation. Numbers: examples/poc_residue_crt.py.

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)

• poc_residue_crt — py -3.11 examples/poc_residue_crt.py

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

—

Same category (image)

harmonic_rotation


*Provenance: residue.py — RESIDUE 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.