相关名词:
- 主色:图像区域的主要颜色
- 色样(Swatch):一段相近的颜色区间
- 饱和度:色彩的鲜艳程度。饱和度越高,越逼近于原始色,饱和度越低,颜色越接近于白色。
引入方式:
implementation 'androidx.palette:palette:1.0.0'
场景:
如下图所示,可以根据网易云歌曲封面图的主色(或之一)对文字、背景颜色、图案颜色进行定制化处理。
六种主色及其含义:
-
Vibrant (有活力的): 具有既不亮也不暗的高饱和度的颜色
-
Vibrant dark(有活力的 暗色): 具有亮的高饱和度的颜色
-
Vibrant light(有活力的 亮色): 具有暗的高饱和度的颜色
-
Muted (柔和的): 具有既不亮也不暗的低饱和度的颜色
-
Muted dark(柔和的 暗色): 具有亮的低饱和度的颜色
-
Muted light(柔和的 亮色): 具有暗的低饱和度的颜色
总之,6种主色主要靠饱和度和亮度进行区分,在代码中可以看到,platte对亮、暗、既不亮也不暗的区分如下:
也即:
platte使用方法
初始化
platte提供两种初始化方式,分别是同步和异步
// SynchronousPalette p = Palette.from(bitmap).generate();// AsynchronousPalette.from(bitmap).generate(new PaletteAsyncListener() {public void onGenerated(Palette p) {// Use generated instance}})
色样Swatch
Swatch是Platte定义的静态类,提供获取RGB颜色空间下的主色、Hsl颜色空间下的主色、文字标题色、文字内容色等方法。
-
public int getRgb()
-
public float[] getHsl()
-
public int getTitleTextColor()
-
public int getBodyTextColor()
实现原理
1. 对图像建立颜色直方图
2.直方图量化为16个颜色区域
3.对每个区域生成一个色样
4.计算每个色样在6个主色下的得分
5.返回不同主色下得分最高的色样
直方图化
/**
* Execute color quantization.
*
* @param pixels histogram representing an image's pixel data
* @param maxColors The maximum number of colors that should be in the result palette.
* @param filters Set of filters to use in the quantization stage
*/
public void quantize(final int[] pixels, final int maxColors, final Palette.Filter[] filters) {mFilters = filters;// 2^15final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)];for (int i = 0; i < pixels.length; i++) {final int quantizedColor = quantizeFromRgb888(pixels[i]);// Now update the pixel value to the quantized value pixels[i] = quantizedColor;// And update the histogram hist[quantizedColor]++;}// Now let's count the number of distinct colors int distinctColorCount = 0;for (int color = 0; color < hist.length; color++) {if (hist[color] > 0 && shouldIgnoreColor(color)) {// If we should ignore the color, set the population to 0 hist[color] = 0;}if (hist[color] > 0) {// If the color has population, increase the distinct color count distinctColorCount++;}}// Now lets go through create an array consisting of only distinct colors final int[] colors = mColors = new int[distinctColorCount];int distinctColorIndex = 0;for (int color = 0; color < hist.length; color++) {if (hist[color] > 0) {colors[distinctColorIndex++] = color;}}if (distinctColorCount <= maxColors) {// The image has fewer colors than the maximum requested, so just return the colors mQuantizedColors = new ArrayList<>();for (int color : colors) {mQuantizedColors.add(new Swatch(approximateToRgb888(color), hist[color]));}} else {// We need use quantization to reduce the number of colors mQuantizedColors = quantizePixels(maxColors);}
}
-
新建直方图,用于记录将RGB888量化到RGB565后的颜色直方图。
-
统计不同颜色个数,(注:接近白色、黑色,或接近I线的红色则不计,并清空对应计数)
-
新建数组colors,长度为不同颜色的个数,用来存储直方图中数量不为0的颜色的索引。
-
如果不同颜色个数过少(少于16),那直接根据当前的不同颜色创建样式即可。
-
否则,将其量化为16种颜色并创建样式。
量化
在量化过程中,新建优先队列对量化后的结果进行存储,每次取出队首,然后拆分成两份后放入。量化以VBox为单位,VBox用于表示一段连续的颜色索引,并记录rgb三个通道最大值/最小值,开始将整个颜色作为放入一个VBox,然后置于优先队列中。每次拆分中,按照颜色个数均分成两份。最后计算每个VBox的平均颜色,由此新建Swatch并返回。在Swatch中字体颜色会根据平均颜色对黑色/白色的透明度进行计算。
打分
对于指定主色类型,将对每个hsl在指定范围内对swatch进行打分,并选择分数最高者。(如果都不在范围内,那么返回空)
/*** Returns the dominant swatch from the palette.** <p>The dominant swatch is defined as the swatch with the greatest population (frequency)* within the palette.</p>*/@Nullablepublic Swatch getDominantSwatch() {return mDominantSwatch;}@Nullableprivate Swatch findDominantSwatch() {int maxPop = Integer.MIN_VALUE;Swatch maxSwatch = null;for (int i = 0, count = mSwatches.size(); i < count; i++) {Swatch swatch = mSwatches.get(i);if (swatch.getPopulation() > maxPop) {maxSwatch = swatch;maxPop = swatch.getPopulation();}}return maxSwatch;
待优化项
-
效率一般,对整个视频实时性不是特别友好(先分了16个色样,然后逐个判断色样是不是亮暗、是不是活泼柔和,然后返回。而不是一开始以亮暗、活泼柔和这个目标去筛选颜色)
-
一个色样拆分为两个时,仅仅是简单地以数量进行平均分,而不是寻找最优分类点(fisher、svm)
参考demo
https://github.com/DingMouRen/PaletteImageView
参考文献
根据一张图片获取主颜色的算法是什么样的? - 知乎
效果
测试图像
效果
运行时间
bitmap大小 | 平均执行时间 |
640*400*3 | 40ms |
750*450*3 | 60ms |
1920*1280*3 | 400ms |
其他主色方法
聚类
分类
本文链接:https://my.lmcjl.com/post/8866.html
4 评论