Platte是Google提供的提取图像颜色的类。Platte由Bitmap创建,并提供六种主色和对应的色样。
相关名词:
- 主色:图像区域的主要颜色
- 色样(Swatch):一段相近的颜色区间
- 饱和度:色彩的鲜艳程度。饱和度越高,越逼近于原始色,饱和度越低,颜色越接近于白色。
引入方式:
implementation 'androidx.palette:palette:1.0.0'
场景:
如下图所示,可以根据网易云歌曲封面图的主色(或之一)对文字、背景颜色、图案颜色进行定制化处理。
六种主色及其含义:
-
Vibrant (有活力的): 具有既不亮也不暗的高饱和度的颜色
-
Vibrant dark(有活力的 暗色): 具有亮的高饱和度的颜色
-
Vibrant light(有活力的 亮色): 具有暗的高饱和度的颜色
-
Muted (柔和的): 具有既不亮也不暗的低饱和度的颜色
-
Muted dark(柔和的 暗色): 具有亮的低饱和度的颜色
-
Muted light(柔和的 亮色): 具有暗的低饱和度的颜色
总之,6种主色主要靠饱和度和亮度进行区分,在代码中可以看到,platte对亮、暗、既不亮也不暗的区分如下:
也即:
暗:亮度值0~0.45
既不亮也不暗:亮度值0.3~0.7
亮:亮度值0.55~1
低饱和度:0~0.4
高饱和度:0.35~1
platte使用方法
初始化
platte提供两种初始化方式,分别是同步和异步
// Synchronous
Palette p = Palette.from(bitmap).generate();
// Asynchronous
Palette.from(bitmap).generate(new PaletteAsyncListener() {
public void onGenerated(Palette p) {
// Use generated instance
}
})
由于色样在Platte创建时已经生成,因此推荐使用异步方法创建,防止图像过大阻塞主线程。
色样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^15
final 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>
*/
@Nullable
public Swatch getDominantSwatch() {
return mDominantSwatch;
}
@Nullable
private 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 |
其他主色方法
聚类
将直方图根据颜色距离最后聚成指定数量的簇,取簇内颜色的均值代表主色。
分类
将直方图根据指定颜色分类表分至不同的段内,统计每个段的次数。