Bestimmen Sie die Schriftfarbe anhand der Hintergrundfarbe

248

Bei einem System (z. B. einer Website), mit dem ein Benutzer die Hintergrundfarbe für einen bestimmten Abschnitt anpassen kann, jedoch nicht die Schriftfarbe (um die Anzahl der Optionen auf ein Minimum zu beschränken), gibt es eine Möglichkeit, programmgesteuert zu bestimmen, ob ein "Licht" oder " dunkle "Schriftfarbe ist notwendig?

Ich bin mir sicher, dass es einen Algorithmus gibt, aber ich weiß nicht genug über Farben, Leuchtkraft usw., um es selbst herauszufinden.

Joseph Daigle
quelle

Antworten:

447

Ich bin auf ein ähnliches Problem gestoßen. Ich musste eine gute Methode zur Auswahl kontrastiver Schriftfarben finden, um Textbeschriftungen auf Farbskalen / Heatmaps anzuzeigen. Es musste eine universelle Methode sein und die erzeugte Farbe musste "gut aussehend" sein, was bedeutet, dass das einfache Erzeugen von Komplementärfarben keine gute Lösung war - manchmal erzeugte es seltsame, sehr intensive Farben, die schwer zu sehen und zu lesen waren.

Nach stundenlangen Tests und Versuchen, dieses Problem zu lösen, stellte ich fest, dass die beste Lösung darin besteht, weiße Schrift für "dunkle" Farben und schwarze Schrift für "helle" Farben auszuwählen.

Hier ist ein Beispiel für eine Funktion, die ich in C # verwende:

Color ContrastColor(Color color)
{
    int d = 0;

    // Counting the perceptive luminance - human eye favors green color... 
    double luminance = ( 0.299 * color.R + 0.587 * color.G + 0.114 * color.B)/255;

    if (luminance > 0.5)
       d = 0; // bright colors - black font
    else
       d = 255; // dark colors - white font

    return  Color.FromArgb(d, d, d);
}

Dies wurde für viele verschiedene Farbskalen (Regenbogen, Graustufen, Hitze, Eis und viele andere) getestet und ist die einzige "universelle" Methode, die ich herausgefunden habe.

Bearbeiten
Die Zählformel wurde ain "Wahrnehmungsluminanz" geändert - es sieht wirklich besser aus! Bereits in meiner Software implementiert, sieht gut aus.

Bearbeiten 2 @WebSeed lieferte ein hervorragendes Beispiel für diesen Algorithmus: http://codepen.io/WebSeed/full/pvgqEq/

Gacek
quelle
14
Es ist wahrscheinlich nicht wichtig, aber Sie möchten vielleicht eine bessere Funktion, um die Helligkeit zu berechnen. Stackoverflow.com/questions/596216/…
Josh Lee
1
Das sieht so aus, als wäre es perfekt.
Joseph Daigle
Genau das, was ich heute brauchte.
Chris W
Woher kommen Ihre wahrnehmbaren Luminanzgewichtungen?
Mat
Aus dieser Antwort: stackoverflow.com/questions/596216/…
Gacek
23

Nur für den Fall, dass jemand eine kürzere, vielleicht leichter verständliche Version der Antwort von GaceK haben möchte :

public Color ContrastColor(Color iColor)
{
   // Calculate the perceptive luminance (aka luma) - human eye favors green color... 
   double luma = ((0.299 * iColor.R) + (0.587 * iColor.G) + (0.114 * iColor.B)) / 255;

   // Return black for bright colors, white for dark colors
   return luma > 0.5 ? Color.Black : Color.White;
}

Hinweis: Ich habe die Inversion der Luma entfernt Werts entfernt (damit helle Farben einen höheren Wert haben, was mir natürlicher erscheint und auch die 'Standard'-Berechnungsmethode ist.

Ich habe von hier aus die gleichen Konstanten wie GaceK verwendet , da sie für mich hervorragend funktionierten.

(Sie können dies auch als Erweiterungsmethode mit der folgenden Signatur implementieren :

public static Color ContrastColor(this Color iColor)

Sie können es dann über anrufen foregroundColor = background.ContrastColor().)

Marcus Mangelsdorf
quelle
11

Vielen Dank, dass Sie @Gacek . Hier ist eine Version für Android:

@ColorInt
public static int getContrastColor(@ColorInt int color) {
    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;

    int d;
    if (a < 0.5) {
        d = 0; // bright colors - black font
    } else {
        d = 255; // dark colors - white font
    }

    return Color.rgb(d, d, d);
}

Und eine verbesserte (kürzere) Version:

@ColorInt
public static int getContrastColor(@ColorInt int color) {
    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
    return a < 0.5 ? Color.BLACK : Color.WHITE;
}
Thomas Vos
quelle
Es wäre noch kürzer und einfacher zu lesen, wenn Sie die Änderungen von @Marcus Mangelsdorf anwenden würden (entfernen Sie das 1 - ...Teil und benennen Sie es ainluminance
Ridcully
Mit dem ersten können Sie auch Alpha erfassen.
Brill Pappin
9

Meine schnelle Umsetzung von Gaceks Antwort:

func contrastColor(color: UIColor) -> UIColor {
    var d = CGFloat(0)

    var r = CGFloat(0)
    var g = CGFloat(0)
    var b = CGFloat(0)
    var a = CGFloat(0)

    color.getRed(&r, green: &g, blue: &b, alpha: &a)

    // Counting the perceptive luminance - human eye favors green color...
    let luminance = 1 - ((0.299 * r) + (0.587 * g) + (0.114 * b))

    if luminance < 0.5 {
        d = CGFloat(0) // bright colors - black font
    } else {
        d = CGFloat(1) // dark colors - white font
    }

    return UIColor( red: d, green: d, blue: d, alpha: a)
}
Vito Royeca
quelle
Da r / g / b schnell CGFloats sind, benötigen Sie nicht das "/ 255", um die Luminanz zu berechnen: Lassen Sie Luminanz = 1 - ((0,299 * r) + (0,587 * g) + (0,114 * b))
SuperDuperTango
8

Javascript [ES2015]

const hexToLuma = (colour) => {
    const hex   = colour.replace(/#/, '');
    const r     = parseInt(hex.substr(0, 2), 16);
    const g     = parseInt(hex.substr(2, 2), 16);
    const b     = parseInt(hex.substr(4, 2), 16);

    return [
        0.299 * r,
        0.587 * g,
        0.114 * b
    ].reduce((a, b) => a + b) / 255;
};
matfin
quelle
6

Danke für diesen Beitrag.

Für alle, die interessiert sein könnten, hier ein Beispiel für diese Funktion in Delphi:

function GetContrastColor(ABGColor: TColor): TColor;
var
  ADouble: Double;
  R, G, B: Byte;
begin
  if ABGColor <= 0 then
  begin
    Result := clWhite;
    Exit; // *** EXIT RIGHT HERE ***
  end;

  if ABGColor = clWhite then
  begin
    Result := clBlack;
    Exit; // *** EXIT RIGHT HERE ***
  end;

  // Get RGB from Color
  R := GetRValue(ABGColor);
  G := GetGValue(ABGColor);
  B := GetBValue(ABGColor);

  // Counting the perceptive luminance - human eye favors green color...
  ADouble := 1 - (0.299 * R + 0.587 * G + 0.114 * B) / 255;

  if (ADouble < 0.5) then
    Result := clBlack  // bright colors - black font
  else
    Result := clWhite;  // dark colors - white font
end;
Richard D.
quelle
Es gibt einen kleinen Fehler in der 4. Zeile der letzten Zeile. Ergebnis: = clBlack sollte kein Semikolon danach haben;
Andy k
5

Dies ist eine so hilfreiche Antwort. Danke dafür!

Ich möchte eine SCSS-Version teilen:

@function is-color-light( $color ) {

  // Get the components of the specified color
  $red: red( $color );
  $green: green( $color );
  $blue: blue( $color );

  // Compute the perceptive luminance, keeping
  // in mind that the human eye favors green.
  $l: 1 - ( 0.299 * $red + 0.587 * $green + 0.114 * $blue ) / 255;
  @return ( $l < 0.5 );

}

Finden Sie nun heraus, wie Sie mit dem Algorithmus automatisch Schwebefarben für Menüverknüpfungen erstellen. Helle Header erhalten einen dunkleren Schwebeflug und umgekehrt.

Ricothek
quelle
3

Flattern Implementierung

Color contrastColor(Color color) {
  if (color == Colors.transparent || color.alpha < 50) {
    return Colors.black;
  }
  double luminance = (0.299 * color.red + 0.587 * color.green + 0.114 * color.blue) / 255;
  return luminance > 0.5 ? Colors.black : Colors.white;
}
Mamnarock
quelle
Ich habe nur "statisch" vorangestellt. Vielen Dank!
Pete Alvin
2

Hässliches Python, wenn Sie keine Lust haben, es zu schreiben :)

'''
Input a string without hash sign of RGB hex digits to compute
complementary contrasting color such as for fonts
'''
def contrasting_text_color(hex_str):
    (r, g, b) = (hex_str[:2], hex_str[2:4], hex_str[4:])
    return '000' if 1 - (int(r, 16) * 0.299 + int(g, 16) * 0.587 + int(b, 16) * 0.114) / 255 < 0.5 else 'fff'
Joseph Coco
quelle
2

Ich hatte das gleiche Problem, aber ich musste es in PHP entwickeln . Ich habe die Lösung von @ Garek verwendet und auch diese Antwort: Konvertieren Sie Hex-Farben in RGB-Werte in PHP , um HEX-Farbcode in RGB zu konvertieren.

Also teile ich es.

Ich wollte diese Funktion mit der angegebenen HEX-Hintergrundfarbe verwenden, aber nicht immer ab '#'.

//So it can be used like this way:
$color = calculateColor('#804040');
echo $color;

//or even this way:
$color = calculateColor('D79C44');
echo '<br/>'.$color;

function calculateColor($bgColor){
    //ensure that the color code will not have # in the beginning
    $bgColor = str_replace('#','',$bgColor);
    //now just add it
    $hex = '#'.$bgColor;
    list($r, $g, $b) = sscanf($hex, "#%02x%02x%02x");
    $color = 1 - ( 0.299 * $r + 0.587 * $g + 0.114 * $b)/255;

    if ($color < 0.5)
        $color = '#000000'; // bright colors - black font
    else
        $color = '#ffffff'; // dark colors - white font

    return $color;
}
GregV
quelle
1

Als Kotlin / Android-Erweiterung:

fun Int.getContrastColor(): Int {
    // Counting the perceptive luminance - human eye favors green color...
    val a = 1 - (0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255
    return if (a < 0.5) Color.BLACK else Color.WHITE
}
Gabriel
quelle
1

Eine Implementierung für Ziel-c

+ (UIColor*) getContrastColor:(UIColor*) color {
    CGFloat red, green, blue, alpha;
    [color getRed:&red green:&green blue:&blue alpha:&alpha];
    double a = ( 0.299 * red + 0.587 * green + 0.114 * blue);
    return (a > 0.5) ? [[UIColor alloc]initWithRed:0 green:0 blue:0 alpha:1] : [[UIColor alloc]initWithRed:255 green:255 blue:255 alpha:1];
}
Andreas Lytter
quelle
2
Sie sollten hier eine Beschreibung hinzufügen, um andere Benutzer wissen zu lassen, was in Ihrem Code vor sich geht
Michael
1

iOS Swift 3.0 (UIColor-Erweiterung):

func isLight() -> Bool
{
    if let components = self.cgColor.components, let firstComponentValue = components[0], let secondComponentValue = components[1], let thirdComponentValue = components[2] {
        let firstComponent = (firstComponentValue * 299)
        let secondComponent = (secondComponentValue * 587)
        let thirdComponent = (thirdComponentValue * 114)
        let brightness = (firstComponent + secondComponent + thirdComponent) / 1000

        if brightness < 0.5
        {
            return false
        }else{
            return true
        }
    }  

    print("Unable to grab components and determine brightness")
    return nil
}
Josh O'Connor
quelle
1
Die Funktion funktioniert wie erwartet, aber seien Sie vorsichtig mit Flusen und
Kraftguss
1
Großartig, sehen Sie sich mein Beispiel für Swift 4 unten an. Sie können die Reduzierung des Codes sehen
RichAppz
1

Swift 4 Beispiel:

extension UIColor {

    var isLight: Bool {
        let components = cgColor.components

        let firstComponent = ((components?[0]) ?? 0) * 299
        let secondComponent = ((components?[1]) ?? 0) * 587
        let thirdComponent = ((components?[2]) ?? 0) * 114
        let brightness = (firstComponent + secondComponent + thirdComponent) / 1000

        return !(brightness < 0.6)
    }

}

UPDATE - Es 0.6wurde festgestellt, dass dies ein besserer Prüfstand für die Abfrage war

RichAppz
quelle
Dies ist in mehreren Situationen sehr wahrscheinlich, da ein RGB-Farbraum angenommen wird. Die Anzahl der Elemente CGColor.componentsvariiert je nach Farbraum: UIColor.whiteWenn sie beispielsweise in eine CGColor umgewandelt werden, gibt es nur zwei: Sie [1.0, 1.0]stellen eine Graustufenfarbe (bei vollständig Weiß) mit einem vollständigen Alpha dar. Ein besseres Mittel zum Extrahieren der RGB-Elemente einer UIColor istUIColor.getRed(_ red:, green:, blue:, alpha:)
Scott Matthewman
1

Beachten Sie, dass es in der Google Closure Library einen Algorithmus dafür gibt , der auf eine w3c-Empfehlung verweist: http://www.w3.org/TR/AERT#color-contrast . In dieser API stellen Sie jedoch eine Liste der vorgeschlagenen Farben als Ausgangspunkt bereit.

/**
 * Find the "best" (highest-contrast) of the suggested colors for the prime
 * color. Uses W3C formula for judging readability and visual accessibility:
 * http://www.w3.org/TR/AERT#color-contrast
 * @param {goog.color.Rgb} prime Color represented as a rgb array.
 * @param {Array<goog.color.Rgb>} suggestions Array of colors,
 *     each representing a rgb array.
 * @return {!goog.color.Rgb} Highest-contrast color represented by an array.
 */
goog.color.highContrast = function(prime, suggestions) {
  var suggestionsWithDiff = [];
  for (var i = 0; i < suggestions.length; i++) {
    suggestionsWithDiff.push({
      color: suggestions[i],
      diff: goog.color.yiqBrightnessDiff_(suggestions[i], prime) +
          goog.color.colorDiff_(suggestions[i], prime)
    });
  }
  suggestionsWithDiff.sort(function(a, b) { return b.diff - a.diff; });
  return suggestionsWithDiff[0].color;
};


/**
 * Calculate brightness of a color according to YIQ formula (brightness is Y).
 * More info on YIQ here: http://en.wikipedia.org/wiki/YIQ. Helper method for
 * goog.color.highContrast()
 * @param {goog.color.Rgb} rgb Color represented by a rgb array.
 * @return {number} brightness (Y).
 * @private
 */
goog.color.yiqBrightness_ = function(rgb) {
  return Math.round((rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000);
};


/**
 * Calculate difference in brightness of two colors. Helper method for
 * goog.color.highContrast()
 * @param {goog.color.Rgb} rgb1 Color represented by a rgb array.
 * @param {goog.color.Rgb} rgb2 Color represented by a rgb array.
 * @return {number} Brightness difference.
 * @private
 */
goog.color.yiqBrightnessDiff_ = function(rgb1, rgb2) {
  return Math.abs(
      goog.color.yiqBrightness_(rgb1) - goog.color.yiqBrightness_(rgb2));
};


/**
 * Calculate color difference between two colors. Helper method for
 * goog.color.highContrast()
 * @param {goog.color.Rgb} rgb1 Color represented by a rgb array.
 * @param {goog.color.Rgb} rgb2 Color represented by a rgb array.
 * @return {number} Color difference.
 * @private
 */
goog.color.colorDiff_ = function(rgb1, rgb2) {
  return Math.abs(rgb1[0] - rgb2[0]) + Math.abs(rgb1[1] - rgb2[1]) +
      Math.abs(rgb1[2] - rgb2[2]);
};
Paul
quelle
0

Wenn Sie Farbräume für visuelle Effekte bearbeiten, ist es im Allgemeinen einfacher, in HSL (Farbton, Sättigung und Helligkeit) als in RGB zu arbeiten. Das Verschieben von Farben in RGB, um natürlich ansprechende Effekte zu erzielen, ist konzeptionell recht schwierig, während das Konvertieren in HSL, das Manipulieren dort und das erneute Konvertieren im Konzept intuitiver ist und ausnahmslos besser aussehende Ergebnisse liefert.

Wikipedia hat eine gute Einführung in HSL und den eng verwandten HSV. Und es gibt kostenlosen Code im Internet, um die Konvertierung durchzuführen ( hier ist zum Beispiel eine Javascript-Implementierung ).

Welche genaue Transformation Sie verwenden, ist Geschmackssache, aber ich persönlich hätte gedacht, dass das Umkehren der Komponenten Farbton und Helligkeit in erster Näherung eine gute kontrastreiche Farbe erzeugen würde, aber Sie können leicht subtilere Effekte erzielen.

Cruachan
quelle
1
Ja, aber denken Sie auch daran, dass das menschliche Auge Grün viel dominanter als andere Farben und Blau weniger sehen kann (weshalb Blau in Bildformaten weniger Farbbits erhält).
Wchargin
1
Tatsächlich. Wenn wir zu HSL wechseln, können wir genauso gut den vollen Sprung zu YUV machen und die menschliche Wahrnehmung berücksichtigen.
David Bradbury
0

Sie können jeden Farbtontext auf jedem Farbtonhintergrund haben und sicherstellen, dass er lesbar ist. Das mache ich die ganze Zeit. Es gibt eine Formel dafür in Javascript für lesbaren Text in Farbe - STW * Wie auf diesem Link steht, ist die Formel eine Variation der Berechnung der inversen Gamma-Anpassung, obwohl sie meiner Meinung nach etwas besser zu handhaben ist. Die Menüs auf der rechten Seite dieses Links und die zugehörigen Seiten verwenden zufällig generierte Farben für Text und Hintergrund, die immer lesbar sind. Also ja, klar, es kann gemacht werden, kein Problem.

Dave Collier
quelle
0

Eine Android-Variante, die auch das Alpha erfasst.

(danke @ thomas-vos)

/**
 * Returns a colour best suited to contrast with the input colour.
 *
 * @param colour
 * @return
 */
@ColorInt
public static int contrastingColour(@ColorInt int colour) {
    // XXX /programming/1855884/determine-font-color-based-on-background-color

    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(colour) + 0.587 * Color.green(colour) + 0.114 * Color.blue(colour)) / 255;
    int alpha = Color.alpha(colour);

    int d = 0; // bright colours - black font;
    if (a >= 0.5) {
        d = 255; // dark colours - white font
    }

    return Color.argb(alpha, d, d, d);
}
Brill Pappin
quelle
0

baseR-Version von @ Gaceks Antwort zu erhalten luminance(Sie können Ihren eigenen Schwellenwert leicht anwenden)

# vectorized
luminance = function(col) c(c(.299, .587, .114) %*% col2rgb(col)/255)

Verwendung:

luminance(c('black', 'white', '#236FAB', 'darkred', '#01F11F'))
# [1] 0.0000000 1.0000000 0.3730039 0.1629843 0.5698039
MichaelChirico
quelle
0

Basierend auf Gaceks Antwort und nachdem ich das Beispiel von @ WebSeed mit der WAVE- Browsererweiterung analysiert habe, habe ich die folgende Version entwickelt, die schwarzen oder weißen Text basierend auf dem Kontrastverhältnis auswählt (wie in den W3C- Richtlinien für die Barrierefreiheit von Webinhalten (WCAG) 2.1 definiert ) ). anstelle der Leuchtdichte.

Dies ist der Code (in Javascript):

// As defined in WCAG 2.1
var relativeLuminance = function (R8bit, G8bit, B8bit) {
  var RsRGB = R8bit / 255.0;
  var GsRGB = G8bit / 255.0;
  var BsRGB = B8bit / 255.0;

  var R = (RsRGB <= 0.03928) ? RsRGB / 12.92 : Math.pow((RsRGB + 0.055) / 1.055, 2.4);
  var G = (GsRGB <= 0.03928) ? GsRGB / 12.92 : Math.pow((GsRGB + 0.055) / 1.055, 2.4);
  var B = (BsRGB <= 0.03928) ? BsRGB / 12.92 : Math.pow((BsRGB + 0.055) / 1.055, 2.4);

  return 0.2126 * R + 0.7152 * G + 0.0722 * B;
};

var blackContrast = function(r, g, b) {
  var L = relativeLuminance(r, g, b);
  return (L + 0.05) / 0.05;
};

var whiteContrast = function(r, g, b) {
  var L = relativeLuminance(r, g, b);
  return 1.05 / (L + 0.05);
};

// If both options satisfy AAA criterion (at least 7:1 contrast), use preference
// else, use higher contrast (white breaks tie)
var chooseFGcolor = function(r, g, b, prefer = 'white') {
  var Cb = blackContrast(r, g, b);
  var Cw = whiteContrast(r, g, b);
  if(Cb >= 7.0 && Cw >= 7.0) return prefer;
  else return (Cb > Cw) ? 'black' : 'white';
};

Ein funktionierendes Beispiel finden Sie in meiner Abzweigung des Codepens von @ WebSeed, der in WAVE keine kontrastarmen Fehler erzeugt.

Seirios
quelle