图像主题色的提取

工作时遇到一个需求:提取图片主题色,通过某种映射关系,选取ui给出的对应颜色。脑海中浮现如果只是纯前端如何实现呢?

一、思路与准备

利用canvas获取图像像素信息,然后用某种算法将主题颜色提取出来。

1.1 了解Canvas画布真实像素原理

MDN: 事实上,你可以直接通过getImageData,返回一个imageData对象,获取场景像素数据。

imageData对象包含下列几个只读属性:

  • width:图片宽度,单位是像素
  • height:图片高度,单位是像素
  • dataUint8ClampedArray类型的一维数组,包含着RGBA格式的整型数据,范围在0至255之间(包括255)。

data属性返回一个 Uint8ClampedArray,它可以被使用作为查看初始像素数据。每个像素用4个 1 bytes值(按照红,绿,蓝和透明值的顺序,"RGBA"格式) 来代表。每个颜色值部份用0至255来代表。每个部分被分配到一个在数组内连续的索引,左上角像素的红色部分在数组的索引0位置。像素从左到右被处理,然后往下,遍历整个数组。 Uint8ClampedArray 包含高度 × 宽度 × 4 bytes数据,索引值从0到(高度×宽度×4)-1

1.2 了解中位切分法 (Median cut)

中位切分法 / MMCQ算法 是指的是 将色彩空间里的颜色按照密集程度分组,先将整个 ColorSpace 视为一个整体A,然后通过计算颜色差值:

找到 median point,然后切割成两个分组A1 和 A2,然后再找到最大的一个组(比如A1),计算差值找到 median point, 继续切割成A3,A4,现在我们有了三个分组(A2,A3,A4),继续找到最大分组切割,直到达到所需的颜色数量 N。然后得到 N 个分组,这 N 个分组的 median point 的颜色值即为要求的颜色值。颜色比例即为 N 个分组的大小比值。

其中color-thief库就是基于中位切分法实现的。

Leptonica 作者在报告 Median-Cut Color Quantization 中总结了这一算法存在的一些问题,其中主要问题是有可能存在某些条件下 VBox 体积很大但只包含少量像素。解决的方法是,每次进行切分时,并不是对上一次切分得到的所有VBox进行切分,而是通过一个优先级队列进行排序,刚开始时这一队列以VBox仅以VBox所包含的像素数作为优先级考量,当切分次数变多之后,将体积*包含像素数作为优先级。

除此之外,算法中最重要的部分是统计色彩分布直方图。我们需要将三维空间中的任意一点对应到一维坐标中的整数,这样才能以最快地速度定位这一颜色。如果采用全部的24位信息,那么我们用于保存直方图的数组长度至少要是224=16777216,既然是要提取颜色主题(或是颜色量化),我们可以将颜色由RGB各8位压缩至5位,这样数组长度只有215=32768

// color-thief部分源码: 
var sigbits = 5,
    rshift = 8 - sigbits,
// get reduced-space color index for a pixel
function getColorIndex(r, g, b) {
    return (r << (2 * sigbits)) + (g << sigbits) + b;
}
function getHisto(pixels) {
    var histosize = 1 << (3 * sigbits),
        histo = new Array(histosize),
        index, rval, gval, bval;
    pixels.forEach(function(pixel) {
        rval = pixel[0] >> rshift;
        gval = pixel[1] >> rshift;
        bval = pixel[2] >> rshift;
        index = getColorIndex(rval, gval, bval);
        histo[index] = (histo[index] || 0) + 1;
    });
    return histo;
}
复制代码

参考:

Pixels and Palettes: Extracting Color Palettes From Images

Pixel_manipulation_with_canvas_By MDN

MMCQ算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值