Wie kann ich überprüfen, ob die Hex-Farbe „zu schwarz“ ist?

111

Ich versuche, die Dunkelheit einer von einem Farbwähler ausgewählten Farbe zu bewerten, um festzustellen, ob sie "zu schwarz" ist, und wenn ja, setzen Sie sie auf Weiß. Ich dachte, ich könnte die ersten Zeichen des Hex-Werts verwenden, um dies zu erreichen. Es funktioniert, aber es wechselt auch einige rechtmäßige "helle" Farben.

Ich habe Code, der dies tut:

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

Es muss einen effizienteren Weg mit Hex-Mathematik geben, um zu wissen, dass eine Farbe einen bestimmten Grad an Dunkelheit überschritten hat? Zum Beispiel, wenn Lichtfarbe + "irgendein Hex-Wert" <= "irgendein Hex-Wert", dann setzen Sie es auf Weiß.

Ich habe tinyColor hinzugefügt, was hierfür von Nutzen sein könnte, aber ich weiß es nicht genau.

Dshiz
quelle
1
Haben Sie versucht, einen Farbwähler aufzustellen und die Werte zu überprüfen? Mir ist aufgefallen, dass es dunkel wird, wenn R, G und B alle unter ~ 70 sind. Dies ist vielleicht nicht der richtige Weg, aber es ist einer.
Rick Kuipers
1
Da Sie tinyColor bereits verwenden, wandeln Sie die Farbe in HSL um und sehen Sie sich die L- Komponente an. 1 = weiß, 0 = schwarz
Andreas
4
@Andreas HSL Leichtigkeit berücksichtigt nicht die menschliche Wahrnehmung. Ein L-Wert von 0,5 hat für verschiedene Farbtöne eine unterschiedliche wahrgenommene Helligkeit.
Alnitak
1
@Alnitak Du hast recht, aber die Beschreibung des TO ist nicht so genau. Jeder Wert unter 3/8 hätte also für seinen Zweck dunkel genug sein können.
Andreas
1
@Andreas das hängt davon ab - wenn Sie sich die ITU-Luminanzwerte in meiner Antwort ansehen, werden Sie feststellen, dass Blau nur als 1/10 so hell wie Grün wahrgenommen wird.
Alnitak

Antworten:

226

Sie müssen die drei RGB-Komponenten einzeln extrahieren und dann eine Standardformel verwenden, um die resultierenden RGB-Werte in ihre wahrgenommene Helligkeit umzuwandeln.

Angenommen, eine Farbe mit sechs Zeichen:

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
}

BEARBEITEN

Seit Mai 2014 hat tinycolornun eine getBrightness()Funktion, obwohl die CCIR601-Gewichtungsfaktoren anstelle der oben genannten ITU-R-Faktoren verwendet werden.

BEARBEITEN

Der resultierende Luma-Wertebereich ist 0..255, wobei 0 am dunkelsten und 255 am hellsten ist. Werte größer als 128 werden von als hell angesehen tinycolor. (schamlos aus den Kommentaren von @ pau.moreno und @Alnitak kopiert)

Alnitak
quelle
11
Ich habe seit einiger Zeit keine guten Manipulationen mehr in Javascript gesehen. cooles Zeug. en.wikipedia.org/wiki/Rec._709#Luma_coefficients
jbabey
2
Guter Code, aber nach dem Testen schlage ich var luma = (r + g + b) / 3 vor; if (luma <128) {// ist nützlicher. }
Terry Lin
2
@ TerryLin Warum? Die angegebenen Koeffizienten sind die Standard- ITU-Werte, die die Tatsache berücksichtigen, dass Grün heller als Rot (und dann Blau) wahrgenommen wird .
Alnitak
1
Der resultierende lumaWertebereich ist 0..255, wo 0der dunkelste und 255der hellste ist (die drei Koeffizienten summieren sich zu eins).
pau.moreno
1
@gabssnake erst seit Mai 2014, und der isDark()Schwellenwert ist bei 128 fest codiert
Alnitak
21

Die TinyColor- Bibliothek (Sie haben sie bereits erwähnt) bietet verschiedene Funktionen zum Überprüfen und Bearbeiten von Farben, darunter:

Skalee
quelle
15

Ich habe diese WooCommerce Wordpress PHP-Funktion ( wc_hex_is_light ) gefunden und in JavaScript konvertiert. Funktioniert gut!

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;
}
Sergio Cabral
quelle
1
Super cool, danke! Ich habe es mit mehreren Farben getestet, die Erkennung war bei allen korrekt :)
David Dal Busco
colorIsDarkOrLight (color) {var hex = color.replace ("#", ""); var c_r, c_g, c_b, Helligkeit = ""; if (hex.length == 3) {c_r = parseInt (hex.str (0, 2), 16); c_g = parseInt (hexsubstr (1, 2), 16); c_b = parseInt (hexsubstr (2, 2), 16); Helligkeit = (c_r * 299 + c_g * 587 + c_b * 114) / 1000; } else {c_r = parseInt (hex.str (0, 2), 16); c_g = parseInt (hexsubstr (2, 2), 16); c_b = parseInt (hexsubstr (4, 2), 16); } Rückgabehelligkeit> 155; },
Pedro Henrique
Verwenden Sie mit Hex 3 Zeichen und 6
Pedro Henrique
6

Sie können die Luminanz berechnen :

Die Luminanz ist somit ein Indikator dafür, wie hell die Oberfläche erscheint.

Es ist also eine gute Wahl, ob der Text weiß oder schwarz sein soll.

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];
}

Mit der obigen Methode können Sie die Farbe in verschiedenen Formaten übergeben, aber der Algorithmus ist im Grunde nur in luminance_get .

Als ich es benutzte, stellte ich die Farbe auf Schwarz ein, wenn die Luminanz größer war als 180ansonsten Weiß.

Robin
quelle
5

Hier gibt es einen wichtigen Unterschied zwischen Luminanz und Helligkeit. Die Luminanz am Ende des Tages ist ein Maß dafür, wie viel Energie durch einen bestimmten Bereich fließt, und ignoriert vollständig, wie unsere Wahrnehmungssysteme diese Energie wahrnehmen. Die Helligkeit hingegen ist ein Maß dafür, wie wir diese Energie wahrnehmen, und berücksichtigt die Beziehung zwischen Luminanz und unserem Wahrnehmungssystem. (Aus Verwirrung gibt es einen Begriff namens relative Luminanz, der synonym mit Helligkeitsbegriffen verwendet zu werden scheint. Er hat mich gut gestolpert).

Um genau zu sein, suchen Sie nach "Helligkeit" oder "Wert" oder "relativ Luminanz", wie andere vorgeschlagen haben. Sie können dies auf verschiedene Arten berechnen (so soll es menschlich sein!) Http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness

  1. Nimm das Maximum von R, G und B.
  2. Nehmen Sie den Durchschnitt von Max und Min aus R, G und B.
  3. Nehmen Sie den Durchschnitt aller drei.
  4. Verwenden Sie einen gewichteten Durchschnitt, wie andere hier vorgeschlagen haben.
David Nguyen
quelle
AFAIK nur die auf der Wikipedia-Seite beschriebene Luma- Berechnung ist ein wahrnehmungsbasiertes Modell.
Alnitak
2
Es ist schön, auf die Unterscheidung zwischen physischer Lichtenergie und wahrgenommener Helligkeit hinzuweisen, aber ich denke, Sie haben die Dinge ziemlich durcheinander gebracht. Der Abschnitt des Wikipedia-Artikels, auf den Sie verlinkt haben, enthält einen vierten Aufzählungspunkt, in dem es heißt: "Eine wahrnehmungsrelevantere Alternative ist die Verwendung von Luma Y 'als Helligkeitsdimension" (Hervorhebung von mir) und anschließend die in Alnitak dargestellte Formel und Robins Antworten. Mit anderen Worten, die Methode, die Sie ausgelassen und empfohlen haben, entspricht am besten der menschlichen Wahrnehmung.
John Y
@JohnY ja, das habe ich versucht zu sagen - er hat den einzigen ausgelassen, der tatsächlich zum Rest seiner Antwort passt.
Alnitak
Ja, es stellte sich heraus, dass ich der einzige war, der hier verwirrt war. Ich bin damit einverstanden :) Ich wollte nur den Hauptpunkt klarstellen, dass es einen Unterschied zwischen Energie und Wahrnehmung gibt. Ich werde meine Antwort entsprechend aktualisieren.
David Nguyen
4

Dies funktioniert mit hex zB #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;
}

Sie können es ändern, um zurückzukehren trueoder falsedurch Änderung

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

zu

return (yiq < 40);
TheCrazyProfessor
quelle
@Vivek, weil Sie vollständige Hex-Werte (# 000000 und #ffffff) verwenden müssen
TheCrazyProfessor
2

Eine mögliche Lösung wäre, Ihre Farbe von RGB in HSB zu konvertieren . HSB steht für Farbton, Sättigung und Helligkeit (auch als HSV bekannt, wobei V für Wert steht). Dann müssen Sie nur noch einen Parameter überprüfen: Helligkeit.

Ohad
quelle
1

Mir ist klar, dass dieses Gespräch ein paar Jahre alt ist, aber es ist immer noch relevant. Ich wollte hinzufügen, dass mein Team das gleiche Problem in Java (SWT) hatte und fand dies etwas genauer:

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;
    }
}
Chris Clark
quelle