共计 6894 个字符,预计需要花费 18 分钟才能阅读完成。
// // 八叉树算法
// (function () {// var OctreeNode = function () {
// this.isLeaf = false;
// this.pixelCount = 0;
// this.red = 0;
// this.green = 0;
// this.blue = 0;
// this.children = [null, null, null, null, null, null, null, null];
// this.next = null;
// }
// var root = null,
// leafNum = 0,
// colorMap = null,
// reducible = null;
// function createNode(index, level) {// var node = new OctreeNode();
// if (level === 7) {
// node.isLeaf = true;
// leafNum++;
// } else {
// // 将其丢到第 level 层的 reducible 链表中
// node.next = reducible[level];
// reducible[level] = node;
// }
// return node;
// }
// function addColor(node, color, level) {// if (node.isLeaf) {
// node.pixelCount += 1;
// node.red += color.r;
// node.green += color.g;
// node.bllue += color.b;
// }
// else {
// var str = "";
// var r = color.r.toString(2);
// var g = color.g.toString(2);
// var b = color.b.toString(2);
// while (r.length < 8) r = '0' + r;
// while (g.length < 8) g = '0' + g;
// while (b.length < 8) b = '0' + b;
// str += r[level];
// str += g[level];
// str += b[level];
// var index = parseInt(str, 2);
// if (null === node.children[index]) {// node.children[index] = createNode(index, level + 1);
// }
// if (undefined === node.children[index]) {// console.log(index, level, color.r.toString(2));
// }
// addColor(node.children[index], color, level + 1);
// }
// }
// function reduceTree() {
// // 找到最深层次的并且有可合并节点的链表
// var level = 6;
// while (null == reducible[level]) {
// level -= 1;
// }
// // 取出链表头并将其从链表中移除
// var node = reducible[level];
// reducible[level] = node.next;
// // 合并子节点
// var r = 0;
// var g = 0;
// var b = 0;
// var count = 0;
// for (var i = 0; i < 8; i++) {// if (null === node.children[i]) continue;
// r += node.children[i].red;
// g += node.children[i].green;
// b += node.children[i].blue;
// count += node.children[i].pixelCount;
// leafNum--;
// }
// // 赋值
// node.isLeaf = true;
// node.red = r;
// node.green = g;
// node.blue = b;
// node.pixelCount = count;
// leafNum++;
// }
// function buidOctree(imageData, maxColors) {
// var total = imageData.length / 4;
// for (var i = 0; i < total; i++) {
// // 添加颜色
// addColor(root, {// r: imageData[i * 4],
// g: imageData[i * 4 + 1],
// b: imageData[i * 4 + 2]
// }, 0);
// // 合并叶子节点
// while (leafNum > maxColors) reduceTree();
// }
// }
// function colorsStats(node, object) {// if (node.isLeaf) {// var r = parseInt(node.red / node.pixelCount);
// var g = parseInt(node.green / node.pixelCount);
// var b = parseInt(node.blue / node.pixelCount);
// var color = r + ',' + g + ',' + b;
// if (object[color]) object[color] += node.pixelCount;
// else object[color] = node.pixelCount;
// return;
// }
// for (var i = 0; i < 8; i++) {// if (null !== node.children[i]) {// colorsStats(node.children[i], object);
// }
// }
// }
// window.themeColor = function (img, callback) {// var canvas = document.createElement('canvas'),
// ctx = canvas.getContext('2d'),
// width = 0,
// height = 0,
// imageData = null,
// length = 0,
// blockSize = 1;
// width = canvas.width = img.width;
// height = canvas.height = img.height;
// ctx.drawImage(img, 0, 0, width, height);
// imageData = ctx.getImageData(0, 0, width, height).data;
// root = new OctreeNode();
// colorMap = {};
// reducible = {};
// leafNum = 0;
// buidOctree(imageData, 8)
// colorsStats(root, colorMap)
// var arr = [];
// for (var key in colorMap) {// arr.push(key);
// }
// arr.sort(function (a, b) {// return colorMap[a] - colorMap[b];
// })
// arr.forEach(function (item, index) {// arr[index] = item.split(',')
// })
// callback(arr)
// }
// })()
// 中位切分法
(function () {
/**
* 颜色盒子类
*
* @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) { // r:0,g:1,b:2
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
}
// 另一种切割颜色判断方法,根据数量和差值的乘积进行判断,自己试验后发现效果不如中位数方法,但是少了排序,性能应该有所提高
// var count = 0;
// var colorMin = arr[0].color;
// var colorMax = arr[arr.length - 1].color
// for (var i = 0; i < arr.length; i++) {// count += arr[i].count;
// var item = arr[i];
// if (count * (item.color - colorMin) > (total - count) * (colorMax - item.color)) {
// return {
// color: item.color,
// count: count
// }
// }
// }
return {
color: colorMax,
count: count
}
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, 8)
}
function themeColor(img, callback) {var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
width = 0,
height = 0,
imageData = null,
length = 0,
blockSize = 1,
cubeArr = [];
width = canvas.width = img.width;
height = canvas.height = img.height;
ctx.drawImage(img, 0, 0, width, height);
imageData = ctx.getImageData(0, 0, width, height).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())
}
callback(colorArr);
}
window.themeColor = themeColor
})()
正文完
发表至: javascript
2020-06-20