图片主题色

// 中位切分法
/**
 * 颜色盒子类
 *
 * @param {Array} colorRange    [[rMin, rMax],[gMin, gMax], [bMin, bMax]] 颜色范围
 * @param {any} total   像素总数, imageData / 4
 * @param {any} data    像素数据集合
 */
function ColorBox(colorRange, total, data) {
  this.colorRange = colorRange;
  this.total = total;
  this.data = data;
  this.volume = (colorRange[0][1] - colorRange[0][0]) * (colorRange[1][1] - colorRange[1][0]) * (colorRange[2][1] - colorRange[2][0]);
  this.rank = this.total * (this.volume);
}

ColorBox.prototype.getColor = function () {
  var total = this.total;
  var data = this.data;

  var redCount = 0,
    greenCount = 0,
    blueCount = 0;

  for (var i = 0; i < total; i++) {
    redCount += data[i * 4];
    greenCount += data[i * 4 + 1];
    blueCount += data[i * 4 + 2];
  }

  return [parseInt(redCount / total), parseInt(greenCount / total), parseInt(blueCount / total)];
}

// 获取切割边
function getCutSide(colorRange) {
  var arr = [];
  for (var i = 0; i < 3; i++) {
    arr.push(colorRange[i][1] - colorRange[i][0]);
  }
  return arr.indexOf(Math.max(arr[0], arr[1], arr[2]));
}

// 切割颜色范围
function cutRange(colorRange, colorSide, cutValue) {
  var arr1 = [];
  var arr2 = [];
  colorRange.forEach(function (item) {
    arr1.push(item.slice());
    arr2.push(item.slice());
  })
  arr1[colorSide][1] = cutValue;
  arr2[colorSide][0] = cutValue;
  return [arr1, arr2];
}

// 找到出现次数为中位数的颜色
function getMedianColor(colorCountMap, total) {
  var arr = [];
  for (var key in colorCountMap) {
    arr.push({
      color: parseInt(key),
      count: colorCountMap[key]
    })
  }

  var sortArr = __quickSort(arr);
  var medianCount = 0;
  var medianColor = 0;
  var medianIndex = Math.floor(sortArr.length / 2)

  for (var i = 0; i <= medianIndex; i++) {
    medianCount += sortArr[i].count;
  }

  return {
    color: parseInt(sortArr[medianIndex].color),
    count: medianCount
  }

  function __quickSort(arr) {
    if (arr.length <= 1) {
      return arr;
    }
    var pivotIndex = Math.floor(arr.length / 2),
      pivot = arr.splice(pivotIndex, 1)[0];

    var left = [],
      right = [];
    for (var i = 0; i < arr.length; i++) {
      if (arr[i].count <= pivot.count) {
        left.push(arr[i]);
      }
      else {
        right.push(arr[i]);
      }
    }
    return __quickSort(left).concat([pivot], __quickSort(right));
  }
}

// 切割颜色盒子
function cutBox(colorBox) {
  var colorRange = colorBox.colorRange,
    cutSide = getCutSide(colorRange),
    colorCountMap = {},
    total = colorBox.total,
    data = colorBox.data;

  // 统计出各个值的数量
  for (var i = 0; i < total; i++) {
    var color = data[i * 4 + cutSide];

    if (colorCountMap[color]) {
      colorCountMap[color] += 1;
    }
    else {
      colorCountMap[color] = 1;
    }
  }
  var medianColor = getMedianColor(colorCountMap, total);
  var cutValue = medianColor.color;
  var cutCount = medianColor.count;
  var newRange = cutRange(colorRange, cutSide, cutValue);
  var box1 = new ColorBox(newRange[0], cutCount, data.slice(0, cutCount * 4)),
    box2 = new ColorBox(newRange[1], total - cutCount, data.slice(cutCount * 4))
  return [box1, box2];
}

// 队列切割
function queueCut(queue, num) {

  while (queue.length < num) {

    queue.sort(function (a, b) {
      return a.rank - b.rank
    });
    var colorBox = queue.pop();
    var result = cutBox(colorBox);
    queue = queue.concat(result);
  }

  return queue.slice(0, num);
}

function getImageData(src) {
  return new Promise((resolve, reject) => {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    const img = new Image();

    img.onload = () => {
      canvas.height = img.height;
      canvas.width = img.width;
      context.drawImage(img, 0, 0)

      const data = context.getImageData(0, 0, img.width, img.height).data

      resolve({ img, data })
    }
    img.onerror = () => reject(Error('Image loading failed.'))
    img.crossOrigin = ''
    img.src = src
  })
}

// 亮度
function luminance(r, g, b) {
  var a = [r, g, b].map((v) => {
    v /= 255;
    return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
  });
  return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}

function getColors(color) {

  const lum1 = luminance(...color);
  let result = [];
  for (let r = 0; r <= 255; r += 5) {
    for (let g = 0; g <= 255; g += 5) {
      for (let b = 0; b <= 255; b += 5) {
        const lum2 = luminance(r, g, b);
        
        const brightest = Math.max(lum1, lum2);
        const darkest = Math.min(lum1, lum2);
        // 对比度
        if ((brightest + 0.05) / (darkest + 0.05) >= 16) {
          result.push([r, g, b]);
        }
      }
    }
  }
  const step = parseInt(result.length / 4);
  const array = [];
  for (let i = 0; i < 4; i++) {
    array.push(result[i * step]);
  }
  return array;
}

export default function imageColor(url, callback) {

  var canvas = document.createElement('canvas'),
    ctx = canvas.getContext('2d'),
    width = 0,
    height = 0,
    imageData = null,
    length = 0,
    blockSize = 1,
    cubeArr = [];

  getImageData(url).then(({ img, data }) => {
    width = canvas.width = img.width;
    height = canvas.height = img.height;
    imageData = data;

    var total = imageData.length / 4;
    
    var rMin = 255,
      rMax = 0,
      gMin = 255,
      gMax = 0,
      bMin = 255,
      bMax = 0;

    // 获取范围
    for (var i = 0; i < total; i++) {
      var red = imageData[i * 4],
        green = imageData[i * 4 + 1],
        blue = imageData[i * 4 + 2];

      if (red < rMin) {
        rMin = red;
      }

      if (red > rMax) {
        rMax = red;
      }

      if (green < gMin) {
        gMin = green;
      }

      if (green > gMax) {
        gMax = green;
      }

      if (blue < bMin) {
        bMin = blue;
      }

      if (blue > bMax) {
        bMax = blue;
      }
    }

    var colorRange = [[rMin, rMax], [gMin, gMax], [bMin, bMax]];
    var colorBox = new ColorBox(colorRange, total, imageData);

    var colorBoxArr = queueCut([colorBox], 8);

    var colorArr = [];
    for (var j = 0; j < colorBoxArr.length; j++) {
      colorBoxArr[j].total && colorArr.push(colorBoxArr[j].getColor())
    }

    let r = 0, g = 0, b = 0;
    colorArr.forEach(c => {
      r += c[0];
      g += c[1];
      b += c[2];
    })
    const result = getColors([parseInt(r / colorArr.length), parseInt(g / colorArr.length), parseInt(b / colorArr.length)]);

    callback(result);

  })
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值