Accueil > Articles > System.Drawing.Color et l’espace de couleurs TSL (HSL)

System.Drawing.Color et l’espace de couleurs TSL (HSL)

En informatique, les couleurs sont représentées par trois composantes : le rouge, le vert et le bleu, codées généralement chacune sur 8 bits et formant l’espace colorimétrique RVB (RGB en anglais). Cette représentation est la plus efficace pour un ordinateur, d’une part pour le stockage des données, mais aussi pour le rendu sur écran, les pixels étant formés de trois points rouge vert et bleu plus ou moins lumineux. Cependant, pour un œil humain cette représentation n’est pas très naturelle et il est difficile de réussir à décomposer correctement une couleur en ses composantes RGB sans faire de nombreux essais et sans bien connaître les règles de composition des couleurs (savoir que la lumière jaune est un mélange de lumière rouge et verte par exemple). Pour remédier à ce problème, il existe d’autres types d’espaces colorimétriques, dont le plus connu est l’espace HSL ou TSL, pour Hue Saturation Lightness ou Teinte Saturation Luminosité.

L’espace TSL est donc utile dès qu’il s’agit d’offrir à un utilisateur des fonctions liées à la sélection d’une couleur. Il est aussi très pratique pour obtenir une palette de couleurs respectant certains critères : couleurs fortement dissemblables ou au contraire très proches (respect d’une teinte dominante, d’un rendu « pastel » ou « vif »), etc.
Cependant, contrairement à l’espace RGB, il n’est pas aussi simple à implémenter qu’il n’y parait, et il existe de nombreux problèmes à prendre en compte si l’on souhaite aboutir à un résultat correct.

 

 

TSL, TSV, HSL/HLS, HSV, HSB : Plusieurs noms pour le même espace ?

 

TSL, TSV, HSL/HLS, HSV, HSB sont différentes appellations qui désignent en fait le même espace colorimétrique, à quelques détails près. De la même façon que pour l’espace RGB, chacune des lettres désigne une composante, c’est-à-dire l’une des trois valeurs permettant de caractériser une couleur. On va donc commencer par une présentation des différentes composantes de cet espace colorimétriques, et surtout faire un peu de ménage parmi toute la documentation que l’on peut trouver sur le sujet.

Si ces termes ne vous évoquent rien, vous avez pourtant sans doute déjà pu voir ou utiliser les composantes de cet espace dans un logiciel de dessin ou de retouche photo, comme Paint.NET, Photoshop, etc. L’exemple ci-dessous provient du logiciel de dessin vectoriel Inkscape (équivalent gratuit d’Illustrator) :

TSL sous Inkscape

TSL sous Inkscape

 

A noter que l’alpha (transparence) est représentée ici en plus des trois composantes de l’espace colorimétrique. Ce n’est pas trop la peine de s’attarder dessus car elle est prise en charge de façon identique quelque soit l’espace utilisé (RGB, HSL, HSV ou autre) : Il s’agit toujours d’une valeur codée sur 8 bits (0 à 255) et n’intervenant pas dans les calculs propres à l’espace qui nous intéresse ici.

 

La Teinte (Hue en anglais)

La Teinte (Hue) est la composante la plus importante et la plus fiable de l’espace HSL (et HSV) car c’est la seule réellement constante quel que soit la formule mathématique utilisée. Bien souvent, c’est cette composante qui justifie a elle seule de proposer l’utilisation de l’espace HSL car c’est celle que l’on associe le plus naturellement avec la notion de « couleur ». Elle correspond en fait aux différentes longueurs d’ondes du domaine visible du spectre électromagnétique que l’on représente habituellement par un « arc-en-ciel ». C’est d’ailleurs sous cette forme que l’on retrouve la plupart des contrôles proposant le choix de la teinte.

Il existe cependant deux représentations distinctes pour la teinte : celle d’un dégradé linéaire (l’exemple de l’introduction) ou celle sous forme d’un cercle. En effet, comme on peut le constater avec la représentation linéaire, les teintes de début et de fin du dégradé sont identiques : il s’agit d’un rouge vif. Il est donc possible de relier les extrémités de ce dégradé pour former un cercle. C’est pourquoi la teinte est habituellement associée à un angle compris entre 0° et 360° permettant de désigner une position sur ce cercle. Cette représentation est aussi appelée un cercle chromatique. Celui ci-dessous correspond à celui proposé par le logiciel GIMP (équivalent gratuit de Photoshop) :

Cercle de sélection TSV

 

Ce cercle chromatique a de nombreuses caractéristiques très intéressantes pour la sélection des couleurs, et les travaux graphiques en général. Par exemple deux teintes opposées sur ce cercle (séparées de 180°) sont dites « complémentaires » car leur association correspond à un gris neutre (centre du cercle). Plus généralement, ce cercle permet de déterminer les associations de couleurs à utiliser suivant le résultat souhaité : palette de couleurs autour d’une même teinte, séries de couleurs fortement dissemblables, etc.
Bien que ce cercle corresponde en fait plutôt à un anneau, il est courant de le compléter par un disque dont le rayon correspond à une des autres composantes de l’espace HSL ou HSV. C’est le cas de l’application Paint.NET par exemple, qui varie ici la saturation :

Espace HSV dans Paint.NET

Espace HSV dans Paint.NET

 

Un autre aspect intéressant de la teinte est d’être indépendante des autres composantes. Ainsi, il est tout à fait possible de faire passer un rouge au blanc ou au noir, puis de revenir à un rouge quelconque uniquement en faisant varier la luminosité et/ou la saturation. En d’autres termes, un blanc ou un noir conservent l’information de la teinte. Avec l’espace RGB, cette même manipulation ne serait pas possible.

A noter que le terme anglais utilisé est « Hue » mais on peut aussi voir parfois le terme « Tint ». La différence entre ces deux termes est aussi subtile que la distinction entre « teinte » et « nuance » en français, et n’a donc pas beaucoup d’importance ici.

 

La Saturation

La Saturation caractérise la vivacité, la densité ou encore la pureté d’une couleur. Ainsi, lorsque l’on parle d’une « couleur vive », c’est qu’il s’agit d’une couleur dont la saturation est maximale (100%). A l’opposé, on aura une couleur « terne », voire désaturée, qui finira par correspondre à un gris neutre (0%).

Variation de la Saturation

Variation de la Saturation

 

Une image sans saturation est donc une image en « noir et blanc », ou plutôt en niveaux de gris. Dans l’espace RGB, cela équivaut à avoir R = G = B pour chaque couleur. Habituellement, une couleur de saturation nulle se positionne au centre du cercle colorimétrique.

On peut noter que la composante S, de la même façon que la teinte, est présente dans chaque variante de l’espace HSL : HSV, HSB, etc. Il y a cependant une grande différence qui peut entraîner des erreurs : contrairement à la teinte, une même valeur de saturation peut donner des résultats différents suivant l’espace utilisé : HSL ou HSV. Un rouge très pâle par exemple (R = 255, G = 220, B = 220) aura une saturation de 100% et une luminosité supérieure à 90% dans l’espace HSL, alors que dans l’espace HSV la saturation sera proche de 10% et la Valeur de 100%. Cela vient en partie de l’interprétation de la couleur blanche : considérée comme une couleur désaturée (S = 0) dans l’espace HSV, alors que dans l’espace HSL c’est une couleur de luminosité 100% quelle que soit la valeur de ses autres composantes.

Sur cet aspect, l’espace HSV est d’ailleurs un peu plus proche de l’espace RGB que ne l’est l’espace HSL. Ainsi, si l’on considère qu’un gris neutre (S = 0 donc) est une couleur dont les composantes respectent la règle R = G = B, le noir (R = G = B = 0) et le blanc (R = G = B = 255) devraient être aussi des couleurs désaturées. C’est le cas dans l’espace HSV, mais pas tout à fait dans l’espace HSL où le blanc peut avoir n’importe quelle saturation entre 0 et 100%, du moment que la luminosité est à 100%.

 

La Luminosité / Luminance / Valeur / Brillance etc.

Termes anglais : Luminosity / Lightness / Value / Brightness.

Autant de noms différents pour une même composante ne présage rien de bon. C’est effectivement difficile de s’y retrouver et pourtant cela a une importance sur le rendu final de la couleur car les calculs effectués ne sont pas forcément les mêmes. Il y a de quoi passer de nombreuses heures dans des encyclopédies pour réussir à déterminer la subtile nuance permettant de les différencier, ce qui ne facilite pas la compréhension de cet espace colorimétrique.

Dans notre cas, on ne retiendra que les deux espaces les plus connus : TSL / HSL (français / anglais) et TSV / HSV. La brillance B correspondant habituellement à l’espace HSV.

Quelle que soit la définition de cette troisième composante, la valeur minimale (0%) correspond toujours à une absence de luminosité, donc au noir. La différence vient de la valeur maximale (100%) correspondant à la luminosité / luminosity d’une couleur dans un cas (l’espace HSL), et à la brillance / brightness ou clarté / lightness dans l’autre : l’espace HSV.

Habituellement, les termes « luminance » et « luminosité » sont utilisés pour désigner la même chose, ce qui n’est pas tout à fait correct si l’on s’en tient aux définitions exactes. Cependant, dans notre cas ici la différence n’a pas beaucoup d’importance. Ceux qui sont intéressés par des recherches plus poussées sur le sujet peuvent jeter un œil aux différentes références disponibles dans cet article, en particulier celui sur les espaces HSL et HSV de Wikipédia (en anglais) et celui sur la relation entre la luminosité, la valeur et la luminance (en anglais aussi).

On retiendra donc :

  • Luminosité / Luminosity – Luminance – Clarté / Lightness : Espace HSL
  • Brillance / Brightness – Valeur / Value : Espace HSV

Pour la luminosité, il faut s’imaginer l’effet d’une couleur très intense capturée par un appareil photo : celle-ci sera perçue finalement comme un blanc.
Pour la valeur ou la brillance, c’est un peu la même chose qu’une lampe de couleur (la diode rouge témoin de veille d’un appareil électronique par exemple) alimentée à pleine puissance. Les deux dégradés ci-dessous montrent l’effet d’une variation de la luminosité ou de la valeur de 0 à 100% sur une couleur :

Variation de la luminosité

Variation de la luminosité

Variation de la Valeur

Variation de la Valeur

Les 100% de la brillance ou valeur correspondent à 50% de la luminosité.

 

Attention, j’ai déjà vu plusieurs fois des confusions entre ces différents termes et leurs espaces colorimétriques associés (d’ailleurs moi-même je ne suis jamais très sûr du terme à utiliser), notamment des cas où les composantes affichées étaient celles de l’espace HSL alors que le calcul effectué était celui de l’espace HSV et inversement. Voici donc une petite astuce pour mieux s’y retrouver :

Pour être sûr des formules utilisées, il y a une méthode très simple : il suffit de pousser au maximum les trois composantes (0° ou 360° pour la teinte, 100% pour la saturation et 100% pour la luminosité ou la valeur). Si la couleur finale est un rouge vif, l’espace utilisé est le HSV. Si c’est un blanc pur, c’est le HSL.

Certaines applications ont fait le choix d’utiliser les deux espaces pour proposer la méthode la plus pratique (ou la plus naturelle) suivant l’action à effectuer. Par exemple Paint.NET affiche les composantes TSV dans le sélecteur de couleur, mais propose le réglage des composantes TSL dans la fenêtre d’ajustement de l’image.

 

Les méthodes disponibles dans le .NET Framework

 

Après ce petit rappel sur les définitions de l’espace colorimétrique HSL et ses variantes, voyons maintenant ce qui nous est proposé dans le .NET Framework.

 

GetHue, GetSaturation et GetBrightness

La première mention de l’espace HSL se trouve dans la structure Color elle-même, avec la présence de trois méthodes : GetHue, GetSaturation et GetBrightness. Attention : habituellement la brillance est assimilée à la composante V (Value), mais ici le calcul effectué est pourtant celui de la luminance (composante L). L’espace colorimétrique représenté ici est donc l’espace HSL. Pour le confirmer, il suffit de vérifier le calcul effectué par la méthode au moyen de Reflector, ou de comparer les valeurs avec celles proposées par un logiciel graphique, comme Paint.NET ou GIMP. L’application de test disponible dans la dernière partie de l’article permet aussi de comparer les valeurs avec celles des espaces HSL et HSV. Toutefois, sans aller jusque-là, la simple définition de cette méthode donnée par Microsoft sur MSDN suffit pour lever les doutes.

A noter que la présence de méthodes plutôt que d’accesseurs permet de garder à l’esprit que les valeurs HSB (ou plutôt HSL) ne sont pas stockées dans la structure mais calculées à chaque appel.

Curieusement, Microsoft n’a pas jugé utile de proposer la conversion dans l’autre sens, et en particulier une méthode FromHSL par exemple qui aurait été pourtant bien utile. Les méthodes proposées ne sont donc pas suffisantes malheureusement.

 

La classe ColorDialog

Il existe un autre moyen d’accéder à l’espace TSL via le .NET Framework : la classe ColorDialog. Pour rappel, l’espace de nom System.Windows.Forms offre un certain nombre de classes permettant de faire apparaitre des boîtes de dialogue standard comme la boîte de dialogue « Ouvrir un fichier » (OpenFileDialog), « Imprimer » (PrintDialog) ou la sélection d’un dossier (FolderBrowserDialog). Ces boîtes de dialogues sont d’ailleurs directement accessibles via le Designer de Visual Studio. Dans le cas de la ColorDialog, il est possible d’utiliser l’espace TSL pour choisir une couleur en affichant le mode avancé de cette boîte de dialogue :

ColorDialog et TSL

ColorDialog et TSL

 

Cependant, il n’y a pas de code récupérable ici pour pouvoir utiliser les calculs de l’espace TSL en dehors de cette boîte de dialogue, tout simplement parce que ces différentes classes se contentent en fait d’appeler les boîtes de dialogue natives de Windows. Le code concernant la conversion TSL / RVB n’est donc pas contenu dans le .NET Framework. Il existe d’ailleurs une documentation intéressante à ce sujet sur MSDN (en anglais) : Windows User Experience Interaction Guidelines – Color.

Les plus curieux auront d’ailleurs noté que Windows permet la saisie des composantes TSL entre les valeurs 0 et 240 (239 pour la teinte en raison de son caractère cyclique). Une explication rapide est donnée dans la page du lien MSDN précédent : il s’agit en fait d’un choix destiné à faciliter le codage de ces composantes sur 32 bits : 8 bits par valeur + 8 bits pour la transparence (alpha). Pourquoi 240 et non 255 pour le maximum ? Sans doute pour simplifier les calculs, 240 correspondant exactement aux 2/3 des 360° habituels de la teinte, en plus d’être un multiple de 2, 3, et 5.

 

La classe ControlPaint

Oui je sais, j’aime bien fouiller dans les entrailles du .NET Framework, mais c’est essentiellement pour deux raisons : déjà pour mieux comprendre le fonctionnement interne des méthodes, mais aussi parce que je déteste découvrir plus tard que la fonction que j’ai passé tant de temps à coder existait déjà, « cachée » ailleurs dans le Framework ! Après tout, la vraie difficulté du C# ne vient pas du langage lui-même, mais de la connaissance des espaces de nom et des classes du .NET Framework.
Pour en revenir à notre sujet, il existe donc une autre classe exploitant l’espace colorimétrique TSL : La classe ControlPaint (dans System.Windows.Forms).
Cette classe assez peu connue permet par exemple de dessiner facilement des rectangles de sélection. Mise à part le lien MSDN précédent, on peut trouver un article intéressant sur cette classe ici.
Concernant ce qui nous intéresse, cette classe ne propose rien de plus au premier abord. Il faut regarder un peu plus loin, dans ses membres privés, pour constater qu’elle utilise en interne l’espace HSL. Ainsi, elle dispo d’une structure privée nommée HLSColor. Celle-ci contient notamment une méthode ColorFromHLS qui nous aurait été bien utile ici, si toutefois elle avait été exposé publiquement. Dommage. Néanmoins, il est toujours possible de récupérer les méthodes nécessaires à l’aide de Reflector :

La classe ControlPaint

La classe ControlPaint

 

Classe statique et méthodes d’extension

 

Faute d’avoir pu trouver les méthodes de conversion directement dans le .NET Framework, j’ai donc codé mes propres fonctions dans deux fichiers source C#. Dans le premier fichier il s’agit d’une simple classe statique. Dans le second il s’agit de méthodes d’extension destinées à enrichir la structure System.Color. Malheureusement, il n’est pas possible d’étendre les membres statiques de cette structure, les méthodes FromHSL et FromHSV ne seront donc disponibles que sur une instance de Color, ce qui n’est pas vraiment l’idéal.
Évidemment ce n’est qu’une base de travail, il est tout à fait possible d’optimiser le code et les calculs pour améliorer la rapidité d’exécution suivant les besoins.

Fichiers csharp :
Application de test :

Pour les fonctions je ne me suis basé sur les exemples de code du site EasyRGB dont un lien était disponible sur la page Wikipedia dédiée à l’espace HSL.

J’ai aussi mis à disposition l’application Windows.Forms qui m’a servi de base pour tester et corriger la classe statique ColorHelper. Elle permet de faire varier les composantes d’une couleur dans l’espace RGB, HSL ou HSV et d’observer la couleur finale obtenue. Cette application n’est pas aussi simple qu’elle n’y parait car les contrôles utilisés (NumericUpDown et TrackBar) sont tous dépendants entre eux, c’est-à-dire que la modification de l’un d’eux provoque la mise à jour de tous les autres, de façon passive (simple copie de valeurs) ou active (calcul des nouvelles valeurs avant mise à jour) suivant les cas. Petite astuce au passage lorsque ce genre de problématique se présente : attention à ne pas oublier d’empêcher le déclenchement en cascade des événements (ValueChanged par exemple).
En dehors des valeurs des composantes, on a aussi le temps d’exécution des méthodes de conversion de la classe statique, ainsi que l’affichage des valeurs obtenues par les méthodes GetHue, GetSaturation et GetBrightness de la structure System.Drawing.Color. On peut ainsi constater que ces composantes correspondent à celles de l’espace HSL (ce que confirme la documentation MSDN), ce qui est incorrect car la brillance, ou brightness, est normalement l’équivalent de la composante V (valeur) de l’espace HSV. On constate aussi que, globalement, les calculs de l’espace HSV sont plus rapides que ceux de l’espace HSL, du moins en ce qui concerne cet exemple de code. Si l’on souhaite utiliser uniquement la teinte, il est donc préférable de faire appel aux méthodes de conversion de l’espace HSV.

Application de test

Application de test

 

Pour finir voici un petit exemple d’utilisation de cette classe, pour montrer l’utilité du calcul de la teinte. Le code ci-dessous permet de créer un tableau de 5 couleurs représentant chacune une courbe d’un graphique. Le but étant d’avoir des couleurs suffisamment différentes entre elles tout en se détachant de la couleur de fond pour qu’un utilisateur puisse les distinguer facilement.

int nbColors = 5;
Color baseColor = Color.CornflowerBlue;
Color[] lineColor = new Color[nbColors];

float colorStep = 360f / (float)nbColors;

for (int i = 0; i < nbColors; i++)
{
    lineColor[i] = ColorHelper.FromHSL(
        colorStep * i,              // Hue
        baseColor.GetSaturation(),  // Saturation
        baseColor.GetBrightness()   // Lightness
        );
}

La couleur de base sert de référence pour la saturation et la luminosité. Il suffit ensuite de diviser l’intervalle de valeurs des teintes (0 à 360°) par le nombre de couleurs souhaitées pour obtenir n couleurs différentes.

 

Conclusion

 

Je ne m’attendais pas vraiment à devoir faire autant de recherches sur un sujet à priori simple. C’est en essayant de mettre au propre un petit programme qui utilisait un calcul de teinte que je me suis rendu compte de la confusion qu’il pouvait exister entre toutes les appellations de ce même espace de couleurs et en particulier à la différence entre l’espace TSL et TSV. En raison de ce problème ainsi que de l’absence de vraie norme pour les calculs et les plages de valeurs des paramètres (0 à 1, 100, 240 ou encore 255 par exemple), cet espace colorimétrique est rarement utilisé comme référence pour identifier une couleur de façon précise entre différents systèmes. Il est surtout utilisé en interne dans une même application, pour aider l’utilisateur à choisir les couleurs ou pour certaines manipulations spécifiques comme la retouche d’images, la sélection de couleurs aléatoires, etc. Dommage toutefois que ces différentes méthodes de calcul et de conversions (au moins celles utilisées par Windows) n’aient pas été intégrées directement dans le .NET Framework, ou du moins pas complètement.

 

Fichiers csharp :
Application de test :

 

Pour aller plus loin :
Article sur les couleurs (Wikipedia)
Le spectre électromagnétique (Wikipedia)
Espace HSL/HSV (Wikipedia, article en anglais)
Espace TSL et TSV (Wikipedia)
Relation entre la luminosité, la valeur et la luminance (Wikipedia, article en anglais)
Synthèse additive (Wikipedia)

Codage informatique des couleurs (Wikipedia)
Exemples de code pour les méthodes de conversion de couleurs
Guide Microsoft (MSDN) sur la couleur

 

  1. Pas encore de commentaire
  1. Pas encore de trackbacks