本文分为两部分,此为第二部分,第一部分可以查看 这里。
接着上文讲的,可以调用MedianCutQuantizer对象的getQuantizedColors()这个方法可以获取调色板。我们可以以颜色使用的数量和比重来对这个集合进行降序显示。很不幸的是结果表明大多数图像用的颜色是黑色和白色(或相近的颜色),这颜色根本不能让我们的应用显得更独特,所以我们要考虑到底选择什么颜色了。
对于我自己的应用来说,我准备使用以下的调色方案:
- 第一位的主色是一种鲜艳的颜色;
- 第二位主色是区别一于第一位主色的另一种亮色;
- 第三位主色是和第一位和第二位主色对比强烈的颜色;
- 一种主要的字体颜色,和整体主色对比明显,可读性强;
- 第二种主要字体颜色就是白色或者黑色,取决于整体主色的亮度,可读性强。
这篇文章主要讲的也就是怎么选择这些颜色。
主色
根据以上我的需求,我决定使用以下因素的平均值:
- 鲜艳度;
- 热度(受欢迎程度)。
鲜艳度
这个其实也很简单,首先要把RGB颜色模型转化成HSV颜色模型,使用Android内置的[Color.RGBToHSV()](https://developer.android.com/reference/android/graphics/Color.html#RGBToHSV(int, int, int, float[]))方法可以做到。如果不明白HSV颜色模型可以看 这里。
简单地说,这个圆柱形就代表了RGB颜色模型,通过三个坐标来表示颜色:Hue,Saturation和Value(明度)。
HSV颜色模型,来自 Wikipedia
我使用一个简单的方式去计算鲜艳度,通过饱和度(saturation )和色度(value)。在人眼看来这两个值越高,鲜艳度就越高。
1 2 3 4 5 6 7 8 9 10 11 |
; html-script: false ] public float[] getHsv() { float[] hsv = new float[3]; Color.RGBToHSV(r, g, b, hsv); return hsv; } public float calculateColorfulness() { float[] hsv = getHsv(); return hsv[1] * hsv[2]; } |
计算的结果会在0.0到1.0的范围内。
热度
还记得之前说过每个颜色都有一个绑定的值吗?这里可以使用这个值来决定一种颜色在调色板中的受欢迎程度。记住值得范围是在0.0到1.0之间。
也就是说我们得到了如下的简单的调色板:
1 2 3 4 5 6 7 8 9 10 11 |
| Color | Count | ---------------------- | White | 200 | | Purple | 175 | | Black | 150 | | Red | 125 | | Orange | 100 | | Blue | 50 | ---------------------- | Total | 800 | |
我们可以通过图像中的这个比例来计算出颜色占有的比例,上图中有800像素,以紫色为例,它的颜色比例为:175 / 800 = ~0.22。可是这个值很小,只能接近1而已。
反之我们可以选择调色板中最受欢迎的颜色作为基准来计算这个比例。还是用上一个例子,白色是最受欢迎的颜色,所以紫色的比例就是:175 / 200 = 0.87。相对于颜色的受欢迎程度来说,这个更具代表性。
最终值
这里要使用这些值来结合成一个最终的值,这样简单合成没有问题,但是之前说了黑白色是最受欢迎的颜色,考虑到这个,这里我们做一个权重,来决定一些属性的重要程度,这种情况下我们提高了鲜艳度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
; html-script: false ] static float weightedAverage(float... values) { assert values.length % 2 == 0; float sum = 0; float sumWeight = 0; for (int i = 0; i = SECONDARY_MIN_DIFF_HUE_PRIMARY) { return candidate; } } // If we get here, just return the second weighted color return mWeightedPalette[1]; |
第三位主色
这种颜色和上面第二位主色很相似,但是这次就不找Hue值了,我们直接对比前两种颜色就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
; html-script: false ] // Contrast values are in the range 0-255. private static final int TERTIARY_MIN_CONTRAST_PRIMARY = 20; private static final int TERTIARY_MIN_CONTRAST_SECONDARY = 90; ... // Find the first color which has sufficient contrast from both the primary & secondary for (ColorNode color : mWeightedPalette) { if (ColorUtils.calculateContrast(color, primary) >= TERTIARY_MIN_CONTRAST_PRIMARY && ColorUtils.calculateContrast(color, secondary) >= TERTIARY_MIN_CONTRAST_SECONDARY) { return color.getRgb(); } } // We couldn't find a colour. In that case use the primary colour, modifying it's // brightness by 45% return ColorUtils.changeBrightness(secondary.getRgb(), 0.45f); |
来看一下,calculateContrast()这个方法哪来的?这个我也想了很久,其实它来自这篇文章 color contrast。
最后我再把RGB颜色模型转换成了YIQ颜色模型,仅仅携带了Y(亮度)值,之后你可以对比下两种颜色的亮度值,看看在明度上有什么不一样,再用临界值来试试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
; html-script: false ] /** * @return difference in luma. Possible values are 0 (no difference) to * 255 (max difference). */ private static final int calculateContrast(int rgbColor1, int rgbColor2) { return Math.abs(calculateYiqLuma(rgbColor1) - calculateYiqLuma(rgbColor2)); } /** * @return luma value. Values are in the range 0-255. */ public static final int calculateYiqLuma(int color) { return (299 * Color.red(color) + 587 * Color.green(color) + 114 * Color.green(color)) / 1000; } |
代码
本文第一部分的时候,我说过要发代码的,看这里:
https://gist.github.com/chrisbanes/ba8e7b9ec0e40f6949c6
这代码也许跑不起来,所以需要修改一下然后包含到你的APP中,这是仅仅是为了让你知道怎么把它集成到APP中,所有重要的东西都在这里了,你只需要考虑怎么集成进你的APP就行了。加油吧。