偏光カメラ(画素の上に偏光子を載せた市販の偏光イメージセンサを使う機種)は 2×2 の画素ブロックに 0/45/90/135 度の偏光子を並べたモザイクを 1 枚で出します。polarization_demosaic がそれを 4 枚(0/45/90/135)に戻し(双線形。1 次の場は厳密に戻り、縁はモザイクを鏡映して位相を保つ)、そのまま polarization_stokes / polarization_dolp_map / polarization_separate に渡せます(角度の既定が一致しているので引数無しで繋がる)。センサの配列が違えば layout=((a00, a01), (a10, a11)) で渡します。カラー偏光センサ(4×4 ブロック)は polarization_demosaic_color が (4, H, W, 3) に戻します —— 各偏光子位置の画素は半解像度の Bayer なので raw_demosaic_bilinear で展開し、戻してから各チャネルを偏光 demosaic する(2 つの厳密な段の合成)。
試料の Mueller 行列は、既知の生成器(PSG)と検光子(PSA)の列で撮った強度から mueller_from_intensities が最小二乗で取り戻します。強度は 16 成分に線形なので観測行列 outer(a_i, g_i) を積んで解くだけです。★設計が S3 を見られないとき(偏光板だけ、階数 9)は階数を言って断り、擬似逆行列のもっともらしい答えを返しません。画像ごと(N, H, W)に渡せば画素ごとに解けます。
取り戻した行列が物理的か(Cloude の coherency 行列が半正定値)、純粋か(脱偏光しない)、どれだけ脱偏光しているか(Gil–Bernabeu 指数)、受動か(透過率 ≤ 1)は mueller_checks が一度に返します。数値は固有値ごと載せるので、「どれだけ非物理か」まで見えます。
Turn a polarisation camera’s mosaic (a 2×2 block of 0/45/90/135° analysers, as on commercial on-chip polarisation sensors) into four full-resolution images with polarization_demosaic (bilinear, exact on affine fields, phase-preserving mirrored border) that chain straight into polarization_stokes / polarization_dolp_map. Recover a sample’s Mueller matrix from intensities measured through known generator/analyser states with mueller_from_intensities (linear least squares on outer(a_i, g_i); refuses rank-deficient designs with the rank instead of returning a pseudo-inverse guess). Check any 4×4 matrix with mueller_checks: Cloude coherency eigenvalues (physical), purity, the Gil–Bernabeu depolarisation index and passivity.
向く: 偏光カメラの生フレームの前処理、鏡面反射と拡散の分離(polarization_separate)、偏光素子や試料の Mueller 行列の計測と健全性確認、光弾性・応力可視化の前段。
向かない: ★demosaic は双線形だけで、エッジ適応や勾配法はありません(輪郭では 1 画素幅のにじみが出ます)。★mueller_from_intensities は生成器・検光子が正確に既知である前提で、素子の誤差は結果にそのまま乗ります(較正は別の問題)。★mueller_checks の tol は測定雑音の水準で置いてください。雑音つきで回復した行列は最小固有値が僅かに負に出るのが普通で、tol を勘で小さくすると「非物理」に化けます(例で実演)。
import fullseye as fs
import numpy as np
raw = np.load("polarcam_frame.npy") # (H, W) のモザイク、値は [0, 1]
sweep = fs.polarization_demosaic(raw) # (4, H, W): I_0, I_45, I_90, I_135
dolp = fs.polarization_dolp_map(sweep) # 画素ごとの直線偏光度
print(fs.stokes_analyze(fs.polarization_stokes(sweep))) # 場全体の Stokes → dop / azimuth
# Mueller: 既知の PSG / PSA(各 N 個の 4x4)で撮った N 個の強度から
M = fs.mueller_from_intensities(intensities, psg, psa) # (4, 4)。階数 < 16 なら ValueError
print(fs.mueller_checks(M, tol=1e-3)) # physical / pure / depolarization_index / passive
polarization_demosaic / polarization_demosaic_color / mueller_from_intensities / mueller_checks(2026-09-18、optics 台帳 polarization)、既存の polarization_stokes / polarization_dolp_map / polarization_separate / stokes_analyze / mueller_element / mueller_applypolarization_camera_pipeline(合成の Stokes 場 → モザイク → 4 枚 → DoLP、Mueller の回復と階数の拒否、物理性)、poc_polarization_specular(1024 px の生モザイク、半解像度の4方向像、Stokes・DoLP・AoLP、消光比と飽和を含む鏡面分離)tests/test_optics.py(1 次の場で厳密、配列の読み替え、sweep op との接続、既知 M の回復 1e-10、階数 9 の拒否、全素子の物理性、非物理と利得の判定)tools/diff_polarization_external.py / tests/test_polarization_external_diff.py、外部 3 本は任意依存で無ければ skip): 偏光板・リターダ・QWP/HWP の Mueller は pypolar / py-pol / polanalyser と 1e-16 で一致、Mueller 最小二乗は polanalyser と 5e-16、DoLP 地図は 9e-16、demosaic の内側は 16 bit 量子化の範囲。差が出た所は全部規約差: 回転子は能動(+θ)で相手の受動 R(θ) = こちらの −θ / Jones は規約が library ごとに違うので Mueller に写して比較(fullseye の Stokes 写像経由で pypolar の Mueller 素子と 1e-15)/ 楕円率は偏光成分で正規化(pypolar は S0)/ demosaic の縁。不具合は見つからなかった。docs/REFERENCES.md)