Bagaimana cara secara otomatis menghasilkan warna N "berbeda"?

194

Saya menulis dua metode di bawah ini untuk secara otomatis memilih N warna yang berbeda. Ia bekerja dengan mendefinisikan fungsi linear satu demi satu pada kubus RGB. Keuntungan dari ini adalah Anda juga bisa mendapatkan skala progresif jika itu yang Anda inginkan, tetapi ketika N menjadi besar, warnanya bisa mulai terlihat serupa. Saya juga bisa membayangkan membagi RGB cube secara merata menjadi sebuah kisi dan kemudian menggambar poin. Adakah yang tahu metode lain? Saya mengesampingkan mendefinisikan daftar dan kemudian hanya bersepeda melalui itu. Saya juga harus mengatakan saya biasanya tidak peduli jika mereka berbenturan atau tidak terlihat bagus, mereka hanya harus berbeda secara visual.

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);
}
pekerjaan
sumber
4
Pertanyaan Programmer yang sangat relevan dengan jawaban yang menarik: " Pembuatan skema warna - teori dan algoritma ."
Alexey Popkov
2
Persepsi warna manusia tidak linear, sayangnya. Anda mungkin juga perlu memperhitungkan perubahan Bezold – Brücke jika Anda menggunakan intensitas yang berbeda-beda. Ada juga informasi yang baik di sini: vis4.net/blog/posts/avoid-equidistant-hsv-colors
spex

Jawaban:

80

Anda dapat menggunakan model warna HSL untuk membuat warna Anda.

Jika semua yang Anda inginkan adalah rona berbeda (kemungkinan), dan sedikit variasi pada cahaya atau saturasi, Anda dapat mendistribusikan rona seperti:

// 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);
}
strager
sumber
2
Teknik ini cerdas. Saya yakin itu akan mendapatkan hasil estetika yang lebih daripada milik saya.
mqp
45
Ini mengasumsikan bahwa nilai rona dengan spasi yang sama memiliki persepsi yang sama. Bahkan mengabaikan berbagai bentuk buta warna, ini tidak benar bagi kebanyakan orang: perbedaan antara 120 ° (hijau) dan 135 ° (sangat sedikit hijau mint) tidak terlihat, sedangkan perbedaan antara 30 ° (oranye) dan 45 ° (persik) cukup jelas. Anda perlu spasi non-linear di sepanjang rona untuk hasil terbaik.
Phrogz
18
@mquander - Tidak pintar sama sekali. Tidak ada yang dapat mencegah algoritma ini secara tidak sengaja memilih dua warna yang hampir identik. Jawaban saya lebih baik, dan jawaban ohadsc jauh lebih baik.
Rocketmagnet
1
Ini salah karena alasan yang telah disebutkan, tetapi juga karena Anda tidak memilih secara seragam .
sam hocevar
3
@ strager berapa nilai yang diharapkan dari randf ()
Killrawr
242

Pertanyaan-pertanyaan ini muncul dalam beberapa diskusi SO:

Berbagai solusi diusulkan, tetapi tidak ada yang optimal. Untungnya, sains menyelamatkan

Sewenang-wenang N

2 yang terakhir akan gratis melalui sebagian besar perpustakaan / proxy universitas.

N terbatas dan relatif kecil

Dalam hal ini, orang bisa mencari solusi daftar. Artikel yang sangat menarik dalam subjek ini tersedia secara gratis:

Ada beberapa daftar warna yang perlu dipertimbangkan:

  • Daftar 11 warna Boynton yang hampir tidak pernah bingung (tersedia di kertas pertama bagian sebelumnya)
  • 22 warna kontras maksimum Kelly (tersedia di kertas di atas)

Saya juga bertemu dengan Palette ini oleh seorang mahasiswa MIT. Terakhir, Tautan berikut mungkin berguna untuk mengkonversi antara sistem warna / koordinat yang berbeda (beberapa warna dalam artikel tidak ditentukan dalam RGB, misalnya):

Untuk daftar Kelly dan Boynton, saya sudah membuat konversi ke RGB (dengan pengecualian putih dan hitam, yang seharusnya jelas). Beberapa kode 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);
}

Dan berikut adalah nilai RGB dalam representasi hex dan 8-bit-per-channel:

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

Untuk Anda semua pengembang Java, berikut adalah warna JavaFX:

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

berikut ini adalah warna kelly yang tidak disortir menurut urutan di atas.

warna kelly tidak disortir

berikut ini adalah warna kelly yang disortir berdasarkan rona (perhatikan bahwa beberapa warna kuning tidak terlalu kontras)

 warna kelly diurutkan

Ohad Schneider
sumber
+1 Terima kasih banyak atas jawaban yang luar biasa ini! BTW tautan colour-journal.org/2010/5/10 sudah mati, artikel ini masih tersedia di web.archive.org .
Alexey Popkov
16
Jawaban yang bagus, terima kasih! Saya telah mengambil kebebasan mengubah dua set warna ini menjadi jsfiddle yang nyaman di mana Anda dapat melihat warna dalam aksi.
David Mills
1
Hanya perhatikan, masing-masing hanya ada 20 dan 9 warna. Saya menduga itu karena putih dan hitam dihilangkan.
David Mills
2
Apakah layanan web belum tersedia?
Janus Troelsen
38

Seperti jawaban Uri Cohen, tetapi sebagai gantinya. Akan mulai dengan menggunakan warna yang berjauhan. Deterministik.

Sampel, warna kiri terlebih dahulu: Sampel

#!/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)
Janus Troelsen
sumber
+1 untuk sampel, sangat bagus, dan menunjukkan skema yang menarik juga. Jawaban lain di sini akan ditingkatkan dengan melakukan hal yang sama dan kemudian dapat dengan mudah dibandingkan.
Don Hatch
3
Jumlah lambda terlalu tinggi. Lambda kuat dengan yang ini.
Gyfis
Tampak hebat, tetapi macet ketika saya mencoba menjalankannya pada 2.7
Elad Weiss
33

Ini ide. Bayangkan sebuah silinder HSV

Tetapkan batas atas dan bawah yang Anda inginkan untuk Kecerahan dan Kejenuhan. Ini mendefinisikan cincin penampang persegi dalam ruang.

Sekarang, sebarkan N poin secara acak di dalam ruang ini.

Kemudian menerapkan algoritma repulsion berulang pada mereka, baik untuk jumlah iterasi tetap, atau sampai titik stabil.

Sekarang Anda harus memiliki N poin yang mewakili N warna yang sekitar berbeda mungkin dalam ruang warna yang Anda minati.

Hugo

Roket
sumber
30

Demi generasi yang akan datang, saya tambahkan di sini jawaban yang diterima dengan 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
Uri Cohen
sumber
18

Semua orang tampaknya telah kehilangan keberadaan ruang warna YUV yang sangat berguna yang dirancang untuk mewakili perbedaan warna yang dirasakan dalam sistem visual manusia. Jarak di YUV mewakili perbedaan dalam persepsi manusia. Saya membutuhkan fungsi ini untuk MagicCube4D yang mengimplementasikan kubus Rubik 4-dimensi dan jumlah tak terbatas dari teka-teki berkelok-kelok 4D lainnya yang memiliki jumlah wajah yang berubah-ubah.

Solusi saya mulai dengan memilih titik-titik acak di YUV dan kemudian secara iteratif memecah dua titik terdekat, dan hanya mengkonversi ke RGB ketika mengembalikan hasilnya. Metodenya O (n ^ 3) tapi itu tidak masalah untuk jumlah kecil atau yang bisa di-cache. Ini tentu bisa dibuat lebih efisien tetapi hasilnya tampak sangat baik.

Fungsi ini memungkinkan spesifikasi opsional batas kecerahan agar tidak menghasilkan warna di mana tidak ada komponen yang lebih terang atau lebih gelap dari jumlah yang diberikan. IE Anda mungkin tidak ingin nilai dekat dengan hitam atau putih. Ini berguna ketika warna yang dihasilkan akan digunakan sebagai warna dasar yang kemudian diarsir melalui pencahayaan, layering, transparansi, dll. Dan harus tetap terlihat berbeda dari warna dasar mereka.

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

}
Melinda Green
sumber
Apakah ada versi C # dari kode ini di mana saja? Saya mencoba mengonversinya dan menjalankannya dengan argumen yang sama dengan yang Anda berikan untuk menghasilkanVisuallyDistinctColors () dan tampaknya berjalan sangat lambat. Apakah itu diharapkan?
Chris Smith
Apakah Anda mendapatkan hasil yang sama? Ini banyak cepat untuk kebutuhan saya, tetapi seperti saya katakan, saya belum berusaha untuk mengoptimalkannya, jadi jika itu satu-satunya masalah Anda, Anda mungkin harus memperhatikan alokasi / deallokasi memori. Saya tidak tahu apa-apa tentang manajemen memori C #. Paling buruk, Anda dapat mengurangi 1.000 putaran konstan ke sesuatu yang lebih kecil dan perbedaan kualitas bahkan mungkin tidak terlihat.
Melinda Green
1
Palet saya harus mengandung warna-warna tertentu tetapi saya ingin mengisi ekstra. Saya suka metode Anda b / c. Saya dapat menempatkan warna yang diperlukan pertama di array yuv Anda dan kemudian memodifikasi "j = 0" untuk mulai mengoptimalkan setelah warna yang diperlukan. Saya berharap perpisahan pasangan terburuk sedikit lebih pintar tapi saya bisa mengerti mengapa itu sulit.
Ryan
Saya pikir metode yuv2rgb Anda kehilangan klem (0,255).
Ryan
yuv2rgb semuanya mengapung, bukan byte Ryan. Silakan menulis ke [email protected] untuk membahas.
Melinda Green
6

Model warna HSL mungkin cocok untuk warna "sortasi", tetapi jika Anda mencari warna yang berbeda secara visual, Anda pasti membutuhkan model warna Lab .

CIELAB dirancang untuk secara seragam dipersepsikan sehubungan dengan penglihatan warna manusia, yang berarti bahwa jumlah perubahan numerik yang sama dalam nilai-nilai ini sesuai dengan jumlah perubahan visual yang dirasakan secara sama.

Setelah Anda tahu itu, menemukan subset optimal N warna dari berbagai warna masih merupakan masalah (NP) yang sulit, jenis yang mirip dengan masalah salesman keliling dan semua solusi menggunakan algoritma k-mean atau sesuatu yang tidak akan benar-benar Tolong.

Yang mengatakan, jika N tidak terlalu besar dan jika Anda mulai dengan seperangkat warna terbatas, Anda akan dengan mudah menemukan subset warna yang sangat baik menurut jarak Lab dengan fungsi acak sederhana.

Saya telah memberi kode alat semacam itu untuk penggunaan saya sendiri (Anda dapat menemukannya di sini: https://mokole.com/palette.html ), inilah yang saya dapatkan untuk N = 7: masukkan deskripsi gambar di sini

Ini semua javascript jadi silakan untuk melihat sumber halaman dan menyesuaikannya untuk kebutuhan Anda sendiri.

fbparis
sumber
1
Mengenai » jumlah perubahan numerik yang sama [...] jumlah yang sama dari perubahan yang dirasakan secara visual «. Saya bermain-main dengan pemilih warna CIE Lab dan tidak bisa mengkonfirmasi ini sama sekali. Saya akan menunjukkan warna lab menggunakan rentang Ldari 0 hingga 128 dan adan bdari -128 hingga 128. ¶ Saya mulai dengan L= 0, a= -128, b= -128 yang berwarna biru cerah. Lalu saya bertambah atiga kali. ❶ Perubahan besar (+128) a= 50 menghasilkan warna biru yang sedikit lebih gelap. ❷ (+85) a= 85 hasil masih berwarna biru. ❸ Namun, perubahan yang relatif kecil (+43) a= 128 benar-benar mengubah warna menjadi fuchsia.
Socowi
Ini sangat berguna bagi saya. Akan sangat ideal jika hasilnya mudah disalin-tempel.
Mitchell van Zuylen
5

Inilah solusi untuk mengelola masalah "berbeda" Anda, yang sepenuhnya berlebihan:

Buat unit bola dan jatuhkan poin di atasnya dengan biaya memukul mundur. Jalankan sistem partikel hingga mereka tidak lagi bergerak (atau delta "cukup kecil"). Pada titik ini, masing-masing poin sejauh mungkin dari satu sama lain. Konversi (x, y, z) ke rgb.

Saya menyebutkannya karena untuk kelas masalah tertentu, solusi jenis ini dapat bekerja lebih baik daripada brute force.

Saya awalnya melihat pendekatan ini di sini untuk mempelajari bulatan.

Sekali lagi, solusi yang paling jelas dari melintasi ruang HSL atau ruang RGB mungkin akan berfungsi dengan baik.

alas tiang
sumber
1
Itu ide yang bagus, tetapi mungkin masuk akal untuk menggunakan kubus, bukan bola.
Rocketmagnet
1
Itulah yang dilakukan solusi berbasis YUV saya tetapi untuk kotak 3D (bukan kubus).
Melinda Green
3

Saya akan mencoba untuk memperbaiki saturasi dan luminasi maksimum dan fokus pada rona saja. Seperti yang saya lihat, H dapat bergerak dari 0 hingga 255 dan kemudian membungkusnya. Sekarang jika Anda menginginkan dua warna yang kontras, Anda akan mengambil sisi berlawanan dari cincin ini, yaitu 0 dan 128. Jika Anda menginginkan 4 warna, Anda akan dipisahkan oleh 1/4 dari 256 panjang lingkaran, yaitu 0, 64.128.192. Dan tentu saja, seperti yang disarankan orang lain saat Anda membutuhkan N warna, Anda bisa memisahkannya dengan 256 / N.

Apa yang akan saya tambahkan ke ide ini adalah dengan menggunakan representasi terbalik dari angka biner untuk membentuk urutan ini. Lihat ini:

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

... dengan cara ini jika Anda membutuhkan N warna yang berbeda, Anda hanya dapat mengambil angka N pertama, membalikkannya, dan Anda mendapatkan poin sejauh mungkin (untuk N menjadi kekuatan dua) sementara pada saat yang sama mempertahankan bahwa setiap awalan dari urutan berbeda banyak.

Ini adalah tujuan penting dalam kasus penggunaan saya, karena saya memiliki grafik di mana warna diurutkan berdasarkan area yang dicakup oleh warna ini. Saya ingin area terbesar dari grafik memiliki kontras yang besar, dan saya setuju dengan beberapa area kecil untuk memiliki warna yang mirip dengan yang dari 10 teratas, karena jelas bagi pembaca yang mana yang mana hanya dengan mengamati area tersebut.

qbolec
sumber
Inilah yang saya lakukan dalam jawaban saya, meski sedikit lebih " matematis ". Lihat fungsinya getfracs. Tapi pendekatan Anda cepat dan "sederhana" dalam bahasa tingkat rendah: bit membalikkan di C .
Janus Troelsen
Hanya memperhatikan Ikan Konyol melakukannya juga
Janus Troelsen
1

Jika N cukup besar, Anda akan mendapatkan warna yang mirip. Hanya ada begitu banyak dari mereka di dunia.

Mengapa tidak mendistribusikannya secara merata melalui spektrum, seperti:

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

Jika Anda ingin mencampur urutan sehingga warna yang serupa tidak bersebelahan, Anda mungkin dapat mengacak daftar yang dihasilkan.

Apakah saya memikirkan ini?

mqp
sumber
2
Ya, Anda sedang memikirkan ini. Persepsi warna manusia tidak linear, sayangnya. Anda mungkin juga perlu memperhitungkan perubahan Bezold – Brücke jika Anda menggunakan intensitas yang berbeda-beda. Ada juga informasi yang baik di sini: vis4.net/blog/posts/avoid-equidistant-hsv-colors
spex
1

Ini sepele di MATLAB (ada perintah hsv):

cmap = hsv(number_of_colors)
Arturo
sumber
1

Saya telah menulis paket untuk R yang disebut qualpalr yang dirancang khusus untuk tujuan ini. Saya sarankan Anda melihat sketsa untuk mengetahui cara kerjanya, tetapi saya akan mencoba merangkum poin-poin utama.

qualpalr mengambil spesifikasi warna dalam ruang warna HSL (yang telah dijelaskan sebelumnya dalam utas ini), memproyeksikannya ke ruang warna DIN99d (yang secara seragam seragam) dan menemukan nyang memaksimalkan jarak minimum di antara mereka.

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

masukkan deskripsi gambar di sini

Johan Larsson
sumber
1

Saya pikir algoritma rekursif sederhana ini melengkapi jawaban yang diterima, untuk menghasilkan nilai rona yang berbeda. Saya membuatnya untuk hsv, tetapi dapat digunakan untuk ruang warna lain juga.

Ini menghasilkan rona dalam siklus, terpisah sebanyak mungkin satu sama lain dalam setiap siklus.

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

Saya tidak dapat menemukan algoritma semacam ini di sini. Saya harap ini membantu, ini posting pertama saya di sini.

David Fernandez
sumber
0

Fungsi OpenCV ini menggunakan model warna HSV untuk menghasilkan nwarna yang terdistribusi secara merata di sekitar 0 <= H <= 360º dengan maksimum S = 1.0 dan V = 1.0. Fungsi menampilkan warna BGR dalam 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;
}
Warga kehormatan
sumber