polarization op• Datenarten: polsweep → table
• Aufruf: import fullseye as fs; fs.ledger.polarization_stokes_map(images, angles_deg=(0.0, 45.0, 90.0, 135.0), max_violation_frac=0.0) (die Implementierung direkt: import specularity; specularity.polarization_stokes_map(images, angles_deg=(0.0, 45.0, 90.0, 135.0), max_violation_frac=0.0); aus dem Register: opsspecular.get("polarization_stokes_map"))
• GPU: nein (nur CPU-Referenzimplementierung)
Pixelweise lineare Stokes-Bilder mit DoLP- und AoLP-Karten aus einem Analysatordurchlauf (4 DoFP-Winkel oder beliebige N >= 3, kleinste Quadrate). → dict von (H, W)-Karten.
> Die ausführliche Beschreibung unten ist der Originaltext — Zusammenfassung und Überschriften sind übersetzt.
polarization_stokes answers for the whole field (a spatial mean); this is the same fit kept per pixel:
I(t) = 0.5 (S0 + S1 cos 2t + S2 sin 2t) solved for every pixel — in closed form for the four angles 0/45/90/135 of a division-of-focal-plane sensor (S0 = (I0 + I45 + I90 + I135)/2, S1 = I0 - I90, S2 = I45 - I135, the least-squares solution for that design), and by least squares for any other set of N >= 3 analyser angles.
Returns a dict:
• s0, s1, s2 — (H, W) linear Stokes images. Stack them with a zero S3 (np.stack([s0, s1, s2, 0 * s0], -1)) and they feed optics.mueller_apply's field form directly.
• dolp — sqrt(S1^2 + S2^2) / S0 in [0, 1].
• aolp_deg — 0.5 * atan2(S2, S1) in degrees, mapped into [0, 180) (an axis, so 0 and 180 are the same).
• lit — bool, S0 > 0.
• aolp_defined — bool, S0 > 0 and hypot(S1, S2) > 1e-12 * S0.
• residual — RMS over the N frames of frame - fitted Malus curve, in the frames' units (added 2026-10-24). A sweep of a real scene fits exactly, so a large value says the frames do not agree with each other:
a misregistered or saturated channel. For the four DoFP angles it is
|I0 + I90 - I45 - I135| / 4; for three angles it is always 0 (the
fit is exact and nothing is left to check).
Where the ratios do not exist, the value is 0 and a mask says so — never NaN. At S0 = 0 DoLP is 0/0: it is returned as 0.0, the same
convention polarization_dolp_map states, with lit False. Where the linear part is zero (unpolarised light, or no light) AoLP has no
orientation: aolp_deg is 0.0 there and aolp_defined is False. "Zero" is relative — hypot(S1, S2) <= 1e-12 * S0 — because the fit leaves rounding dust: a uniform unpolarised sweep fits DoLP ~3e-16 at every scale from 1e-6 to 1e9 (measured), and with an exact-zero test 400/400 such pixels reported an "angle" that was pure rounding. DoLP itself is returned as fitted (that 3e-16 is an honest number). Use the masks before thresholding or averaging angles; a 0 in aolp_deg is not a measurement of 0 degrees.
Angles. *angles_deg* and the returned AoLP use the library convention:
from +x (along a row) toward +y (down the rows), clockwise on a displayed image (see _DEFAULT_ANGLES in this module and the polarisation section
of optics). A synthetic sweep made with optics.mueller_element("polarizer", t) of a beam at 21.5 degrees comes back as 21.5; the same frames labelled with negated angles come back as 158.5 — the test file pins both.
Physicality. The fit refuses sweeps whose fitted minimum is negative (as polarization_separate, controlled by *max_violation_frac*). When that clamp is allowed, a pixel can fit hypot(S1, S2) > S0; its (S1, S2) is then scaled back onto S0 (as polarization_stokes does for the mean) and a negative S0 is set to 0, so every returned pixel is a physical Stokes vector. With the default max_violation_frac=0 that only touches rounding-level excesses.
Ground truth (closed form, in tests/test_polarization_per_pixel_2026_10_23.py):
unpolarised light through an ideal polariser → DoLP 1 and AoLP = the polariser angle, per pixel, for a whole field of different angles; through optics.mueller_partial_polarizer → DoLP (ER - 1)/(ER + 1); Malus's law frames rebuilt from the returned S0, S1, S2 reproduce the input frames; the spatial mean of s0, s1, s2 equals polarization_stokes (no clamp in play).
S3 is not returned: linear analysers cannot measure it (see polarization_stokes).
Raises ValueError: as polarization_separate — *images* is not an (N, H, W) stack with N >= 3, exceeds the caps, or is non-finite; *angles_deg* does not match N or leaves the fit rank-deficient; more than *max_violation_frac* of the pixels fit a negative minimum. Also (2026-10-24): *images* has no pixels; *angles_deg* is ill-conditioned or looks like radians; a frame value below the negative floor counts as a violating pixel.
• Leitfaden zur Familie specular_photometric
• Katalog der Beispieldaten (Download-URLs / Lizenzen) — 2-D nutzt skimage.data (BSD/Public Domain) plus synthetische Bilder, 3-D nennt Download-URLs echter Datenquellen (Stanford, PDS, …).
• Herkunft und Literatur der Operatoren — die Quellen der Forschung/Verfahren, auf denen diese Operatorfamilie beruht.
• Der kanonische Algorithmus (Autor, Jahr) und seine Anwendungen stehen im Familienleitfaden oben.
• polarization_camera_pipeline — py -3.11 examples/polarization_camera_pipeline.py
table als Eingabe)—
polarization)polarization_render · polarization_separate · polarization_dolp_map · polarization_stokes
*Provenance: specularity.py — SPECULAR Operator-Registry. Diese Notiz wird von tools/opdocs.py md erzeugt (nicht von Hand bearbeiten).*
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.