wake-up-neo.com

Was sind die praktischen Unterschiede beim Arbeiten mit Farben in einem linearen und einem nichtlinearen RGB-Raum?

Was ist die grundlegende Eigenschaft eines linearen RGB-Raums und was ist die grundlegende Eigenschaft eines nichtlinearen? Was ändert sich, wenn über die Werte in jedem Kanal in diesen 8 (oder mehr) Bits gesprochen wird?

In OpenGL sind Farben 3 + 1-Werte, und damit meine ich RGB + Alpha, wobei 8 Bit für jeden Kanal reserviert sind, und dies ist der Teil, den ich klar bekomme.

Aber wenn es um Gammakorrektur geht, verstehe ich nicht, wie sich die Arbeit in einem nichtlinearen RGB-Raum auswirkt.

Da ich weiß, wie man eine Kurve in einer Grafiksoftware für die Fotobearbeitung verwendet, ist meine Erklärung, dass Sie in einem linearen RGB-Raum die Werte so nehmen, wie sie sind, ohne Manipulation und ohne Mathematikfunktion, stattdessen wenn sie nicht linear sind Kanal entwickelt sich normalerweise nach einem klassischen Leistungsfunktionsverhalten.

Selbst wenn ich diese Erklärung als die reale betrachte, verstehe ich immer noch nicht, was ein realer linearer Raum ist, weil nach der Berechnung alle nichtlinearen RGB-Räume linear werden und am wichtigsten ist, dass ich nicht den Teil bekomme, in dem ein Nicht vorhanden ist - Der lineare Farbraum ist für das menschliche Auge besser geeignet, da letztendlich alle RGB-Räume für das, was ich verstehe, linear sind.

66
Ken

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 werden wir die Dinge vereinfachen, 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. Praktisch 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 auf der Grundlage der Beiträge von zwei Lichtquellen berechnen oder weil Sie ein transparentes Objekt über einem undurchsichtigen Objekt hinzufügen), können Sie dies einfach durch Hinzufügen von tun zwei Zahlen zusammen. Wenn Sie eine Art von 2D-Überblendung oder 3D-Schattierung oder fast jede Bildverarbeitung ausführen, möchten Sie Ihre Intensitäten in einem linearen Farbraum, also können Sie einfach addieren, subtrahieren, multiplizieren und Teilen Sie Zahlen, um den gleichen Effekt auf die Intensitäten zu erzielen. Die meisten Farbverarbeitungs- und Rendering-Algorithmen liefern nur mit linearem RGB korrekte Ergebnisse, sofern Sie nicht alles zusätzlich gewichten.

Das hört sich ganz einfach an, 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 Graustufen besser unterscheiden als helle Graustufen. Insbesondere wenn Sie 8 Bit zur Darstellung Ihrer Intensität verwenden und dies in einem linearen Farbraum tun, werden Sie zu viele helle und nicht genügend dunkle Schattierungen erhalten. Sie bekommen Streifen in Ihren dunklen Bereichen, während Sie in Ihren hellen Bereichen Bits für verschiedene Weißtöne 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 . Nach dem sRGB-Standard müssen Sie eine Kurve verwenden, um Ihre Farben nichtlinear zu machen. Die Kurve ist unten flacher, sodass Sie mehr dunkle Grautöne und oben steilere, sodass Sie weniger helle Grautöne haben. Wenn Sie die Zahl verdoppeln, verdoppeln Sie die Intensität. Wenn Sie also sRGB-Farben addieren, erhalten Sie ein Ergebnis, das heller ist, als es sein sollte. Heutzutage interpretieren die meisten Monitore ihre Eingangsfarben als sRGB. Also, wenn Sie eine Farbe auf dem Bildschirm platzieren oder in einer 8-Bit-Textur pro Kanal speichern, speichern Sie sie als sRGB, damit Sie diese 8 Bits optimal nutzen können.

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 Konvertierung von sRGB in linear und beim Schreiben eine Konvertierung von linear in sRGB durchführen. Wie wir bereits gesagt haben, dass lineare 8-Bit-Intensitäten nicht genügend Dunkelheiten haben, würde dies Probleme verursachen. Daher gibt es eine weitere praktische Regel: Verwenden Sie keine linearen 8-Bit-Farben, wenn Sie können vermeide es. Es wird immer üblicher, die Regel zu befolgen, dass 8-Bit-Farben immer sRGB sind, sodass Sie Ihre Umwandlung von sRGB in lineare Farben gleichzeitig mit der Erhöhung Ihrer Intensität von 8 auf 16 Bit oder von Ganzzahl auf Gleitkomma durchführen. Wenn Sie Ihre Gleitkomma-Verarbeitung abgeschlossen haben, werden Sie gleichzeitig mit der Konvertierung in sRGB auf 8 Bit beschränkt. Wenn Sie diese Regeln befolgen, müssen Sie sich nie um die Gammakorrektur kümmern.

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 umgekehrt 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 Gleitkommawert von 0 bis 1. Wenn Sie also 8-Bit-Ganzzahlen lesen, möchten Sie zuerst durch 255 teilen, und wenn Sie 8-Bit-Ganzzahlen schreiben, möchten Sie mit 255 multiplizieren Zum Schluss, so 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 unterscheiden als verschiedene Farbtöne (technisch gesehen hat es eine bessere Luminanzauflösung als die Chrominanz), sodass Sie Ihre 24-Bit-Werte noch besser nutzen können, indem Sie die Helligkeit getrennt vom Farbton speichern. Dies ist, was YUV-, YCrCb- usw. Darstellungen versuchen. Der Y-Kanal ist die allgemeine Helligkeit der Farbe und verwendet mehr Bits (oder hat eine höhere räumliche Auflösung) als die beiden anderen Kanäle. Auf diese Weise müssen Sie nicht (immer) eine Kurve anwenden, wie dies bei RGB-Intensitäten der Fall ist. YUV ist ein linearer Farbraum. Wenn Sie also die Zahl im Y-Kanal verdoppeln, verdoppeln Sie die Helligkeit der Farbe. Sie können jedoch keine YUV-Farben wie bei RGB-Farben addieren oder multiplizieren Bildverarbeitung, nur zur Speicherung und Übertragung.

Ich denke, das beantwortet Ihre Frage, also 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 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 hilfreich, da Sie dadurch mehr Dunkelheiten als Lichter erhalten konnten. Sie konnten jedoch auch nicht feststellen, wie hell Ihre Farben auf der CRT des Benutzers sein würden, es sei denn, Sie haben sie zuerst kalibriert. Gammakorrektur bedeutet, dass Sie die Farben, mit denen Sie beginnen, transformieren (wahrscheinlich linear) und sie für das Gamma der CRT des Benutzers transformieren. OpenGL stammt aus dieser Zeit, weshalb sein sRGB-Verhalten manchmal etwas verwirrend ist. Aber GPU-Anbieter tendieren jetzt dazu, mit der oben beschriebenen Konvention zu arbeiten: Wenn Sie eine 8-Bit-Intensität in einer Textur oder einem Framebuffer speichern, ist dies sRGB, und wenn Sie Farben verarbeiten, ist dies linear. Beispielsweise hat ein OpenGL ES 3.0, jeder Framebuffer und jede Textur ein "sRGB-Flag", das Sie aktivieren können, um die automatische Konvertierung beim Lesen und Schreiben zu aktivieren. Sie müssen überhaupt keine explizite sRGB-Konvertierung oder Gammakorrektur durchführen.

178
Dan Hulme

Ich bin kein "Experte für menschliche Farberkennung", habe aber bei der YUV-> RGB-Konvertierung ähnliche Erfahrungen gemacht. Es gibt unterschiedliche Gewichte für R/G/B-Kanäle. Wenn Sie also die Quellfarbe um x ändern, ändern sich die RGB-Werte in der Menge.

Wie gesagt, ich bin sowieso kein Experte. Wenn Sie eine farbkorrekte Transformation durchführen möchten, sollten Sie dies im YUV-Raum tun und dann in RGB konvertieren (oder die mathematisch äquivalente Operation für RGB ausführen, achten Sie darauf Datenverlust). Ich bin mir auch nicht sicher, ob YUV die beste native Darstellung von Farben ist, aber Videokameras bieten dieses Format. Hier bin ich auf das Problem gestoßen.

Hier ist die magische YUV-> RGB-Formel mit eingeschlossenen Geheimzahlen: http://www.fourcc.org/fccyvrgb.php

1
ern0