polarization op• データ種: polsweep → image2d
• 呼び出し: import fullseye as fs; fs.ledger.polarization_separate(images, angles_deg=(0.0, 45.0, 90.0, 135.0), max_violation_frac=0.0) (実装を直接呼ぶなら import specularity; specularity.polarization_separate(images, angles_deg=(0.0, 45.0, 90.0, 135.0), max_violation_frac=0.0)、台帳から引くなら opsspecular.get("polarization_separate"))
• 台帳経由の戻り値: fullseye.ledger.polarization_separate(...) は宣言 out 型 image2d の値だけを返す(本体は補助情報も返す)。捨てられた側が要るときは fullseye.ledger.polarization_separate.raw(...)、または specularity.polarization_separate を直接呼ぶ。
Split a polariser sweep into its unpolarised and linearly polarised radiance. → (diffuse, specular), both (H, W).
Fitting I(t) = 0.5 (S0 + S1 cos 2t + S2 sin 2t) per pixel gives I_min and I_max in closed form, and the classical separation (Wolff & Boult 1991; Nayar, Fang & Boult 1997) reads
diffuse = 2 * I_min (the unpolarised radiance)
specular = I_max - I_min (the linearly polarised radiance)
with diffuse + specular = I_min + I_max = the total scene radiance, so nothing is lost or invented. Round-tripping polarization_render through this operator returns the inputs with a maximum absolute error of 3.9e-16 for the four angles of a division-of-focal-plane sensor and 4.4e-16 for a bare three-angle sweep (measured in tests/test_specularity.py).
Read the names as shorthand. What is recovered exactly is the unpolarised and polarised parts. Calling them diffuse and specular assumes diffuse reflection is unpolarised and specular reflection is fully linearly polarised — true near Brewster's angle for a dielectric, false at normal incidence, where the specular reflection is unpolarised and this operator returns all of it as "diffuse", and unreliable for metals. The polarisation route is complementary to the colour route (specular_diffuse_split), not a replacement: it needs no illuminant colour and works on textured, multi-material surfaces, but it needs a favourable geometry.
*max_violation_frac* is the fraction of pixels allowed to fit a negative minimum radiance before the call fails. The default 0 is fail-closed: a negative fitted minimum means the modulation exceeded the mean, which no analyser can produce, and it usually means the frames and *angles_deg* are out of order. Raise it to clamp sensor-noise-level violations to zero as a deliberate, recorded choice.
Raises ValueError: *images* is not an (N, H, W) stack of at least 3 frames, or exceeds MAX_LIGHTS / MAX_STACK_ELEMENTS; *angles_deg* does not match the frame count or leaves the fit rank-deficient (two angles equal modulo 180); more than *max_violation_frac* of the pixels fit a negative minimum.
• specular_photometric ファミリ ガイド
• サンプルデータ カタログ(DL URL / ライセンス) — 2-D は skimage.data(BSD/public)+ 合成、3-D は実データ源(Stanford/PDS 等)の DL URL。
• 演算子の来歴・参考文献 — この op 族の元になった研究/手法の出典。
• アルゴリズムの正典(著者・年)と用途は上記ファミリ使い方ガイドに記載。
• poc_polarization_specular — py -3.11 examples/poc_polarization_specular.py
• specular_photometric — py -3.11 examples/specular_photometric.py
image2d を入力に取れる)polarization)polarization_render · polarization_dolp_map · polarization_stokes
*Provenance: specularity.py — SPECULAR operator registry. この per-op ノートは tools/opdocs.py md が自動生成(手編集しない)。*
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.