用于检查图像是否有白色背景的Javascript Canvas循环



我想在HTML中循环大约50个图像,从每个图像中提取src,检查图像背景的主色是否为白色,然后根据结果添加一些css样式(例如填充(。

到目前为止,我有这个代码,但由于某些原因,它不起作用。代码可以单独工作,但当放在for循环中时,它不起作用。通常,这个循环要么根本不工作,要么只工作到某个时刻,然后只输出默认结果"0"#FFFFFF";因为画布本身是白色的。

我不知道为什么它不起作用。我一直想把它修好,但没有用。

在这里演示(请浏览(:https://jsbin.com/tucegomemi/1/edit?html,js,控制台,输出

JAVASCRIPT在这里:

var i;
var GlobalVariable;
for (i=0; i < document.querySelectorAll('img').length ; i++) {

let canvas = document.getElementById("canvas"),
canvasWidth = canvas.width,
canvasHeight = canvas.height,
c = canvas.getContext("2d"),
img = new Image();
img.crossOrigin="anonymous";
img.src = document.querySelectorAll('img')[i].src

// Prepare the canvas
var ptrn = c.createPattern(img, 'repeat'); 
c.fillStyle = "white";
c.fillRect(0,0,canvasWidth,canvasHeight);
c.fillStyle = ptrn;
c.fillRect(0,0,canvasWidth,canvasHeight);

// Get img data
var imgData = c.getImageData(0, 0, canvasWidth, canvasHeight),
data = imgData.data,
colours = {};

// Build an object with colour data.
for (var y = 0; y < canvasHeight; ++y) {
for (var x = 0; x < canvasWidth; ++x) {
var index = (y * canvasWidth + x) * 4,
r = data[index],
g = data[++index],
b = data[++index],
rgb = rgbToHex(r,g,b);

if(colours[rgb]){
colours[rgb]++;
}else{
colours[rgb] = 1;
}
}
}

// Determine what colour occurs most.
var most = {
colour:'',
amount:0
};
for(var colour in colours){
if(colours[colour] > most.amount){
most.amount = colours[colour];
most.colour = colour;
}
}
GlobalVariable =  most.colour; 
console.log(i);  
console.log(GlobalVariable);  
if (GlobalVariable !== "#ffffff") {document.querySelectorAll('img')[i].style.padding = "50px" ;}  
}

function rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}


等待加载图像

当图像在页面上时,您可以等待页面加载事件启动。只有当所有图像都已加载(或未能加载(时,才会触发此项。请务必阅读链接,因为使用加载事件需要注意一些事项。

此外,由于图像在页面上,因此无需使用new Image创建图像的副本。您可以直接从页面使用图像。

此外,我假设所有图像都将加载。如果图像不加载,将出现问题

从你的代码来看,它的效率非常低,因此这个例子是一个完全重写,试图运行得更快,消耗更少的能量。

注意:该示例使用了一个仅在内存中的临时画布。页面上不需要画布。注意:如果一个像素的计数大于图像中像素数的一半,它将停止计数。

addEventListener("load",() => {         // wait for page (and images to load) 
const toHex = val => (val & 0xFF).toString(16).padStart(2,"0");  // mask the to hex and pad with 0 if needed
const pixel2CSScolor = px => `#${toHex(px >> 16)}${toHex(px >> 8)}${toHex(px)}`;
const images = document.querySelectorAll('img');
const canvas = document.createElement("canvas"); // Only need one canvas
const ctx = canvas.getContext("2d");             // and only one context
for (const image of images) {                    // do each image in turn
const w = canvas.width = image.naturalWidth; // size to fit image
const h = canvas.height = image.naturalHeight;
ctx.fillStyle = "#FFF";
ctx.fillRect(0, 0, w, h);
ctx.drawImage(image, 0, 0);
const imgData = ctx.getImageData(0, 0, w, h);
const pixels = new Uint32Array(imgData.data.buffer); // get a pixel view of data (RGBA as one number)
const counts = {};                                   // a map of color counts
var idx = pixels.length, maxPx, maxCount = 0;        // track the most frequent pixel count and type
while (idx-- > 0) {
const pixel = pixels[idx];  // get pixel
const count = counts[pixel] = counts[pixel] ? counts[pixel] + 1 : 1;
if (count > maxCount) {
maxCount = count;
maxPx = pixel;
if (count > pixels.length / 2) { break }
}
}
image._FOUND_DOMINATE_COLOR = pixel2CSScolor(maxPx);
}
}); 

每个图像都附加了一个名为_FOUND_DOMINATE_COLOR的新属性,该属性包含一个颜色为CSS十六进制颜色的字符串

更好的方式

由于我不确定图像的格式和内容,上面的例子是包罗万象的解决方案。

如果图像有大面积的相似颜色,或者图像有很多噪声,则可以使用GPU渲染为您进行大部分计数。这是通过以越来越小的比例绘制图像来完成的。drawImage函数将对像素值进行平均。

这意味着,当你的代码查看像素数据时,它要小得多,图像大小的一半,内存和CPU负载减少4倍,大小的四分之一,工作量减少16倍。

下一个示例将图像缩小到其自然大小的1/4,然后使用平均像素值来找到颜色。注意,为了获得最佳结果,图像的宽度和高度应至少大于16个像素

addEventListener("load",() => {        
const toHex = val => (val & 0xFF).toString(16).padStart(2,"0");  
const pixel2CSScolor = px => `#${toHex(px >> 16)}${toHex(px >> 8)}${toHex(px)}`;
const reduceImage = image => {
const w = canvas.width = image.naturalWidth;         
const h = canvas.height = image.naturalHeight;
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "#FFF";
ctx.fillRect(0, 0, w, h);
ctx.drawImage(image, 0, 0);
ctx.globalCompositeOperation = "copy";
ctx.drawImage(canvas, 0, 0, w / 2, h / 2);
ctx.drawImage(canvas, 0, 0, w / 2, h / 2,  0, 0, w / 4, h / 4);
return new Uint32Array(ctx.getImageData(0, 0, w / 4 | 0, h / 4 | 0).data.buffer);
}
const images = document.querySelectorAll('img');
const canvas = document.createElement("canvas"); 
const ctx = canvas.getContext("2d");             
for (const image of images) {                    
const pixels = reduceImage(image), counts = {};                                   
var idx = pixels.length, maxPx, maxCount = 0;       
while (idx-- > 0) {
const pixel = pixels[idx];  // get pixel
const count = counts[pixel] = counts[pixel] ? counts[pixel] + 1 : 1;
if (count > maxCount) {
maxCount = count;
maxPx = pixel;
if (count > pixels.length / 2) { break }
}
}
image._FOUND_DOMINATE_COLOR = pixel2CSScolor(maxPx);
}
}); 

更新

由于评论中有一些问题,下一个片段是检查以确保一切正常。

除了我在评论中详细说明的更正之外,我找不到任何代码问题。

由于下一个标题中列出的原因,我确实更改了一些名称,并增加了更多的图像缩小步骤

颜色频率不等于主导颜色

下面的示例显示了两个图像,加载时填充设置为找到的颜色。你会注意到右边的图像似乎没有得到正确的颜色。

这是因为有很多棕色,但没有一个棕色是最常见的。

在我的答案中寻找主导色调。我解决了这个问题,找到了一个更符合人类感知的解决方案。

工作示例

低端设备警告。其中一个图像是~9Mpx

addEventListener("load",() => { geMostFrequentColor() },{once: true});
const downScaleSteps = 4;
function geMostFrequentColor() {
const toHex = val => (val & 0xFF).toString(16).padStart(2,"0");  
const pixel2CSScolor = px => `#${toHex(px >> 16)}${toHex(px >> 8)}${toHex(px)}`;
const reduceImage = image => {
var w = canvas.width = image.naturalWidth, h = canvas.height = image.naturalHeight, step = 0;
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "#FFF";
ctx.fillRect(0, 0, w, h);
ctx.drawImage(image, 0, 0);
ctx.globalCompositeOperation = "copy";
while (step++ < downScaleSteps) {
ctx.drawImage(canvas, 0, 0, w, h, 0, 0, w /= 2, h /= 2);
}
return new Uint32Array(ctx.getImageData(0, 0, w | 0, h | 0).data.buffer);
}
const images = document.querySelectorAll('img');
const canvas = document.createElement("canvas"); 
const ctx = canvas.getContext("2d");  
var imgCount = 0;           
for (const image of images) {           
info.textContent = "Processing image: " + imgCount++;
const pixels = reduceImage(image), counts = {};    
let idx = pixels.length, maxPx, maxCount = 0;      
while (idx-- > 0) {
const pixel = pixels[idx];  // get pixel
const count = counts[pixel] = counts[pixel] ? counts[pixel] + 1 : 1;
if (count > maxCount) {
maxCount = count;
maxPx = pixel;
if (count > pixels.length / 2) { break }
}
}
image._MOST_FREQUENT_COLOR = pixel2CSScolor(maxPx);
image.style.background = image._MOST_FREQUENT_COLOR;
}
info.textContent = "All Done!";
}
img {
height: 160px;
padding: 20px;
}
<div id="info">Loading...</div>
<img src="https://upload.wikimedia.org/wikipedia/commons/d/dd/Olympus-BX61-fluorescence_microscope.jpg"  crossorigin="anonymous">
<img src="https://upload.wikimedia.org/wikipedia/commons/a/a5/Compound_Microscope_(cropped).JPG" alt="Compound Microscope (cropped).JPG"  crossorigin="anonymous"><br>
Images from wiki no attribution required.

您当前正在绘制一个空图像。图像需要一些时间才能加载,所以您必须等待它的出现。

一旦加载完成,就使用onload回调将图像绘制到画布上。在此事件之后,其他所有过程都应继续。

img = new Image();
img.crossOrigin = "anonymous";
img.src = document.querySelectorAll('img')[i].src;
img.onload = function() {
c.clearRect(0, 0, canvasWidth, canvasHeight);
c.drawImage(img, 0, 0, canvasWidth, canvasHeight); 
// Continue here
}

最新更新