我有这样的图像:
并希望在图片中找到特定的颜色(#ffa400
/ #ffffff
)并获取其百分比出现值(橙色可能为:75%
,白色:25%
)
注意:请注意,我不想像Color Thief那样获得平均颜色(在这种情况下:橙色),但是我喜欢找到SPECIFIC颜色。
下面是一个片段,所以这就是我到目前为止所做的一切,但代码似乎并不起作用。我的想法似乎也不是一个有效的& 快速方式。
function extract_colors(img) {
var canvas = document.createElement("canvas");
var c = canvas.getContext('2d');
c.width = canvas.width = img.width;
c.height = canvas.height = img.height;
c.clearRect(0, 0, c.width, c.height);
c.drawImage(img, 0, 0, img.width, img.height);
return getColors(c);
}
function getColors(c) {
var col, colors = {};
var pixels, r, g, b, a;
r = g = b = a = 0;
pixels = c.getImageData(0, 0, c.width, c.height);
for (var i = 0, data = pixels.data; i < data.length; i + = 4) {
r = data[i];
g = data[i + 1];
b = data[i + 2];
a = data[i + 3];
if (a < (255 / 2))
continue;
col = rgbToHex(r, g, b);
if (col == 'ffa400') { // find color #ffa400
if (!colors[col])
colors[col] = 0;
colors[col] + +;
}
}
return colors;
}
function rgbToHex(r, g, b) {
return ((r << 16) | (g << 8) | b).toString(16);
}
var img = document.getElementById('img');
out.innerHTML = extract_colors(img)
&#13;
<img id='img' width=100 src='https://i.stack.imgur.com/EPDlQ.png'>
<p id='out'>...</p>
&#13;
编辑1.0
我试图使用这样的代码,但它似乎也没有用。
var orangeMatches=0, whiteMatches=0
Jimp.read("https://i.stack.imgur.com/EPDlQ.png").then(function (image) {
image.scan(0, 0, image.bitmap.width, image.bitmap.height, function (x, y, idx) {
var red = this.bitmap.data[ idx + 0 ];
var green = this.bitmap.data[ idx + 1 ];
var blue = this.bitmap.data[ idx + 2 ];
var alpha = this.bitmap.data[ idx + 3 ];
if (red == 255 && green == 164 && blue == 0 && alpha == 1){
orangeMatches++;
}
if (red == 255 && green == 255 && blue == 255 && alpha == 1){
whiteMatches++;
}
});
console.log(orangeMatches, whiteMatches)
});
编辑2.0
我希望有一些宽容价值(10%),这样就不会计算每种颜色,但匹配在一起的颜色(例如#ffa400#ffa410)会一起计算。< / p>
答案 0 :(得分:5)
一些注释
图片中的主色实际上是ffa500,而不是ffa400。实际上根本没有ffa400的出现。
另请注意,这是一个消除锯齿的位图图形。在视觉上我们只看到两种颜色,但是为了使从一种颜色到另一种颜色的过渡平滑,产生了一组“中间颜色”。换句话说,ffffff和ffa500的总和不会完全是100%。 (这就是为什么下面我的函数的输出将显示比所讨论的两个颜色更多的颜色)
解决方案1
此解决方案的基本思想是获取图片中的每种颜色,将这些颜色存储在一个共同的对象中,在该对象中计算每种颜色的出现次数。返回此对象后,我们可以稍后计算输出中某些特定颜色的出现次数。
这将返回如下内容:
{
ffa500: 7802,
ffa501: 4,
ffa502: 2,
...,
ffffff: 1919,
total: 10000
}
现在您可以通过这种方式访问给定颜色的出现位置:
var imageData = extract_colors(img),
white = imageData.ffffff,
total = imageData.total;
要显示给定颜色覆盖的图像的百分比,请执行以下操作(toFixed()只是将值限制为两位小数):
whitePct = (white / total * 100).toFixed(2);
工作样本
function getColors(ctx) {
// Get the canvas data
var pixels = ctx.getImageData(0, 0, ctx.width, ctx.height),
data = pixels.data,
// Set up our output object to collect color data
output = {};
// For each color we encounter, check the
// output object. If the color already exists
// there, simply increase its counted value.
// If it does not, create a new key.
for (var i = 0; i < data.length; i+=4) {
var r = data[i],
g = data[i + 1],
b = data[i + 2],
col = rgbToHex(r, g, b);
if( output[col] )
output[col]++
else
output[col] = 1
}
// Count total
var total = 0;
for(var key in output) {
total = total + parseInt(output[key])
}
output.total = total;
// Return the color data as an object
return output;
}
// Our elements
var img = document.getElementById('img'),
out = document.getElementById('out');
// Retrieve the image data
var imageData = extract_colors(img),
// Count our given colors
white = imageData.ffffff,
orange = imageData.ffa500,
total = imageData.total,
// Calculate percentage value
whitePct = (white / total * 100).toFixed(2),
orangePct = (orange / total * 100).toFixed(2);
out.innerHTML = `White: ${whitePct}%<br/>Orange: ${orangePct}%`;
// See the console for all colors identified
console.log(extract_colors(img))
// ----- These functions are left untouched ----- \\
function extract_colors(img) {
var canvas = document.createElement("canvas");
var c = canvas.getContext('2d');
c.width = canvas.width = img.width;
c.height = canvas.height = img.height;
c.clearRect(0, 0, c.width, c.height);
c.drawImage(img, 0, 0, img.width, img.height);
return getColors(c);
}
function rgbToHex(r, g, b) {
return ((r << 16) | (g << 8) | b).toString(16);
}
p, img {
margin: 10px 5px;
float: left
}
<!-- Image converted to base64 to keep it locally availible -->
<img id='img' width=100 src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAADImlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4QUUwNzk4N0I2QjIxMUUzQUQzMkIzMTA4REQ0Nzc1MiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4QUUwNzk4OEI2QjIxMUUzQUQzMkIzMTA4REQ0Nzc1MiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkI1NDU5MjAyQjZCMTExRTNBRDMyQjMxMDhERDQ3NzUyIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjhBRTA3OTg2QjZCMjExRTNBRDMyQjMxMDhERDQ3NzUyIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwv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'>
<p id='out'>...</p>
解决方案2
(考虑这个解决方案草案。如果要用于实时网站,可能需要进行一些调整,但至少它应该有效!)
这里的想法是用我们想要映射为参数的颜色调用extract_colors()
。构建一个公共对象来保存输出,并为每种颜色填充一个对象。颜色对象由计数器和rgb值组成。使用isNeighborColor()
函数,rgb值不仅用于匹配特定颜色,还用于匹配与其相近的任何颜色。根据需要调整公差等级。 (理想情况下,这是我猜的另一个论点)。
调用指定图像的函数以及计算出现次数的颜色:
var imageData = extract_colors(img, 'ffffff', 'ffa500'),
这将输出每种颜色的对象:
{
"ffffff": {
"counter": 1979,
"rgb": {
"r": 255,
"g": 255,
"b": 255
}
},
"ffa500": {
"counter": 7837,
"rgb": {
"r": 255,
"g": 165,
"b": 0
}
},
"total": 9816
}
我们想要的是访问counter
值:
var white = imageData.ffffff.counter;
工作样本
// Added a rest parameter for unlimited color input
function extract_colors(img, ...colors) {
var canvas = document.createElement("canvas");
var c = canvas.getContext('2d');
c.width = canvas.width = img.width;
c.height = canvas.height = img.height;
c.clearRect(0, 0, c.width, c.height);
c.drawImage(img, 0, 0, img.width, img.height);
return getColors(c, ...colors);
}
// Unchanged
function rgbToHex(r, g, b) {
return ((r << 16) | (g << 8) | b).toString(16);
}
// From: https://stackoverflow.com/a/5624139/2311559
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
// From: https://stackoverflow.com/a/11506531/2311559
// Slightly modified
function isNeighborColor(color1, color2, tolerance) {
tolerance = tolerance || 32;
return Math.abs(color1.r - color2.r) <= tolerance
&& Math.abs(color1.g - color2.g) <= tolerance
&& Math.abs(color1.b - color2.b) <= tolerance;
}
function getColors(ctx, ...colors) {
var pixels = ctx.getImageData(0, 0, ctx.width, ctx.height),
data = pixels.data,
output = {};
// Build an 'output' object. This will hold one object for
// each color we want to check. The color objects will
// each consist of a counter and an rgb value. The counter
// is used to count the occurrence of each color. The rgb
// value is used to match colors with a certain tolerance
// using the 'isNeighborColor()' function above.
for (var i = 0; i < colors.length; i++) {
output[colors[i]] = {
counter: 0,
rgb: hexToRgb(colors[i])
};
}
// For each pixel, match its color against the colors in our
// 'output' object. Using the 'isNeighborColor()' function
// we will also match colors close to our input, given the
// tolerance defined.
for (var i = 0; i < data.length; i+=4) {
var r = data[i],
g = data[i + 1],
b = data[i + 2],
colobj = {r, g, b},
col = rgbToHex(r, g, b);
Object.keys(output).map(function(objectKey, index) {
var rgb = output[objectKey].rgb,
count = output[objectKey].counter;
if(isNeighborColor(rgb, colobj)) {
output[objectKey].counter = count+1;
}
});
}
// Count total
var total = 0;
for(var key in output) {
total = total + parseInt(output[key].counter)
}
output.total = total;
// Return the color data as an object
return output;
}
// Our elements
var img = document.getElementById('img'),
out = document.getElementById('out');
// Retrieve the image data
var imageData = extract_colors(img,'ffffff','ffa500'),
// Count our given colors
white = imageData.ffffff.counter,
orange = imageData.ffa500.counter,
total = imageData.total,
// Calculate percentage value
whitePct = (white / total * 100).toFixed(2),
orangePct = (orange / total * 100).toFixed(2);
out.innerHTML = `White: ${whitePct}%<br/>Orange: ${orangePct}%`;
// Optional console output
console.log(extract_colors(img,'ffffff','ffa500'));
p, img {
margin: 10px 5px;
float: left
}
<!-- Image converted to base64 to keep it locally availible -->
<img id='img' width=100 src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAADImlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4QUUwNzk4N0I2QjIxMUUzQUQzMkIzMTA4REQ0Nzc1MiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4QUUwNzk4OEI2QjIxMUUzQUQzMkIzMTA4REQ0Nzc1MiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkI1NDU5MjAyQjZCMTExRTNBRDMyQjMxMDhERDQ3NzUyIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjhBRTA3OTg2QjZCMjExRTNBRDMyQjMxMDhERDQ3NzUyIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwv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'>
<p id='out'>...</p>