decode_fringe — 3D structured_light op

• Data kinds: images → depth

• Call: import fullseye as fs; fs.ledger.decode_fringe(phase_shift_images, ref_phase=None, k=1.0, mask=None, min_modulation=None) -> 'np.ndarray' (to call the implementation directly, import fringe; fringe.decode_fringe(phase_shift_images, ref_phase=None, k=1.0, mask=None, min_modulation=None) -> 'np.ndarray'; from the registry, ops3d.get("decode_fringe"))

Usage

Decode a phase-shift image series in one go: wrapped → unwrap → (subtract the reference to get) height.

phase_shift_images: N-step phase-shifted fringe image sequence ((N,H,W) or a sequence of length N).

ref_phase: unwrapped phase of the reference plane (height 0). With None, height = k·(unwrapped).

k: phase→height calibration constant.

mask: optional. True=valid pixel. Invalid pixels are NaN.

min_modulation: optional. If given, pixels with modulation < this value are invalidated as low-confidence.

Returns: height map (2D float). With ref_phase=None, returns the continuous phase multiplied by k. Invalid pixels are NaN.

Procedure (as implemented): wrapped_phase → build the confidence mask (if min_modulation is given, modulation(stack) >= min_modulation; if mask is given, AND with it) → unwrap_phase_2d(wrapped, mask=confidence mask) → if ref_phase is None, k * unwrapped, otherwise phase_to_height (k * (unwrapped - ref_phase)).

Arguments:

• phase_shift_images: (N, H, W), N >= 3, equally spaced phase shifts.

• ref_phase: scalar or (H, W). Pass the phase of the reference plane unwrapped by the same procedure (so that the global offset of unwrap_phase_2d is treated the same in both, it is safest to use for the reference the result of decoding it with this op with ref_phase=None).

• k: phase→height calibration constant (unit/rad). Corresponds to the reciprocal of phase_gain of synthesize_fringes.

• mask: (H, W) bool, True = valid. min_modulation: lower bound of the modulation depth (around [0, 1]; drops shadowed and saturated pixels).

Validation (ValueError): image sequence not 3-D, N < 3, non-finite / mask of a different shape / ref_phase of a different shape / zero valid pixels. Without scikit-image, RuntimeError.

Returns: (H, W) float64. Invalid pixels are NaN. The unit is the unit of k.

Caution: it relies on spatial unwrapping, so in scenes split into islands by steps or occlusion, each island may keep a height offset of 2πm k. When absolute height is needed, the path is graycode_decode → absolute_phase → triangulate_column.

Background guides (the physics and conventions behind this op)

• depth_sensors — Depth sensors — ranging principles, real-device figures, how dropouts appear

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.

Runnable examples (verified samples that actually call this op)

• structured_light — py -3.11 examples_3d/structured_light.py

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

depth_to_points · tsdf_from_depth · to_points · fuse_to_voxel · depth_to_organized_points · normals_from_depth · occlusion_edges · bearing_angle_image

Same category (structured_light)

wrapped_phase · unwrap_phase_2d · graycode_decode · synthesize_fringes · absolute_phase · triangulate_column


*Provenance: fringe.py — 3D 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.