Jak sprawdzić, czy kolor szesnastkowy jest „zbyt czarny”?


111

Próbuję ocenić odcień koloru wybranego przez próbnik kolorów, aby sprawdzić, czy jest „zbyt czarny”, a jeśli tak, ustaw go na biały. Pomyślałem, że mógłbym użyć pierwszych znaków wartości szesnastkowej, aby to zrobić. Działa, ale zmienia też pewne „jasne” kolory.

Mam kod, który robi to:

        if (lightcolor.substring(0,3) == "#00"|| lightcolor.substring(0,3) == "#010"){
            lightcolor="#FFFFFF";
            color=lightcolor;
        }

Musi istnieć bardziej efektywny sposób z matematyką szesnastkową, aby wiedzieć, że kolor przekroczył pewien poziom ciemności? Na przykład jeśli lightcolor + "jakaś wartość szesnastkowa" <= "jakaś wartość szesnastkowa", a następnie ustaw ją na biało.

Dodałem tinyColor, który może się przydać, ale nie wiem na pewno.


1
Czy próbowałeś uruchomić próbnik kolorów i sprawdzić wartości? Zauważyłem, że kiedy R, G i B mają mniej niż 70 lat, robi się ciemno. To może nie być właściwy sposób, ale tak jest.
Rick Kuipers

1
Ponieważ używasz już tinyColor, przekształć kolor na HSL i przyjrzyj się komponentowi L. 1 = biały, 0 = czarny
Andreas

4
@Andreas HSL lekkość nie bierze pod uwagę ludzkiej percepcji. Wartość L równa 0,5 będzie miała różną postrzeganą jasność dla różnych odcieni.
Alnitak

1
@Alnitak Masz rację, ale opis TO nie jest dokładnie taki. Więc każda wartość poniżej 3/8 mogłaby być wystarczająco ciemna dla jego celu.
Andreas,

1
@ Andreas, to zależy - jeśli spojrzysz na wartości luminancji ITU w mojej odpowiedzi, zobaczysz, że niebieski jest postrzegany jako tylko 1/10 tak jasny jak zielony.
Alnitak

Odpowiedzi:


226

Musisz osobno wyodrębnić trzy składniki RGB, a następnie użyć standardowej formuły, aby przekonwertować wynikowe wartości RGB na ich postrzeganą jasność.

Zakładając sześcioznakowy kolor:

var c = c.substring(1);      // strip #
var rgb = parseInt(c, 16);   // convert rrggbb to decimal
var r = (rgb >> 16) & 0xff;  // extract red
var g = (rgb >>  8) & 0xff;  // extract green
var b = (rgb >>  0) & 0xff;  // extract blue

var luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; // per ITU-R BT.709

if (luma < 40) {
    // pick a different colour
}

EDYTOWAĆ

Od maja 2014 tinycolorma teraz getBrightness()funkcję, aczkolwiek wykorzystuje współczynniki wagowe CCIR601 zamiast powyższych ITU-R.

EDYTOWAĆ

Wynikowy zakres wartości luminancji to 0..255, gdzie 0 to najciemniej, a 255 to najjaśniej. Wartości większe niż 128 są uważane za niewielkie tinycolor. (bezwstydnie skopiowane z komentarzy @ pau.moreno i @Alnitak)


12
od jakiegoś czasu nie widziałem dobrych manipulacji bitami w javascript. fajne rzeczy. en.wikipedia.org/wiki/Rec._709#Luma_coefficients
jbabey

2
Dobry kod, ale po przetestowaniu sugeruję var luma = (r + g + b) / 3; if (luma <128) {// będzie bardziej przydatna. }
Terry Lin,

2
@TerryLin Dlaczego? Podane współczynniki są standardowymi wartościami ITU, które uwzględniają fakt, że kolor zielony jest postrzegany jaśniej niż czerwony (a następnie niebieski).
Alnitak

1
Wynikowy lumazakres wartości to 0..255, gdzie 0jest najciemniejszy i 255najjaśniejszy (suma trzech współczynników wynosi jeden).
pau.moreno

1
@gabssnake tylko od maja 2014, a isDark()próg jest zakodowany na stałe na 128
Alnitak

21

TinyColor biblioteka (już wspomniałem) udostępnia kilka funkcji do kontrolowania i manipulowania kolory, a wśród nich:


15

Znalazłem tę funkcję PHP WooCommerce Wordpress ( wc_hex_is_light ) i przekonwertowałem na JavaScript. Działa w porządku!

function wc_hex_is_light(color) {
    const hex = color.replace('#', '');
    const c_r = parseInt(hex.substr(0, 2), 16);
    const c_g = parseInt(hex.substr(2, 2), 16);
    const c_b = parseInt(hex.substr(4, 2), 16);
    const brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
    return brightness > 155;
}

1
Super fajnie, dzięki! Przetestowałem to z kilkoma kolorami, wykrywanie było poprawne dla wszystkich :)
David Dal Busco

colorIsDarkOrLight (color) {var hex = color.replace ("#", ""); var c_r, c_g, c_b, Brightness = ""; if (hex.length == 3) {c_r = parseInt (hex.substr (0, 2), 16); c_g = parseInt (hex.substr (1, 2), 16); c_b = parseInt (hex.substr (2, 2), 16); jasność = (c_r * 299 + c_g * 587 + c_b * 114) / 1000; } else {c_r = parseInt (hex.substr (0, 2), 16); c_g = parseInt (hex.substr (2, 2), 16); c_b = parseInt (hex.substr (4, 2), 16); } jasność powrotu> 155; },
Pedro Henrique

Użyj z heksagonalnymi 3 znakami i 6
Pedro Henrique

6

Możesz obliczyć luminancję :

Luminancja jest zatem wskaźnikiem jasności powierzchni.

Dlatego dobrze jest wybrać, czy tekst ma być biały czy czarny.

var getRGB = function(b){
    var a;
    if(b&&b.constructor==Array&&b.length==3)return b;
    if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))return[parseInt(a[1]),parseInt(a[2]),parseInt(a[3])];
    if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];
    if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],
16)];
    if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];
    return (typeof (colors) != "undefined")?colors[jQuery.trim(b).toLowerCase()]:null
};

var luminance_get = function(color) {
    var rgb = getRGB(color);
    if (!rgb) return null;
        return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2];
}

Powyższa metoda pozwala na przekazywanie koloru w różnych formatach, ale algorytm jest w zasadzie tylko w luminance_get.

Kiedy go użyłem, ustawiałem kolor na czarny, jeśli luminancja była większa niż 180biała, w przeciwnym razie.


5

Istnieje tutaj ważna różnica między luminancją a jasnością. Luminancja pod koniec dnia jest miarą tego, ile energii przemieszcza się przez określony obszar i całkowicie ignoruje sposób, w jaki nasze systemy percepcyjne postrzegają tę energię. Z drugiej strony jasność jest miarą tego, jak postrzegamy tę energię i bierze pod uwagę związek między luminancją a naszym systemem percepcyjnym. (Dla niejasności istnieje termin zwany luminancją względną, który wydaje się być używany jako synonim jasności. To mnie zaskoczyło).

Mówiąc dokładniej, szukasz „jasności”, „wartości” lub „względnej luminancji”, jak sugerowali inni. Możesz to obliczyć na kilka różnych sposobów (to znaczy być człowiekiem!) Http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness

  1. Wykorzystaj maksimum R, G i B.
  2. Weź średnią maksimum i minimum z R, G i B.
  3. Weź średnią ze wszystkich trzech.
  4. Użyj średniej ważonej, zgodnie z sugestiami innych osób.

AFAIK tylko obliczenia lumy opisane na stronie Wikipedii są modelem opartym na percepcji.
Alnitak

2
Miło jest zwrócić uwagę na różnicę między fizyczną energią światła a postrzeganą jasnością, ale myślę, że wszystko jest dość pomieszane. Sekcja artykułu w Wikipedii, do której prowadziło łącze, zawiera czwarty punkt, który stwierdza: „ Bardziej istotną percepcyjnie alternatywą jest użycie lumy, Y ′, jako wymiaru lekkości” (podkreślenie moje), a następnie podaje wzór przedstawiony w artykule Alnitaka i odpowiedzi Robin. Innymi słowy, metoda, którą pominąłeś i przeciwko której zalecałeś, jest tą, która najlepiej pasuje do ludzkiej percepcji.
John Y

@JohnY tak, to właśnie próbowałem powiedzieć - pominął jedyną, która faktycznie pasuje do reszty jego odpowiedzi.
Alnitak

Tak, okazuje się, że byłem tu jedynym zdezorientowanym. Nie przeszkadza mi to :) Chciałem tylko powiedzieć, że jest różnica między energią a percepcją. Odpowiednio zaktualizuję swoją odpowiedź.
David Nguyen

4

Działa to z hexem, np. #Fefefe

function isTooDark(hexcolor){
    var r = parseInt(hexcolor.substr(1,2),16);
    var g = parseInt(hexcolor.substr(3,2),16);
    var b = parseInt(hexcolor.substr(4,2),16);
    var yiq = ((r*299)+(g*587)+(b*114))/1000;
    // Return new color if to dark, else return the original
    return (yiq < 40) ? '#2980b9' : hexcolor;
}

Możesz go zmienić, aby powrócić truelub falsezmienić

return (yiq < 40) ? '#2980b9' : hexcolor;

do

return (yiq < 40);

@Vivek to dlatego, że musisz używać pełnych wartości szesnastkowych (# 000000 i #ffffff)
TheCrazyProfessor

2

Możliwym rozwiązaniem byłoby przekonwertowanie koloru z RGB na HSB . HSB oznacza odcień, nasycenie i jasność (znane również jako HSV, gdzie V oznacza wartość). W takim razie do sprawdzenia pozostaje tylko jeden parametr: jasność.


1

Zdaję sobie sprawę, że ta rozmowa ma kilka lat, ale nadal jest aktualna. Chciałem dodać, że mój zespół miał ten sam problem w Javie (SWT) i stwierdziłem, że jest to nieco dokładniejsze:

private Color getFontColor(RGB bgColor) {
    Color COLOR_BLACK = new Color(Display.getDefault(), 0, 0, 0);
    Color COLOR_WHITE = new Color(Display.getDefault(), 255, 255, 255);

    double luminance = Math.sqrt(0.241 
       * Math.pow(bgColor.red, 2) + 0.691 * Math.pow(bgColor.green, 2) +  0.068 
       * Math.pow(bgColor.blue, 2));
    if (luminance >= 130) {
        return COLOR_BLACK;
    } else {
        return COLOR_WHITE;
    }
}
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.