颜色集算法浅议
发布者:
YunShui
颜色生成在项目运用中比较常见,项目中自定义的固定颜色库很容易被消耗殆尽,必须选取一个比较合适的算法来生成人眼辨识度大的颜色集。
1. RGB、HSL颜色空间
###1.1 简介
RGB表示红(Red)、绿(Green)、蓝(Blue)三种颜色按照不同的比例混合,在屏幕上重现256 x 256 x 256种颜色,使用比较广泛。
HSL是工业颜色标准,通过对色调(Hue)、饱和度(Saturation)、亮度(Lightness)三种通道变化得到几乎包括了人类视力所能感知的所有颜色,在屏幕上也可以重现256 x 256 x 256种颜色。
1.2. RGB、HSL颜色互转
1.2.1 RGB转换为HSL
* 把RGB值转成[0, 1]
数值;
* 找出R、G和B中最大值;
* 计算亮度:L = (maxcolor + mincolor)/2
* 如果最大和最小的颜色值相同,即表示灰色,那么S定义为0,而H未定义并在程序中通常写成0;
* 否则,根据亮度L计算饱和度S:
* `if L < 0.5, S = (maxcolor-mincolor) / (maxcolor + mincolor)`
* `if L >= 0.5, S = (maxcolor-mincolor) / (2.0-maxcolor-mincolor)`
-
计算色调H:
-
if R = maxcolor, H = (G - B) / (maxcolor - mincolor)
-
if G=maxcolor, H=2.0 + (B - R) / (maxcolor - mincolor)
-
if B=maxcolor, H=4.0 + (R - G) / (maxcolor - mincolor)
-
-
H = H x 60.0,如果H为负值,则加360。 Java示例:
public static int[] hueToRGB(double hue, double saturation, double lightness) {
int[] rgb = new int[3];
if (saturation == 0) {
rgb[0] = (int) lightness * 255;
rgb[1] = (int) lightness * 255;
rgb[2] = (int) lightness * 255;
} else {
double var2;
if (lightness < 0.5) {
var2 = lightness * (1d + saturation);
} else {
var2 = (lightness + saturation) - (lightness * saturation);
}
double var1 = 2 * lightness - var2;
rgb[0] = (int) Math.round(255d * hueToRGB(var1, var2, hue + (1d / 3d)));
rgb[1] = (int) Math.round(255d * hueToRGB(var1, var2, hue));
rgb[2] = (int) Math.round(255d * hueToRGB(var1, var2, hue - (1d / 3d)));
}
return rgb;
}
1.2.3 HSL转换为RGB * if S = 0,表示灰色,定义R、G和B都为L; * 否则,测试L:
* `if L < 0.5 , temp2 = L x (1.0 + S)`
* `if L >= 0.5, temp2 = L + S - L x S`
temp1 = 2.0 x L - temp2
- 把H转换到0 ~ 1。
-
对于R、G、B,计算另外的临时值temp3。方法如下:
-
for R, temp3 = H + 1.0 / 3.0
-
for G, temp3 = H
-
for B, temp3 = H - 1.0 / 3.0
-
if temp3 < 0, temp3 = temp3 + 1.0
-
if temp3 > 1, temp3 = temp3 - 1.0
-
Java示例:
private static double hueToRGB(double var1, double var2, double varHue) {
if (varHue < 0) {
varHue += 1d;
}
if (varHue > 1d) {
varHue -= 1d;
}
if ((6d * varHue) < 1d) {
return (var1 + (var2 - var1) * 6 * varHue);
}
if ((2d * varHue) < 1d) {
return var2;
}
if ((3 * varHue) < 2) {
return (var1 + (var2 - var1) * ((2d / 3d - varHue) * 6));
}
return var1;
}
2. 随机颜色生成
采用随机数生成RGB颜色,可调节R、G、B基数使颜色的可辨识度大,但不能保证生成的颜色间差异性。 Java示例:
int red = ((int) Math.round(new Double(Math.random() * 128))) + 128;
int green = ((int) Math.round(new Double(Math.random() * 128))) + 128;
int blue = ((int) Math.round(new Double(Math.random() * 128))) + 128;
得到R、G、B颜色后可根据1.2.1小节转换成HSL颜色空间,也可以随机生成HSL颜色空间。
3. 互补色集
3.1 互补色生成步骤
- 转换颜色为HSL;
- 转换Hue为角度值(0⁰ ~ 360⁰),得到Hue值的相反值(如:50⁰ 相反值为230⁰);
- 保持S和L值,转换为回原颜色空间
3.2互补色集设计
- 根据互补色思路可知,如果算法固定S、L值,只调节H值:
- 初始值H为0;
- 下一个值为0⁰ 的互补色,即为180⁰,此时色盘被划分为2个部分。如果下一个值的互补色已经被使用,颜色的选取为距离上一个颜色值最远的最大空间中值。
Java实现如下:
public synchronized int[] getNext(String key) {
int[] rgb = colorMap.get(key);
if (rgb != null) {
return rgb;
}
index ++;
double hue = getNextHueValue();
rgb = ColorConverter.toRGB(hue, hsl[1], hsl[2]);
colorMap.put(key, rgb);
return rgb;
}
private double getNextHueValue() {
if(ranges.isEmpty()) {
init();
ranges.add(new Range(0.0, 0.5));
ranges.add(new Range(0.5, 1.0));
}
if (index == 1) {
return lastValue = 0;
} else if (index == 2) {
return lastValue = 0.5;
}
Range range = fetchMaxDistanceRange();
int idx = ranges.indexOf(range);
ranges.remove(idx);
double halfRange = range.getDistance() / 2.0;
ranges.add(idx, new Range(range.getStart() + halfRange, range.getEnd()));
ranges.add(idx, new Range(range.getStart(), range.getEnd() - halfRange));
return lastValue = range.getStart() + halfRange;
}
private void init() {
if (hsl != null) return;
Random rGen = new Random();
double hue = rGen.nextDouble();
double saturation = 0.48 + 0.2 * rGen.nextDouble();
double brightness = 0.72 + 0.08 * rGen.nextDouble();
hsl = new double[] { hue, saturation, brightness };
}
色图:
随着划分的增加,区间越来越小,颜色的差异性也越来越小。另外此算法中还可动态修改S、L值以获取更多、差异更大的颜色值。
4. 自定义颜色集
当需要的颜色较多时,互补色的设计思路得到的结果颜色集区分度很小,很难达到要求。一种比较可控的思路是在程序中自定义颜色通道值集,生成颜色集:
- 定义Hue值集为:
[0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330]
- 定义Saturation值集为:
[1/4, 2/4, 3/4, 1]
,S值为0时颜色为灰色,区分度小; - 定义Lightness值集为:
[1/8, 2/8, 3/8, 5/8, 6/8, 7/8]
,L值为0和1时,颜色为白色和黑色,1/2和3/8、4/8的颜色太接近;
根据自定义颜色集,可得到288中颜色值,色图如下:
从上面的结果来还看,生成的颜色人眼区分度比较大,而且颜色可选取288种,满足一般的项目需求。
5. 总结
颜色集设计需考虑人眼的可区分度,理论算法中的机器颜色差异性规则在人眼中不一定适用,必须经过大量的数据进行人眼分析,才能得到更恰当的算法。
睿初科技软件开发技术博客,转载请注明出处
blog comments powered by Disqus
发布日期
标签
最近发表
- volatile与多线程
- TDD practice in UI: Develop and test GUI independently by mockito
- jemalloc源码解析-核心架构
- jemalloc源码解析-内存管理
- boost::bind源码分析
- 小试QtTest
- 一个gtk下的目录权限问题
- Django学习 - Model
- Code snippets from C & C++ Code Capsule
- Using Eclipse Spy in GUI products based on RCP
文章分类
- cpp 3
- wxwidgets 4
- swt/jface 1
- chrome 3
- memory_management 5
- eclipse 1
- 工具 4
- 项目管理 1
- cpplint 1
- 算法 1
- 编程语言 1
- python 5
- compile 1
- c++ 7
- 工具 c++ 1
- 源码分析 c++ 3
- c++ boost 2
- data structure 1
- wxwidgets c++ 1
- template 1
- boost 1
- wxsocket 1
- wxwidget 2
- java 2
- 源码分析 1
- 网路工具 1
- eclipse插件 1
- django 1
- gtk 1
- 测试 1
- 测试 tdd 1
- multithreading 1