本文分為兩部分,此為第二部分,第一部分可以檢視 這裡。
接著上文講的,可以呼叫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就行了。加油吧。