Jakie są praktyczne różnice podczas pracy z kolorami w liniowej i nieliniowej przestrzeni RGB?

88

Jaka jest podstawowa właściwość liniowej przestrzeni RGB, a jaka jest podstawowa właściwość przestrzeni nieliniowej? Kiedy mówimy o wartościach w każdym kanale w tych 8 (lub więcej) bitach, co się zmienia?

W OpenGL kolory mają wartości 3 + 1, a przez to mam na myśli RGB + alpha, z 8 bitami zarezerwowanymi dla każdego kanału, i to jest ta część, którą dostaję wyraźnie.

Ale jeśli chodzi o korekcję gamma, nie rozumiem, jaki jest efekt pracy w nieliniowej przestrzeni RGB.

Ponieważ wiem, jak używać krzywej w oprogramowaniu graficznym do edycji zdjęć, moje wyjaśnienie jest takie, że w liniowej przestrzeni RGB przyjmuje się wartości takie, jakie są, bez manipulacji i bez dołączonej funkcji matematycznej, zamiast tego, gdy są one nieliniowe. kanał zwykle ewoluuje zgodnie z klasycznym zachowaniem funkcji potęgowej.

Nawet jeśli przyjmuję to wyjaśnienie jako prawdziwe, nadal nie rozumiem, czym jest prawdziwa przestrzeń liniowa, ponieważ po obliczeniu wszystkie nieliniowe przestrzenie RGB stają się liniowe, a co najważniejsze, nie dostaję części, w której nie -liniowa przestrzeń kolorów jest bardziej odpowiednia dla ludzkiego oka, ponieważ ostatecznie wszystkie przestrzenie RGB są liniowe dla tego, co rozumiem.

Rozpoznać
źródło
Praktycznie rzecz biorąc, możesz określić liniowy lub standardowy RGB jako przestrzeń kolorów w SVG i nie mam pojęcia, jaki jest efekt, poza tym, że wydaje się to ważne :-)
Michael Mullany
@MichaelMullany ta liniowa rzecz wygląda bardziej jak wskazówka dla użytkownika niż prawdziwie charakterystyczna cecha.
Ken,
Coś, co mi pomogło: myślę, że to nauczyciel matematyki powiedział mi „myśl, kiedy słyszysz linearne”. Nie jestem pewien, czy to coś pomoże, ale dla mnie było to „O tak!” moment
Joe Plante
Powiązane informacje na temat Gamma: Czy muszę korygować gamma ostateczne kolory?
legends2k

Odpowiedzi:

229

Załóżmy, że pracujesz z kolorami RGB: każdy kolor jest reprezentowany z trzema intensywnościami lub jasnościami. Masz do wyboru „linear RGB” i „sRGB”. Na razie uprościmy rzeczy, ignorując trzy różne intensywności i założymy, że masz tylko jedną intensywność: to znaczy, że masz do czynienia tylko z odcieniami szarości.

W liniowej przestrzeni kolorów związek między przechowywanymi liczbami a ich intensywnością jest liniowy. W praktyce oznacza to, że jeśli podwoisz liczbę, podwoisz intensywność (jasność szarości). Jeśli chcesz dodać dwie intensywności razem (ponieważ obliczasz intensywność na podstawie udziału dwóch źródeł światła lub ponieważ dodajesz przezroczysty obiekt na nieprzezroczystym obiekcie), możesz to zrobić, po prostu dodając dwie liczby razem. Jeśli wykonujesz jakiekolwiek mieszanie 2D lub cieniowanie 3D lub prawie dowolne przetwarzanie obrazu, chcesz, aby twoje intensywności były w liniowej przestrzeni kolorów, więc możesz po prostu dodawać, odejmować, mnożyć i dzielić liczby, aby mieć taki sam wpływ na intensywność. Większość algorytmów przetwarzania i renderowania kolorów daje poprawne wyniki tylko przy liniowym RGB, chyba że dodasz dodatkowe wagi do wszystkiego.

Brzmi to bardzo prosto, ale jest problem. Wrażliwość ludzkiego oka na światło jest mniejsza przy niskiej intensywności niż przy wysokiej intensywności. To znaczy, jeśli sporządzisz listę wszystkich intensywności, które możesz rozróżnić, będzie więcej ciemnych niż jasnych. Innymi słowy, ciemne odcienie szarości odróżniają się lepiej niż w przypadku jasnych odcieni szarości. W szczególności, jeśli używasz 8 bitów do reprezentowania swojej intensywności i robisz to w liniowej przestrzeni kolorów, otrzymasz zbyt wiele jasnych odcieni, a za mało ciemnych. W ciemnych obszarach pojawiają się pasy, podczas gdy w jasnych obszarach marnujesz kawałki na różne odcienie prawie bieli, których użytkownik nie może odróżnić.

Aby uniknąć tego problemu i jak najlepiej wykorzystać te 8 bitów, zwykle używamy sRGB . Standard sRGB określa krzywą, której należy użyć, aby kolory były nieliniowe. Krzywa jest płytsza na dole, więc możesz mieć więcej ciemnych szarości i bardziej stromą na górze, dzięki czemu masz mniej jasnych szarości. Jeśli podwoisz liczbę, zwiększysz intensywność ponad dwukrotnie. Oznacza to, że jeśli dodasz razem kolory sRGB, otrzymasz wynik jaśniejszy niż powinien. Obecnie większość monitorów interpretuje kolory wejściowe jako sRGB. Tak więc, kiedy umieszczasz kolor na ekranie lub przechowujesz go w teksturze 8-bitowej na kanał, zapisz go jako sRGB , aby jak najlepiej wykorzystać te 8 bitów.

Zauważysz, że mamy teraz problem: chcemy, aby nasze kolory były przetwarzane w przestrzeni liniowej, ale przechowywane w sRGB. Oznacza to, że w końcu wykonujesz konwersję sRGB do liniowej przy odczycie i konwersję do sRGB przy zapisie. Jak już powiedzieliśmy, liniowe 8-bitowe intensywności nie mają wystarczającej ilości ciemności, spowodowałoby to problemy, więc jest jeszcze jedna praktyczna zasada: nie używaj 8-bitowych liniowych kolorów, jeśli możesz tego uniknąć. Powszechnie przyjmuje się zasadę, że 8-bitowe kolory to zawsze sRGB, więc konwersję sRGB na liniową przeprowadza się w tym samym czasie, gdy zwiększa się intensywność z 8 do 16 bitów lub z liczb całkowitych na zmiennoprzecinkowe; podobnie, po zakończeniu przetwarzania zmiennoprzecinkowego zawężasz do 8 bitów w tym samym czasie, gdy konwertujesz do sRGB. Jeśli będziesz przestrzegać tych zasad,

Kiedy czytasz obraz sRGB i chcesz uzyskać liniowe intensywności, zastosuj następującą formułę do każdej intensywności:

float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);

W drugą stronę, jeśli chcesz zapisać obraz jako sRGB, zastosuj następującą formułę do każdego natężenia liniowego:

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 )

W obu przypadkach wartość zmiennoprzecinkowa wynosi od 0 do 1, więc jeśli czytasz 8-bitowe liczby całkowite, które chcesz najpierw podzielić przez 255, a jeśli piszesz 8-bitowe liczby całkowite, które chcesz pomnożyć przez 255 ostatni, w taki sam sposób, jak zwykle. To wszystko, co musisz wiedzieć, aby pracować z sRGB.

Do tej pory miałem do czynienia tylko z jedną intensywnością, ale są sprytniejsze rzeczy do zrobienia z kolorami. Ludzkie oko lepiej rozróżnia różne jasności niż różne odcienie (technicznie rzecz biorąc, ma lepszą rozdzielczość luminancji niż chrominancja), więc możesz jeszcze lepiej wykorzystać swoje 24 bity, przechowując jasność oddzielnie od odcienia. To właśnie próbują robić reprezentacje YUV, YCrCb itp. Kanał Y to ogólna jasność koloru i wykorzystuje więcej bitów (lub ma większą rozdzielczość przestrzenną) niż pozostałe dwa kanały. W ten sposób nie musisz (zawsze) stosować krzywej, tak jak w przypadku intensywności RGB. YUV to liniowa przestrzeń kolorów, więc jeśli podwoisz liczbę w kanale Y, podwoisz jasność koloru, ale nie możesz dodawać ani mnożyć kolorów YUV razem, tak jak w przypadku kolorów RGB, więc

Myślę, że to odpowiada na twoje pytanie, więc zakończę krótką notatką historyczną. Przed sRGB stare CRT miały wbudowaną nieliniowość. Jeśli podwoisz napięcie dla piksela, zwiększysz intensywność ponad dwukrotnie. O ile więcej było różne dla każdego monitora, a ten parametr nazywano gamma . To zachowanie było przydatne, ponieważ oznaczało, że możesz uzyskać więcej ciemności niż świateł, ale oznaczało również, że nie możesz powiedzieć, jak jasne będą twoje kolory na CRT użytkownika, chyba że najpierw go skalibrowałeś. Korekcja gammaoznacza przekształcenie kolorów, od których zaczynasz (prawdopodobnie liniowe) i przekształcenie ich dla gamma CRT użytkownika. OpenGL pochodzi z tej epoki, dlatego jego zachowanie w sRGB jest czasami trochę zagmatwane. Ale dostawcy GPU mają teraz tendencję do pracy z konwencją, którą opisałem powyżej: kiedy przechowujesz 8-bitową intensywność w teksturze lub buforze ramki, jest to sRGB, a kiedy przetwarzasz kolory, jest to liniowe. Na przykład w OpenGL ES 3.0, każdy bufor ramki i tekstura mają „flagę sRGB”, którą można włączyć, aby umożliwić automatyczną konwersję podczas czytania i pisania. Nie musisz w ogóle jawnie wykonywać konwersji sRGB ani korekcji gamma.

Dan Hulme
źródło
6
niesamowita odpowiedź, dziękuję, zawsze wspaniale jest widzieć wyjaśnione rzeczy, poprosiłbym tylko o książkę lub zasób, który Twoim zdaniem będzie wystarczająco dobry na ten temat, przestrzenie kolorów i jakie są formuły użyte do konwersji sRGB <-> liniowe lub jaka funkcja może przybliżyć to zachowanie.
Ken,
Obawiam się, że nie znam żadnych dobrych książek ani zasobów. Strona Wikipedii jest obszerna i zawiera wszystkie informacje o punkcie bieli i małej gamie (o czym nie wspomniałem, ponieważ większość ludzi nie musi o tym wiedzieć), ale to sprawia, że ​​jest trochę nieprzenikniona .
Dan Hulme,
1

Nie jestem „ekspertem w wykrywaniu kolorów”, ale podobną rzecz spotkałem przy konwersji YUV-> RGB. Istnieją różne wagi dla kanałów R / G / B, więc jeśli zmienisz kolor źródłowy o x, wartości RGB zmienią inną ilość.

Jak powiedziałem, i tak nie jestem ekspertem, myślę, że jeśli chcesz zrobić jakąś transformację poprawiającą kolory, powinieneś to zrobić w przestrzeni YUV, a następnie przekonwertować na RGB (lub wykonać matematycznie równoważną operację na RGB, uwaga utraty danych). Nie jestem też pewien, czy YUV jest najlepszą natywną reprezentacją kolorów, ale kamery wideo zapewniają ten format, właśnie tam napotkałem problem.

Oto magiczna formuła YUV-> RGB z dołączonymi tajnymi liczbami: http://www.fourcc.org/fccyvrgb.php

ern0
źródło
1
Uważaj na konwersje RGB <-> YUV i odwrotnie. Nie jestem pewien, czy tak jest w każdym przypadku, ale przestrzeń kolorów YUV czasami konwertuje się na zakres 16-235 zamiast 0-255 w 24-bitowym RGB. Możesz więc tracić dane za każdym razem, gdy wykonujesz konwersję przestrzeni kolorów. Większość ludzi ma tendencję do utrzymywania tej samej przestrzeni kolorów, jeśli możesz temu zapobiec.
Joe Plante