如何自动生成N个“不同的”颜色?

我在下面写了两种方法来自动selectN种不同的颜色。 它通过在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); } 

您可以使用HSL颜色模型来创build您的颜色。

如果所有你想要的是不同的色调(可能),以及亮度或饱和度的细微变化,你可以像这样分配色调:

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

这个问题出现在很多SO讨论中:

  • 生成唯一颜色的algorithm
  • 生成独特的颜色
  • 在图中生成截然不同的RGB颜色
  • 如何为任何自然数n生成n种不同的颜色?

提出了不同的解决scheme,但都不是最佳的。 幸运的是, 科学来拯救

任意N

  • 分类图像的彩色显示 (免费下载)
  • Web服务个性化地图着色 (免费下载,Web服务解决scheme应在下个月提供)
  • 一个select高对比度颜色集的algorithm (作者提供了一个免费的C ++实现)
  • 高对比度的颜色组合 (问题的第一个algorithm)

最后两个将通过大多数大学图书馆/代理免费。

N是有限的并且相对较小

在这种情况下,人们可以去寻求解决办法。 这个主题非常有趣的文章是免费的:

  • 颜色字母表和颜色编码的限制

有几个颜色列表要考虑:

  • Boynton列出的11种颜色几乎不会混淆(可在上一节的第一篇文章中find)
  • 凯利22种颜色的最大对比度(可在上面的文件中find)

我也遇到了一个麻省理工学院学生的调色板。 最后,下面的链接可能会在不同的颜色系统/坐标之间进行转换(例如,文章中的某些颜色未用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); } 

这里是hex和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 }; 

以下是按照上述顺序的未分类的凯利颜色。

未分类的凯利颜色

以下是根据色调分类的凯利色(注意一些黄色不是很对比)

分类的凯利颜色

这是一个想法。 想象一下HSV气瓶

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

现在,在这个空间内随机地分散N个点。

然后对它们应用迭代排斥algorithm,要么进行固定数量的迭代,要么直到点稳定。

现在你应该在你感兴趣的颜色空间内有N个代表N个颜色的点,

雨果

像Uri Cohen的回答一样,但是却是一个发电机。 将开始通过使用很远的颜色。 确定性。

示例,首先留下颜色: 样品

 #!/usr/bin/env python3.3 import colorsys import itertools from fractions import Fraction def zenos_dichotomy(): """ 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 getfracs(): """ [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 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) bias = lambda x: (math.sqrt(x/3)/Fraction(2,3)+Fraction(1,3))/Fraction(6,5) # can be used for the v in hsv to map linear values 0..1 to something that looks equidistant def genhsv(h): 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 genrgb = lambda x: colorsys.hsv_to_rgb(*x) flatten = itertools.chain.from_iterable gethsvs = lambda: flatten(map(genhsv,getfracs())) getrgbs = lambda: map(genrgb, gethsvs()) def genhtml(x): uint8tuple = map(lambda y: int(y*255), x) return "rgb({},{},{})".format(*uint8tuple) gethtmlcolors = lambda: map(genhtml, getrgbs()) if __name__ == "__main__": print(list(itertools.islice(gethtmlcolors(), 100))) 

为了几代人的缘故,我在这里添加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 

每个人似乎都错过了非常有用的YUV色彩空间的存在,这个色彩空间被devise来代表人类视觉系统中的感知色差。 YUV的距离代表人类感知的差异。 我需要MagicCube4D的这个function来实现4维魔方和无限数量的其他具有任意数量的面的4D曲折拼图。

我的解决scheme首先selectYUV中的随机点,然后迭代分解最近的两个点,并在返回结果时仅转换为RGB。 该方法是O(n ^ 3),但是对于小数字或者可以被caching的数据并不重要。 当然可以提高效率,但结果似乎很好。

该function允许任意指定亮度阈值,从而不产生没有分量比给定量更亮或更暗的颜色。 IE你可能不希望接近黑色或白色的值。 当生成的颜色将被用作后来通过照明,分层,透明度等等进行阴影处理的基色时,此function非常有用,并且仍然必须显示与基色不同的颜色。

 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: ON^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)); } } 

这是一个解决scheme来pipe理你的“独特”的问题,这是完全夸大的:

创build一个单位的领域,并放弃点与排斥收费。 运行一个粒子系统,直到它们不再移动(或者增量“足够小”)。 在这一点上,每个点尽可能远离彼此。 将(x,y,z)转换为rgb。

我提到它是因为对于某些types的问题,这种types的解决scheme比蛮力更好。

我原本在这里看到了这个方法来计算一个球体。

再一次,遍历HSL空间或RGB空间的最明显的解决scheme可能会工作得很好。

我会尽量修复饱和度和光照,以最大限度地关注色相。 正如我所看到的,H可以从0到255,然后环绕。 现在,如果你想要两个对比的颜色,你会采取这个环的相反的两边,即0和128.如果你想要4种颜色,你会采取一些分开的256的长度的四分之一的圆圈,即0,64,128,192。 当然,正如其他人所build议的,当你需要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的幂),同时保留每个前缀序列差别很大。

在我的使用案例中,这是一个重要的目标,因为我有一个图表,颜色按照这个颜色覆盖的区域sorting。 我希望图表中最大的区域有较大的对比度,我认为一些小区域的颜色与前十个区域的颜色相似,因为对于读者来说,哪一个区域是哪一个区域显而易见。

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

如果你想混合序列,以便类似的颜色不相邻,你也许可以洗牌结果列表。

我是否承认这一点?

这在MATLAB中是微不足道的(有一个hsv命令):

 cmap = hsv(number_of_colors) 

我已经为R写了一个名为qualpalr的包,专门为此devise的。 我build议你看看这个小插曲 ,看看它是如何工作的,但我会尽量总结一下主要观点。

qualpalr在HSL颜色空间 (在此线程中已经描述过)中对颜色进行了规范,将其投影到DIN99d颜色空间(这在感知上是统一的),并find最大化它们之间的最小距离的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) 

在这里输入图像描述

我认为这个简单的recursionalgorithm补充了接受的答案,以便生成不同的色调值。 我做了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; } } 

我无法在这里find这种algorithm。 我希望它有帮助,这是我在这里的第一篇文章。