Éclaircir une couleur par programme

Motivation

Je voudrais trouver un moyen de prendre une couleur arbitraire et d’éclaircir quelques nuances, de façon à pouvoir créer un beau dégradé d’une version à une version plus claire. Le dégradé sera utilisé comme arrière-plan dans une interface utilisateur.

Possibilité 1

Évidemment, je peux simplement diviser les valeurs RVB et les augmenter individuellement d’un certain montant. Est-ce réellement ce que je veux?

Possibilité 2

Ma deuxième reflection a été de convertir le RVB en HSV / HSB / HSL (Teinte, Saturation, Valeur / Luminosité / Luminosité), d’augmenter un peu la luminosité, de diminuer légèrement la saturation, puis de le reconvertir en RVB. Cela aura-t-il l’effet désiré en général?

J’irais pour la deuxième option. D’une manière générale, l’espace RVB n’est pas vraiment adapté à la manipulation des couleurs (création d’une transition d’une couleur à l’autre, éclaircissement / assombrissement d’une couleur, etc.). Voici deux sites que j’ai trouvés avec une recherche rapide pour convertir de / en RVB vers / depuis HSL:

  • des “fondamentaux de l’informatique graphique”
  • un code source en C # – devrait être facile à adapter à d’autres langages de programmation.

Comme Wedge l’a dit , vous voulez vous multiplier pour rendre les choses plus lumineuses, mais cela ne fonctionne que jusqu’à ce que l’une des couleurs devienne saturée (c’est-à-dire des résultats de 255 ou plus). À ce stade, vous pouvez simplement fixer les valeurs à 255, mais vous modifierez subtilement la teinte à mesure que vous vous sentirez plus léger. Pour conserver la teinte, vous souhaitez conserver le ratio de (moyen le plus bas) / (le plus bas).

Voici deux fonctions en Python. La première implémente l’approche naïve qui consiste à fixer les valeurs RVB à 255 si elles sont dépassées. Le second redissortingbue les valeurs en excès pour garder la teinte intacte.

def clamp_rgb(r, g, b): return min(255, int(r)), min(255, int(g)), min(255, int(b)) def redissortingbute_rgb(r, g, b): threshold = 255.999 m = max(r, g, b) if m <= threshold: return int(r), int(g), int(b) total = r + g + b if total >= 3 * threshold: return int(threshold), int(threshold), int(threshold) x = (3 * threshold - total) / (3 * m - total) gray = threshold - x * m return int(gray + x * r), int(gray + x * g), int(gray + x * b) 

J’ai créé un dégradé à partir de la valeur RVB (224 128,0) et en le multipliant par 1,0, 1,1, 1,2, etc. jusqu’à 2,0. La moitié supérieure est le résultat en utilisant clamp_rgb et la moitié inférieure est le résultat avec redissortingbute_rgb . Je pense qu’il est facile de voir que la redissortingbution des débordements donne un meilleur résultat, sans avoir à quitter l’espace colorimésortingque RVB.

Gradient de légèreté avec serrage (haut) et redistribution (bas)

A titre de comparaison, voici le même gradient dans les espaces colorimésortingques HLS et HSV, mis en œuvre par le module colorsys de Python. Seule la composante L été modifiée et le serrage a été effectué sur les valeurs RVB résultantes. Les résultats sont similaires, mais nécessitent des conversions d’espace colorimésortingque pour chaque pixel.

Gradient de légèreté avec HLS (en haut) et HSV (en bas)

En C #:

 public static Color Lighten(Color inColor, double inAmount) { return Color.FromArgb( inColor.A, (int) Math.Min(255, inColor.R + 255 * inAmount), (int) Math.Min(255, inColor.G + 255 * inAmount), (int) Math.Min(255, inColor.B + 255 * inAmount) ); } 

Je l’ai utilisé partout.

La classe ControlPaint dans l’espace de noms System.Windows.Forms a des méthodes statiques Light and Dark:

 public static Color Dark(Color baseColor, float percOfDarkDark); 

Ces méthodes utilisent une implémentation privée de HLSColor. Je souhaite que cette structure soit publique et dans System.Drawing.

Vous pouvez également utiliser GetHue, GetSaturation, GetBrightness on Color struct pour obtenir des composants HSB. Malheureusement, je n’ai pas trouvé la conversion inverse.

Convertissez-le en RVB et interpolez linéairement entre la couleur d’origine et la couleur cible (souvent blanche). Donc, si vous voulez 16 nuances entre deux couleurs, vous faites:

 for(i = 0; i < 16; i++) { colors[i].R = start.R + (i * (end.R - start.R)) / 15; colors[i].G = start.G + (i * (end.G - start.G)) / 15; colors[i].B = start.B + (i * (end.B - start.B)) / 15; } 

Pour obtenir une version plus claire ou plus sombre d’une couleur donnée, vous devez modifier sa luminosité. Vous pouvez le faire facilement, même sans convertir votre couleur en couleur HSL ou HSB. Par exemple, pour rendre une couleur plus claire, vous pouvez utiliser le code suivant:

 float correctionFactor = 0.5f; float red = (255 - color.R) * correctionFactor + color.R; float green = (255 - color.G) * correctionFactor + color.G; float blue = (255 - color.B) * correctionFactor + color.B; Color lighterColor = Color.FromArgb(color.A, (int)red, (int)green, (int)blue); 

Si vous avez besoin de plus de détails, lisez l’article complet sur mon blog .

Une question très similaire, avec des réponses utiles, a été posée précédemment: Comment déterminer une variante de couleur plus sombre ou plus claire d’une couleur donnée?

Réponse courte: multipliez les valeurs RVB par une constante si vous avez juste besoin de “bien”, traduisez en HSV si vous avez besoin de précision.

J’ai utilisé la réponse d’Andrew et la réponse de Mark pour faire ceci (à partir de 1/2013 aucune entrée de gamme pour ff).

 function calcLightness(l, r, g, b) { var tmp_r = r; var tmp_g = g; var tmp_b = b; tmp_r = (255 - r) * l + r; tmp_g = (255 - g) * l + g; tmp_b = (255 - b) * l + b; if (tmp_r > 255 || tmp_g > 255 || tmp_b > 255) return { r: r, g: g, b: b }; else return { r:parseInt(tmp_r), g:parseInt(tmp_g), b:parseInt(tmp_b) } } 

entrer la description de l'image ici

Convertir en HS (LVB), en augmentant la luminosité puis en convertissant en RVB, est le seul moyen d’alléger la couleur de manière fiable sans affecter les valeurs de teinte et de saturation (par exemple, éclaircir la couleur sans la modifier autrement).

Je l’ai fait dans les deux sens – vous obtenez de bien meilleurs résultats avec Possibilité 2.

Tout algorithme simple que vous construisez pour Possibility 1 ne fonctionnera probablement que pour un nombre limité de saturations initiales.

Vous voudriez regarder dans Poss 1 si (1) vous pouvez restreindre les couleurs et les luminosités utilisées et (2) vous effectuez beaucoup le calcul dans un rendu.

La génération de l’arrière-plan d’une interface utilisateur ne nécessitera pas beaucoup de calculs d’ombrage. Je suggère donc Poss 2.

-Al.

Si vous voulez produire un fondu en dégradé, je vous suggère l’optimisation suivante: Plutôt que de faire RVB-> HSB-> RVB pour chaque couleur, vous devez uniquement calculer la couleur cible. Une fois que vous connaissez le RVB cible, vous pouvez simplement calculer les valeurs intermédiaires dans l’espace RVB sans avoir à effectuer des conversions. Que vous calculiez une transition linéaire d’utilisation, vous devez choisir une courbe.

Méthode 1: Convertir RVB en HSL, ajuster HSL, reconvertir en RVB.

Méthode 2: Lerp les valeurs de couleur RVB – http://en.wikipedia.org/wiki/Lerp_(computing)

Voir ma réponse à cette question similaire pour une implémentation C # de la méthode 2.

Faites comme si vous aviez alpha mélangé au blanc:

 oneMinus = 1.0 - amount r = amount + oneMinus * r g = amount + oneMinus * g b = amount + oneMinus * b 

amount est de 0 à 1, 0 retourne la couleur d’origine et 1 retourne le blanc.

Vous souhaiterez peut-être vous fondre dans la couleur d’arrière-plan si vous souhaitez éclaircir l’affichage d’un élément désactivé:

 oneMinus = 1.0 - amount r = amount * dest_r + oneMinus * r g = amount * dest_g + oneMinus * g b = amount * dest_b + oneMinus * b 

(dest_r, dest_g, dest_b) est la couleur mélangée et la amount est (dest_r, dest_g, dest_b) entre 0 et 1, zéro retournant (r, g, b) et 1 renvoyant (dest.r, dest.g, dest.b)

Je n’ai pas trouvé cette question avant que cela ne devienne une question liée à ma question initiale.

Cependant, en tirant parti de ces excellentes réponses. J’ai reconstitué une belle fonction à deux lignes pour cela:

Éclaircir ou assombrir par programmation une couleur hexadécimale (ou RVB, et mélanger les couleurs)

C’est une version de la méthode 1. Mais avec la saturation prise en compte. Comme Keith l’a dit dans sa réponse ci-dessus; utiliser Lerp pour résoudre apparemment le même problème que Mark a mentionné, mais sans redissortingbution. Les résultats de shadeColor2 devraient être beaucoup plus proches de la procédure HSL, mais sans la surcharge.

J’aurais essayé le numéro 1 en premier, mais le numéro 2 semble plutôt bon. Essayez de le faire vous-même et voyez si vous êtes satisfait des résultats, il semble que cela vous prendra peut-être 10 minutes pour préparer un test.

Techniquement, je ne pense pas non plus que ce soit correct, mais je pense que vous voulez une variante de l’option # 2. Le problème étant que RGB 990000 et “lightening” ont été ajoutés au canal rouge (Value, Brightness, Lightness) jusqu’à ce que vous arriviez à FF. Après cela (rouge uni), cela prendrait la saturation pour atteindre le blanc uni.

Les conversions deviennent gênantes, surtout depuis que vous ne pouvez pas aller directement à RGB et Lab, mais je pense que vous voulez vraiment séparer les valeurs de chrominance et de luminance, et simplement modifier la luminance pour vraiment atteindre ce que vous voulez.

Vous trouverez du code pour convertir entre les espaces de couleur de la bibliothèque ruby ​​color-tools

J’ai un article de blog qui montre comment faire cela dans Delphi. C’est assez simple car les fonctions ColorRGBToHSL et ColorHLSToRGB font partie de la bibliothèque standard.

Voici un exemple d’allégement d’une couleur RVB en Python:

 def lighten(hex, amount): """ Lighten an RGB color by an amount (between 0 and 1), eg lighten('#4290e5', .5) = #C1FFFF """ hex = hex.replace('#','') red = min(255, int(hex[0:2], 16) + 255 * amount) green = min(255, int(hex[2:4], 16) + 255 * amount) blue = min(255, int(hex[4:6], 16) + 255 * amount) return "#%X%X%X" % (int(red), int(green), int(blue)) 

Un peu tard pour la soirée, mais si vous utilisez javascript ou nodejs , vous pouvez utiliser la bibliothèque tinycolor et manipuler la couleur comme vous le souhaitez:

 tinycolor("red").lighten().desaturate().toHexSsortingng() // "#f53d3d"