动手开发一个色卡生成器

2,155 阅读2分钟

这是我参与更文挑战的第 5 天,活动详情查看:更文挑战

前言

一个设计师朋友希望我能够帮他做一个根据图片生成色卡的页面,效果类似下面这张图: image.png

开发

要分析出图片的色彩组成比例,我们就是要能够获取到这张图片上的色彩信息。 在 HTML5 普及的今天,我们可以使用 Canvas 的 getImageData 来获取图片的颜色信息。

**CanvasRenderingContext2D**.getImageData() 返回一个ImageData对象,用来描述canvas区域隐含的像素数据,这个区域通过矩形表示,起始点为_(sx, sy)、_宽为_sw、高为_sh。

获取到颜色信息后,我们只需要统计每种颜色出现次数,然后选择颜色出现次数最多的几种颜色就可以实现了。

收集像素信息

export default function collectPixelData (img) {
  const canvas = document.createElement('canvas')
  const context = canvas.getContext && canvas.getContext('2d')
  context.drawImage(img, 0, 0)
  const idata = context.getImageData(0, 0, canvas.width, canvas.height);
  
  let curIndex = 0
  const step = 4

  const cdata = idata.data
  const arrayOfPixels = []
  while (curIndex + 4 < cdata.length) {
    const [r, g, b, a] = [cdata[curIndex], cdata[curIndex+1], cdata[curIndex+2]]
    arrayOfPixels.push([r,g,b])
  }
  return arrayOfPixels
} 

getImageData 返回的是一个数组,每四个代表一个像素点的 [r,g,b,a] 信息。万万没想到,当我上传图片等待控制台打印信息的时候,页面崩溃了😠。 万事开头难,经过冷静的思考,我猜测可能是图片信息过多导致的。之后加了一个抽样的逻辑,每 10 个像素抽样 1 个,这样就能流畅地获取图片的像素信息了。

while (curIndex + 4 < cdata.length) {
  const [r, g, b, a] = [cdata[curIndex], cdata[curIndex+1], cdata[curIndex+2]]
  arrayOfPixels.push([r,g,b])
  // 抽样的逻辑
  curIndex += 9 * 4
}

像素构成分析

像素信息获取到之后,我们就可以做统计分析了。代码如下:

function rankColors(colorArr) {
  const colorMap = {}
  for (const item of colorArr) {
    const key = item.join('-')
    if (colorMap[key]) {
      colorMap[key] += 1
    } else {
      colorMap[key] = 1
    }
  }
  const colors = Object.keys(colorMap).map(item => ({color: item.split('-'), count: colorMap[item]}))
    .sort((a, b) => b.count - a.count)
  
  return {colors, totalCount: colors.length}
}

最后的结果如图: image.png 面部的粉色没有展示出来。 image.png 上面的两个结果效果都不太理想。图一是主色调分布抽样不够准确,图二是颜色过于分散。 经过分析日志,其实图片上肉眼看起来一样的颜色在程序里的的颜色数值也会不同(比如 rgb(125,0,0) 和 rgb(126,0,0),这就导致了分析不准和颜色过于分散的原因。

颜色提取算法

上面出现的问题怎么解决呢?一个思路就是在颜色相近的颜色可归类为一个颜色,这就涉及到算法的知识了,图像主题色提取算法这篇文章对算法有更为详细的介绍。 后来找到了 quantize 这个开源库可以来优化颜色的提取,优化后的结果如下: image.png 准确了一些,至少脸部的粉色被分析出来了。 image.png 还是不太准确,车尾灯的红色也没有展示。

后记

上文示例的源码地址: wuse-color。另外,已经有基于 quantize 获取图片主题色的开源项目了,仓库地址:color-thief

后来又找到一个颜色分析做的非常好的网站: adobe color。具体效果如下: image.png 它不但考虑了颜色的比例,而且也结合了颜色的位置关系来生成色卡,可以说是非常赞了!

参考资料