photometric op• 数据种类:normalmap → image2d
• 调用: import fullseye as fs; fs.ledger.integrate_normals(normals, mask=None)(要直接调用实现,import photometric; photometric.integrate_normals(normals, mask=None);从台账取用则 ops3d.get("integrate_normals"))
• GPU:无(仅 CPU 参考实现)
法线场 → 高度场 z,用 Frankot-Chellappa 积分。→ z HxW(常数项自由,以均值 0 为基准)。
步骤:把法线 (H,W,3) 转为梯度 p = -nx/nz、q = -ny/nz(|nz| < 1e-6 的像素把 nz 替换为 ±1e-6 以避免除零),在 FFT 域中求最小二乘解 Z(wx,wy) = (-j·wx·P - j·wy·Q) / (wx² + wy²)(直流分量为 0),逆变换后减去平均值使其为 0。给出 mask (bool HxW) 时,先把 mask 外的梯度置 0 再积分(该区域被当作平坦处理)。返回值为 float64 的 (H,W)。
注意事项:
• 由于假定周期边界,边缘会像图像上下、左右相连那样发生畸变(整体倾斜由下面的★补回)。
• 绝对高度无法确定(平均为 0)。以 1 像素的网格间距积分,因此 z 也是像素单位的高度,换算为实际尺寸需乘以像素间距。
• nz ≈ 0(轮廓附近、与视线平行的面)处梯度发散,波纹会扩散到周围。请用 mask 排除或事先平滑法线。
surface_normals 是其逆变换,把 photometric_stereo 的法线传到这里即可得到形状(高度场)。空输入(normals 为 0 个元素)会引发 ValueError。
★整体倾斜(2026-11-09):周期边界的 Frankot-Chellappa 把梯度的平均(直流)置为 0, 以前会把倾斜的平面还原成水平(倾斜 0.2 px/px → 恢复 0.0000、rms 5.5 px)。积分之后把平均梯度的一次项 mean(p)·(x − x̄) + mean(q)·(y − ȳ) 加回(平均取整个网格,包括 mask 外的 0)。倾斜平面严格恢复,平均梯度为 0 的面(加 mask 的球等)只会有舍入误差级的变化。
需要原始周期解时用 integrate_gradients。★补回的倾斜是平均值,因此对离群值很弱(独立评审 2026-11-09):平坦面上只要有 20 个像素的法线朝同一方向大幅倾斜(nz 0.002,能通过 1e-3 的截断),rms 误差就从 10.7 变为 27.9 px(恶化 2.6 倍)。沿轮廓一侧排列的倾斜法线在实际数据中很常见,请先用 mask 去掉再传入。不改用中位数等稳健估计, 是因为在台阶侧壁这类由少数大梯度承担真实倾斜的面上会丢失倾斜。
另一个限制:倾斜是测得的法线的平均,照度差立体拍不到的台阶侧壁等不包括在内 —— 有台阶的零件上补回的倾斜与真值的平均梯度不同(三维方式比较的 PoC 中为 −0.066〜−0.084, 真值 +0.0125 px/px)。
★输入的关卡:(H,W,3) 以外、非有限的法线(1 个 NaN 像素会经 FFT 使所有像素变为 NaN)引发 ValueError。|nz| < 1e-3(梯度超过 1000,轮廓、与视线平行的面)的像素把梯度置 0 并从积分中排除,并以 RuntimeWarning 告知个数(nz=0 的 1 个像素曾让高度摆动 2.3e5 px)。
• 示例数据目录(下载 URL / 许可证) —— 2-D 用 skimage.data(BSD/公有领域)加合成图,3-D 给出真实数据源(Stanford/PDS 等)的下载 URL。
• 算子来历与参考文献 —— 该算子族所依据的研究/方法出处。
• photometric_stereo — py -3.11 examples_3d/photometric_stereo.py
image2d 作为输入)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 算子登记表。本条目由 tools/opdocs.py md 自动生成(请勿手工编辑)。*
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.