shape_from_polarization opnormalmap → image2dimport fullseye as fs; fs.ledger.sfp_height(normals, mask=None, max_zenith_deg=88.0) (実装を直接呼ぶなら import sfpolar; sfpolar.sfp_height(normals, mask=None, max_zenith_deg=88.0)、台帳から引くなら opsoptics.get("sfp_height"))Integrate a normal map to a height map with Frankot-Chellappa on a mirror-extended domain, so the periodic boundary of the FFT does not bend the result.
Gradients p = -nx/nz, q = -ny/nz are formed inside mask (zero
outside — the object stands on a flat background); slopes steeper than
max_zenith_deg (the occluding rim, where nz -> 0 and the gradient
diverges) are clamped to that zenith with the azimuth kept. The field is
then reflected into a 2H x 2W even-symmetric domain (p changes sign
under the mirror in x, q under the mirror in y) and handed to
:func:photometric.integrate_gradients — the same Frankot-Chellappa
solver, without its wrap-around. Measured on a 64 x 80 tilted plane
(slopes 0.3 and -0.2): largest height error 0.06 px here against 18 px with
the periodic solver, which cannot represent a ramp at all (the residual
0.06 px is the kink of the half-sample mirror at the seam).
Returns (H, W) float64 heights in pixel units (multiply by the pixel
pitch), with the background (outside mask) at mean 0, or the whole image
at mean 0 when there is no mask.
Compared with :func:photometric.integrate_normals (registered in ops3d):
that operator integrates as is (periodic, no validation, no rim clamp);
this one validates the field (unit, finite, facing the camera) and fixes
the boundary.
Raises ValueError: normals not a finite (H, W, 3) field of
unit normals with nz >= 0; mask not boolean of shape (H, W) or
empty; max_zenith_deg outside (0, 90).
optics の全 op は入力を検証してから計算する(黙って通さない):
_mm / _um / _deg / _mrad。mm と µm の取り違えは crash ではなく「もっともらしく間違った答え」なので、名前で防ぐ。大きさから単位を推測する処理は一切しない。ValueError — float('50') は成功してしまうため、未パースの設定値が長さとして通り抜ける(実測: thin_lens('50', '200') がもっともらしい 66.667 mm を返していた)。bool も True == 1 の暗黙昇格として拒否。ValueError(実数枠のみ。虚部の無言切り捨て・マスク剥がしを拒否)。NaN/Inf は全入力で ValueError。depth_of_field の過焦点距離以遠の far_mm = inf(それが過焦点距離の定義)と gaussian_beam のウエストでの wavefront_radius_mm = inf(平面波面の曲率半径)。どちらも有限の相棒(far_is_infinite / curvature_per_mm)を併せて返す。それ以外の無言 NaN/Inf は内部で検出して ValueError —「float64 が溢れた」と「答えが無限大」は別の主張なので、後者の顔で前者を返さない。optics.MAX_GRID(4096)、供給された場/PSF/開口は optics.MAX_FIELD_ELEMENTS(2^24)、ABCD 素子列は optics.MAX_SYSTEM_ELEMENTS(1024)、Zernike は MAX_ZERNIKE_TERMS(512)/ MAX_ZERNIKE_ORDER(40)/ MAX_ZERNIKE_BASIS(2^25)。小さな引数から巨大な内部確保が起きる経路(実測: n_max=40 × 4096² で 108 GB)を fail-closed で塞ぐ。| 物理的に不可能な状態も拒否: 偏光度 > 1 の Stokes ベクトル、負の透過率、負の強度、n- | m | が奇数などの不正な Zernike 添字。 |
py -3.11 examples/poc_phase_measuring_deflectometry.pypy -3.11 examples/poc_shape_from_polarization.pyimage2d を入力に取れる)fraunhofer_pattern · pupil_psf · pupil_blur · psf_to_mtf · edge_spread · veiling_glare_index · emva_pair_statistics · emva_defect_pixels
shape_from_polarization)sfp_dolp_model · dolp_to_zenith · aolp_to_azimuth · sfp_render · sfp_normals · sfp_disambiguate · sfp_estimate_index
Provenance: sfpolar.py — OPTICS operator registry. この per-op ノートは tools/opdocs.py md が自動生成(手編集しない)。
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.