Làm thế nào để tự động tạo ra màu sắc khác biệt của Niên?


194

Tôi đã viết hai phương pháp dưới đây để tự động chọn N màu khác biệt. Nó hoạt động bằng cách xác định hàm tuyến tính piecewise trên khối RGB. Lợi ích của việc này là bạn cũng có thể có được thang đo lũy tiến nếu đó là những gì bạn muốn, nhưng khi N lớn, màu sắc có thể bắt đầu trông tương tự. Tôi cũng có thể tưởng tượng chia đều khối RGB thành một mạng và sau đó vẽ các điểm. Có ai biết phương pháp nào khác không? Tôi loại trừ việc xác định một danh sách và sau đó chỉ đi xe đạp qua nó. Tôi cũng nên nói rằng tôi thường không quan tâm nếu họ đụng độ hoặc trông không đẹp, họ chỉ cần phải khác biệt về mặt thị giác.

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
Các lập trình viên có liên quan mạnh mẽ đặt câu hỏi với câu trả lời thú vị: "Tạo sơ đồ màu - lý thuyết và thuật toán ."
Alexey Popkov

2
Nhận thức màu sắc của con người không phải là tuyến tính, không may. Bạn cũng có thể cần phải tính đến ca làm việc của Bezold Nhận Brücke nếu bạn đang sử dụng các cường độ khác nhau. Ngoài ra còn có thông tin tốt ở đây: vis4.net/blog/posts/avoid-equidistant-hsv-colors
spex

Câu trả lời:


80

Bạn có thể sử dụng mô hình màu HSL để tạo màu của bạn.

Nếu tất cả những gì bạn muốn là khác nhau (có thể) và các thay đổi nhỏ về độ sáng hoặc độ bão hòa, bạn có thể phân phối các màu như vậy:

// 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
Kỹ thuật này là thông minh. Tôi cá là nó sẽ nhận được nhiều kết quả thẩm mỹ hơn của tôi.
mqp

45
Điều này giả định rằng các giá trị màu sắc cách đều nhau là khác nhau về mặt nhận thức. Ngay cả việc giảm giá các hình thức mù màu khác nhau, điều này không đúng với hầu hết mọi người: sự khác biệt giữa 120 ° (xanh lá cây) và 135 ° (xanh hơi bạc hà) là không thể chấp nhận được, trong khi chênh lệch giữa 30 ° (cam) và 45 ° (đào) là khá rõ ràng. Bạn cần một khoảng cách phi tuyến tính dọc theo màu sắc để có kết quả tốt nhất.
Phrogz

18
@mquander - Không thông minh chút nào. Không có gì để ngăn thuật toán này vô tình chọn hai màu gần như giống hệt nhau. Câu trả lời của tôi là tốt hơn, và câu trả lời của ohadsc tốt hơn nhiều .
Rocketmagnet

1
Điều này sai vì những lý do đã được đề cập, nhưng cũng vì bạn không chọn đồng đều .
sam hocevar

3
@strager giá trị mong đợi của randf ()
Killrawr

242

Câu hỏi này xuất hiện trong một vài cuộc thảo luận về SO:

Các giải pháp khác nhau được đề xuất, nhưng không có giải pháp nào là tối ưu. May mắn thay, khoa học đến để giải cứu

Trọng tài N

2 cuối cùng sẽ được miễn phí thông qua hầu hết các thư viện / proxy của trường đại học.

N là hữu hạn và tương đối nhỏ

Trong trường hợp này, người ta có thể đi đến một giải pháp danh sách. Một bài viết rất thú vị trong chủ đề có sẵn miễn phí:

Có một số danh sách màu để xem xét:

  • Danh sách 11 màu của Boynton gần như không bao giờ bị nhầm lẫn (có sẵn trong bài viết đầu tiên của phần trước)
  • 22 màu tương phản tối đa của Kelly (có sẵn trong bài báo ở trên)

Tôi cũng chạy vào Bảng màu này bởi một sinh viên MIT. Cuối cùng, các liên kết sau có thể hữu ích trong việc chuyển đổi giữa các hệ thống / tọa độ màu khác nhau (ví dụ, một số màu trong các bài viết không được chỉ định trong RGB):

Đối với danh sách của Kelly và Boynton, tôi đã thực hiện chuyển đổi sang RGB (ngoại trừ màu trắng và đen, điều này là hiển nhiên). Một số mã 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);
}

Và đây là các giá trị RGB trong các biểu diễn hex và 8 bit trên mỗi kênh:

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

Đối với tất cả các nhà phát triển Java của bạn, đây là các màu 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
};

Sau đây là các màu kelly chưa được sắp xếp theo thứ tự trên.

màu sắc kelly chưa phân loại

Sau đây là các màu kelly được sắp xếp theo màu sắc (lưu ý rằng một số màu vàng không tương phản lắm)

 sắp xếp màu kelly


+1 Cảm ơn bạn rất nhiều vì câu trả lời tuyệt vời này! BTW liên kết colour-journal.org/2010/5/10 đã chết, bài viết này vẫn có sẵn tại web.archive.org .
Alexey Popkov


16
Câu trả lời tuyệt vời, cảm ơn! Tôi đã tự do biến hai bộ màu này thành một jsfiddle tiện lợi, nơi bạn có thể thấy các màu hoạt động.
David Mills

1
Chỉ cần lưu ý rằng chỉ có 20 và 9 màu trong các danh sách đó, tương ứng. Tôi đoán đó là vì trắng và đen bị bỏ qua.
David Mills

2
Dịch vụ web đã có sẵn chưa?
Janus Troelsen

38

Giống như câu trả lời của Uri Cohen, nhưng là một máy phát điện thay thế. Sẽ bắt đầu bằng cách sử dụng màu sắc cách xa nhau. Quyết đoán.

Mẫu, màu trái trước: mẫu vật

#!/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 cho mẫu, rất đẹp và hiển thị lược đồ cũng hấp dẫn. Các câu trả lời khác ở đây sẽ được cải thiện bằng cách làm tương tự và sau đó có thể dễ dàng so sánh.
Don nở

3
Số lượng lambdas quá cao. Lambda là mạnh mẽ với điều này.
Gyfis

Trông thật tuyệt, nhưng bị kẹt khi tôi cố chạy nó vào ngày 2.7
Elad Weiss

33

Đây là một ý tưởng. Hãy tưởng tượng một xi lanh HSV

Xác định giới hạn trên và dưới mà bạn muốn cho Độ sáng và Độ bão hòa. Điều này xác định một vòng tiết diện vuông trong không gian.

Bây giờ, phân tán N điểm ngẫu nhiên trong không gian này.

Sau đó áp dụng thuật toán lực đẩy lặp trên chúng, cho một số lần lặp cố định hoặc cho đến khi các điểm ổn định.

Bây giờ bạn nên có N điểm đại diện cho N màu khác nhau nhất có thể trong không gian màu bạn quan tâm.

Hugo


30

Vì lợi ích của các thế hệ sau, tôi thêm vào đây câu trả lời được chấp nhận trong 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

18

Mọi người dường như đã bỏ lỡ sự tồn tại của không gian màu YUV rất hữu ích được thiết kế để thể hiện sự khác biệt về màu sắc trong hệ thống thị giác của con người. Khoảng cách trong YUV thể hiện sự khác biệt trong nhận thức của con người. Tôi cần chức năng này cho MagicCube4D, nơi thực hiện các khối Rubik 4 chiều và không giới hạn số câu đố xoắn 4D khác có số mặt tùy ý.

Giải pháp của tôi bắt đầu bằng cách chọn các điểm ngẫu nhiên trong YUV và sau đó lặp lại phá vỡ hai điểm gần nhất và chỉ chuyển đổi sang RGB khi trả về kết quả. Phương thức này là O (n ^ 3) nhưng điều đó không quan trọng đối với số lượng nhỏ hoặc số có thể được lưu trữ. Nó chắc chắn có thể được thực hiện hiệu quả hơn nhưng kết quả có vẻ là tuyệt vời.

Chức năng cho phép đặc tả tùy chọn các ngưỡng độ sáng để không tạo ra màu sắc trong đó không có thành phần nào sáng hơn hoặc tối hơn số lượng đã cho. IE bạn có thể không muốn các giá trị gần với đen hoặc trắng. Điều này rất hữu ích khi các màu kết quả sẽ được sử dụng làm màu cơ bản được tô bóng sau đó thông qua ánh sáng, lớp, độ trong suốt, v.v. và vẫn phải xuất hiện khác với màu cơ bản.

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

}

Có phiên bản C # của mã này ở bất cứ đâu không? Tôi đã thử chuyển đổi nó và chạy với cùng các đối số mà bạn đã truyền vào để tạoVisinglyDistotypeColors () và nó dường như chạy rất chậm. Điều đó có được mong đợi không?
Chris Smith

Bạn có nhận được kết quả tương tự? Nó rất nhanh cho nhu cầu của tôi nhưng như tôi đã nói, tôi đã không cố gắng tối ưu hóa nó, vì vậy nếu đó là vấn đề duy nhất của bạn, có lẽ bạn nên chú ý đến việc phân bổ / giải quyết bộ nhớ. Tôi không biết gì về quản lý bộ nhớ C #. Tệ nhất, bạn có thể giảm hằng số vòng lặp ngoài 1.000 xuống một cái gì đó nhỏ hơn và sự khác biệt về chất lượng thậm chí có thể không đáng chú ý.
Melinda Green

1
Bảng màu của tôi phải chứa một số màu nhất định nhưng tôi muốn điền vào các tính năng bổ sung. Tôi thích phương pháp của bạn b / c Tôi có thể đặt các màu yêu cầu của mình lên đầu tiên trong mảng yuv của bạn và sau đó sửa đổi "j = 0" để bắt đầu tối ưu hóa sau các màu yêu cầu của tôi. Tôi ước việc chia tay những cặp xấu nhất sẽ thông minh hơn một chút nhưng tôi có thể hiểu tại sao điều đó lại khó khăn.
Ryan

Tôi nghĩ rằng phương pháp yuv2rgb của bạn thiếu kẹp (0,255).
Ryan

yuv2rgb là tất cả các float, không phải byte Ryan. Hãy viết thư cho melinda@superliminal.com để thảo luận.
Melinda Green

6

Mô hình màu HSL có thể rất phù hợp để "sắp xếp" các màu, nhưng nếu bạn đang tìm kiếm các màu khác biệt trực quan, bạn chắc chắn cần mô hình màu Lab thay thế.

CIELAB được thiết kế để thống nhất về mặt nhận thức đối với tầm nhìn màu của con người, có nghĩa là cùng một lượng thay đổi số trong các giá trị này tương ứng với cùng một lượng thay đổi nhận thức trực quan.

Khi bạn biết rằng, việc tìm tập hợp con tối ưu của N màu từ nhiều dải màu vẫn là một vấn đề khó (NP), tương tự như vấn đề Nhân viên bán hàng du lịch và tất cả các giải pháp sử dụng thuật toán k-mean hoặc một cái gì đó sẽ không thực sự Cứu giúp.

Điều đó nói rằng, nếu N không quá lớn và nếu bạn bắt đầu với một bộ màu hạn chế, bạn sẽ dễ dàng tìm thấy một tập hợp màu sắc rất khác biệt theo khoảng cách Lab với một hàm ngẫu nhiên đơn giản.

Tôi đã mã hóa một công cụ như vậy để sử dụng cho riêng mình (bạn có thể tìm thấy nó ở đây: https://mokole.com/palette.html ), đây là những gì tôi nhận được cho N = 7: nhập mô tả hình ảnh ở đây

Đó là tất cả javascript, vì vậy hãy xem nguồn gốc của trang và điều chỉnh nó theo nhu cầu của riêng bạn.


1
Về » cùng một lượng thay đổi số [...] cùng một lượng thay đổi nhận thức trực quan «. Tôi đã chơi xung quanh với bộ chọn màu CIE Lab và không thể xác nhận điều này. Tôi sẽ biểu thị màu sắc trong phòng thí nghiệm bằng cách sử dụng dải L0-128 và abtừ -128 đến 128. ¶ tôi bắt đầu với L= 0, a= -128, b= -128 mà là một màu xanh tươi sáng. Sau đó tôi tăng agấp ba lần. Thay đổi lớn (+128) a= 50 dẫn đến màu xanh chỉ hơi đậm hơn một chút. (+85) a= 85 kết quả vẫn có màu xanh lam. Tuy nhiên, thay đổi tương đối nhỏ (+43) a= 128 thay đổi hoàn toàn màu sắc thành fuchsia.
Socowi

Điều này rất hữu ích cho tôi. Tuy nhiên, sẽ rất lý tưởng nếu kết quả dễ dàng sao chép-dán.
Mitchell van Zuylen

5

Đây là một giải pháp để quản lý vấn đề "khác biệt" của bạn, vấn đề hoàn toàn bị che lấp:

Tạo một quả cầu đơn vị và thả các điểm trên đó với phí đẩy lùi. Chạy một hệ thống hạt cho đến khi chúng không còn di chuyển (hoặc delta là "đủ nhỏ"). Tại thời điểm này, mỗi điểm càng xa nhau càng tốt. Chuyển đổi (x, y, z) sang rgb.

Tôi đề cập đến nó bởi vì đối với một số loại vấn đề nhất định, loại giải pháp này có thể hoạt động tốt hơn so với lực lượng vũ phu.

Ban đầu tôi thấy cách tiếp cận này ở đây để vận chuyển một quả cầu.

Một lần nữa, các giải pháp rõ ràng nhất về việc vượt qua không gian HSL hoặc không gian RGB có thể sẽ hoạt động tốt.


1
Đó là một ý tưởng tốt, nhưng có lẽ có ý nghĩa khi sử dụng một khối lập phương, thay vì một hình cầu.
Rocketmagnet

1
Đó là những gì giải pháp dựa trên YUV của tôi làm nhưng đối với hộp 3D (không phải khối lập phương).
Melinda Green

3

Tôi sẽ cố gắng khắc phục độ bão hòa và độ sáng đến mức tối đa và chỉ tập trung vào màu sắc. Theo tôi thấy, H có thể đi từ 0 đến 255 và sau đó quấn quanh. Bây giờ nếu bạn muốn hai màu tương phản, bạn sẽ lấy các cạnh đối diện của vòng này, tức là 0 và 128. Nếu bạn muốn có 4 màu, bạn sẽ lấy một số cách nhau 1/4 của 256 chiều dài của vòng tròn, tức là 0, 64,128,192. Và tất nhiên, như những người khác đề xuất khi bạn cần N màu, bạn chỉ có thể tách chúng ra 256 / N.

Những gì tôi sẽ thêm vào ý tưởng này là sử dụng một đại diện đảo ngược của một số nhị phân để tạo thành chuỗi này. Nhìn vào cái này

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

... theo cách này nếu bạn cần N màu khác nhau, bạn chỉ cần lấy số N đầu tiên, đảo ngược chúng và bạn nhận được càng nhiều điểm càng xa càng tốt (vì N là lũy thừa của hai) trong khi đồng thời bảo toàn từng tiền tố của trình tự khác nhau rất nhiều.

Đây là một mục tiêu quan trọng trong trường hợp sử dụng của tôi, vì tôi có một biểu đồ trong đó màu sắc được sắp xếp theo khu vực được bao phủ bởi màu này. Tôi muốn các khu vực lớn nhất của biểu đồ có độ tương phản lớn, và tôi thấy ổn với một số khu vực nhỏ có màu tương tự như các màu trong top 10, vì người đọc chỉ quan sát khu vực đó là điều hiển nhiên.


Đây là những gì tôi đã làm trong câu trả lời của mình, mặc dù " toán học " hơn một chút . Xem chức năng getfracs. Nhưng cách tiếp cận của bạn là nhanh chóng và "đơn giản" trong ngôn ngữ ở mức độ thấp: chút đảo ngược trong C .
Janus Troelsen


1

Nếu N đủ lớn, bạn sẽ nhận được một số màu trông tương tự. Chỉ có rất nhiều người trong số họ trên thế giới.

Tại sao không phân phối đều chúng qua phổ, như vậy:

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

Nếu bạn muốn trộn chuỗi để các màu tương tự không nằm cạnh nhau, bạn có thể trộn danh sách kết quả.

Tôi đang suy nghĩ về điều này?


2
Vâng, bạn đang suy nghĩ này. Nhận thức màu sắc của con người không phải là tuyến tính, không may. Bạn cũng có thể cần phải tính đến ca làm việc của Bezold Nhận Brücke nếu bạn đang sử dụng các cường độ khác nhau. Ngoài ra còn có thông tin tốt ở đây: vis4.net/blog/posts/avoid-equidistant-hsv-colors
spex

1

Đây là chuyện nhỏ trong MATLAB (có lệnh hsv):

cmap = hsv(number_of_colors)

1

Tôi đã viết một gói cho R được gọi là Qualpalr được thiết kế riêng cho mục đích này. Tôi khuyên bạn nên nhìn vào họa tiết để tìm hiểu làm thế nào nó hoạt động, nhưng tôi sẽ cố gắng tóm tắt những điểm chính.

Qualpalr lấy thông số màu trong không gian màu HSL (đã được mô tả trước đây trong chủ đề này), chiếu nó vào không gian màu DIN99d (đồng nhất về mặt nhận thức) và tìm nkhoảng cách tối đa giữa bất kỳ oif nào.

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

nhập mô tả hình ảnh ở đây


1

Tôi nghĩ rằng thuật toán đệ quy đơn giản này bổ sung cho câu trả lời được chấp nhận, để tạo ra các giá trị màu sắc riêng biệt. Tôi đã làm nó cho hsv, nhưng cũng có thể được sử dụng cho các không gian màu khác.

Nó tạo ra màu sắc theo chu kỳ, càng riêng biệt càng tốt cho nhau trong mỗi chu kỳ.

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

Tôi đã không thể tìm thấy loại thuật toán ở đây. Tôi hy vọng nó có ích, đó là bài viết đầu tiên của tôi ở đây.


0

Hàm OpenCV này sử dụng mô hình màu HSV để tạo ra các nmàu phân bố đều xung quanh 0 <= H <= 360º với tối đa S = 1.0 và V = 1.0. Hàm xuất các màu BGR theo 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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.