Wie generiere ich automatisch N "unterschiedliche" Farben?


194

Ich habe die beiden folgenden Methoden geschrieben, um automatisch N verschiedene Farben auszuwählen. Es funktioniert durch Definieren einer stückweise linearen Funktion auf dem RGB-Würfel. Dies hat den Vorteil, dass Sie auch eine progressive Skala erhalten können, wenn Sie dies wünschen. Wenn N jedoch groß wird, können die Farben ähnlich aussehen. 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 gut aussehen, sie müssen nur visuell unterschiedlich 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);
}

4
Stark relevante Frage der Programmierer mit interessanten Antworten: " Erzeugung von Farbschemata - Theorie und Algorithmen ."
Alexey Popkov

2
Die menschliche Farbwahrnehmung ist leider nicht linear. Möglicherweise müssen Sie auch die Bezold-Brücke-Verschiebung berücksichtigen, wenn Sie unterschiedliche Intensitäten verwenden. Es gibt auch gute Informationen hier: vis4.net/blog/posts/avoid-equidistant-hsv-colors
spex

Antworten:


80

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

2
Diese Technik ist klug. Ich wette, es wird ästhetischere Ergebnisse erzielen als meine.
mqp

45
Dies setzt voraus, dass gleich beabstandete Farbtonwerte wahrnehmungsmäßig unterschiedlich unterschiedlich sind. Selbst wenn verschiedene Formen der Farbenblindheit nicht berücksichtigt werden, gilt dies für die meisten Menschen nicht: Der Unterschied zwischen 120 ° (grün) und 135 ° (sehr leicht mintgrün) ist nicht wahrnehmbar, während der Unterschied zwischen 30 ° (orange) und 45 ° (Pfirsich) nicht erkennbar ist. ist ganz offensichtlich. Für beste Ergebnisse benötigen Sie einen nichtlinearen Abstand entlang des Farbtons.
Phrogz

18
@mquander - Es ist überhaupt nicht klug. Nichts hindert diesen Algorithmus daran, versehentlich zwei nahezu identische Farben auszuwählen. Meine Antwort ist besser und die Antwort von ohadsc ist viel besser.
Raketenmagnet

1
Dies ist aus den bereits genannten Gründen falsch, aber auch, weil Sie nicht einheitlich auswählen .
Sam Hocevar

3
@strager was ist der erwartete Wert von randf ()
Killrawr

242

Diese Frage taucht in einigen SO-Diskussionen auf:

Es werden verschiedene Lösungen vorgeschlagen, aber keine ist optimal. Zum Glück kommt die Wissenschaft zur Rettung

Willkürlich N.

Die letzten 2 sind über die meisten Universitätsbibliotheken / Proxies kostenlos.

N ist endlich und relativ klein

In diesem Fall könnte man sich für eine Listenlösung entscheiden. Ein sehr interessanter Artikel zu diesem Thema ist frei verfügbar:

Es sind mehrere Farblisten zu berücksichtigen:

  • Boyntons Liste von 11 Farben, die fast nie verwechselt werden (verfügbar im ersten Artikel des vorherigen Abschnitts)
  • Kellys 22 Farben mit maximalem Kontrast (verfügbar im obigen Papier)

Ich bin auch auf diese Palette von einem MIT-Studenten gestoßen. Schließlich können die folgenden Links beim Konvertieren zwischen verschiedenen Farbsystemen / -koordinaten hilfreich sein (einige Farben in den Artikeln sind beispielsweise nicht in RGB angegeben):

Für Kellys und Boyntons Liste habe ich bereits die Konvertierung in RGB vorgenommen (mit Ausnahme von Weiß und Schwarz, was offensichtlich sein sollte). Etwas C # -Code:

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

Und hier sind die RGB-Werte in Hex- und 8-Bit-pro-Kanal-Darstellungen:

kelly_colors_hex = [
    0xFFB300, # Vivid Yellow
    0x803E75, # Strong Purple
    0xFF6800, # Vivid Orange
    0xA6BDD7, # Very Light Blue
    0xC10020, # Vivid Red
    0xCEA262, # Grayish Yellow
    0x817066, # Medium Gray

    # The following don't work well for people with defective color vision
    0x007D34, # Vivid Green
    0xF6768E, # Strong Purplish Pink
    0x00538A, # Strong Blue
    0xFF7A5C, # Strong Yellowish Pink
    0x53377A, # Strong Violet
    0xFF8E00, # Vivid Orange Yellow
    0xB32851, # Strong Purplish Red
    0xF4C800, # Vivid Greenish Yellow
    0x7F180D, # Strong Reddish Brown
    0x93AA00, # Vivid Yellowish Green
    0x593315, # Deep Yellowish Brown
    0xF13A13, # Vivid Reddish Orange
    0x232C16, # Dark Olive Green
    ]

kelly_colors = dict(vivid_yellow=(255, 179, 0),
                    strong_purple=(128, 62, 117),
                    vivid_orange=(255, 104, 0),
                    very_light_blue=(166, 189, 215),
                    vivid_red=(193, 0, 32),
                    grayish_yellow=(206, 162, 98),
                    medium_gray=(129, 112, 102),

                    # these aren't good for people with defective color vision:
                    vivid_green=(0, 125, 52),
                    strong_purplish_pink=(246, 118, 142),
                    strong_blue=(0, 83, 138),
                    strong_yellowish_pink=(255, 122, 92),
                    strong_violet=(83, 55, 122),
                    vivid_orange_yellow=(255, 142, 0),
                    strong_purplish_red=(179, 40, 81),
                    vivid_greenish_yellow=(244, 200, 0),
                    strong_reddish_brown=(127, 24, 13),
                    vivid_yellowish_green=(147, 170, 0),
                    deep_yellowish_brown=(89, 51, 21),
                    vivid_reddish_orange=(241, 58, 19),
                    dark_olive_green=(35, 44, 22))

Für alle Java-Entwickler sind hier die JavaFX-Farben:

// Don't forget to import javafx.scene.paint.Color;

private static final Color[] KELLY_COLORS = {
    Color.web("0xFFB300"),    // Vivid Yellow
    Color.web("0x803E75"),    // Strong Purple
    Color.web("0xFF6800"),    // Vivid Orange
    Color.web("0xA6BDD7"),    // Very Light Blue
    Color.web("0xC10020"),    // Vivid Red
    Color.web("0xCEA262"),    // Grayish Yellow
    Color.web("0x817066"),    // Medium Gray

    Color.web("0x007D34"),    // Vivid Green
    Color.web("0xF6768E"),    // Strong Purplish Pink
    Color.web("0x00538A"),    // Strong Blue
    Color.web("0xFF7A5C"),    // Strong Yellowish Pink
    Color.web("0x53377A"),    // Strong Violet
    Color.web("0xFF8E00"),    // Vivid Orange Yellow
    Color.web("0xB32851"),    // Strong Purplish Red
    Color.web("0xF4C800"),    // Vivid Greenish Yellow
    Color.web("0x7F180D"),    // Strong Reddish Brown
    Color.web("0x93AA00"),    // Vivid Yellowish Green
    Color.web("0x593315"),    // Deep Yellowish Brown
    Color.web("0xF13A13"),    // Vivid Reddish Orange
    Color.web("0x232C16"),    // Dark Olive Green
};

Das Folgende sind die unsortierten Kellyfarben gemäß der obigen Reihenfolge.

unsortierte Kellyfarben

Das Folgende sind die sortierten Kellyfarben nach Farbtönen (beachten Sie, dass einige Gelbtöne nicht sehr kontrastreich sind).

 sortierte Kellyfarben


+1 Vielen Dank für diese tolle Antwort! Übrigens , der Link colour-journal.org/2010/5/10 ist tot. Dieser Artikel ist weiterhin unter web.archive.org verfügbar .
Alexey Popkov


16
Tolle Antwort, danke! Ich habe mir erlaubt,
David Mills

1
Ich habe gerade bemerkt, dass diese Listen nur 20 bzw. 9 Farben enthalten. Ich vermute, es liegt daran, dass Weiß und Schwarz weggelassen werden.
David Mills

2
Ist der Webservice schon verfügbar?
Janus Troelsen

38

Wie Uri Cohens Antwort, ist aber stattdessen ein Generator. Beginnen Sie mit Farben, die weit voneinander entfernt sind. Deterministisch.

Beispiel, linke Farben zuerst: Stichprobe

#!/usr/bin/env python3.5
from typing import Iterable, Tuple
import colorsys
import itertools
from fractions import Fraction
from pprint import pprint

def zenos_dichotomy() -> Iterable[Fraction]:
    """
    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 fracs() -> Iterable[Fraction]:
    """
    [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 Fraction(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)

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

HSVTuple = Tuple[Fraction, Fraction, Fraction]
RGBTuple = Tuple[float, float, float]

def hue_to_tones(h: Fraction) -> Iterable[HSVTuple]:
    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

def hsv_to_rgb(x: HSVTuple) -> RGBTuple:
    return colorsys.hsv_to_rgb(*map(float, x))

flatten = itertools.chain.from_iterable

def hsvs() -> Iterable[HSVTuple]:
    return flatten(map(hue_to_tones, fracs()))

def rgbs() -> Iterable[RGBTuple]:
    return map(hsv_to_rgb, hsvs())

def rgb_to_css(x: RGBTuple) -> str:
    uint8tuple = map(lambda y: int(y*255), x)
    return "rgb({},{},{})".format(*uint8tuple)

def css_colors() -> Iterable[str]:
    return map(rgb_to_css, rgbs())

if __name__ == "__main__":
    # sample 100 colors in css format
    sample_colors = list(itertools.islice(css_colors(), 100))
    pprint(sample_colors)

+1 für das Beispiel, sehr schön und zeigt, dass das Schema auch attraktiv ist. Die anderen Antworten hier würden durch dasselbe verbessert und könnten dann leicht verglichen werden.
Don Hatch

3
Die Menge an Lambdas ist verdammt hoch. Das Lambda ist stark mit diesem.
Gyfis

Sieht gut aus, bleibt aber hängen, wenn ich versuche, es auf 2.7
Elad Weiss

33

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

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

Streuen Sie nun N Punkte zufällig in diesen Raum.

Wenden Sie dann einen iterativen Abstoßungsalgorithmus auf sie 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


30

Für die 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

18

Jeder scheint die Existenz des sehr nützlichen YUV-Farbraums übersehen zu haben, der entworfen wurde, um wahrgenommene Farbunterschiede im menschlichen visuellen System darzustellen. Entfernungen in YUV repräsentieren Unterschiede in der menschlichen Wahrnehmung. Ich brauchte diese Funktionalität für MagicCube4D, das 4-dimensionale Rubik-Würfel und eine unbegrenzte Anzahl anderer 4D-Twisty-Puzzles mit einer beliebigen Anzahl von Gesichtern implementiert.

Meine Lösung beginnt damit, zufällige Punkte in YUV auszuwählen und dann die nächsten zwei Punkte iterativ aufzubrechen und erst dann in RGB zu konvertieren, 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 gemacht werden, aber die Ergebnisse scheinen ausgezeichnet zu sein.

Die Funktion ermöglicht die optionale Angabe von Helligkeitsschwellenwerten, um keine Farben zu erzeugen, bei denen keine Komponente heller oder dunkler als die angegebenen Mengen ist. IE Sie möchten möglicherweise 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, Überlagerung, Transparenz usw. schattiert werden und sich dennoch von ihren Grundfarben unterscheiden müssen.

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

}

Gibt es irgendwo eine C # -Version dieses Codes? Ich habe versucht, es zu konvertieren und mit denselben Argumenten auszuführen, die Sie an generateVisuallyDistinctColors () übergeben haben, und es scheint sehr langsam zu laufen. Wird das erwartet?
Chris Smith

Erhalten Sie die gleichen Ergebnisse? Es ist schnell genug für meine Bedürfnisse, aber wie gesagt, ich habe nicht versucht, es zu optimieren. Wenn dies Ihr einziges Problem ist, sollten Sie wahrscheinlich auf die Speicherzuweisung / Freigabe achten. Ich weiß nichts über C # -Speicherverwaltung. Im schlimmsten Fall können Sie die Konstante der äußeren Schleife von 1.000 auf einen kleineren Wert reduzieren, und der Qualitätsunterschied ist möglicherweise nicht einmal spürbar.
Melinda Green

1
Meine Palette muss bestimmte Farben enthalten, aber ich wollte die Extras ausfüllen. Ich mag Ihre Methode b / c Ich kann meine erforderlichen Farben zuerst in Ihr yuv-Array einfügen und dann "j = 0" ändern, um nach meinen erforderlichen Farben mit der Optimierung zu beginnen. Ich wünschte, die Trennung der schlimmsten Paare wäre etwas klüger, aber ich kann verstehen, warum das schwierig ist.
Ryan

Ich denke, Ihrer yuv2rgb-Methode fehlt die Klemme (0,255).
Ryan

yuv2rgb ist alles Floats, nicht Bytes Ryan. Bitte schreiben Sie an melinda@superliminal.com, um dies zu besprechen.
Melinda Green

6

Das HSL-Farbmodell eignet sich möglicherweise gut zum "Sortieren" von Farben. Wenn Sie jedoch nach visuell unterschiedlichen Farben suchen, benötigen Sie definitiv das Lab- Farbmodell.

CIELAB wurde so konzipiert, dass es in Bezug auf das menschliche Farbsehen wahrnehmungsmäßig einheitlich ist, was bedeutet, dass das gleiche Ausmaß an numerischen Änderungen dieser Werte ungefähr dem gleichen Ausmaß an visuell wahrgenommenen Veränderungen entspricht.

Sobald Sie wissen, dass das Finden der optimalen Teilmenge von N Farben aus einer Vielzahl von Farben immer noch ein (NP) schwieriges Problem ist, das dem Problem des reisenden Verkäufers ähnelt und alle Lösungen, die k-Mittelwert-Algorithmen verwenden, oder etwas anderes nicht wirklich Hilfe.

Das heißt, wenn N nicht zu groß ist und Sie mit einem begrenzten Satz von Farben beginnen, finden Sie leicht eine sehr gute Teilmenge von unterschiedlichen Farben entsprechend einer Laborentfernung mit einer einfachen Zufallsfunktion.

Ich habe ein solches Tool für meinen eigenen Gebrauch codiert (Sie finden es hier: https://mokole.com/palette.html ). Folgendes habe ich für N = 7 erhalten: Geben Sie hier die Bildbeschreibung ein

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


1
In Bezug auf » gleiche Menge an numerischen Änderungen [...] gleiche Menge an visuell wahrgenommenen Veränderungen «. Ich habe mit einem CIE Lab-Farbwähler herumgespielt und konnte dies überhaupt nicht bestätigen. Ich werde lab Farben bezeichnen die Bereiche mit L0-128 und aund bvon -128 bis 128. ¶ Ich begann mit L= 0, a= -128, b= -128 , die ein helles Blau. Dann habe ich adreimal zugenommen . ❶ Die große Änderung (+128) a= 50 führt zu einem nur geringfügig dunkleren Blau. ❷ (+85) a= 85 ergibt immer noch blau. ❸ Die relativ kleine Änderung (+43) a= 128 ändert jedoch die Farbe vollständig in Fuchsia.
Socowi

Das ist sehr nützlich für mich. Es wäre jedoch ideal, wenn die Ergebnisse leicht kopiert und eingefügt werden könnten.
Mitchell van Zuylen

5

Hier ist eine Lösung zur Verwaltung Ihres "eindeutigen" Problems, die völlig übertrieben ist:

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

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

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

Auch hier funktionieren die naheliegendsten Lösungen zum Durchlaufen von HSL- oder RGB-Speicher wahrscheinlich einwandfrei.


1
Das ist eine gute Idee, aber es ist wahrscheinlich sinnvoll, einen Würfel anstelle einer Kugel zu verwenden.
Raketenmagnet

1
Das ist es, was meine YUV-basierte Lösung tut, außer für eine 3D-Box (kein Würfel).
Melinda Green

3

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

Was ich dieser Idee hinzufügen möchte, ist die Verwendung einer umgekehrten Darstellung einer Binärzahl, 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, diese umkehren und so viele entfernte Punkte wie möglich erhalten (wobei N die Zweierpotenz ist), während Sie gleichzeitig jedes Präfix der beibehalten Reihenfolge unterscheidet sich sehr.

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 haben, da es für den Leser offensichtlich war, welcher welcher ist, indem er nur den Bereich beobachtete.


Dies habe ich in meiner Antwort getan, wenn auch etwas " mathematischer ". Siehe die Funktion getfracs. Aber Ihr Ansatz ist schnell und „einfach“ in Low-Level - Sprachen: Bit in C umgekehrt .
Janus Troelsen


1

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 liegen, können Sie die resultierende Liste möglicherweise mischen.

Denke ich das?


2
Ja, Sie denken darüber nach. Die menschliche Farbwahrnehmung ist leider nicht linear. Möglicherweise müssen Sie auch die Bezold-Brücke-Verschiebung berücksichtigen, wenn Sie unterschiedliche Intensitäten verwenden. Es gibt auch gute Informationen hier: vis4.net/blog/posts/avoid-equidistant-hsv-colors
spex

1

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

cmap = hsv(number_of_colors)

1

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

qualpalr nimmt eine Spezifikation von Farben im HSL-Farbraum (der zuvor in diesem Thread beschrieben wurde), projiziert sie in den DIN99d-Farbraum (der wahrnehmungsmäßig einheitlich ist) und findet die n, die den Mindestabstand zwischen ihnen maximieren.

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

Geben Sie hier die Bildbeschreibung ein


1

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

Es erzeugt Farbtöne in Zyklen, die in jedem Zyklus so getrennt wie möglich voneinander 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 diese Art von Algorithmus hier nicht finden. Ich hoffe es hilft, es ist mein erster Beitrag hier.


0

Diese OpenCV-Funktion verwendet das HSV-Farbmodell, um ngleichmäßig verteilte Farben um 0 <= H <= 360º mit maximal S = 1,0 und V = 1,0 zu erzeugen . Die Funktion gibt die BGR-Farben aus in bgr_mat:

void distributed_colors (int n, cv::Mat_<cv::Vec3f> & bgr_mat) {
  cv::Mat_<cv::Vec3f> hsv_mat(n,CV_32F,cv::Vec3f(0.0,1.0,1.0));
  double step = 360.0/n;
  double h= 0.0;
  cv::Vec3f value;
  for (int i=0;i<n;i++,h+=step) {
    value = hsv_mat.at<cv::Vec3f>(i);
    hsv_mat.at<cv::Vec3f>(i)[0] = h;
  }
  cv::cvtColor(hsv_mat, bgr_mat, CV_HSV2BGR);
  bgr_mat *= 255;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.