sfp_disambiguate — OPTICS shape_from_polarization op

• Datenarten: table → normalmap

• Aufruf: import fullseye as fs; fs.ledger.sfp_disambiguate(candidates, prior='convex', mask=None, center=None, prior_normals=None, coarse_depth=None, brewster_tol=0.02, invalid='raise') (die Implementierung direkt: import sfpolar; sfpolar.sfp_disambiguate(candidates, prior='convex', mask=None, center=None, prior_normals=None, coarse_depth=None, brewster_tol=0.02, invalid='raise'); aus dem Register: opsoptics.get("sfp_disambiguate"))

Verwendung

Wählt je Pixel eine Normale aus den Kandidaten von sfp_normals mit einem angegebenen Vorwissen — Konvexität mit Brewster-Kamm-Segmentierung oder eine grobe Tiefe/Normalenkarte (normalmap).

> Die ausführliche Beschreibung unten ist der Originaltext — Zusammenfassung und Überschriften sind übersetzt.

prior="convex" (an object bulging toward the camera, such as a dome, a lens or a cap): the azimuth is the candidate pointing away from *center* (default: the centroid of *mask*). For the specular model the two zenith branches are separated by the Brewster ridge — the pixels whose DoLP is within *brewster_tol* of 1: the connected region of the mask that contains *center* (minus the ridge) takes the branch below Brewster's angle, the rest of the mask the branch above. Inside the ridge band the branch is read from the radial slope of the DoLP (a convex surface tilts more toward its rim, so the DoLP rises outward below Brewster's angle and falls outward above it). If the object never reaches Brewster's angle there is no ridge and every pixel stays on the low branch, which is right for a shallow dome. If noise breaks the ridge, the outside leaks in and takes the low branch — measured in the tests, not hidden.

prior="normals" / prior="depth": per pixel, the candidate closest in angle to *prior_normals* (H, W, 3) — or to the normals of *coarse_depth* (H, W) (height toward the camera, pixel units, photometric.surface_normals). This resolves both ambiguities at once, and a coarse depth needs to be right only to within the angular distance between candidates.

Pixels outside *mask* (default candidates["valid"]) are set to (0, 0, 1) (flat background, the convention of photometric.integrate_normals). Invalid pixels inside the mask (valid is False: dark, physically impossible, or out of the model's

reach): invalid="raise" refuses (fail-closed), "exclude" drops them to the background.

Returns an (H, W, 3) float64 unit normal map (the normalmap sort).

Raises ValueError: *candidates* not an sfp_normals result; unknown *prior* / *invalid*; a prior's array missing or of the wrong shape; an empty mask; invalid pixels inside the mask with invalid="raise".

Familienweiter Eingabevertrag (fail-closed)

Jeder optics-Operator prüft seine Eingabe vor der Berechnung (nichts rutscht stillschweigend durch):

• Einheiten stecken im Argumentnamen — _mm / _um / _deg / _mrad. Eine Verwechslung von mm und µm stürzt nicht ab, sondern liefert eine plausibel aussehende falsche Antwort; der Name verhindert das. Aus der Größenordnung wird nie auf die Einheit geschlossen.

• Strings lösen ValueError aus — float('50') gelingt, sodass ein ungeparster Konfigurationswert als Länge durchrutschen würde (gemessen: thin_lens('50', '200') lieferte plausible 66,667 mm). bool wird als implizite Hochstufung True == 1 ebenfalls abgelehnt.

• complex / Masked Arrays lösen ValueError aus (nur reelle Slots; das stille Verwerfen des Imaginärteils bzw. Abstreifen der Maske wird abgelehnt). NaN/Inf löst bei jeder Eingabe ValueError aus.

• Division durch null und Verwandtes wird namentlich abgelehnt: Brennweite 0, Krümmungsradius 0, Brechzahl <= 0, undurchlässige Blende (alles 0, die Normierung wird 0/0), PSF mit Summe <= 0, Stokes-Vektor mit S0 = 0 und ein Objekt im vorderen Brennpunkt (Bild im Unendlichen).

• Nur zwei Operatoren liefern einen nicht-endlichen Wert, und beide halten das vertraglich fest: depth_of_field liefert jenseits der hyperfokalen Distanz far_mm = inf (genau das bedeutet die hyperfokale Distanz), und gaussian_beam liefert an der Taille wavefront_radius_mm = inf (der Krümmungsradius einer ebenen Wellenfront). Beide liefern zusätzlich einen endlichen Partner (far_is_infinite / curvature_per_mm). Jedes andere stille NaN/Inf wird intern erkannt und löst ValueError aus — "float64 ist übergelaufen" und "die Antwort ist unendlich" sind verschiedene Aussagen; die erste wird nie im Gewand der zweiten geliefert.

• Größenobergrenzen: erzeugte Gitter durch optics.MAX_GRID (4096), übergebene Felder/PSFs/Blenden durch optics.MAX_FIELD_ELEMENTS (2^24), ABCD-Elementketten durch optics.MAX_SYSTEM_ELEMENTS (1024), Zernike durch MAX_ZERNIKE_TERMS (512) / MAX_ZERNIKE_ORDER (40) / MAX_ZERNIKE_BASIS (2^25). Damit werden Pfade fail-closed geschlossen, in denen ein kleines Argument eine riesige interne Allokation auslöst (gemessen: n_max=40 × 4096² braucht 108 GB).

• Physikalisch unmögliche Zustände werden ebenfalls abgelehnt: Stokes-Vektor mit Polarisationsgrad > 1, negative Transmission, negative Intensität und ungültige Zernike-Indizes wie ungerades n-|m|.

Ausführlicher Anwendungsleitfaden

• Leitfaden zur Familie optics_imaging

Referenzen (Beispieldaten, Literatur)

• 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.

Ausführbare Beispiele (verifizierte Samples, die diesen Operator wirklich aufrufen)

• poc_shape_from_polarization — py -3.11 examples/poc_shape_from_polarization.py

Typkompatible Folge-Operatoren (nehmen normalmap als Eingabe)

grating_rgb · thin_film_rgb · ward_anisotropic · micro_normals · blast_normals · finish_shade · oren_nayar · clearcoat_shade

Gleiche Kategorie (shape_from_polarization)

sfp_dolp_model · dolp_to_zenith · aolp_to_azimuth · sfp_render · sfp_normals · sfp_estimate_index · sfp_height


*Provenance: sfpolar.py — OPTICS 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.