polarization_demosaic — OPTICS polarization op

• Data kinds: image2d → polsweep

• Call: import fullseye as fs; fs.ledger.polarization_demosaic(raw, layout=((90.0, 45.0), (135.0, 0.0))) (implementation को सीधे call करना हो तो import optics; optics.polarization_demosaic(raw, layout=((90.0, 45.0), (135.0, 0.0))), registry से लेना हो तो opsoptics.get("polarization_demosaic"))

उपयोग

polarisation-sensor mosaic को चार analyser images में बाँटता है।

> नीचे का विस्तृत विवरण मूल text में है — सारांश और headings का अनुवाद हो चुका है।

A polarisation camera puts four micro-polarisers on each 2x2 block of pixels; the raw frame is one (H, W) array in which neighbouring pixels saw the scene through different analysers. This returns a (4, H, W) array of images [I_0, I_45, I_90, I_135] (the polsweep sort, in POLARIZATION_SWEEP_ANGLES order, so the result feeds specularity.polarization_stokes and polarization_dolp_map directly), each interpolated to full resolution.

Interpolation is bilinear, the same estimate a Bayer demosaic makes for a colour plane that occupies one pixel in four: a missing pixel is the mean of its measured 4-neighbours (edge-adjacent) or 4-neighbours (diagonal), which is exactly the [[1, 2, 1], [2, 4, 2], [1, 2, 1]] / 4 kernel applied to the masked plane. Ground truth: on a plane that is linear in x and y the interpolation is exact (a bilinear estimate of an affine field is the field), so the test plants four affine fields, mosaics them, and demands every returned image equal its field to 1e-12 away from the border. The border is handled by mirroring the mosaic two pixels outward before interpolating (an even, non-duplicating reflection keeps the 2x2 phase), so a uniform field is exact up to the edge and a gradient is mirrored there — a symmetric bias on the outermost row and column, and said so. Against Polanalyser's OpenCV bilinear path the interior agrees to the 16-bit quantisation (1.8e-5) and only the outer two pixels differ (2026-09-18).

*layout* is the angle at each 2x2 position, ((a00, a01), (a10, a11)) in degrees; the default is the Sony IMX250MZR block. Every angle in POLARIZATION_SWEEP_ANGLES must appear exactly once.

Raises ValueError: *raw* is not a 2-D array, has an odd height or width (the block would be cut), contains non-finite values, or *layout* is not a permutation of the four sweep angles.

Provenance: the layout convention and the "four Bayer planes" reading of the mosaic follow Polanalyser (Maeda, MIT); the interpolation is the classic bilinear Bayer demosaic. This is a re-implementation from that description, not copied code, and it does not depend on OpenCV. Colour polarisation sensors (IMX250MYR, a 4x4 block) are handled by polarization_demosaic_color.

पूरी family का input contract (fail-closed)

optics के सभी ops गणना से पहले input की जाँच करते हैं (कुछ भी चुपचाप पास नहीं होता):

• Unit argument के नाम में ही लिखी होती है — _mm / _um / _deg / _mrad। mm और µm की गड़बड़ी crash नहीं करती, बल्कि "सही दिखने वाला पर गलत उत्तर" देती है, इसलिए नाम से इसे रोका जाता है। मान के आकार से unit का अनुमान बिल्कुल नहीं लगाया जाता।

• String से ValueError — float('50') सफल हो जाता है, इसलिए बिना parse किया config value length के रूप में निकल जाता (मापा गया: thin_lens('50', '200') सही दिखने वाला 66.667 mm लौटा रहा था)। bool को भी True == 1 वाले implicit promotion के रूप में अस्वीकार किया जाता है।

• complex / masked array से ValueError (केवल real-valued slots; imaginary part को चुपचाप छोड़ना या mask हटाना अस्वीकार)। हर input पर NaN/Inf से ValueError।

• शून्य से भाग और उससे जुड़े मामलों को नाम लेकर अस्वीकार किया जाता है: focal length 0, radius of curvature 0, refractive index <= 0, पूरी तरह opaque aperture (सब 0, इसलिए normalisation 0/0), ऐसा PSF जिसका योग <= 0 हो, S0 = 0 वाला Stokes vector, और front focal point पर रखा object (image अनंत पर)।

• केवल 2 ops non-finite value लौटाते हैं, और वह भी contract के रूप में लिखा हुआ: depth_of_field hyperfocal distance से आगे far_mm = inf (hyperfocal distance की परिभाषा यही है) और gaussian_beam waist पर wavefront_radius_mm = inf (plane wavefront की radius of curvature)। दोनों साथ में एक finite साथी (far_is_infinite / curvature_per_mm) भी लौटाते हैं। इसके अलावा कोई भी चुपचाप आया NaN/Inf अंदर ही पकड़ा जाता है और ValueError देता है — "float64 overflow हुआ" और "उत्तर अनंत है" अलग-अलग दावे हैं, इसलिए पहले को दूसरे का चेहरा पहनाकर नहीं लौटाया जाता।

• Size की सीमा: generate होने वाले grid के लिए optics.MAX_GRID (4096), दिए गए field/PSF/aperture के लिए optics.MAX_FIELD_ELEMENTS (2^24), ABCD element chain के लिए optics.MAX_SYSTEM_ELEMENTS (1024), Zernike के लिए MAX_ZERNIKE_TERMS (512) / MAX_ZERNIKE_ORDER (40) / MAX_ZERNIKE_BASIS (2^25)। छोटे argument से बहुत बड़ा internal allocation होने वाले रास्तों (मापा गया: n_max=40 × 4096² पर 108 GB) को fail-closed ढंग से बंद किया गया है।

• भौतिक रूप से असंभव states भी अस्वीकार: degree of polarisation > 1 वाला Stokes vector, ऋणात्मक transmittance, ऋणात्मक intensity, और n-|m| odd जैसे अमान्य Zernike indices।

उपयोग की विस्तृत guide

• optics_imaging family guide

संदर्भ (sample data, साहित्य)

• Sample data catalog (DL URL / license) — 2-D के लिए skimage.data (BSD/public) + synthetic, 3-D के लिए real data sources (Stanford/PDS आदि) के DL URL।

• Operators का स्रोत और संदर्भ — इस op family के मूल research/methods के स्रोत।

• Algorithm का मानक स्रोत (लेखक, वर्ष) और उपयोग ऊपर की family usage guide में दिए गए हैं।

चलाए जा सकने वाले उदाहरण (verified samples जो सच में यह op call करते हैं)

• polarization_camera_pipeline — py -3.11 examples/polarization_camera_pipeline.py

वे अगले ops जिनसे type जुड़ता है (polsweep को input के रूप में लेते हैं)

—

इसी category के (polarization)

jones_element · jones_apply · stokes_from_jones · mueller_element · mueller_apply · stokes_analyze · polarization_demosaic_color · mueller_from_intensities


*Provenance: optics.py — OPTICS operator registry. यह per-op note tools/opdocs.py md से अपने-आप generate होता है (हाथ से edit न करें)।*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.