218 votes

Comment générer automatiquement N couleurs "distinctes"?

J'ai écrit les deux méthodes ci-dessous pour sélectionner automatiquement la N des couleurs distinctes. Il fonctionne par la définition d'une fonction linéaire par morceaux sur le cube RGB. L'avantage de ceci est que vous pouvez également obtenir un barème progressif, si c'est ce que vous voulez, mais quand N devient grand, les couleurs peuvent commencer à ressembler. Je peux aussi imaginer uniformément la subdivision de l'RVB cube en un treillis et en dessinant des points. Quelqu'un sait-il d'autres méthodes? Je me suis écarté de la définition d'une liste, et puis juste à vélo à travers elle. Je dois aussi dire que je n'ai généralement pas de soins si elles se contredisent ou ne sont pas très belles, elles ont juste besoin d'être visuellement distincts.

public static List<Color> pick(int num) {
	List<Color> colors = new ArrayList<Color>();
	if (num < 2)
		return colors;
	float dx = 1.0f / (float) (num - 1);
	for (int i = 0; i < num; i++) {
		colors.add(get(i * dx));
	}
	return colors;
}

public static Color get(float x) {
	float r = 0.0f;
	float g = 0.0f;
	float b = 1.0f;
	if (x >= 0.0f && x < 0.2f) {
		x = x / 0.2f;
		r = 0.0f;
		g = x;
		b = 1.0f;
	} else if (x >= 0.2f && x < 0.4f) {
		x = (x - 0.2f) / 0.2f;
		r = 0.0f;
		g = 1.0f;
		b = 1.0f - x;
	} else if (x >= 0.4f && x < 0.6f) {
		x = (x - 0.4f) / 0.2f;
		r = x;
		g = 1.0f;
		b = 0.0f;
	} else if (x >= 0.6f && x < 0.8f) {
		x = (x - 0.6f) / 0.2f;
		r = 1.0f;
		g = 1.0f - x;
		b = 0.0f;
	} else if (x >= 0.8f && x <= 1.0f) {
		x = (x - 0.8f) / 0.2f;
		r = 1.0f;
		g = 0.0f;
		b = x;
	}
	return new Color(r, g, b);
}

258voto

Ohad Schneider Points 10485

Cette question apparaît dans un certain nombre de discussions:

Différentes solutions sont proposées, mais aucune n'est optimal. Heureusement, la science vient à la rescousse

Arbitraire N

Les 2 derniers seront gratuits sur la plupart des bibliothèques universitaires et des mandataires.

N est fini et relativement petite

Dans ce cas, on pourrait aller pour une liste de solution. Un article très intéressant dans le sujet est librement disponible:

Il y a plusieurs listes de couleur à prendre en compte:

  • Boynton la liste des 11 couleurs qui ne sont presque jamais confondu (disponible dans le premier article de la section précédente)
  • Kelly 22 les couleurs de contraste maximum (disponible dans le document ci-dessus)

J'ai aussi couru dans cette Palette par un élève de MIT. Enfin, Les liens suivants peuvent être utiles dans la conversion entre les différents systèmes de couleurs / coordonnées (certaines couleurs dans les articles ne sont pas spécifiés dans le modèle RVB, par exemple):

Pour Kelly et Boynton la liste, j'ai déjà fait la conversion en RVB. Un code C#:

public static ReadOnlyCollection<Color> KellysMaxContrastSet
{
    get { return _kellysMaxContrastSet.AsReadOnly(); }
}

private static readonly List<Color> _kellysMaxContrastSet = new List<Color>
{
    UIntToColor(0xFFFFB300), //Vivid Yellow
    UIntToColor(0xFF803E75), //Strong Purple
    UIntToColor(0xFFFF6800), //Vivid Orange
    UIntToColor(0xFFA6BDD7), //Very Light Blue
    UIntToColor(0xFFC10020), //Vivid Red
    UIntToColor(0xFFCEA262), //Grayish Yellow
    UIntToColor(0xFF817066), //Medium Gray

    //The following will not be good for people with defective color vision
    UIntToColor(0xFF007D34), //Vivid Green
    UIntToColor(0xFFF6768E), //Strong Purplish Pink
    UIntToColor(0xFF00538A), //Strong Blue
    UIntToColor(0xFFFF7A5C), //Strong Yellowish Pink
    UIntToColor(0xFF53377A), //Strong Violet
    UIntToColor(0xFFFF8E00), //Vivid Orange Yellow
    UIntToColor(0xFFB32851), //Strong Purplish Red
    UIntToColor(0xFFF4C800), //Vivid Greenish Yellow
    UIntToColor(0xFF7F180D), //Strong Reddish Brown
    UIntToColor(0xFF93AA00), //Vivid Yellowish Green
    UIntToColor(0xFF593315), //Deep Yellowish Brown
    UIntToColor(0xFFF13A13), //Vivid Reddish Orange
    UIntToColor(0xFF232C16), //Dark Olive Green
};

public static ReadOnlyCollection<Color> BoyntonOptimized
{
    get { return _boyntonOptimized.AsReadOnly(); }
}

private static readonly List<Color> _boyntonOptimized = new List<Color>
{
    Color.FromArgb(0, 0, 255),      //Blue
    Color.FromArgb(255, 0, 0),      //Red
    Color.FromArgb(0, 255, 0),      //Green
    Color.FromArgb(255, 255, 0),    //Yellow
    Color.FromArgb(255, 0, 255),    //Magenta
    Color.FromArgb(255, 128, 128),  //Pink
    Color.FromArgb(128, 128, 128),  //Gray
    Color.FromArgb(128, 0, 0),      //Brown
    Color.FromArgb(255, 128, 0),    //Orange
};

static public Color UIntToColor(uint color)
{
    var a = (byte)(color >> 24);
    var r = (byte)(color >> 16);
    var g = (byte)(color >> 8);
    var b = (byte)(color >> 0);
    return Color.FromArgb(a, r, g, b);
}

85voto

strager Points 41713

Vous pouvez utiliser le modèle de couleur HSL pour créer vos couleurs.

Si tout ce que vous voulez, ce sont des teintes différentes (probables) et de légères variations de luminosité ou de saturation, vous pouvez répartir les teintes comme suit:

 // assumes hue [0, 360), saturation [0, 100), lightness [0, 100)

for(i = 0; i < 360; i += 360 / num_colors) {
    HSLColor c;
    c.hue = i;
    c.saturation = 90 + randf() * 10;
    c.lightness = 50 + randf() * 10;

    addColor(c);
}
 

42voto

Janus Troelsen Points 5121

Comme Uri Cohen répond, mais est un générateur à la place. Commencera par utiliser des couleurs très éloignées. Déterministe

Échantillon, couleurs laissées en premier: échantillon

 #!/usr/bin/env python3.3
import colorsys
import itertools
from fractions import Fraction

def zenos_dichotomy():
    """
    http://en.wikipedia.org/wiki/1/2_%2B_1/4_%2B_1/8_%2B_1/16_%2B_%C2%B7_%C2%B7_%C2%B7
    """
    for k in itertools.count():
        yield Fraction(1,2**k)

def getfracs():
    """
    [Fraction(0, 1), Fraction(1, 2), Fraction(1, 4), Fraction(3, 4), Fraction(1, 8), Fraction(3, 8), Fraction(5, 8), Fraction(7, 8), Fraction(1, 16), Fraction(3, 16), ...]
    [0.0, 0.5, 0.25, 0.75, 0.125, 0.375, 0.625, 0.875, 0.0625, 0.1875, ...]
    """
    yield 0
    for k in zenos_dichotomy():
        i = k.denominator # [1,2,4,8,16,...]
        for j in range(1,i,2):
            yield Fraction(j,i)

bias = lambda x: (math.sqrt(x/3)/Fraction(2,3)+Fraction(1,3))/Fraction(6,5) # can be used for the v in hsv to map linear values 0..1 to something that looks equidistant

def genhsv(h):
    for s in [Fraction(6,10)]: # optionally use range
        for v in [Fraction(8,10),Fraction(5,10)]: # could use range too
            yield (h, s, v) # use bias for v here if you use range

genrgb = lambda x: colorsys.hsv_to_rgb(*x)

flatten = itertools.chain.from_iterable

gethsvs = lambda: flatten(map(genhsv,getfracs()))

getrgbs = lambda: map(genrgb, gethsvs())

def genhtml(x):
    uint8tuple = map(lambda y: int(y*255), x)
    return "rgb({},{},{})".format(*uint8tuple)

gethtmlcolors = lambda: map(genhtml, getrgbs())

if __name__ == "__main__":
    print(list(itertools.islice(gethtmlcolors(), 100)))
 

35voto

Uri Cohen Points 1417

Pour les générations à venir, j'ajoute ici la réponse acceptée en Python.

 import numpy as np
import colorsys

def _get_colors(num_colors):
    colors=[]
    for i in np.arange(0., 360., 360. / num_colors):
        hue = i/360.
        lightness = (50 + np.random.rand() * 10)/100.
        saturation = (90 + np.random.rand() * 10)/100.
        colors.append(colorsys.hls_to_rgb(hue, lightness, saturation))
    return colors
 

33voto

Rocketmagnet Points 1660

Voici une idée. Imaginez un HSV cylindre

Définir les limites supérieure et inférieure que vous voulez pour la Luminosité et la Saturation. Ceci définit une section carrée de l'anneau à l'intérieur de l'espace.

Maintenant, disperser les N points au hasard au sein de cet espace.

Puis appliquer un processus itératif de répulsion de l'algorithme sur eux, soit pour un nombre fixe d'itérations, ou jusqu'à ce que les points se stabiliser.

Maintenant, vous devriez avoir N points représentant les N couleurs qui sont aussi différentes que possible au sein de l'espace de couleur qui vous intéresse.

Hugo

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X