等待图片加载
由于图像在页面上,您可以等待page load event 触发。仅当所有图像已加载(或加载失败)时才会触发。请阅读链接,因为使用加载事件有一些注意事项。
由于图像在页面上,因此无需使用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);
}
});
更新
由于 cmets 中有一些问题,接下来的 sn-p 是检查以确保一切正常。
除了我在 cmets 中详细说明的更正之外,我找不到代码有任何问题。
出于下一个标题中所述的原因,我确实更改了一些名称并进一步增加了图像缩小步骤
色频不等于主色
下面的示例显示了两个图像,加载时填充设置为找到的颜色。您会注意到右边的图像似乎没有正确的颜色。
这是因为棕色有很多,但没有一种棕色是最常见的。
在我的回答 Finding dominant hue. 中,我解决了这个问题,并找到了一个更符合人类感知的解决方案。
工作示例
。低端设备的警告。其中一张图片是 ~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.