N "고유 한"색상을 자동으로 생성하는 방법은 무엇입니까?


194

N 가지 색상을 자동으로 선택하기 위해 아래 두 가지 방법을 작성했습니다. RGB 큐브에서 부분 선형 함수를 정의하여 작동합니다. 이것의 장점은 원하는 경우 점진적 스케일을 얻을 수 있지만 N이 커지면 색상이 비슷해지기 시작할 수 있다는 것입니다. RGB 큐브를 격자로 균등하게 세분화 한 다음 점을 그리는 것을 상상할 수도 있습니다. 다른 방법을 아는 사람이 있습니까? 목록을 정의하지 않고 순환합니다. 나는 또한 그들이 충돌하거나 멋지게 보이지 않는다면 일반적으로 신경 쓰지 말고 시각적으로 구별해야한다고 말해야합니다.

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
관련성이 높은 프로그래머는 " 색 구성표 생성-이론 및 알고리즘 "이라는 흥미로운 답변을 제시 합니다 .
Alexey Popkov 11

2
인간의 색상 인식은 불행히도 선형 적이 지 않습니다. 다양한 강도를 사용하는 경우 Bezold–Brücke 이동을 고려해야 할 수도 있습니다. 여기에 좋은 정보가 있습니다 : vis4.net/blog/posts/avoid-equidistant-hsv-colors
spex

답변:


80

HSL 컬러 모델을 사용할 수 있습니다 귀하의 색상을 만들 수 있습니다.

원하는 색조가 다를 수 있고, 가벼움이나 채도에 약간의 차이가있는 경우 색조를 다음과 같이 분배 할 수 있습니다.

// 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
이 기술은 똑똑합니다. 나는 그것이 내 것보다 더 심미적 인 결과를 얻을 것이라고 확신합니다.
mqp

45
이것은 동일한 간격의 색조 값이 동일하게 지각 적으로 다르다고 가정합니다. 다양한 형태의 색맹을 할인하더라도 대부분의 사람들에게 해당되는 것은 아닙니다. 아주 분명합니다. 최상의 결과를 얻으려면 색조를 따라 비선형 간격이 필요합니다.
Phrogz

18
@ mquander-전혀 똑똑하지 않습니다. 이 알고리즘이 실수로 거의 동일한 두 가지 색상을 선택하는 것을 막을 방법은 없습니다. 내 대답이 더 좋고 ohadsc의 대답이 훨씬 좋습니다.
Rocketmagnet 2016 년

1
이것은 이미 언급 한 이유로 잘못 되었으나 균일하게 선택 하지 않기 때문 입니다.
sam hocevar

3
@strager randf ()의 예상 값은 무엇입니까
Killrawr

242

이 질문은 몇 가지 SO 토론에서 나타납니다.

다른 솔루션이 제안되었지만 최적의 솔루션은 없습니다. 다행히 과학 이 구조에 온다

임의의 N

마지막 2 개는 대부분의 대학 도서관 / 프록시를 통해 무료입니다.

N은 유한하고 상대적으로 작습니다

이 경우 목록 솔루션을 사용할 수 있습니다. 주제에 대한 매우 흥미로운 기사를 자유롭게 사용할 수 있습니다.

고려해야 할 몇 가지 색상 목록이 있습니다.

  • 거의 혼란스럽지 않은 Boynton의 11 가지 색상 목록 (이전 섹션의 첫 번째 논문에서 사용 가능)
  • 켈리의 최대 명암 대비 22 가지 색상 (위의 논문에서 사용 가능)

나는 또한 우연히 MIT의 학생에 의해 팔레트입니다. 마지막으로, 다음 링크는 다른 색상 시스템 / 좌표 간 변환에 유용 할 수 있습니다 (예를 들어 기사의 일부 색상은 RGB로 지정되지 않음).

Kelly와 Boynton의 목록을 위해 이미 RGB로 변환했습니다 (흰색과 검은 색 제외). 일부 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);
}

다음은 16 진수 및 채널당 8 비트 표현의 RGB 값입니다.

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

모든 Java 개발자에게 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
};

다음은 위의 순서에 따라 분류되지 않은 켈리 색상입니다.

분류되지 않은 켈리 색상

다음은 색조에 따라 정렬 된 켈리 색상입니다 (일부 노란색은 매우 대조적이지 않습니다)

 켈리 색상 정렬


+1이 위대한 답변에 대단히 감사합니다! colour-journal.org/2010/5/10 링크 가 죽었 으므로이 기사는 여전히 web.archive.org 에서 볼 수 있습니다 .
Alexey Popkov


16
좋은 답변, 감사합니다! 이 두 가지 색상 세트를 색상을 실제로 볼 수
David Mills

1
이 목록에는 각각 20 색과 9 색만 있습니다. 흰색과 검은 색이 생략되어 있기 때문에 추측하고 있습니다.
David Mills

2
웹 서비스를 아직 사용할 수 있습니까?
Janus Troelsen

38

Uri Cohen의 답변과 같지만 대신 발전기입니다. 멀리 떨어진 색상을 사용하여 시작합니다. 결정 론적.

샘플, 왼쪽 색상이 먼저 : 견본

#!/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, 매우 좋으며 구성표도 매력적입니다. 여기에 다른 답변은 동일한 작업을 수행하여 개선 한 다음 쉽게 비교할 수 있습니다.
돈 해치

3
람다의 양이 너무 높습니다. 람다는 이것으로 강하다.
Gyfis

좋아 보이지만 2.7에서 실행하려고하면
멈춤

33

여기 아이디어가 있습니다. HSV 실린더를 상상해보십시오

밝기 및 채도에 대한 상한 및 하한을 정의하십시오. 이것은 공간 내에서 정사각형 단면 링을 정의합니다.

이제이 공간 내에서 N 개의 점을 무작위로 산란시킵니다.

그런 다음 고정 된 반복 횟수에 대해 또는 포인트가 안정화 될 때까지 반복 반발 알고리즘을 적용하십시오.

이제 관심있는 색 공간 내에서 가능한 한 다른 N 개의 색을 나타내는 N 개의 점이 있어야합니다.

휴고


30

다음 세대를 위해 파이썬으로 받아 들여진 대답을 여기에 추가합니다.

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

모두가 인간 시각 시스템에서 인식 된 색상 차이를 나타내도록 설계된 매우 유용한 YUV 색상 공간의 존재를 놓친 것 같습니다. YUV의 거리는 인간의 인식의 차이를 나타냅니다. 4 차원 Rubik의 큐브와 임의의 수의 얼굴을 가진 다른 4D 트위스 티 퍼즐을 구현하는 MagicCube4D 에이 기능이 필요했습니다.

내 솔루션은 YUV에서 임의의 점을 선택한 다음 가장 가까운 두 점을 반복적으로 나누고 결과를 반환 할 때만 RGB로 변환하여 시작합니다. 이 방법은 O (n ^ 3)이지만 작은 수나 캐시 할 수있는 것은 중요하지 않습니다. 확실히 더 효율적으로 만들 수는 있지만 결과는 훌륭합니다.

이 기능을 사용하면 지정된 양보다 밝거나 어두운 구성 요소가없는 색상을 생성하지 않도록 밝기 임계 값을 선택적으로 지정할 수 있습니다. IE 당신은 검은 색이나 흰색에 가까운 값을 원하지 않을 수 있습니다. 결과 색상이 나중에 조명, 레이어링, 투명도 등을 통해 음영 처리되고 기본 색상과 다르게 나타나야하는 기본 색상으로 사용될 때 유용합니다.

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 # 버전이 어디에 있습니까? 나는 그것을 변환하고 generateVisuallyDistinctColors ()에 전달 한 것과 동일한 인수로 실행하려고 시도했지만 실제로 느리게 실행되는 것 같습니다. 기대 되나요?
Chris Smith

같은 결과를 얻습니까? 내 요구에 충분히 빠르지 만, 내가 말했듯이 최적화를 시도하지 않았으므로 이것이 유일한 문제라면 메모리 할당 / 할당 해제에주의를 기울여야합니다. 나는 C # 메모리 관리에 대해 아무것도 모른다. 최악의 경우 1,000 개의 외부 루프 상수를 더 작은 것으로 줄이고 품질 차이가 눈에 띄지 않을 수도 있습니다.
Melinda Green

1
팔레트에는 특정 색상이 포함되어야하지만 추가 내용을 채우고 싶었습니다. 나는 당신의 방법 b / c를 좋아합니다. 필요한 색상을 yuv 배열에 먼저 넣은 다음 "j = 0"을 수정하여 필요한 색상 후에 최적화를 시작할 수 있습니다. 최악의 짝짓기가 조금 더 똑똑해 졌으면 좋겠지 만 왜 어려운지 이해할 수 있습니다.
Ryan

yuv2rgb 메소드에 clamp (0,255)가 누락 된 것 같습니다.
Ryan

yuv2rgb는 바이트 Ryan이 아닌 모든 float입니다. 논의하려면 melinda@superliminal.com으로 문의하십시오.
Melinda Green

7

HSL 색상 모델은 "정렬"색상에 적합 할 수 있지만 시각적으로 다른 색상을 찾으려면 대신 실험실 색상 모델이 필요 합니다.

CIELAB은 사람의 색각과 관련하여 지각 적으로 균일하게 설계되었습니다. 즉,이 값에서 같은 양의 수치 변화는 시각적으로 인식 된 변화와 거의 같은 양에 해당합니다.

일단 광범위한 색상에서 N 색상의 최적 하위 집합을 찾는 것은 여행 영업 사원 문제 와 비슷한 일종의 (NP) 어려운 문제이며 k- 평균 알고리즘을 사용하는 모든 솔루션 또는 실제로는 그렇지 않습니다. 도움.

즉, N이 너무 크지 않고 제한된 색상 세트로 시작하면 간단한 임의 함수를 사용하여 Lab 거리에 따라 구별되는 색상의 매우 우수한 하위 세트를 쉽게 찾을 수 있습니다.

나는 내 자신의 사용법을 위해 그러한 도구를 코딩했다 (여기에서 찾을 수 있습니다 : https://mokole.com/palette.html ), 여기 N = 7에 대해 얻은 것이 있습니다 : 여기에 이미지 설명을 입력하십시오

그것은 모두 자바 스크립트이므로 페이지 소스를 살펴보고 자신의 필요에 맞게 조정하십시오.


1
» 같은 양의 수치 변화 [...] 같은 양의 시각적으로인지 된 변화 «에 대하여. 나는 CIE Lab 컬러 피커를 가지고 놀았으며 이것을 확인할 수 없었습니다. 나는 L0에서 128 사이의 범위 ab-128에서 128 사이의 범위 를 사용하여 실험실 색상을 표시 할 것입니다. ¶ 밝은 파란색 인 L= 0, a= -128, b= -128로 시작했습니다 . 그런 다음 a세 번 증가 했습니다. ❶ 큰 변화 (+128) a= 50은 약간 진한 파란색을냅니다. ❷ (+85) a= 85는 여전히 파란색입니다. ❸ 그러나 상대적으로 작은 변화 (+43) a= 128은 색을 자홍색으로 완전히 바꿉니다.
Socowi

이것은 나에게 매우 유용합니다. 그러나 결과를 복사하여 쉽게 붙여 넣는 것이 이상적입니다.
Mitchell van Zuylen

5

다음은 "고유 한"문제를 관리하기위한 솔루션입니다.

반구 요금으로 유닛 구체와 드롭 포인트를 만듭니다. 파티클 시스템이 더 이상 움직이지 않을 때까지 실행합니다 (또는 델타가 "충분히 작습니다"). 이 시점에서 각 지점은 가능한 멀리 떨어져 있습니다. (x, y, z)를 rgb로 변환하십시오.

특정 유형의 문제에 대해서는 이러한 유형의 솔루션이 무차별 대입보다 더 효과적 일 수 있기 때문에 언급했습니다.

원래이 방법을 보았습니다. 구를 테셀레이션하기 .

다시 말하지만 HSL 공간 또는 RGB 공간을 통과하는 가장 확실한 솔루션은 아마도 잘 작동 할 것입니다.


1
좋은 생각이지만 구보다는 큐브를 사용하는 것이 좋습니다.
Rocketmagnet

1
그것이 YUV 기반 솔루션이하는 것이 아니라 3D 상자 (입방체가 아님)를위한 것입니다.
Melinda Green

3

채도와 루미 네이션을 최대한으로 고치고 색조에만 집중하려고합니다. 내가 알다시피, H는 0에서 255까지 갈 수 있고 랩핑됩니다. 이제 두 개의 대조되는 색상을 원한다면이 링의 반대쪽 (예 : 0과 128)을 사용합니다. 4 개의 색상을 원한다면 256 개의 원 길이의 1/4, 즉 0, 64,128,192로 구분됩니다. 물론 N 컬러가 필요할 때 다른 사람들이 제안했듯이 256 / N으로 분리 할 수 ​​있습니다.

이 아이디어에 추가 할 것은 이진 숫자의 역 표현을 사용 하여이 시퀀스를 형성하는 것입니다. 이거 봐요:

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

...이 방법으로 N 개의 다른 색상이 필요한 경우 처음 N 개의 숫자를 가져 와서 뒤집을 수 있으며 가능한 한 먼 지점 (N은 2의 거듭 제곱)을 얻는 동시에 동시에 각 접두사를 유지합니다. 순서가 많이 다릅니다.

색상 이이 색상으로 덮여있는 영역별로 색상이 정렬 된 차트가 있었으므로 이것이 유스 케이스에서 중요한 목표였습니다. 나는 차트의 가장 큰 영역이 큰 대비를 갖기를 원했고 일부 작은 영역은 상위 10의 색상과 비슷한 색상을 사용하는 것이 좋았습니다. 독자는 영역을 관찰하여 어느 것이 어느 것인지 알 수 있었기 때문입니다.


좀 더 " 수학적 " 이지만 이것은 내 대답에서 내가 한 일 입니다. 기능을 참조하십시오 getfracs. 그러나 당신의 접근 방식은 저수준 언어에서 빠르고 간단합니다 : C에서 비트 반전 .
Janus Troelsen


1

N이 충분히 크면 비슷한 색상을 얻을 수 있습니다. 세계에는 너무 많은 사람들이 있습니다.

다음과 같이 스펙트럼을 통해 고르게 분포시키지 마십시오.

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

비슷한 색상이 나란히 표시되지 않도록 시퀀스를 혼합하려면 결과 목록을 섞을 수 있습니다.

나는 이것을 생각하고 있습니까?


2
예, 당신은 이것을 생각하고 있습니다. 인간의 색상 인식은 불행히도 선형 적이 지 않습니다. 다양한 강도를 사용하는 경우 Bezold–Brücke 이동을 고려해야 할 수도 있습니다. 여기에 좋은 정보가 있습니다 : vis4.net/blog/posts/avoid-equidistant-hsv-colors
spex

1

이것은 MATLAB에서 사소한 것입니다 (hsv 명령이 있습니다).

cmap = hsv(number_of_colors)

1

R에 대한 패키지를 작성했습니다. 이 목적을 위해 특별히 설계된 qualpalr . 비 네트 를 살펴보고 그 작동 방식을 알아볼 것을 권장 하지만 요점을 요약하려고 노력할 것입니다.

qualpalr는 HSL 색상 공간 (이 스레드에서 이전에 설명)에서 색상의 사양을 취하여 DIN99d 색상 공간 (지각 적으로 균일 함)으로 투사 n하여 oif 간의 최소 거리를 최대화합니다.

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

여기에 이미지 설명을 입력하십시오


1

이 간단한 재귀 알고리즘은 명확한 색조 값을 생성하기 위해 허용되는 답변을 보완한다고 생각합니다. hsv 용으로 만들었지 만 다른 색상 공간에도 사용할 수 있습니다.

각주기에서 가능한 한 서로 다른 주기로 색조를 생성합니다.

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

여기서 이런 종류의 알고리즘을 찾을 수 없었습니다. 도움이 되길 바랍니다. 여기가 첫 번째 게시물입니다.


0

이 OpenCV 기능은 HSV 색상 모델을 사용하여 n최대 S = 1.0 및 V = 1.0으로 0 <= H <= 360º 주위에 균일하게 분포 된 색상 을 생성 합니다. 이 기능은 BGR 색상을 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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.