N "farklı" renk otomatik olarak nasıl oluşturulur?


194

N farklı rengi otomatik olarak seçmek için aşağıdaki iki yöntemi yazdım. RGB küp üzerinde parçalı bir doğrusal işlev tanımlayarak çalışır. Bunun yararı, istediğiniz şeyse aşamalı bir ölçek elde edebilmenizdir, ancak N büyüdüğünde renkler benzer görünmeye başlayabilir. RGB küpünü bir kafese eşit olarak alt bölümlere ayırmayı ve sonra nokta çizmeyi de hayal edebiliyorum. Başka yöntem bilen var mı? Bir liste tanımlamayı ve sonra sadece bisiklete binmeyi reddediyorum. Ayrıca, genellikle çatışmaları veya hoş görünmemesi umurumda değil, sadece görsel olarak farklı olmaları gerektiğini söylemeliyim.

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

5
Kesinlikle alakalı Programcılar ilginç cevaplarla soru soruyor: " Renk şemaları üretimi - teori ve algoritmalar ."
Alexey Popkov

2
İnsan rengi algısı maalesef doğrusal değildir. Farklı yoğunluklar kullanıyorsanız, Bezold – Brücke vardiyasını da hesaba katmanız gerekebilir. İyi bilgi burada da vardır: vis4.net/blog/posts/avoid-equidistant-hsv-colors
spex

Yanıtlar:


80

Sen kullanabilirsiniz HSL renk modelini renklerini oluşturmak için.

İstediğiniz tek şey farklı tonlar (muhtemelen) ve hafiflik veya doygunlukta küçük değişiklikler varsa, tonları şu şekilde dağıtabilirsiniz:

// 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
Bu teknik akıllıdır. Bahse girerim benden daha estetik sonuçlar alır.
mqp

45
Bu, eşit aralıklı renk tonu değerlerinin eşit olarak algısal olarak farklı olduğunu varsayar. Çeşitli renk körlüğü formlarını indirgemek bile, bu çoğu insan için doğru değildir: 120 ° (yeşil) ve 135 ° (çok hafif nane yeşili) arasındaki fark algılanamazken, 30 ° (turuncu) ve 45 ° (şeftali) arasındaki fark oldukça açıktır. En iyi sonuçlar için ton boyunca doğrusal olmayan bir aralığa ihtiyacınız vardır.
Phrogz

18
@mquander - Hiç akıllı değil. Bu algoritmanın yanlışlıkla neredeyse aynı iki rengi seçmesini engelleyecek hiçbir şey yoktur. Cevabım daha iyi ve ohadsc'in cevabı çok daha iyi.
Rocketmagnet

1
Bu, daha önce belirtilen nedenlerden dolayı yanlıştır, ancak aynı zamanda düzgün bir şekilde seçmediğiniz için .
sam hocevar

3
@strager randf () beklenen değeri nedir
Killrawr

242

Bu sorular birkaç SO tartışmasında yer almaktadır:

Farklı çözümler önerilmiştir, ancak hiçbiri optimal değildir. Neyse ki, bilim kurtarmaya geliyor

Keyfi N

Son 2 üniversite kütüphanesi / vekili aracılığıyla ücretsiz olacaktır.

N sonlu ve nispeten küçük

Bu durumda, bir liste çözümü için gidilebilir. Konuyla ilgili çok ilginç bir makale serbestçe kullanılabilir:

Dikkate alınması gereken birkaç renk listesi vardır:

  • Boynton'un neredeyse hiç karıştırılmamış 11 renk listesi (önceki bölümün ilk makalesinde mevcuttur)
  • Kelly'nin maksimum kontrastlı 22 rengi (yukarıdaki kağıtta mevcut)

Ayrıca bir MIT öğrencisi tarafından bu Palete rastladım . Son olarak, aşağıdaki bağlantılar farklı renk sistemleri / koordinatları arasında dönüştürme işleminde yararlı olabilir (örneğin, makalelerdeki bazı renkler RGB'de belirtilmez):

Kelly ve Boynton'ın listesi için, zaten RGB'ye dönüşümü yaptım (açık olması gereken beyaz ve siyah hariç). Bazı C # kodları:

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

Ve onaltılık ve kanal başına 8 bitlik sunumlardaki RGB değerleri şunlardır:

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

Tüm Java geliştiricileri için JavaFX renkleri:

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

Aşağıdaki sıralamaya göre sıralanmamış kelly renkleri.

sıralanmamış kelly renkleri

tonlara göre sıralanmış kelly renkleri aşağıdadır (bazı sarıların çok zıt olmadığını unutmayın)

 kelly renkleri sıralanmış


+1 Bu harika cevap için çok teşekkür ederim! BTW colour-journal.org/2010/5/10 bağlantısı öldü, bu makaleye hala web.archive.org adresinden ulaşılabilir .
Alexey Popkov


16
Harika cevap, teşekkürler! Bu iki renk setini, renkleri çalışırken görebileceğiniz uygun bir jsfiddle'a
David Mills

1
Sadece bu listelerde sadece 20 ve 9 renk olduğunu fark ettim. Sanırım bunun nedeni beyaz ve siyahın atlanmış olması.
David Mills

2
Web hizmeti henüz mevcut değil mi?
Janus Troelsen

38

Uri Cohen'in cevabı gibi ama onun yerine bir jeneratör. Renkleri çok ayrı kullanarak başlayacak. Deterministik.

Örnek, önce sol renkler: örneklem

#!/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 için örnek, çok güzel ve şemasının da çekici olduğunu gösterir. Buradaki diğer cevaplar aynısını yaparak geliştirilebilir ve daha sonra kolayca karşılaştırılabilir.
Don Hatch

3
Lambda miktarı çok yüksek. Lambda bu konuda güçlü.
Gyfis

Harika görünüyor, ama 2.7'de çalıştırmaya çalıştığımda takılıyor
Elad Weiss

33

İşte bir fikir. Bir HSV silindiri hayal edin

Parlaklık ve Doygunluk için istediğiniz üst ve alt sınırları tanımlayın. Bu, boşluk içindeki kare kesitli bir halkayı tanımlar.

Şimdi, N dağılması bu alana rastgele işaret eder.

Ardından, sabit sayıda yineleme için veya noktalar stabil hale gelinceye kadar üzerlerine tekrarlamalı itme algoritması uygulayın.

Şimdi, ilgilendiğiniz renk uzayında olabildiğince farklı N renklerini temsil eden N noktalarınız olmalıdır.

Hugo


30

Gelecek nesiller uğruna buraya Python'a kabul edilen cevabı ekliyorum.

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

Herkes, insan görsel sisteminde algılanan renk farklılıklarını temsil etmek için tasarlanan çok kullanışlı YUV renk uzayının varlığını kaçırmış gibi görünüyor. YUV'daki mesafeler insan algısındaki farklılıkları temsil eder. Bu işlevselliğe 4 boyutlu Rubik küplerini ve rastgele sayıda yüzü olan diğer 4D bükümlü bulmacaları uygulayan MagicCube4D için ihtiyacım vardı.

Benim çözümüm YUV'da rasgele noktalar seçerek ve ardından en yakın iki noktayı tekrarlayarak ve sadece sonucu döndürürken RGB'ye dönüştürerek başlar. Yöntem O (n ^ 3) şeklindedir, ancak küçük sayıların veya önbelleğe alınabileceklerin önemi yoktur. Kesinlikle daha verimli hale getirilebilir, ancak sonuçlar mükemmel görünüyor.

Fonksiyon, hiçbir bileşenin verilen miktarlardan daha parlak veya daha koyu olmadığı renkler üretmemek için isteğe bağlı parlaklık eşiklerinin belirlenmesine izin verir. IE siyah veya beyaza yakın değerler istemeyebilirsiniz. Bu, ortaya çıkan renkler daha sonra aydınlatma, katmanlama, saydamlık vb. Yoluyla gölgelenen temel renkler olarak kullanıldığında ve yine de temel renklerinden farklı görünmesi gerektiğinde yararlıdır.

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

}

Bu kodun herhangi bir yerinde bir C # sürümü var mı? Dönüştürme denedim ve createVisuallyDistinctColors () için geçti aynı argümanlar ile çalışan ve gerçekten yavaş çalışıyor gibi görünüyor. Bu beklenen mi?
Chris Smith

Aynı sonuçları mı alıyorsunuz? İhtiyaçlarım için çok hızlı ama dediğim gibi, optimize etmeye çalışmadım, bu yüzden tek sorun buysa, muhtemelen bellek ayırma / dağıtmaya dikkat etmelisiniz. C # bellek yönetimi hakkında hiçbir şey bilmiyorum. En kötüsü, 1000 dış döngü sabitini daha küçük bir şeye indirgeyebilirsiniz ve kalite farkı bile fark edilmeyebilir.
Melinda Green

1
Paletim belirli renkler içermeli ama ekstraları doldurmak istedim. Ben yöntem b / c gibi ilk benim yuva dizi gerekli renkleri koyabilir ve sonra gerekli renkler sonra optimize etmeye başlamak için "j = 0" değiştirebilir. En kötü çiftlerin parçalanmasının biraz daha akıllı olmasını dilerdim ama bunun neden zor olduğunu anlayabiliyorum.
Ryan

Bence yuv2rgb yönteminde kelepçe (0,255) eksik.
Ryan

yuv2rgb bayt Ryan değil, tüm yüzer. Tartışmak için lütfen melinda@superliminal.com adresine yazınız.
Melinda Green

7

HSL renk modeli renkleri "ayırmak" için çok uygun olabilir, ancak görsel olarak farklı renkler arıyorsanız bunun yerine Lab renk modeline kesinlikle ihtiyacınız vardır .

CIELAB, insan renk görme açısından algısal olarak aynı olacak şekilde tasarlanmıştır, yani bu değerlerde aynı miktarda sayısal değişiklik, görsel olarak algılanan yaklaşık aynı miktarda değişime karşılık gelir.

Bir kez öğrendikten sonra, geniş bir renk yelpazesinden N renklerin en uygun alt kümesini bulmak hala (NP) zor bir problemdir, bu da Seyahat satıcısı problemine benzer ve k-mean algoritmaları veya bir şey kullanan tüm çözümler Yardım.

Bununla birlikte, N çok büyük değilse ve sınırlı bir renk setiyle başlarsanız, basit bir rastgele fonksiyona sahip bir Laboratuar mesafesine göre çok iyi bir farklı renk alt kümesi bulacaksınız.

Kendi kullanımım için böyle bir araç kodladım (burada bulabilirsiniz: https://mokole.com/palette.html ), işte N = 7 için aldığım şey: resim açıklamasını buraya girin

Hepsi javascript olduğundan, sayfanın kaynağına bir göz atmaktan ve kendi ihtiyaçlarınıza göre uyarlamaktan çekinmeyin.


1
» Aynı miktarda sayısal değişiklik [...] ile aynı miktarda görsel olarak algılanan değişiklik «. Bir CIE Lab renk seçici ile oynadım ve bunu hiç doğrulayamadım. I aralıkları kullanarak laboratuar renkleri göstereceğiz L0 ile 128 ve ave b-128 128 için ¶ ile başladı L, = 0 a= -128, b= parlak mavi olan -128. Sonra aüç kat arttım. Change Büyük değişiklik (+128) a= 50, sadece biraz daha koyu mavi ile sonuçlanır. ❷ (+85) a= 85 sonuç hala mavi. ❸ Ancak, nispeten küçük değişiklik (+43) a= 128, rengi tamamen fuşya olarak değiştirir.
Socowi

Bu benim için çok faydalı. Yine de sonuçların kopyalanması kolay olsaydı ideal olurdu.
Mitchell van Zuylen

5

Tamamen abartılı "farklı" sorununuzu yönetmek için bir çözüm:

Bir birim küre oluşturun ve itme ücretleri ile üzerinde puan bırakın. Artık hareket etmedikçe (veya delta "yeterince küçük" olana kadar bir parçacık sistemi çalıştırın. Bu noktada, noktaların her biri mümkün olduğunca birbirinden uzaktır. (X, y, z) öğesini rgb'ye dönüştürün.

Bahsetmeliyim çünkü bazı problem sınıfları için bu tür bir çözüm kaba kuvvetten daha iyi çalışabilir.

Aslında bu yaklaşımı bir küreyi yormak için gördüm .

Yine, HSL uzayını veya RGB alanını çaprazlamanın en belirgin çözümleri muhtemelen iyi çalışacaktır.


1
Bu iyi bir fikir, ama bir küre yerine bir küp kullanmak muhtemelen mantıklı.
Rocketmagnet

1
YUV tabanlı çözümüm bunu yapıyor ancak bir 3D kutu için (küp değil).
Melinda Green

3

Doygunluğu ve parlaklığı maksimuma çıkarmaya ve sadece renk tonuna odaklanmaya çalışırdım. Gördüğüm gibi, H 0 ila 255 arasında gidebilir ve sonra etrafına sarılabilir. Şimdi iki zıt renk istiyorsanız, bu halkanın karşı taraflarını alırsınız, yani 0 ve 128. 4 renk istiyorsanız, dairenin 256 uzunluğunun 1 / 4'ü ile ayrılmış, yani 0, 64,128,192. Ve elbette, diğerlerinin N renklere ihtiyacınız olduğunda önerdiği gibi, bunları 256 / N ile ayırabilirsiniz.

Bu fikre ekleyeceğim şey, bu diziyi oluşturmak için ikili bir sayının ters bir gösterimini kullanmaktır. Şuna bak:

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

... bu şekilde N farklı renge ihtiyacınız varsa, ilk N sayılarını alabilir, tersine çevirebilir ve aynı zamanda her bir önekin korunmasını mümkün olduğunca koruyabilirsiniz (N'nin iki güç olduğu için). dizisi çok farklıdır.

Renklerin bu rengin kapsadığı alana göre sıralandığı bir grafiğim olduğu için bu benim kullanım durumumda önemli bir hedefti. Grafiğin en büyük alanlarının büyük bir kontrast olmasını istedim ve bazı küçük alanların ilk 10'a benzer renklere sahip olması iyi oldu, çünkü okuyucu için hangisinin sadece alanı gözlemleyerek açık olduğu gibi.


Biraz daha " matematiksel " olmasına rağmen, cevabımda bunu yaptım . Fonksiyona bakın getfracs. Ancak yaklaşımınız düşük seviyeli dillerde hızlı ve "basit": C'de bit tersine çevirme .
Janus Troelsen


1

N yeterince büyükse, benzer görünümlü renkler elde edersiniz. Dünyada sadece birçoğu var.

Neden onları spektrumda eşit olarak dağıtmıyorsunuz, şöyle:

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

Diziyi, benzer renkler yan yana olmayacak şekilde karıştırmak isterseniz, sonuçta ortaya çıkan listeyi karıştırabilirsiniz.

Bunu düşünüyor muyum?


2
Evet, bunu yeterince düşünmüyorsun. İnsan rengi algısı maalesef doğrusal değildir. Farklı yoğunluklar kullanıyorsanız, Bezold – Brücke vardiyasını da hesaba katmanız gerekebilir. İyi bilgi burada da vardır: vis4.net/blog/posts/avoid-equidistant-hsv-colors
spex

1

Bu MATLAB'da önemsizdir (bir hsv komutu vardır):

cmap = hsv(number_of_colors)

1

Bu amaç için özel olarak tasarlanmış qualpalr adlı R için bir paket yazdım . Skeçe bakmanı tavsiye ederimNasıl çalıştığını öğrenmek , ancak ana noktaları özetlemeye çalışacağım.

qualpalr, HSL renk uzayında (daha önce bu iş parçacığında tanımlanmıştır) renklerin bir spesifikasyonunu alır , DIN99d renk uzayına (algısal olarak tekdüze olan) nyansıtır ve bunların arasındaki minimum mesafeyi en üst düzeye çıkarır.

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

resim açıklamasını buraya girin


1

Bu basit özyinelemeli algoritmanın, farklı ton değerleri üretmek için kabul edilen yanıtı tamamladığını düşünüyorum. HSV için yaptım, ancak diğer renk uzayları için de kullanılabilir.

Her döngüde birbirinden olabildiğince ayrı olarak tonlar halinde tonlar üretir.

/**
 * 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;
    }
}

Burada bu tür bir algoritma bulamadım. Umarım yardımcı olur, bu benim ilk yazım.


0

Bu OpenCV işlevi, HSV renk modelini kullanarak nmaksimum S = 1.0 ve V = 1.0 ile 0 <= H <= 360º etrafında eşit olarak dağıtılmış renkler üretir . Fonksiyon BGR renklerini şu şekilde verir 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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.