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.