I wrote the two methods below to automatically select N distinct colors. It works by defining a piecewise linear function on the RGB cube. The benefit of this is you can also get a progressive scale if that's what you want, but when N gets large the colors can start to look similar. I can also imagine evenly subdividing the RGB cube into a lattice and then drawing points. Does anyone know any other methods? I'm ruling out defining a list and then just cycling through it. I should also say I don't generally care if they clash or don't look nice, they just have to be visually distinct.

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

当前回答

为了子孙后代,我在这里添加了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

其他回答

我们只需要一个RGB三联体对的范围,这些三联体之间的距离最大。

我们可以定义一个简单的线性渐变,然后调整渐变的大小以获得所需的颜色数量。

在python中:

from skimage.transform import resize
import numpy as np
def distinguishable_colors(n, shuffle = True, 
                           sinusoidal = False,
                           oscillate_tone = False): 
    ramp = ([1, 0, 0],[1,1,0],[0,1,0],[0,0,1], [1,0,1]) if n>3 else ([1,0,0], [0,1,0],[0,0,1])
    
    coltrio = np.vstack(ramp)
    
    colmap = np.round(resize(coltrio, [n,3], preserve_range=True, 
                             order = 1 if n>3 else 3
                             , mode = 'wrap'),3)
    
    if sinusoidal: colmap = np.sin(colmap*np.pi/2)
    
    colmap = [colmap[x,] for x  in range(colmap.shape[0])]
    
    if oscillate_tone:
        oscillate = [0,1]*round(len(colmap)/2+.5)
        oscillate = [np.array([osc,osc,osc]) for osc in oscillate]
        colmap = [.8*colmap[x] + .2*oscillate[x] for x in range(len(colmap))]
    
    #Whether to shuffle the output colors
    if shuffle:
        random.seed(1)
        random.shuffle(colmap)
        
    return colmap

我认为这个简单的递归算法补充了公认的答案,以产生不同的色调值。我为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;
    }
}

我在这里找不到这种算法。我希望这对你有所帮助,这是我在这里的第一篇文章。

我有个主意。想象一个HSV气缸

定义亮度和饱和度的上限和下限。这在空间内定义了一个正方形的横截面环。

现在,在这个空间中随机散布N个点。

然后对它们应用迭代排斥算法,要么迭代次数固定,要么直到这些点稳定下来。

现在你应该有N个点,代表N种颜色,它们在你感兴趣的颜色空间中尽可能不同。

Hugo

为了子孙后代,我在这里添加了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

我为R写了一个名为qualpalr的包,它是专门为此目的设计的。我建议你看看小插图,看看它是如何工作的,但我会尽量总结要点。

qualpalr在HSL颜色空间(前面在这个线程中描述过)中获取一个颜色规范,将其投射到DIN99d颜色空间(感知上是均匀的),并找到使它们之间的最小距离最大化的n。

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