我正在练习一个单页应用程序,该应用程序使用Web工作者并行处理一些涉及大量循环的像素计算,因此将它们委托给Web工作者。
但是,要执行此操作,我必须首先将像素转换为供Web工作者使用的数据,这意味着要遍历canvas标记中的所有像素以将每个像素数据记录到数组中
const jImgNode = $(imgNode);
const allPointsArray = [];
const cvs = $("#temp-canvas")[0];
const ctx = cvs.getContext("2d");
ctx.drawImage(imgNode, 0, 0);
const thisWidth = jImgNode.attr("data-width") * 1;
const thisHeight = jImgNode.attr("data-height") * 1;
for (let x = 0; x < thisWidth; x++) {
allPointsArray.push([]);
let col = allPointsArray[allPointsArray.length - 1];
for (let y = 0; y < thisHeight; y++) {
let currentPixel = ctx.getImageData(x, y, 1, 1).data;
// capture the color array of all the pixels: [r, g, b, a]
col.push(currentPixel);
}
}
问题是,有一系列图像以这种方式读取,我首先对所有这些图像进行主线程循环以执行上述代码,然后将Promise发送给Web工作者。数据。
虽然进行一次或两次这样的像素读取是没有问题的,但它显然会阻塞浏览器,并明显使浏览器无响应,因为它经历了第一个循环,为每个图像项创建了临时div和canvas标签,因此可以通过上面的代码段阅读。尽管处理像素阵列只需要大约1秒钟,但是这些过程必须从头到尾全部运行,然后才能发布第一个网络工作者的承诺。
也许我的承诺可能有问题,也许有一些方法可以更改它们,以使它们在每次读取像素图之后而不是在最后一次读取像素图之后触发—但现在我想问一下如果有某种功能或技术可以更快地从canvas标签读取像素。
我正在考虑做一些事情,例如探索重复使用一个临时元素和一个临时画布标签,或者甚至使用巨大的“ sprite”画布,该画布非常高并且包含我要处理的所有图像。网格,以便像素读取功能可以运行一次,并推出代表每个单独图像的各种单个网格单元阵列。但是,我很疲倦地做大量工作来更改代码,以尝试这样做并获得相似或更差的结果。
答案 0 :(得分:1)
ctx.getImageData(x,y,width,height)的 width 和 height 参数可以设置为比{ {1}},因此您可以抓取不止一个像素的像素数据。
如果要发送整个上下文的像素数据,则
1
会把它给你。
现在img = ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height);
将是一个平坦的Uint8ClampedArray,其中包含所有r,g,b和所有像素的通道值。然后,您必须循环四次才能获得每个像素值:
img.data
然后,由于您使用的是Web工作程序,因此您可能会对以下事实感兴趣:可以将缓冲区传输到工作程序上下文而不是将其复制(更少的内存使用=>更少的GC =>更好的perfs)。
为此,请将ImageData的TypedArray的缓冲区作为Worker.postMessage的第二个参数传递给Array:
for(let i=0; i<data.length; i+=4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
let a = data[i + 3];
....