Déterminer la couleur de la police basée sur la couleur d'arrière-plan

compte tenu d'un système (un site Web par exemple) qui permet à un utilisateur de personnaliser la couleur de fond pour une section donnée mais pas la couleur de la police (pour maintenir le nombre d'options au minimum), y a-t-il un moyen de déterminer programmatiquement si une couleur de police "claire" ou "sombre" est nécessaire?

je suis sûr qu'il y a un algorithme, mais je ne sais pas assez sur les couleurs, la luminosité, etc pour le comprendre par moi-même.

193
demandé sur wchargin 2009-12-06 20:00:32

16 réponses

j'ai rencontré un problème similaire. Je devais trouver une bonne méthode de sélection de la couleur de police contrastive pour afficher les étiquettes de texte sur colorscales/heatmaps. Il devait être méthode universelle et la couleur générée devait être "belle apparence", ce qui signifie que la simple génération de couleur complémentaire n'était pas une bonne solution - parfois, il a généré des couleurs étranges, très intense qui étaient difficiles à regarder et à lire.

Après de longues heures de test et d'essayer de résoudre ce problème, j'ai trouvé Hors que la meilleure solution est de choisir la police blanche pour les couleurs "foncées", et police noire pour les couleurs "claires".

voici un exemple de fonction que j'utilise dans C#:

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

cela a été testé pour de nombreuses couleurs (arc-en-ciel, échelle de gris, la chaleur, la glace, et beaucoup d'autres) et est la seule méthode "universelle" j'ai découvert.

Modifier

Changé la formule de comptage a à "luminance perceptive" - il semble vraiment mieux! Déjà implémenté dans mon logiciel, ça a l'air génial.

Edit 2 @WebSeed a fourni un excellent exemple de travail de cet algorithme: http://codepen.io/WebSeed/full/pvgqEq/

385
répondu Gacek 2018-07-24 13:07:08

Merci @Gacek . Voici une version pour 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);
}

et une version améliorée (plus courte):

@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;
}
11
répondu Thomas Vos 2017-02-10 12:20:28

juste au cas où quelqu'un voudrait une version plus courte, peut-être plus facile à comprendre de réponse de GaceK :

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

Note: j'ai supprimé l'inversion de la valeur luma (pour rendre les couleurs vives ont une valeur plus élevée, ce qui me semble plus naturel et est également la méthode de calcul "par défaut".

j'ai utilisé les mêmes constantes que GaceK de ici puisqu'ils ont bien travaillé pour moi.

(vous pouvez également mettre en œuvre cette méthode en tant que Extension Method en utilisant la signature suivante:

public static Color ContrastColor(this Color iColor)

vous pouvez alors l'appeler par foregroundColor = background.ContrastColor() .)

9
répondu Marcus Mangelsdorf 2017-10-09 12:21:11

ma mise en œuvre rapide de la réponse de Gacek:

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)
}
6
répondu jovit.royeca 2017-08-03 06:24:24

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;
};
6
répondu matfin 2017-09-20 20:55:05

C'est une réponse utile. Merci pour elle!

j'aimerais partager une version SCSS:

@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 );

}

maintenant comprendre comment utiliser l'algorithme pour auto-créer des couleurs de vol stationnaire pour les liens de menu. Les en-têtes légers ont un vol stationnaire plus sombre, et vice-versa.

5
répondu ricotheque 2016-07-04 04:06:05

Merci pour ce post.

pour ceux qui pourraient être intéressés, voici un exemple de cette fonction dans 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;
4
répondu user3233041 2017-10-09 16:27:36

Laid Python si vous n'avez pas envie de l'écrire :)

'''
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'
1
répondu Joseph Coco 2016-06-02 23:01:02

j'ai eu le MÊME PROBLÈME MAIS j'ai dû le développer dans PHP . J'ai utilisé la solution de @Garek et j'ai aussi utilisé cette réponse: Convertissez hex color en valeurs RGB en PHP convertissez hex color code en RGB.

donc je partage.

je voulais utiliser cette fonction avec une couleur hexadécimale donnée, mais pas toujours à partir de '#'.

//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;
}
1
répondu GregV 2018-05-16 11:30:21

iOS Swift 3.0 (extension UIColor):

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
}
1
répondu Josh O'Connor 2018-06-08 17:54:56

si vous manipulez les espaces de couleur pour l'effet visuel, il est généralement plus facile de travailler en HSL (Teinte, Saturation et légèreté) que RGB. Déplacer des couleurs en RVB pour donner des effets naturellement agréables a tendance à être assez difficile sur le plan conceptuel, alors que convertir en HSL, y manipuler, puis se convertir de nouveau est plus intuitif dans le concept et donne invariablement de meilleurs résultats de recherche.

Wikipedia a un bonne introduction à HSL et le VHS qui lui est étroitement lié. Et il ya du code libre autour du net pour faire la conversion (par exemple voici une implémentation javascript )

quelle transformation précise utilisez-vous est une question de goût, mais personnellement, j'aurais pensé que l'inversion des composantes teinte et légèreté serait certain de générer une bonne couleur de contraste élevé comme une première approximation, mais vous pouvez facilement aller pour des effets plus subtils.

0
répondu Cruachan 2014-06-19 10:34:43

vous pouvez avoir n'importe quel texte de teinte sur n'importe quel arrière-plan de teinte et s'assurer qu'il est lisible. Je le fais tout le temps. Il y a une formule pour cela dans Javascript sur texte lisible en couleur-STW* Comme il est dit sur ce lien, la formule est une variation sur le calcul d'ajustement inverse-gamma, bien QU'un peu plus gérable IMHO. Les menus du côté droit de ce lien et des pages associées utilisent des couleurs aléatoires pour le texte et l'arrière-plan, toujours lisibles. Si oui, clairement, il peut être fait, pas de problème.

0
répondu Dave Collier 2016-01-19 17:48:46

comme extension Kotlin / Android:

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
}
0
répondu Gabriel 2017-11-03 21:57:13

une variante androïde qui capte aussi l'alpha.

(merci @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 https://stackoverflow.com/questions/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);
}
0
répondu Brill Pappin 2018-03-06 16:12:40

Une mise en œuvre pour objective-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];
}
0
répondu Andreas Lytter 2018-04-02 17:34:14

Swift 4 Exemple:

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)
    }

}

mise à jour - constaté que 0.6 était un meilleur lit d'essai pour la requête

0
répondu RichAppz 2018-06-15 10:32:14