Wzór do określania jasności koloru RGB


387

Szukam jakiejś formuły lub algorytmu, aby określić jasność koloru, biorąc pod uwagę wartości RGB. Wiem, że to nie może być tak proste, jak sumowanie wartości RGB razem i posiadanie wyższych sum, by były jaśniejsze, ale jestem trochę niepewny, od czego zacząć.


8
Postrzegam jasność, której chyba szukam, dziękuję.
robmerica

2
Jest dobry artykuł ( Manipulowanie kolorami w .NET - Część 1 ) na temat przestrzeni kolorów i rozmów między nimi, w tym zarówno teorii, jak i kodu (C #). Aby uzyskać odpowiedź , zobacz temat Konwersja między modelami w artykule.
podkreślono

4
Jestem członkiem od wielu lat i nigdy wcześniej tego nie robiłem. Czy mogę zasugerować przejrzenie odpowiedzi i ponowne przemyślenie, którą z nich zaakceptować?
Jive Dadson

Odpowiedzi:


456

Masz na myśli jasność? Czy postrzegasz jasność? Luminancja?

  • Luminancja (standardowa dla niektórych przestrzeni kolorów): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Luminancja (postrzegana opcja 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • Luminancja (postrzegana opcja 2, wolniejsza do obliczenia): sqrt( 0.241*R^2 + 0.691*G^2 + 0.068*B^2 )sqrt( 0.299*R^2 + 0.587*G^2 + 0.114*B^2 )(dzięki @MatthewHerbst ) [3]

26
Zauważ, że oba podkreślają aspekty fizjologiczne: ludzka gałka oczna jest najbardziej wrażliwa na zielone światło, mniej na czerwone i najmniej na niebieskie.
Bob Cross

16
Zauważ też, że wszystkie one są prawdopodobnie liniowe 0-1 RGB i prawdopodobnie masz korekcję gamma 0-255 RGB. Nie są nawróceni, jak myślisz.
Alex Dziwny

4
Niepoprawne. Przed zastosowaniem transformacji liniowej należy najpierw zastosować odwrotność funkcji gamma dla przestrzeni kolorów. Następnie po zastosowaniu funkcji liniowej stosowana jest funkcja gamma.
Jive Dadson

6
Czy w ostatniej formule jest to (0.299 * R) ^ 2, czy też 0.299 * (R ^ 2)?
Kaizer Sozay,

3
@KaizerSozay Jak napisano tutaj, oznaczałoby to 0.299*(R^2)(ponieważ potęgowanie następuje przed pomnożeniem)
Dantevg

298

Myślę, że szukasz formuły konwersji RGB -> Luma .

Fotometryczna / cyfrowa ITU BT.709 :

Y = 0.2126 R + 0.7152 G + 0.0722 B

Cyfrowy ITU BT.601 (nadaje większą wagę komponentom R i B):

Y = 0.299 R + 0.587 G + 0.114 B

Jeśli chcesz wymienić dokładność za wykonanie, istnieją dwie formuły przybliżenia dla tego:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

Można je szybko obliczyć jako

Y = (R+R+B+G+G+G)/6

Y = (R+R+R+B+G+G+G+G)>>3

47
Podoba mi się, że podajesz precyzyjne wartości, ale także zawierasz szybki skrót „wystarczająco blisko”. +1.
Beska

3
@Jonathan Dumaine - dwie formuły szybkiego obliczania obejmują niebieski - pierwszy to (2 * czerwony + Blue+ 3 * zielony) / 6, drugi to (3 * czerwony + Blue+ 4 * zielony) >> 3. przyznane, w obu szybkich przybliżeniach, niebieski ma najniższą wagę, ale nadal tam jest.
Franci Penov

84
@JonathanDumaine To dlatego, że ludzkie oko jest najmniej spostrzegawcze dla Blue ;-)
Christopher Oezbek

4
Szybka wersja działa dobrze. Przetestowane i zastosowane w rzeczywistej aplikacji z tysiącami użytkowników, wszystko wygląda dobrze.
milosmns

10
Szybka wersja jest jeszcze szybsza, jeśli zrobisz to tak: Y = (R<<1+R+G<<2+B)>>3(to tylko 3-4 cykle procesora na ARM), ale myślę, że dobry kompilator zrobi to za ciebie.
rjmunro

105

Dokonałem porównania trzech algorytmów w przyjętej odpowiedzi. Generowałem kolory cyklicznie, w których użyto tylko około 400 kolorów. Każdy kolor jest reprezentowany przez 2x2 pikseli, kolory są sortowane od najciemniejszego do najjaśniejszego (od lewej do prawej, od góry do dołu).

1. zdjęcie - Luminancja (względna)

0.2126 * R + 0.7152 * G + 0.0722 * B

Drugie zdjęcie - http://www.w3.org/TR/AERT#color-contrast

0.299 * R + 0.587 * G + 0.114 * B

Trzecie zdjęcie - model kolorów HSP

sqrt(0.299 * R^2 + 0.587 * G^2 + 0.114 * B^2)

4-ci obraz - WCAG 2.0 SC 1.4.3 względem luminancji i współczynnik kontrastu formuła (patrz @ Synchro za odpowiedź tutaj )

Wzór można czasem dostrzec na 1. i 2. zdjęciu w zależności od liczby kolorów w jednym rzędzie. Nigdy nie zauważyłem żadnego wzoru na zdjęciu z trzeciego lub czwartego algorytmu.

Gdybym musiał wybrać, wybrałbym algorytm numer 3, ponieważ jest on znacznie łatwiejszy do wdrożenia i jest o około 33% szybszy niż czwarty.

Porównanie postrzeganego algorytmu jasności


3
Dla mnie jest to najlepsza odpowiedź, ponieważ oyu używa wzoru obrazu, który pozwala zobaczyć, czy różne odcienie są renderowane przy tej samej luminancji. Dla mnie i mojego obecnego monitora trzecie zdjęcie jest „najlepiej wyglądające”, ponieważ jest również szybsze niż czwarte, co jest plusem
CoffeDeveloper

8
Obraz porównania jest niepoprawny, ponieważ nie podałeś poprawnych danych wejściowych dla wszystkich funkcji. Pierwsza funkcja wymaga liniowego wejścia RGB; Mogę tylko odtworzyć efekt pasmowania, zapewniając nieliniowe (tj. Z korekcją gamma) RGB. Rozwiązując ten problem, nie otrzymujesz artefaktów bandowania, a pierwsza funkcja jest wyraźnym zwycięzcą.
Maks.

1
@Maks. ^2I sqrtzawarte w trzeciej formule są szybszym sposobem aproksymacji liniowego RGB z nieliniowego RGB zamiast ^2.2i ^(1/2.2)byłoby to bardziej poprawne. Niestety stosowanie nieliniowych danych zamiast liniowych jest niezwykle powszechne.
Mark Ransom,

53

Poniżej znajduje się jedyny PRAWIDŁOWY algorytm służący do konwersji obrazów sRGB używanych w przeglądarkach itp. Do skali szarości.

Przed obliczeniem iloczynu wewnętrznego konieczne jest zastosowanie odwrotności funkcji gamma dla przestrzeni kolorów. Następnie zastosujesz funkcję gamma do obniżonej wartości. Brak włączenia funkcji gamma może spowodować błędy do 20%.

W przypadku typowych urządzeń komputerowych przestrzeń kolorów to sRGB. Właściwe liczby dla sRGB to ok. 0,21, 0,72, 0,07. Gamma dla sRGB jest funkcją złożoną, która aproksymuje potęgowanie o 1 / (2.2). Oto cała sprawa w C ++.

// sRGB luminance(Y) values
const double rY = 0.212655;
const double gY = 0.715158;
const double bY = 0.072187;

// Inverse of sRGB "gamma" function. (approx 2.2)
double inv_gam_sRGB(int ic) {
    double c = ic/255.0;
    if ( c <= 0.04045 )
        return c/12.92;
    else 
        return pow(((c+0.055)/(1.055)),2.4);
}

// sRGB "gamma" function (approx 2.2)
int gam_sRGB(double v) {
    if(v<=0.0031308)
        v *= 12.92;
    else 
        v = 1.055*pow(v,1.0/2.4)-0.055;
    return int(v*255+0.5); // This is correct in C++. Other languages may not
                           // require +0.5
}

// GRAY VALUE ("brightness")
int gray(int r, int g, int b) {
    return gam_sRGB(
            rY*inv_gam_sRGB(r) +
            gY*inv_gam_sRGB(g) +
            bY*inv_gam_sRGB(b)
    );
}

5
Tak właśnie definiuje się sRGB. Myślę, że powodem jest to, że unika pewnych problemów numerycznych w pobliżu zera. Nie miałoby to większego znaczenia, gdybyś podniósł liczby do potęg 2,2 i 1 / 2,2.
Jive Dadson

8
JMD - w ramach pracy w laboratorium percepcji wzrokowej wykonałem bezpośrednie pomiary luminancji na monitorach CRT i mogę potwierdzić, że na dole zakresu wartości znajduje się liniowy obszar luminancji.
Jerry Federspiel

2
Wiem, że to jest bardzo stare, ale wciąż tam jest do przeszukania. Nie sądzę, żeby mogło być poprawne. Czy szary (255,255,255) nie powinien być szary (255,0,0) + szary (0,255,0) + szary (0,0,255)? Tak nie jest.
DCBillen

2
@DCBillen: nie, ponieważ wartości znajdują się w nieliniowej przestrzeni sRGB z korekcją gamma, nie można ich po prostu dodać. Jeśli chcesz je dodać, powinieneś to zrobić przed skontaktowaniem się z gam_sRGB.
rdb

1
@DCBillen Rdb jest poprawny. Sposób ich dodania pokazano w funkcji int gray (int r, int g, int b), która „odzywa” gam_sRGB. Boli mnie, że po czterech latach poprawna odpowiedź jest tak nisko oceniana. :-) Nie bardzo .. Poradzę sobie.
Jive Dadson,

45

Odpowiedź „zaakceptowana” jest nieprawidłowa i niekompletna

Jedynymi prawidłowymi odpowiedziami są odpowiedzi @ jive-dadson i @EddingtonsMonkey oraz wsparcie @ nils-pipenbrinck . Pozostałe odpowiedzi (w tym zaakceptowane) zawierają linki lub źródła, które są błędne, nieistotne, nieaktualne lub uszkodzone.

Krótko:

  • sRGB musi zostać ZLINEARYZOWANY przed zastosowaniem współczynników.
  • Luminancja (L lub Y) jest liniowa, podobnie jak światło.
  • Postrzegana lekkość (L *) jest nieliniowa, podobnie jak ludzka percepcja.
  • HSV i HSL nie są nawet zdalnie dokładne pod względem percepcji.
  • Norma IEC dla sRGB określa próg 0,04045, NIE jest to 0,03928 (pochodzi z przestarzałego wczesnego szkicu).
  • Przydatne (tj. W stosunku do percepcji) odległości euklidesowe wymagają percepcyjnie jednolitej kartezjańskiej przestrzeni wektorowej, takiej jak CIELAB. sRGB to nie jeden.

Poniżej znajduje się poprawna i pełna odpowiedź:

Ponieważ ten wątek pojawia się wysoko w wyszukiwarkach, dodaję tę odpowiedź, aby wyjaśnić różne nieporozumienia na ten temat.

Jasność jest atrybutem percepcyjnym, nie ma bezpośredniej miary.

Postrzegana jasność jest mierzona za pomocą niektórych modeli wizyjnych, takich jak CIELAB, tutaj L * (Lstar) jest miarą jasności percepcyjnej i jest nieliniowa w celu przybliżenia nieliniowej krzywej odpowiedzi na widzenie.

Luminancja jest liniową miarą światła, ważoną spektralnie dla normalnego widzenia, ale nieskorygowaną dla nieliniowego postrzegania jasności.

Luma ( liczba pierwsza Y ) to zakodowany gamma, ważony sygnał wykorzystywany w niektórych kodowaniach wideo. Nie należy go mylić z luminancją liniową.

Krzywa gamma lub transferowa (TRC) jest krzywą często podobną do krzywej percepcyjnej i jest powszechnie stosowana do danych obrazu w celu przechowywania lub nadawania w celu zmniejszenia postrzeganego szumu i / lub poprawy wykorzystania danych (i powiązanych przyczyn).

Aby określić postrzeganą jasność , najpierw przekonwertuj wartości obrazu R´G´B´ zakodowane gamma na liniową luminancję ( Llub Y), a następnie na nieliniową jasność postrzeganą ( L*)


ABY ZNALEŹĆ LUMINANCJĘ:

... Ponieważ podobno gdzieś się zgubił ...

Krok pierwszy:

Konwertuj wszystkie 8-bitowe wartości całkowite sRGB na dziesiętne 0,0-1,0

  vR = sR / 255;
  vG = sG / 255;
  vB = sB / 255;

Krok drugi:

Przekształć RGB zakodowany w gamma na wartość liniową. Na przykład sRGB (standard komputerowy) wymaga krzywej mocy około V ^ 2,2, chociaż „dokładna” transformacja to:

sRGB na liniowy

Gdzie V´ to zakodowany gamma kanał R, G lub B sRGB.
Pseudo kod:

function sRGBtoLin(colorChannel) {
        // Send this function a decimal sRGB gamma encoded color value
        // between 0.0 and 1.0, and it returns a linearized value.

    if ( colorChannel <= 0.04045 ) {
            return colorChannel / 12.92;
        } else {
            return pow((( colorChannel + 0.055)/1.055),2.4));
        }
    }

Krok trzeci:

Aby znaleźć Luminancję (Y), zastosuj standardowe współczynniki dla sRGB:

Zastosuj współczynniki Y = R * 0,2126 + G * 0,7152 + B * 0,0722

Pseudokod używający powyższych funkcji:

Y = (0.2126 * sRGBtoLin(vR) + 0.7152 * sRGBtoLin(vG) + 0.0722 * sRGBtoLin(vB))

ABY ZNALEŹĆ OCZYWISTĄ LEKKOŚĆ:

Krok czwarty:

Weź luminancję Y z góry i przekształć w L *

L * z równania Y
Pseudo kod:

function YtoLstar(Y) {
        // Send this function a luminance value between 0.0 and 1.0,
        // and it returns L* which is "perceptual lightness"

    if ( Y <= (216/24389) {       // The CIE standard states 0.008856 but 216/24389 is the intent for 0.008856451679036
            return Y * (24389/27);  // The CIE standard states 903.3, but 24389/27 is the intent, making 903.296296296296296
        } else {
            return pow(Y,(1/3)) * 116 - 16;
        }
    }

L * to wartość od 0 (czarna) do 100 (biała), gdzie 50 to percepcyjna „środkowa szarość”. L * = 50 to odpowiednik Y = 18,4, czyli innymi słowy 18% szara karta, reprezentująca środek ekspozycji fotograficznej (strefa V Ansela Adamsa).

Bibliografia:

IEC 61966-2-1:1999 Standard
Wikipedia sRGB
Wikipedia CIELAB
Wikipedia CIEXYZ
Charles Poynton's Gamma FAQ


@Rotem dziękuję - widziałem kilka dziwnych i niekompletnych stwierdzeń i czułem, że przydałoby się to poprawić, szczególnie, że ten wątek wciąż zajmuje wysokie pozycje w wyszukiwarkach.
Myndex

Stworzyłem demonstrację porównującą BT.601 Luma i CIE 1976 L * Percepcyjny Szary , używając kilku poleceń MATLAB:Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem

@Myndex Użyłem twoich wzorów, aby dostać się do L *, ale wciąż otrzymuję dziwne wyniki, bez względu na to, jakiej używam formuły ... Z twoją, L * z # d05858 jest ciemniejsze niż L * z # c51c2a ... Czy jest jakiś sposób, aby to zrobić dobrze? Dlaczego żadna formuła nie działa zgodnie z oczekiwaniami? :(
sjahan

1
@asdfasdfads Tak, L*a*b*nie uwzględnia szeregu atrybutów psychofizycznych. Efekt Helmholtza-Kohlrauscha jest jeden, ale jest wiele innych. CIELAB w żadnym wypadku nie jest „pełnym” modelem oceny obrazu. W moim poście starałem się jak najlepiej opisać podstawowe pojęcia, nie wchodząc w bardzo głębokie szczegóły. Model Hunt, modele Fairchilda i inne wykonują bardziej kompletną robotę, ale są również znacznie bardziej złożone.
Myndex

1
@Myndex, nieważne, moja implementacja była oparta na zmęczeniu i wynikły z tego moje słabe wyniki :( Dziękuję bardzo za pomoc i post, który ma wielką wartość!
sjahan

11

Znalazłem ten kod (napisany w języku C #), który świetnie sobie radzi z obliczaniem „jasności” koloru. W tym scenariuszu kod próbuje ustalić, czy należy nałożyć biały lub czarny tekst na kolor.


1
Właśnie tego potrzebowałem. Robiłem klasyczne demo „pasków kolorów” i chciałem oznaczyć je kolorami, wybierając najlepszy czarno-biały wybór!
RufusVS

10

Co ciekawe, ta formuła dla RGB => HSV po prostu używa v = MAX3 (r, g, b). Innymi słowy, możesz użyć maksimum (r, g, b) jako V w HSV.

Sprawdziłem i na stronie 575 Hearn & Baker tak też obliczają „Wartość”.

Od Hearn & Baker str. 319



HSV nie jest percepcyjnie jednorodny (i nie jest nawet blisko). Służy jedynie jako „wygodny” sposób regulacji koloru, ale nie ma znaczenia dla percepcji, a V nie odnosi się do prawdziwej wartości L lub Y (luminancja CIE).
Myndex

9

Zamiast zgubić się wśród losowego wyboru formuł wspomnianych tutaj, sugeruję wybrać formułę zalecaną przez standardy W3C.

Oto prosta, ale dokładna implementacja PHP formuł względnego luminancji i kontrastu WCAG 2.0 SC 1.4.3 . Tworzy wartości, które są odpowiednie do oceny wskaźników wymaganych do zgodności z WCAG, jak na tej stronie , i jako takie są odpowiednie i odpowiednie dla dowolnej aplikacji internetowej. Przeniesienie na inne języki jest banalne.

/**
 * Calculate relative luminance in sRGB colour space for use in WCAG 2.0 compliance
 * @link http://www.w3.org/TR/WCAG20/#relativeluminancedef
 * @param string $col A 3 or 6-digit hex colour string
 * @return float
 * @author Marcus Bointon <marcus@synchromedia.co.uk>
 */
function relativeluminance($col) {
    //Remove any leading #
    $col = trim($col, '#');
    //Convert 3-digit to 6-digit
    if (strlen($col) == 3) {
        $col = $col[0] . $col[0] . $col[1] . $col[1] . $col[2] . $col[2];
    }
    //Convert hex to 0-1 scale
    $components = array(
        'r' => hexdec(substr($col, 0, 2)) / 255,
        'g' => hexdec(substr($col, 2, 2)) / 255,
        'b' => hexdec(substr($col, 4, 2)) / 255
    );
    //Correct for sRGB
    foreach($components as $c => $v) {
        if ($v <= 0.04045) {
            $components[$c] = $v / 12.92;
        } else {
            $components[$c] = pow((($v + 0.055) / 1.055), 2.4);
        }
    }
    //Calculate relative luminance using ITU-R BT. 709 coefficients
    return ($components['r'] * 0.2126) + ($components['g'] * 0.7152) + ($components['b'] * 0.0722);
}

/**
 * Calculate contrast ratio acording to WCAG 2.0 formula
 * Will return a value between 1 (no contrast) and 21 (max contrast)
 * @link http://www.w3.org/TR/WCAG20/#contrast-ratiodef
 * @param string $c1 A 3 or 6-digit hex colour string
 * @param string $c2 A 3 or 6-digit hex colour string
 * @return float
 * @author Marcus Bointon <marcus@synchromedia.co.uk>
 */
function contrastratio($c1, $c2) {
    $y1 = relativeluminance($c1);
    $y2 = relativeluminance($c2);
    //Arrange so $y1 is lightest
    if ($y1 < $y2) {
        $y3 = $y1;
        $y1 = $y2;
        $y2 = $y3;
    }
    return ($y1 + 0.05) / ($y2 + 0.05);
}

dlaczego wolisz definicję w3c? osobiście wdrożyłem zarówno CCIR 601, jak i zalecany przez w3c i byłem znacznie bardziej zadowolony z wyników CCIR 601
151496

1
Ponieważ, jak powiedziałem, jest zalecany zarówno przez W3C, jak i WCAG?
Synchro

1
Formuła W3C jest niepoprawna na wielu poziomach. Nie bierze pod uwagę ludzkiej percepcji, używają „prostego” kontrastu z wykorzystaniem luminancji, która jest liniowa i wcale nie jest jednorodna percepcyjnie. Wydaje się, że oparli go między innymi na starych standardach z 1988 r. (!!!), które nie są dziś aktualne (standardy te oparte były na monitorach monochromatycznych, takich jak zielony / czarny, i odnosiły się do całkowitego kontrastu od włączenia do wyłączenia , nie biorąc pod uwagę skali szarości ani kolorów).
Myndex,

1
To kompletne śmieci. Luma jest szczególnie percepcyjny - dlatego ma różne współczynniki dla czerwonego, zielonego i niebieskiego. Wiek nie ma z tym nic wspólnego - doskonała percepcyjna przestrzeń kolorów CIE Lab pochodzi z 1976 roku. Przestrzeń W3C nie jest tak dobra, ale jest to dobre praktyczne przybliżenie, które można łatwo obliczyć. Jeśli masz coś konstruktywnego do zaoferowania, opublikuj to zamiast pustej krytyki.
Synchro

3
Wystarczy dodać / zaktualizować : obecnie badamy algorytmy zastępcze, które lepiej modelują kontrast percepcyjny (dyskusja w Github Issue 695) . Jednak jako osobny problem FYI próg dla sRGB wynosi 0,04045 , a nie 0,03928, do którego odwoływano się z przestarzałej wersji roboczej sRGB. Autorytatywna norma IEC używa 0,04045, a wkrótce pojawi się żądanie ściągnięcia w celu skorygowania tego błędu w WCAG. (zob .: IEC 61966-2-1: 1999) To jest w numerze 360 ​​Github, chociaż wspomnieć, w 8bitach nie ma faktycznej różnicy - pod koniec wątku 360 mam wykresy błędów, w tym 0,04045 / 0,03928 w 8bit.
Myndex

8

Aby dodać to, co powiedzieli inni:

Wszystkie te równania działają trochę dobrze w praktyce, ale jeśli chcesz być bardzo precyzyjny, musisz najpierw przekonwertować kolor na liniową przestrzeń kolorów (zastosować odwrotną gamma obrazu), zrobić średnią wagową kolorów podstawowych i - jeśli chcesz wyświetl kolor - zabierz luminancję z powrotem do gamma monitora.

Różnica luminancji między zapalaniem gamma a robieniem właściwej gamma wynosi do 20% w ciemnych szarościach.


2

Rozwiązałem podobne zadanie dzisiaj w javascript. Ustawiłem tę getPerceivedLightness(rgb)funkcję dla koloru HEX RGB. Zajmuje się efektem Helmholtza-Kohlrauscha za pomocą formuły Fairchilda i Perrotty do korekcji luminancji.

/**
 * Converts RGB color to CIE 1931 XYZ color space.
 * https://www.image-engineering.de/library/technotes/958-how-to-convert-between-srgb-and-ciexyz
 * @param  {string} hex
 * @return {number[]}
 */
export function rgbToXyz(hex) {
    const [r, g, b] = hexToRgb(hex).map(_ => _ / 255).map(sRGBtoLinearRGB)
    const X =  0.4124 * r + 0.3576 * g + 0.1805 * b
    const Y =  0.2126 * r + 0.7152 * g + 0.0722 * b
    const Z =  0.0193 * r + 0.1192 * g + 0.9505 * b
    // For some reason, X, Y and Z are multiplied by 100.
    return [X, Y, Z].map(_ => _ * 100)
}

/**
 * Undoes gamma-correction from an RGB-encoded color.
 * https://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation
 * /programming/596216/formula-to-determine-brightness-of-rgb-color
 * @param  {number}
 * @return {number}
 */
function sRGBtoLinearRGB(color) {
    // Send this function a decimal sRGB gamma encoded color value
    // between 0.0 and 1.0, and it returns a linearized value.
    if (color <= 0.04045) {
        return color / 12.92
    } else {
        return Math.pow((color + 0.055) / 1.055, 2.4)
    }
}

/**
 * Converts hex color to RGB.
 * /programming/5623838/rgb-to-hex-and-hex-to-rgb
 * @param  {string} hex
 * @return {number[]} [rgb]
 */
function hexToRgb(hex) {
    const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
    if (match) {
        match.shift()
        return match.map(_ => parseInt(_, 16))
    }
}

/**
 * Converts CIE 1931 XYZ colors to CIE L*a*b*.
 * The conversion formula comes from <http://www.easyrgb.com/en/math.php>.
 * https://github.com/cangoektas/xyz-to-lab/blob/master/src/index.js
 * @param   {number[]} color The CIE 1931 XYZ color to convert which refers to
 *                           the D65/2° standard illuminant.
 * @returns {number[]}       The color in the CIE L*a*b* color space.
 */
// X, Y, Z of a "D65" light source.
// "D65" is a standard 6500K Daylight light source.
// https://en.wikipedia.org/wiki/Illuminant_D65
const D65 = [95.047, 100, 108.883]
export function xyzToLab([x, y, z]) {
  [x, y, z] = [x, y, z].map((v, i) => {
    v = v / D65[i]
    return v > 0.008856 ? Math.pow(v, 1 / 3) : v * 7.787 + 16 / 116
  })
  const l = 116 * y - 16
  const a = 500 * (x - y)
  const b = 200 * (y - z)
  return [l, a, b]
}

/**
 * Converts Lab color space to Luminance-Chroma-Hue color space.
 * http://www.brucelindbloom.com/index.html?Eqn_Lab_to_LCH.html
 * @param  {number[]}
 * @return {number[]}
 */
export function labToLch([l, a, b]) {
    const c = Math.sqrt(a * a + b * b)
    const h = abToHue(a, b)
    return [l, c, h]
}

/**
 * Converts a and b of Lab color space to Hue of LCH color space.
 * /programming/53733379/conversion-of-cielab-to-cielchab-not-yielding-correct-result
 * @param  {number} a
 * @param  {number} b
 * @return {number}
 */
function abToHue(a, b) {
    if (a >= 0 && b === 0) {
        return 0
    }
    if (a < 0 && b === 0) {
        return 180
    }
    if (a === 0 && b > 0) {
        return 90
    }
    if (a === 0 && b < 0) {
        return 270
    }
    let xBias
    if (a > 0 && b > 0) {
        xBias = 0
    } else if (a < 0) {
        xBias = 180
    } else if (a > 0 && b < 0) {
        xBias = 360
    }
    return radiansToDegrees(Math.atan(b / a)) + xBias
}

function radiansToDegrees(radians) {
    return radians * (180 / Math.PI)
}

function degreesToRadians(degrees) {
    return degrees * Math.PI / 180
}

/**
 * Saturated colors appear brighter to human eye.
 * That's called Helmholtz-Kohlrausch effect.
 * Fairchild and Pirrotta came up with a formula to
 * calculate a correction for that effect.
 * "Color Quality of Semiconductor and Conventional Light Sources":
 * https://books.google.ru/books?id=ptDJDQAAQBAJ&pg=PA45&lpg=PA45&dq=fairchild+pirrotta+correction&source=bl&ots=7gXR2MGJs7&sig=ACfU3U3uIHo0ZUdZB_Cz9F9NldKzBix0oQ&hl=ru&sa=X&ved=2ahUKEwi47LGivOvmAhUHEpoKHU_ICkIQ6AEwAXoECAkQAQ#v=onepage&q=fairchild%20pirrotta%20correction&f=false
 * @return {number}
 */
function getLightnessUsingFairchildPirrottaCorrection([l, c, h]) {
    const l_ = 2.5 - 0.025 * l
    const g = 0.116 * Math.abs(Math.sin(degreesToRadians((h - 90) / 2))) + 0.085
    return l + l_ * g * c
}

export function getPerceivedLightness(hex) {
    return getLightnessUsingFairchildPirrottaCorrection(labToLch(xyzToLab(rgbToXyz(hex))))
}

1

Przestrzeń kolorów HSV powinna załatwić sprawę, zobacz artykuł w Wikipedii, w zależności od języka, w którym pracujesz, możesz uzyskać konwersję biblioteki.

H to odcień, który jest wartością liczbową koloru (tj. Czerwony, zielony ...)

S to nasycenie koloru, tzn. Jak „intensywne” jest

V to „jasność” koloru.


7
Problem z przestrzenią kolorów HSV polega na tym, że możesz mieć takie samo nasycenie i tę samą wartość, ale różne odcienie, dla niebieskiego i żółtego . Żółty jest znacznie jaśniejszy niż niebieski. To samo dotyczy HSL.
Ian Boyd

hsv zapewnia „jasność” koloru w sensie technicznym. w jasności percepcyjnej hsv naprawdę zawodzi
151496

HSV i HSL nie są percepcyjnie dokładne (i nawet nie są blisko). Są przydatne do „kontroli” do regulacji względnego koloru, ale nie do dokładnego przewidywania jasności percepcyjnej. Użyj L * z CIELAB dla percepcyjnej lekkości.
Myndex

1

Wartość luminancji RGB = 0,3 R + 0,59 G + 0,11 B

http://www.scantips.com/lumin.html

Jeśli szukasz koloru zbliżonego do białego, możesz użyć odległości euklidesowej od (255, 255, 255)

Myślę, że przestrzeń kolorów RGB jest percepcyjnie nierównomierna w odniesieniu do odległości euklidesowej L2. Jednolite przestrzenie obejmują CIE LAB i LUV.


1

Formuła odwrotnej gamma autorstwa Jive'a Dadsona musi zostać usunięta w połowie dostosowania, gdy zostanie zaimplementowana w JavaScript, tzn. Zwrot z funkcji gam_sRGB musi być zwrócony int (v * 255); nie zwraca int (v * 255 + .5); Połowa korekty zaokrągla w górę, co może powodować, że wartość jest zbyt wysoka na R = G = B, tj. Triada koloru szarego. Konwersja skali szarości na triadzie R = G = B powinna dawać wartość równą R; to jeden dowód na to, że formuła jest poprawna. Zobacz Nine Shades of Greyscale dla formuły w akcji (bez korekty do połowy).


Wygląda na to, że znasz swoje rzeczy, więc usunąłem +0.5
Jive Dadson

Zrobiłem eksperyment. W C ++ potrzebuje +0.5, więc włożyłem go z powrotem. Dodałem komentarz na temat tłumaczenia na inne języki.
Jive Dadson

1

Zastanawiam się, jak określono te współczynniki rgb. Zrobiłem eksperyment i skończyłem z następującymi:

Y = 0.267 R + 0.642 G + 0.091 B

Bliskie, ale oczywiście różne od dawno ustalonych współczynników ITU. Zastanawiam się, czy współczynniki te mogą być różne dla każdego obserwatora, ponieważ wszyscy możemy mieć różną liczbę stożków i prętów na siatkówce w naszych oczach, a zwłaszcza stosunek między różnymi rodzajami stożków może się różnić.

Na przykład:

ITU BT.709:

Y = 0.2126 R + 0.7152 G + 0.0722 B

ITU BT.601:

Y = 0.299 R + 0.587 G + 0.114 B

Zrobiłem test, szybko przesuwając mały szary pasek na jaskrawym czerwonym, jasnozielonym i jasnoniebieskim tle, i dostosowując szary, aż połączy się w jak największym stopniu. Powtórzyłem również ten test z innymi odcieniami. Powtórzyłem test na różnych wyświetlaczach, nawet na stałym współczynniku gamma 3,0, ale dla mnie wszystko wygląda tak samo. Co więcej, współczynniki ITU są dosłownie złe dla moich oczu.

I tak, prawdopodobnie mam normalne widzenie kolorów.


Czy w swoich eksperymentach linearyzowałeś się najpierw, aby usunąć składnik gamma? Jeśli tego nie zrobiłeś, to mogłoby wyjaśnić twoje wyniki. ALE także współczynniki są powiązane z eksperymentami CIE 1931 i są one średnio 17 obserwatorami, więc tak, istnieje indywidualna wariancja wyników.
Myndex

1

Oto trochę kodu C, który powinien poprawnie obliczyć postrzeganą luminancję.

// reverses the rgb gamma
#define inverseGamma(t) (((t) <= 0.0404482362771076) ? ((t)/12.92) : pow(((t) + 0.055)/1.055, 2.4))

//CIE L*a*b* f function (used to convert XYZ to L*a*b*)  http://en.wikipedia.org/wiki/Lab_color_space
#define LABF(t) ((t >= 8.85645167903563082e-3) ? powf(t,0.333333333333333) : (841.0/108.0)*(t) + (4.0/29.0))


float
rgbToCIEL(PIXEL p)
{
   float y;
   float r=p.r/255.0;
   float g=p.g/255.0;
   float b=p.b/255.0;

   r=inverseGamma(r);
   g=inverseGamma(g);
   b=inverseGamma(b);

   //Observer = 2°, Illuminant = D65 
   y = 0.2125862307855955516*r + 0.7151703037034108499*g + 0.07220049864333622685*b;

   // At this point we've done RGBtoXYZ now do XYZ to Lab

   // y /= WHITEPOINT_Y; The white point for y in D65 is 1.0

    y = LABF(y);

   /* This is the "normal conversion which produces values scaled to 100
    Lab.L = 116.0*y - 16.0;
   */
   return(1.16*y - 0.16); // return values for 0.0 >=L <=1.0
}

0

Proszę zdefiniować jasność. Jeśli szukasz koloru zbliżonego do białego, możesz użyć odległości euklidesowej od (255, 255, 255)


1
Nie, nie można użyć odległości euklidesowej między wartościami sRGB, sRGB nie jest percepcyjnie jednolitą przestrzenią kartezjańską / wektorową. Jeśli chcesz użyć odległości euklidesowej jako miary różnicy kolorów, musisz przynajmniej przekonwertować na CIELAB, lub jeszcze lepiej, użyj CAM, jak CIECAM02.
Myndex

0

„V” HSV jest prawdopodobnie tym, czego szukasz. MATLAB ma funkcję rgb2hsv, a cytowany wcześniej artykuł w Wikipedii jest pełen pseudokodu. Jeśli konwersja RGB2HSV nie jest możliwa, mniej dokładnym modelem byłaby wersja obrazu w skali szarości.


0

Ten link wyjaśnia wszystko dogłębnie, w tym dlaczego te stałe mnożnika istnieją przed wartościami R, G i B.

Edycja: Zawiera również wyjaśnienie jednej z odpowiedzi tutaj (0,299 * R + 0,587 * G + 0,114 * B)


0

Aby określić jasność koloru za pomocą R, przekształcam kolor systemu RGB na kolor systemu HSV.

W moim skrypcie używam wcześniej kodu systemowego HEX z innego powodu, ale możesz także zacząć od kodu systemowego RGB za pomocą rgb2hsv {grDevices}. Dokumentacja jest tutaj .

Oto ta część mojego kodu:

 sample <- c("#010101", "#303030", "#A6A4A4", "#020202", "#010100")
 hsvc <-rgb2hsv(col2rgb(sample)) # convert HEX to HSV
 value <- as.data.frame(hsvc) # create data.frame
 value <- value[3,] # extract the information of brightness
 order(value) # ordrer the color by brightness

0

Dla jasności muszą to być formuły korzystające z pierwiastka kwadratowego

sqrt(coefficient * (colour_value^2))

nie

sqrt((coefficient * colour_value))^2

Dowodem na to jest konwersja triady R = G = B na skalę szarości R. Będzie to prawdą tylko wtedy, gdy wyrównasz wartość koloru, a nie wartość razy razy współczynnik koloru. Zobacz dziewięć odcieni szarości


5
są pomyłki w nawiasach
log0

chyba że użyty współczynnik jest pierwiastkiem kwadratowym z prawidłowego współczynnika.
RufusVS
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.