structured_light op• Data kinds: image2d × image2d → image2d
• Call: import fullseye as fs; fs.ledger.absolute_phase(wrapped, coarse) -> 'np.ndarray' (to call the implementation directly, import fringe; fringe.absolute_phase(wrapped, coarse) -> 'np.ndarray'; from the registry, ops3d.get("absolute_phase"))
• GPU: inside a chain only (mechanical GPU path from the allow-list, CuPy). Stays resident on the GPU only when the neighbouring stage is also on the allow-list. A single call runs on the CPU
Turn a wrapped phase into an absolute one by fixing its fringe order from a coarse but absolute phase estimate.
Φ = wrapped + 2π·round((coarse − wrapped) / 2π)
wrapped: output of wrapped_phase, a 2D array in (-π,π] (high precision, no absolute order).
coarse: 2D array of the same shape. An absolute but coarse phase estimate [rad]. Typically the projector
column number decoded with Gray code, converted to phase (2π·freq·col/width). NaN allowed (output also NaN).
Returns: absolute phase [rad] (2D float64). Invalid pixels (either is NaN) are NaN.
Why it is needed (difference from spatial unwrapping): unwrap_phase_2d follows adjacent pixels to bridge 2π jumps, so (a) a global offset +2πm remains, and (b) the order cannot propagate between islands cut apart by occlusion. This op determines the order independently per pixel, so it is absolute even in scenes split into islands, and needs no propagation. In exchange, the error of coarse must be less than half a period (π).
Requirement (fail-closed): |coarse − Φ_true| < π. If this is violated, round picks the neighbouring order and that pixel alone silently shifts by 2π (= the depth of 1 fringe). Choose the fringe count freq so that the coarseness of the Gray code (±0.5 column), converted to phase, does not exceed half a period (e.g. projector width 512 px, freq 24 → 1 period 21.3 px, Gray's ±0.5 px is comfortably inside).
★An order error cannot be told from the return value (measured 2026-11-09): in multi-frequency temporal unwrapping (16/4/1 mm, ratio 4, 200,000 pixels) with phase noise σφ = 0.10/0.20/0.30/0.40/0.60 rad, 0/0.097/2.57/11.6/36.8 % of the pixels get the 2π order wrong, and all of them come back as finite values. Look at the residual:
take Phi = absolute_phase(w, c); resid = c - Phi and discard pixels with |resid| > 0.8π or so as suspicious (under the conditions above this catches 33–74 % of the errors; it is the sign that |coarse − Φ| < π is broken). The unit of coarse (radians) is not checked either —— passing degrees silently returns values like 1106 rad (true 19.35). There is no op yet that checks the order (an op returning the residual and a flag will come through the op-adding route).
Raises: ValueError on shape mismatch, not 2D, or wrapped far outside (-π,π].
Provenance (public literature): the combined method that obtains absolute phase from Gray code plus phase shifting — Sansoni et al., *Appl. Opt.* 38(31) 1999 / Zhang, *Opt. Lasers Eng.* 48 2010 (review). Projection of the Gray code itself: Inokuchi et al., *ICPR* 1984.
• depth_sensors — Depth sensors — ranging principles, real-device figures, how dropouts appear
• 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.
• structured_light_scan — py -3.11 examples_3d/structured_light_scan.py
image2d as input)fuse_to_voxel · fit_poly_surface · eval_poly_surface · surface_form_error · background_flatten · polar_unwrap · fit_zernike · matcap_shade
structured_light)wrapped_phase · unwrap_phase_2d · graycode_decode · decode_fringe · fringe_ftp_phase · depth_from_focus · synthesize_fringes · 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.