图像主题色提取简单实现

图像主题色提取简单实现

图像主题颜色的应用越来越多,如google相册 可以按照颜色对图片进行检索,design-seeds 上的色彩设计。相关算法主要有中位切分法、八叉树提取法和KMean clustering 等算法。关于这些算法,推荐这两篇文章,写的非常好:图像主题色提取算法 和 图片主题色提取算法小结 ,本文只就中位切分法(Median cut)进行js的简单实现。

RGB色彩模式下,R/G/B的取值分别为0x00~0xff(0~255),将其想象成一个以RGB分别为维度的三维空间,在取值范围内构成一个立方体

此处我以空间均等八分为例(R/G/B各以128切分),取得八个空间的像素点分布,计算其平均值。

HTML 代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
	<meta charset="UTF-8" />
	<link rel="stylesheet" href="style.css">
	<script src="jquery-2.1.0.js"></script>
	<script src="octree.js"></script>
	<script src="color.js"></script>
</head>
<body>
<img src="image/044a.jpg" id="image"/>

<div id="colorx"></div>
<div id="colory"></div>

</body>
</html>

JS 代码(color.js)

$(function() {
  function rgb(r, g, b, count) {
    this.r = r;
    this.g = g;
    this.b = b;
    this.count = count;
  }

  $("#image").one("load",function() {
    var blockSize = 47, // 选取密度
    canvas = document.createElement('canvas'), // 画布
    context = canvas.getContext('2d'), data, width, height, i = -4, length, red, green, blue, count = 0, rgbArray = [];
    for (var j = 0; j < 8; j++) {
      rgbArray[j] = new rgb(0, 0, 0, 0);
    }
    if (!context) {
      // 未获取,设置默认值
      // rgb = { r: 51, g: 104, b: 172 }
    } else {
      height = canvas.height = this.naturalHeight;
      width = canvas.width = this.naturalWidth;
      context.drawImage(this, 0, 0);
      try {
        data = context.getImageData(0, 0, width, height);
        length = data.data.length;
        // 将颜色放入对应位置
        while ((i += blockSize * 4) < length) {
          var rIndex = (data.data[i] - 128) >> 31;
          var gIndex = (data.data[i + 1] - 128) >> 31;
          var bIndex = (data.data[i + 2] - 128) >> 31;
          var index = ((rIndex + 1) << 2) + ((gIndex + 1) << 1)
              + (bIndex + 1); // 计算二进制索引
          rgbArray[index].r += data.data[i];
          rgbArray[index].g += data.data[i + 1];
          rgbArray[index].b += data.data[i + 2];
          rgbArray[index].count++;
        }
  
        rgbArray.sort(function(a, b) {
          return a.count - b.count
        })
        // 计算最终颜色
        var html = "";
        for (var k = 0; k < 8; k++) {
          var r = ~~(rgbArray[k].r / rgbArray[k].count);
          var b = ~~(rgbArray[k].b / rgbArray[k].count);
          var g = ~~(rgbArray[k].g / rgbArray[k].count);
          var rStr = r.toString(16);
          var gStr = g.toString(16);
          var bStr = b.toString(16);
          if (rStr.length === 1)
            rStr = '0' + rStr;
          if (gStr.length === 1)
            gStr = '0' + gStr;
          if (bStr.length === 1)
            bStr = '0' + bStr;
  
          var colorStr = rStr + gStr + bStr;
          if ("000000" != colorStr)
            html += "<div class=\"color\" style=\"background:#"
                + colorStr + "\">#" + colorStr + "</div>";
        }
        $("#colory").append(html).append("<div class=\"clear\"></div>");
      } catch (e) {
        console.info(e)
        //rgb = { r: 51, g: 104, b: 172 }
      }
    }
  })
})

结果展示

其中第一排颜色是根据图片主题色提取算法小结 中的八叉树算法提取的,这张图片色域比较大,两种结果较非常接近,下面更换图片看看结果

结果依旧相近,但中位切分法获取了一个#000000结果被过滤了。

执行效率

在效率上,如本文中八均分的中位切分效率还算可观,但是一旦目标颜色数目上升后,效率会大大降低。对于八叉树算法,先进行颜色遍历建树,然后根据目标颜色树进行合并,目标颜色数目越多,合并的次数会越少,计算效率不降反升。

2 条评论

  1. 一二三兮2017 年 05 月 18 日下午 9:42 回复

    你好,可以开源吗

    • snowtraces*2017 年 05 月 19 日下午 1:24 回复

      可以的