TL; DR: 2 * 1LSB-Dreieck-PDF-Dithering bricht in Randfällen bei 0 und 1 aufgrund von Klemmung. Eine Lösung besteht darin, in diesen Randfällen ein einheitliches 1-Bit-Dithering durchzuführen.
Ich füge eine zweite Antwort hinzu, da dies etwas komplizierter ausfiel, als ich ursprünglich gedacht hatte. Es scheint, dass dieses Problem ein "TODO: muss geklemmt werden?" in meinem Code seit ich von normalisiertem zu dreieckigem Dithering gewechselt bin ... im Jahr 2012. Es fühlt sich gut an, es endlich anzusehen :) Vollständiger Code für die im Beitrag verwendeten Lösungen / Bilder: https://www.shadertoy.com/view/llXfzS
Hier ist zunächst das Problem, das wir bei der Quantisierung eines Signals auf 3 Bit mit 2 * 1LSB-Dreieck-PDF-Dithering betrachten:
- im Wesentlichen, was hotmultimedia zeigte.
Mit zunehmendem Kontrast wird der in der Frage beschriebene Effekt deutlich: Die Ausgabe wird in den Randfällen nicht auf Schwarz / Weiß gemittelt (und reicht tatsächlich weit über 0/1 hinaus, bevor Sie dies tun).
Das Betrachten eines Diagramms bietet etwas mehr Einblick:
(graue Linien markieren 0/1, auch in grau ist das Signal, das wir ausgeben möchten, gelbe Linie ist der Durchschnitt der geditherten / quantisierten Ausgabe, rot ist der Fehler (Signalmittelwert)).
Interessanterweise ist die durchschnittliche Ausgabe nicht nur nicht 0/1 an den Grenzen, sondern auch nicht linear (wahrscheinlich aufgrund des dreieckigen PDF des Rauschens). Wenn man das untere Ende betrachtet, ist es intuitiv sinnvoll, warum der Ausgang divergiert: Wenn das Dither-Signal negative Werte enthält, ändert der Clamp-On-Ausgang den Wert der unteren Dither-Teile des Ausgangs (dh der negativen Werte) Erhöhung des Durchschnittswertes. Eine Abbildung scheint in Ordnung zu sein (einheitliches, symmetrisches 2LSB-Dithering, Durchschnitt immer noch in Gelb):
Wenn wir nur ein normalisiertes 1LSB-Dithering verwenden, gibt es überhaupt keine Probleme an den Kantenfällen, aber dann verlieren wir natürlich die schönen Eigenschaften des dreieckigen Ditherings (siehe z . B. diese Präsentation ).
Eine (pragmatische, empirische) Lösung (Hack) besteht dann darin, auf [-0,5; 0,5 [gleichmäßiges Dithering für den Randfall zurückzugreifen:
float dithertri = (rnd.x + rnd.y - 1.0); //note: symmetric, triangular dither, [-1;1[
float dithernorm = rnd.x - 0.5; //note: symmetric, uniform dither [-0.5;0.5[
float sizt_lo = clamp( v/(0.5/7.0), 0.0, 1.0 );
float sizt_hi = 1.0 - clamp( (v-6.5/7.0)/(1.0-6.5/7.0), 0.0, 1.0 );
dither = lerp( dithernorm, dithertri, min(sizt_lo, sizt_hi) );
Dadurch werden die Randfälle repariert, während das dreieckige Dithering für den verbleibenden Bereich erhalten bleibt:
Um Ihre Frage nicht zu beantworten: Ich weiß nicht, ob es eine mathematisch fundiertere Lösung gibt, und ich bin ebenso gespannt darauf, was Masters of Past getan hat :) Bis dahin haben wir zumindest diesen schrecklichen Hack, um unseren Code funktionsfähig zu halten.
BEARBEITEN
Ich sollte wahrscheinlich den in der Frage gegebenen Problemumgehungsvorschlag zum einfachen Komprimieren des Signals behandeln. Da der Durchschnitt in den Randfällen nicht linear ist, führt das einfache Komprimieren des Eingangssignals nicht zu einem perfekten Ergebnis - obwohl die Endpunkte festgelegt werden:
Verweise