photometric op• Data kinds: normalmap → image2d
• Call: import fullseye as fs; fs.ledger.integrate_normals(normals, mask=None) (to call the implementation directly, import photometric; photometric.integrate_normals(normals, mask=None); from the registry, ops3d.get("integrate_normals"))
Normal field → height field z by Frankot-Chellappa integration. → z, HxW (a constant offset is free; referenced to zero mean).
Procedure: converts the normals (H,W,3) to gradients p = -nx/nz, q = -ny/nz (pixels with |nz| < 1e-6 have nz replaced by ±1e-6 to avoid division by zero), finds the least-squares solution in the FFT domain Z(wx,wy) = (-j·wx·P - j·wy·Q) / (wx² + wy²) (DC component 0), transforms back and subtracts the mean to make it 0. If mask (bool HxW) is given, gradients outside the mask are set to 0 before integrating (that region is treated as flat). Returns float64 (H,W).
Caveats:
• A periodic boundary is assumed, so the borders distort as if the top/bottom and left/right of the image were connected.
• The absolute height is not determined (mean 0). Integration uses a grid spacing of 1 pixel, so z is also a height in pixel units; multiply by the pixel pitch for real dimensions.
• nz ≈ 0 (near contours, surfaces parallel to the line of sight) makes the gradient diverge and spreads ripples into the surroundings. Exclude such pixels with mask or smooth the normals beforehand.
There is no input validation (anything other than (H,W,3) fails in the internal indexing). surface_normals is the inverse transform; passing the normals of photometric_stereo here gives the shape (height field).
• 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.
• photometric_stereo — py -3.11 examples_3d/photometric_stereo.py
image2d as input)fuse_to_voxel · fit_poly_surface · eval_poly_surface · surface_form_error · background_flatten · polar_unwrap · fit_zernike · matcap_shade
photometric)photometric_stereo · surface_normals · render_lambertian
*Provenance: photometric.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.