it-swarm.com.de

Wie man automatisch N "verschiedene" Farben erzeugt?

Ich habe die beiden folgenden Methoden geschrieben, um automatisch N verschiedene Farben auszuwählen. Es funktioniert, indem eine stückweise lineare Funktion für den RGB-Würfel definiert wird. Der Vorteil davon ist, dass Sie auch eine progressive Skala erhalten können, wenn Sie dies möchten, aber wenn N groß wird, können die Farben beginnen, ähnlich auszusehen. Ich kann mir auch vorstellen, den RGB-Würfel gleichmäßig in ein Gitter zu unterteilen und dann Punkte zu zeichnen. Kennt jemand andere Methoden? Ich schließe es aus, eine Liste zu definieren und sie dann einfach durchzugehen. Ich sollte auch sagen, dass es mir im Allgemeinen egal ist, ob sie zusammenstoßen oder nicht schön aussehen, sie müssen nur optisch eindeutig sein.

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

Sie können das HSL-Farbmodell verwenden, um Ihre Farben zu erstellen.

Wenn Sie nur unterschiedliche Farbtöne (wahrscheinlich) und geringfügige Abweichungen in Bezug auf Helligkeit oder Sättigung wünschen, können Sie die Farbtöne folgendermaßen verteilen:

// 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);
}
75
strager

Wie die Antwort von Uri Cohen, aber stattdessen ein Generator. Beginnt mit Farben, die weit voneinander entfernt sind. Deterministisch.

Probe, linke Farben zuerst: sample

#!/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 __== "__main__":
    print(list(itertools.islice(gethtmlcolors(), 100)))
36
Janus Troelsen

Hier ist eine Idee. Stellen Sie sich einen HSV-Zylinder vor

Definieren Sie die gewünschten Ober- und Untergrenzen für Helligkeit und Sättigung. Dies definiert einen Ring mit quadratischem Querschnitt innerhalb des Raums.

Nun streuen Sie N Punkte zufällig in diesem Raum.

Wenden Sie dann einen iterativen Abstoßungsalgorithmus an, entweder für eine feste Anzahl von Iterationen oder bis sich die Punkte stabilisieren.

Jetzt sollten Sie N Punkte haben, die N Farben darstellen, die innerhalb des gewünschten Farbraums so unterschiedlich wie möglich sind.

Hugo

33
Rocketmagnet

Im Interesse der kommenden Generationen füge ich hier die akzeptierte Antwort in Python hinzu.

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
28
Uri Cohen

Jeder scheint die Existenz des sehr nützlichen YUV-Farbraums verpasst zu haben, der zur Darstellung der wahrgenommenen Farbunterschiede im menschlichen visuellen System entwickelt wurde. Abstände in YUV repräsentieren Unterschiede in der menschlichen Wahrnehmung. Ich brauchte diese Funktionalität für MagicCube4D, das 4-dimensionale Rubik's Cubes und eine unbegrenzte Anzahl anderer 4D-Puzzles mit beliebig vielen Gesichtern implementiert.

Meine Lösung beginnt mit der Auswahl von zufälligen Punkten in YUV und der iterativen Aufteilung der nächsten zwei Punkte und der Konvertierung in RGB, wenn das Ergebnis zurückgegeben wird. Die Methode ist O (n ^ 3), aber das spielt keine Rolle für kleine Zahlen oder solche, die zwischengespeichert werden können. Es kann sicherlich effizienter gestaltet werden, aber die Ergebnisse scheinen ausgezeichnet zu sein.

Die Funktion ermöglicht die optionale Angabe von Helligkeitsschwellen, um keine Farben zu erzeugen, bei denen keine Komponente heller oder dunkler als die angegebenen Mengen ist. IE Vielleicht möchten Sie keine Werte in der Nähe von Schwarz oder Weiß. Dies ist nützlich, wenn die resultierenden Farben als Grundfarben verwendet werden, die später durch Beleuchtung, Schichtung, Transparenz usw. schattiert werden und noch müssen unterscheiden sich von ihren Grundfarben.

import Java.awt.Color;
import Java.util.Random;

/**
 * Contains a method to generate N visually distinct colors and helper methods.
 * 
 * @author Melinda Green
 */
public class ColorUtils {
    private ColorUtils() {} // To disallow instantiation.
    private final static float
        U_OFF = .436f,
        V_OFF = .615f;
    private static final long Rand_SEED = 0;
    private static Random Rand = new Random(Rand_SEED);    

    /*
     * Returns an array of ncolors RGB triplets such that each is as unique from the rest as possible
     * and each color has at least one component greater than minComponent and one less than maxComponent.
     * Use min == 1 and max == 0 to include the full RGB color range.
     * 
     * Warning: O N^2 algorithm blows up fast for more than 100 colors.
     */
    public static Color[] generateVisuallyDistinctColors(int ncolors, float minComponent, float maxComponent) {
        Rand.setSeed(Rand_SEED); // So that we get consistent results for each combination of inputs

        float[][] yuv = new float[ncolors][3];

        // initialize array with random colors
        for(int got = 0; got < ncolors;) {
            System.arraycopy(randYUVinRGBRange(minComponent, maxComponent), 0, yuv[got++], 0, 3);
        }
        // continually break up the worst-fit color pair until we get tired of searching
        for(int c = 0; c < ncolors * 1000; c++) {
            float worst = 8888;
            int worstID = 0;
            for(int i = 1; i < yuv.length; i++) {
                for(int j = 0; j < i; j++) {
                    float dist = sqrdist(yuv[i], yuv[j]);
                    if(dist < worst) {
                        worst = dist;
                        worstID = i;
                    }
                }
            }
            float[] best = randYUVBetterThan(worst, minComponent, maxComponent, yuv);
            if(best == null)
                break;
            else
                yuv[worstID] = best;
        }

        Color[] rgbs = new Color[yuv.length];
        for(int i = 0; i < yuv.length; i++) {
            float[] rgb = new float[3];
            yuv2rgb(yuv[i][0], yuv[i][1], yuv[i][2], rgb);
            rgbs[i] = new Color(rgb[0], rgb[1], rgb[2]);
            //System.out.println(rgb[i][0] + "\t" + rgb[i][1] + "\t" + rgb[i][2]);
        }

        return rgbs;
    }

    public static void hsv2rgb(float h, float s, float v, float[] rgb) {
        // H is given on [0->6] or -1. S and V are given on [0->1]. 
        // RGB are each returned on [0->1]. 
        float m, n, f;
        int i;

        float[] hsv = new float[3];

        hsv[0] = h;
        hsv[1] = s;
        hsv[2] = v;
        System.out.println("H: " + h + " S: " + s + " V:" + v);
        if(hsv[0] == -1) {
            rgb[0] = rgb[1] = rgb[2] = hsv[2];
            return;
        }
        i = (int) (Math.floor(hsv[0]));
        f = hsv[0] - i;
        if(i % 2 == 0)
            f = 1 - f; // if i is even 
        m = hsv[2] * (1 - hsv[1]);
        n = hsv[2] * (1 - hsv[1] * f);
        switch(i) {
            case 6:
            case 0:
                rgb[0] = hsv[2];
                rgb[1] = n;
                rgb[2] = m;
                break;
            case 1:
                rgb[0] = n;
                rgb[1] = hsv[2];
                rgb[2] = m;
                break;
            case 2:
                rgb[0] = m;
                rgb[1] = hsv[2];
                rgb[2] = n;
                break;
            case 3:
                rgb[0] = m;
                rgb[1] = n;
                rgb[2] = hsv[2];
                break;
            case 4:
                rgb[0] = n;
                rgb[1] = m;
                rgb[2] = hsv[2];
                break;
            case 5:
                rgb[0] = hsv[2];
                rgb[1] = m;
                rgb[2] = n;
                break;
        }
    }


    // From http://en.wikipedia.org/wiki/YUV#Mathematical_derivations_and_formulas
    public static void yuv2rgb(float y, float u, float v, float[] rgb) {
        rgb[0] = 1 * y + 0 * u + 1.13983f * v;
        rgb[1] = 1 * y + -.39465f * u + -.58060f * v;
        rgb[2] = 1 * y + 2.03211f * u + 0 * v;
    }

    public static void rgb2yuv(float r, float g, float b, float[] yuv) {
        yuv[0] = .299f * r + .587f * g + .114f * b;
        yuv[1] = -.14713f * r + -.28886f * g + .436f * b;
        yuv[2] = .615f * r + -.51499f * g + -.10001f * b;
    }

    private static float[] randYUVinRGBRange(float minComponent, float maxComponent) {
        while(true) {
            float y = Rand.nextFloat(); // * YFRAC + 1-YFRAC);
            float u = Rand.nextFloat() * 2 * U_OFF - U_OFF;
            float v = Rand.nextFloat() * 2 * V_OFF - V_OFF;
            float[] rgb = new float[3];
            yuv2rgb(y, u, v, rgb);
            float r = rgb[0], g = rgb[1], b = rgb[2];
            if(0 <= r && r <= 1 &&
                0 <= g && g <= 1 &&
                0 <= b && b <= 1 &&
                (r > minComponent || g > minComponent || b > minComponent) && // don't want all dark components
                (r < maxComponent || g < maxComponent || b < maxComponent)) // don't want all light components

                return new float[]{y, u, v};
        }
    }

    private static float sqrdist(float[] a, float[] b) {
        float sum = 0;
        for(int i = 0; i < a.length; i++) {
            float diff = a[i] - b[i];
            sum += diff * diff;
        }
        return sum;
    }

    private static double worstFit(Color[] colors) {
        float worst = 8888;
        float[] a = new float[3], b = new float[3];
        for(int i = 1; i < colors.length; i++) {
            colors[i].getColorComponents(a);
            for(int j = 0; j < i; j++) {
                colors[j].getColorComponents(b);
                float dist = sqrdist(a, b);
                if(dist < worst) {
                    worst = dist;
                }
            }
        }
        return Math.sqrt(worst);
    }

    private static float[] randYUVBetterThan(float bestDistSqrd, float minComponent, float maxComponent, float[][] in) {
        for(int attempt = 1; attempt < 100 * in.length; attempt++) {
            float[] candidate = randYUVinRGBRange(minComponent, maxComponent);
            boolean good = true;
            for(int i = 0; i < in.length; i++)
                if(sqrdist(candidate, in[i]) < bestDistSqrd)
                    good = false;
            if(good)
                return candidate;
        }
        return null; // after a bunch of passes, couldn't find a candidate that beat the best.
    }


    /**
     * Simple example program.
     */
    public static void main(String[] args) {
        final int ncolors = 10;
        Color[] colors = generateVisuallyDistinctColors(ncolors, .8f, .3f);
        for(int i = 0; i < colors.length; i++) {
            System.out.println(colors[i].toString());
        }
        System.out.println("Worst fit color = " + worstFit(colors));
    }

}
17
Melinda Green

Hier ist eine Lösung, um Ihr "eindeutiges" Problem zu lösen, das völlig übergangen ist:

Erstellen Sie eine Einheitskugel und lassen Sie Punkte mit abstoßenden Ladungen darauf fallen. Führen Sie ein Partikelsystem aus, bis es sich nicht mehr bewegt (oder das Delta "klein genug" ist). Zu diesem Zeitpunkt ist jeder der Punkte so weit wie möglich voneinander entfernt. Konvertiere (x, y, z) in rgb.

Ich erwähne es, weil für bestimmte Problemklassen diese Art von Lösung besser funktioniert als rohe Gewalt.

Ich habe ursprünglich diesen Ansatz hier gesehen, um eine Kugel zu tesselieren.

Auch hier funktionieren die offensichtlichsten Lösungen zum Durchqueren des HSL-Raums oder des RGB-Raums wahrscheinlich einwandfrei.

5
plinth

Das HSL-Farbmodell ist zwar gut zum "Sortieren" von Farben geeignet, aber wenn Sie nach visuell unterschiedlichen Farben suchen, benötigen Sie definitiv Lab Farbmodell.

CIELAB wurde so konzipiert, dass es in Bezug auf das menschliche Farbsehen wahrnehmungsgleich ist, was bedeutet, dass das gleiche Ausmaß der numerischen Änderung dieser Werte etwa dem gleichen Ausmaß der visuell wahrgenommenen Änderung entspricht.

Sobald Sie das wissen, ist das Finden der optimalen Teilmenge von N Farben aus einem breiten Farbspektrum immer noch ein (NP) schwieriges Problem, ähnlich dem Travelling-Salesman-Problem und allen Lösungen unter Verwendung von k-mean Algorithmen oder so etwas werden nicht wirklich helfen.

Das heißt, wenn N nicht zu groß ist und Sie mit einer begrenzten Menge von Farben beginnen, werden Sie leicht eine sehr gute Teilmenge von eindeutigen Farben gemäß einer Lab-Distanz mit einer einfachen Zufallsfunktion finden.

Ich habe ein solches Tool für meinen eigenen Gebrauch programmiert (Sie finden es hier: https://mokole.com/palette.html ), hier ist, was ich für N = 7 bekommen habe: - enter image description here

Es ist alles Javascript, schauen Sie sich also die Quelle der Seite an und passen Sie sie an Ihre eigenen Bedürfnisse an.

3
fbparis

Ich würde versuchen, Sättigung und Beleuchtung auf Maximum zu bringen und mich nur auf den Farbton zu konzentrieren. Wie ich es sehe, kann H von 0 auf 255 gehen und dann umbrechen. Wenn Sie nun zwei kontrastierende Farben wünschen, würden Sie die gegenüberliegenden Seiten dieses Rings nehmen, d. H. 0 und 128. Wenn Sie 4 Farben wünschen, würden Sie einige durch 1/4 der 256 Länge des Kreises trennen, d. H. 0, 64, 128, 192. Und natürlich, wie andere vorgeschlagen haben, wenn Sie N Farben benötigen, können Sie sie einfach durch 256/N trennen.

Was ich dieser Idee hinzufügen möchte, ist, eine umgekehrte Darstellung einer Binärzahl zu verwenden, um diese Sequenz zu bilden. Schau dir das an:

0 = 00000000  after reversal is 00000000 = 0
1 = 00000001  after reversal is 10000000 = 128
2 = 00000010  after reversal is 01000000 = 64
3 = 00000011  after reversal is 11000000 = 192

... auf diese Weise können Sie, wenn Sie N verschiedene Farben benötigen, einfach die ersten N Zahlen nehmen, sie umkehren und so viele entfernte Punkte wie möglich erhalten (wobei N die Zweierpotenz ist), während Sie gleichzeitig das jeweilige Präfix des Die Reihenfolge ist sehr unterschiedlich.

Dies war ein wichtiges Ziel in meinem Anwendungsfall, da ich ein Diagramm hatte, in dem die Farben nach dem von dieser Farbe abgedeckten Bereich sortiert waren. Ich wollte, dass die größten Bereiche des Diagramms einen großen Kontrast haben, und ich war damit einverstanden, dass einige kleine Bereiche ähnliche Farben wie die Top-10-Bereiche aufweisen, da es für den Leser offensichtlich war, welcher Bereich welcher ist, indem ich nur den Bereich betrachte.

3
qbolec

Ich denke, dieser einfache rekursive Algorithmus ergänzt die akzeptierte Antwort, um eindeutige Farbtonwerte zu generieren. Ich habe es für hsv gemacht, kann aber auch für andere Farbräume verwendet werden.

Sie erzeugt Farbtöne in Zyklen, die in jedem Zyklus so weit wie möglich voneinander entfernt sind.

/**
 * 1st cycle: 0, 120, 240
 * 2nd cycle (+60): 60, 180, 300
 * 3th cycle (+30): 30, 150, 270, 90, 210, 330
 * 4th cycle (+15): 15, 135, 255, 75, 195, 315, 45, 165, 285, 105, 225, 345
 */
public static float recursiveHue(int n) {
    // if 3: alternates red, green, blue variations
    float firstCycle = 3;

    // First cycle
    if (n < firstCycle) {
        return n * 360f / firstCycle;
    }
    // Each cycle has as much values as all previous cycles summed (powers of 2)
    else {
        // floor of log base 2
        int numCycles = (int)Math.floor(Math.log(n / firstCycle) / Math.log(2));
        // divDown stores the larger power of 2 that is still lower than n
        int divDown = (int)(firstCycle * Math.pow(2, numCycles));
        // same hues than previous cycle, but summing an offset (half than previous cycle)
        return recursiveHue(n % divDown) + 180f / divDown;
    }
}

Ich konnte diesen Algorithmus hier nicht finden. Ich hoffe es hilft, es ist mein erster Beitrag hier.

1
David Fernandez

Ich habe ein Paket für R namens qualpalr geschrieben, das speziell für diesen Zweck entwickelt wurde. Ich empfehle, dass Sie sich die Vignette ansehen, um herauszufinden, wie es funktioniert, aber ich werde versuchen, die wichtigsten Punkte zusammenzufassen.

qualpalr nimmt eine Spezifikation von Farben in den HSL-Farbraum (der zuvor in diesem Thread beschrieben wurde), projiziert sie in den (wahrnehmungsmäßig einheitlichen) DIN99d-Farbraum und findet den n das maximiert den minimalen Abstand zwischen irgendwelchen von ihnen.

# Create a palette of 4 colors of hues from 0 to 360, saturations between
# 0.1 and 0.5, and lightness from 0.6 to 0.85
pal <- qualpal(n = 4, list(h = c(0, 360), s = c(0.1, 0.5), l = c(0.6, 0.85)))

# Look at the colors in hex format
pal$hex
#> [1] "#6F75CE" "#CC6B76" "#CAC16A" "#76D0D0"

# Create a palette using one of the predefined color subspaces
pal2 <- qualpal(n = 4, colorspace = "pretty")

# Distance matrix of the DIN99d color differences
pal2$de_DIN99d
#>        #69A3CC #6ECC6E #CA6BC4
#> 6ECC6E      22                
#> CA6BC4      21      30        
#> CD976B      24      21      21

plot(pal2)

enter image description here

1
Johan Larsson

Wenn N groß genug ist, erhalten Sie einige ähnlich aussehende Farben. Es gibt nur so viele von ihnen auf der Welt.

Warum verteilen Sie sie nicht einfach gleichmäßig über das Spektrum?

IEnumerable<Color> CreateUniqueColors(int nColors)
{
    int subdivision = (int)Math.Floor(Math.Pow(nColors, 1/3d));
    for(int r = 0; r < 255; r += subdivision)
        for(int g = 0; g < 255; g += subdivision)
            for(int b = 0; b < 255; b += subdivision)
                yield return Color.FromArgb(r, g, b);
}

Wenn Sie die Sequenz so mischen möchten, dass ähnliche Farben nicht nebeneinander stehen, können Sie die resultierende Liste möglicherweise mischen.

Denke ich das nicht?

1
mqp

Dies ist in MATLAB trivial (es gibt einen hsv-Befehl):

cmap = hsv(number_of_colors)
1
Arturo