Angenommen, Sie arbeiten mit RGB-Farben: Jede Farbe wird mit drei Intensitäten oder Helligkeiten dargestellt. Sie müssen zwischen "linearem RGB" und "sRGB" wählen. Im Moment vereinfachen wir die Dinge, indem wir die drei verschiedenen Intensitäten ignorieren und davon ausgehen, dass Sie nur eine Intensität haben: Das heißt, Sie haben nur mit Graustufen zu tun.
In einem linearen Farbraum ist die Beziehung zwischen den von Ihnen gespeicherten Zahlen und den von ihnen dargestellten Intensitäten linear. In der Praxis bedeutet dies, dass Sie die Intensität (die Helligkeit des Graus) verdoppeln, wenn Sie die Zahl verdoppeln. Wenn Sie zwei Intensitäten addieren möchten (weil Sie eine Intensität basierend auf den Beiträgen zweier Lichtquellen berechnen oder weil Sie ein transparentes Objekt über einem undurchsichtigen Objekt hinzufügen), können Sie dies tun, indem Sie einfach die hinzufügen zwei Zahlen zusammen. Wenn Sie irgendeine Art von 2D-Überblendung oder 3D-Schattierung oder fast jede Bildverarbeitung durchführen, möchten Sie Ihre Intensitäten in einem linearen FarbraumSie können also einfach Zahlen addieren, subtrahieren, multiplizieren und dividieren, um den gleichen Effekt auf die Intensitäten zu erzielen. Die meisten Farbverarbeitungs- und Rendering-Algorithmen liefern nur mit linearem RGB korrekte Ergebnisse, es sei denn, Sie fügen allem zusätzliche Gewichte hinzu.
Das klingt wirklich einfach, aber es gibt ein Problem. Die Lichtempfindlichkeit des menschlichen Auges ist bei niedrigen Intensitäten feiner als bei hohen Intensitäten. Das heißt, wenn Sie eine Liste aller Intensitäten erstellen, die Sie unterscheiden können, gibt es mehr dunkle als helle. Anders ausgedrückt, Sie können dunkle Grautöne besser voneinander unterscheiden als helle Grautöne. Insbesondere wenn Sie 8 Bit zur Darstellung Ihrer Intensität verwenden und dies in einem linearen Farbraum tun, erhalten Sie zu viele helle Schattierungen und nicht genügend dunkle Schattierungen. Sie bekommen Streifen in Ihren dunklen Bereichen, während Sie in Ihren hellen Bereichen Teile auf verschiedenen Weißtönen verschwenden, die der Benutzer nicht unterscheiden kann.
Um dieses Problem zu vermeiden und diese 8 Bits optimal zu nutzen, verwenden wir in der Regel sRGB . Der sRGB-Standard gibt an, dass Sie eine Kurve verwenden müssen, um Ihre Farben nicht linear zu machen. Die Kurve ist unten flacher, sodass Sie mehr dunkle Grautöne und oben steiler haben können, sodass Sie weniger helle Grautöne haben. Wenn Sie die Zahl verdoppeln, verdoppeln Sie die Intensität mehr als. Dies bedeutet, dass Sie beim Hinzufügen von sRGB-Farben ein Ergebnis erzielen, das heller ist, als es sein sollte. Heutzutage interpretieren die meisten Monitore ihre Eingabefarben als sRGB. Also, wenn Sie eine Farbe auf dem Bildschirm setzen, oder es in einer 8-Bit-pro-Kanal - Textur zu speichern, speichern Sie es als sRGB , damit Sie die beste Verwendung dieser 8 Bits zu machen.
Sie werden feststellen, dass wir jetzt ein Problem haben: Wir möchten, dass unsere Farben im linearen Raum verarbeitet, aber in sRGB gespeichert werden. Dies bedeutet, dass Sie beim Lesen eine sRGB-zu-Linear-Konvertierung und beim Schreiben eine Linear-zu-sRGB-Konvertierung durchführen. Da wir bereits gesagt haben, dass lineare 8-Bit-Intensitäten nicht genügend Dunkelheiten aufweisen, würde dies zu Problemen führen. Daher gibt es eine weitere praktische Regel: Verwenden Sie keine linearen 8-Bit-Farben, wenn Sie dies vermeiden können. Es wird üblich, der Regel zu folgen, dass 8-Bit-Farben immer sRGB sind. Sie führen also Ihre sRGB-zu-Linear-Konvertierung gleichzeitig mit der Erweiterung Ihrer Intensität von 8 auf 16 Bit oder von Ganzzahl zu Gleitkomma durch. Wenn Sie Ihre Gleitkomma-Verarbeitung abgeschlossen haben, werden Sie gleichzeitig mit der Konvertierung in sRGB auf 8 Bit eingegrenzt. Wenn Sie diese Regeln befolgen,
Wenn Sie ein sRGB-Bild lesen und lineare Intensitäten wünschen, wenden Sie diese Formel auf jede Intensität an:
float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);
Wenn Sie ein Bild als sRGB schreiben möchten, wenden Sie diese Formel auf jede lineare Intensität an:
float linear = do_processing();
float s;
if (linear <= 0.0031308) s = linear * 12.92;
else s = 1.055 * pow(linear, 1.0/2.4) - 0.055; ( Edited: The previous version is -0.55 )
In beiden Fällen reicht der Gleitkomma-Wert von 0 bis 1. Wenn Sie also 8-Bit-Ganzzahlen lesen, möchten Sie zuerst durch 255 dividieren, und wenn Sie 8-Bit-Ganzzahlen schreiben, möchten Sie mit 255 multiplizieren Zuletzt genauso, wie Sie es normalerweise tun würden. Das ist alles, was Sie wissen müssen, um mit sRGB zu arbeiten.
Bisher habe ich mich nur mit einer Intensität befasst, aber es gibt klügere Dinge, die mit Farben zu tun haben. Das menschliche Auge kann verschiedene Helligkeiten besser voneinander unterscheiden als verschiedene Farbtöne (technisch gesehen hat es eine bessere Luminanzauflösung als Chrominanz), sodass Sie Ihre 24 Bit noch besser nutzen können, indem Sie die Helligkeit getrennt von der Tönung speichern. Dies versuchen YUV-, YCrCb- usw. Darstellungen zu tun. Der Y-Kanal ist die Gesamthelligkeit der Farbe und verwendet mehr Bits (oder hat eine größere räumliche Auflösung) als die beiden anderen Kanäle. Auf diese Weise müssen Sie nicht (immer) eine Kurve anwenden, wie Sie es bei RGB-Intensitäten tun. YUV ist ein linearer Farbraum. Wenn Sie also die Zahl im Y-Kanal verdoppeln, verdoppeln Sie die Helligkeit der Farbe, aber Sie können YUV-Farben nicht wie bei RGB-Farben addieren oder multiplizieren.
Ich denke, das beantwortet Ihre Frage, daher werde ich mit einer kurzen historischen Notiz enden. Vor sRGB war in alten CRTs eine Nichtlinearität eingebaut. Wenn Sie die Spannung für ein Pixel verdoppeln würden, würden Sie die Intensität mehr als verdoppeln. Wie viel mehr war für jeden Monitor unterschiedlich, und dieser Parameter wurde als Gamma bezeichnet . Dieses Verhalten war nützlich, weil es bedeutete, dass Sie mehr Dunkelheiten als Lichter erhalten konnten, aber es bedeutete auch, dass Sie nicht sagen konnten, wie hell Ihre Farben auf der CRT des Benutzers sein würden, es sei denn, Sie haben es zuerst kalibriert. Gamma-Korrekturbedeutet, die Farben, mit denen Sie beginnen (wahrscheinlich linear), zu transformieren und sie für das Gamma der CRT des Benutzers zu transformieren. OpenGL stammt aus dieser Zeit, weshalb sein sRGB-Verhalten manchmal etwas verwirrend ist. Aber GPU-Anbieter arbeiten jetzt in der Regel mit der oben beschriebenen Konvention: Wenn Sie eine 8-Bit-Intensität in einer Textur oder einem Framebuffer speichern, ist dies sRGB, und wenn Sie Farben verarbeiten, ist es linear. Bei einem OpenGL ES 3.0 verfügt beispielsweise jeder Framebuffer und jede Textur über ein "sRGB-Flag", das Sie aktivieren können, um die automatische Konvertierung beim Lesen und Schreiben zu ermöglichen. Sie müssen überhaupt keine explizite sRGB-Konvertierung oder Gammakorrektur durchführen.