在Javascript中高效地迭代图像
我正在用JavaScript做一些图像处理项目,我需要迭代每个图像像素并进行一些其他处理以实现我的目标。在Javascript中高效地迭代图像,javascript,image-processing,canvas,multidimensional-array,web-worker,Javascript,Image Processing,Canvas,Multidimensional Array,Web Worker,我正在用JavaScript做一些图像处理项目,我需要迭代每个图像像素并进行一些其他处理以实现我的目标。 我正在使用画布获取图像像素数据数组。 对于小图像,如尺寸为500x300像素的图像,其工作良好,所需时间可接受。但对于尺寸为3000x3000像素的大型图像,迭代过程正在成为瓶颈,需要花费大量时间,比如10-12秒 那么,有没有什么方法或技巧可以用来减少迭代步骤中使用的时间呢 以下是我的想法:我尝试使用并行web workers(假设为4)来迭代图像数据的相等部分:(例如,0-[len/4]
我正在使用画布获取图像像素数据数组。
对于小图像,如尺寸为500x300像素的图像,其工作良好,所需时间可接受。但对于尺寸为3000x3000像素的大型图像,迭代过程正在成为瓶颈,需要花费大量时间,比如10-12秒 那么,有没有什么方法或技巧可以用来减少迭代步骤中使用的时间呢 以下是我的想法:我尝试使用并行web workers(假设为4)来迭代图像数据的相等部分:(例如,
0-[len/4]
,[len/4]+1-[len/2]
,[len/2]+1-[len*3/4]
,[len*3/4]+1-len
),其中len
是图像数据数组的大小
我怀疑这种方法会更省时,因为Javascript是单线程的
function rgb2grey(pix,offset){
return (0.2989*pix[offset] + 0.5870*pix[offset+1] +
0.1140*pix[offset+2]);
}
function imgcompare(fileData1,fileData2,targetpix){
var len = pix.length;
for (var j = 0; j <len; j+=4) {
var grey1 = rgb2grey(fileData1,j);
var grey2 = rgb2grey(fileData2,j);
if(grey1!=grey2){
targetpix[j] = 255;
targetpix[j+1] = targetpix[j+2] = 0;
}
else{
targetpix[j] = fileData1[j];
targetpix[j+1] = fileData1[j+1];
targetpix[j+2] = fileData1[j+2];
}
targetpix[j+3] = fileData1[j+3];
}
}
功能rgb2grey(像素,偏移){
返回(0.2989*像素[偏移量]+0.5870*像素[偏移量+1]+
0.1140*pix[偏移量+2]);
}
函数imgcompare(fileData1、fileData2、targetpix){
var len=pix.length;
对于(var j=0;j)2D画布API和GPU辅助图像处理。
canvas 2D API提供了一组功能强大的GPU辅助合成操作。很多时候,它们可以替代通过Javascript完成的缓慢逐像素操作和通过getImageData读取像素
很多时候,这可以使处理成为视频或动画的实时解决方案,而且它还有一个优点,即它可以处理污染的画布,否则将无法使用任何其他方法
通过GPU辅助合成的OP处理
在问题示例中,使用canvas 2D composite操作有一定的优化空间。这将使用GPU为您进行每像素数学运算,但您必须创建两个额外的canvas
用红色标记两个图像之间不同的像素
- 创建两个副本
- 使用comp“difference”获取像素差异
- 使用comp“饱和”产生不同的BW
- 通过使用comp“lighter”在其自身上渲染差异来最大化差异
- 使用comp difference反转差异,并在其上渲染白色矩形
- 使用comp“Multiply”将imageA的副本与反向差相乘
- 再次反转面具
- 使用comp“multiply”将差异画布中的通道绿色和蓝色设置为零
- 使用comp“lighter”将遮罩添加到原始遮罩图像
演示
演示加载两个图像,然后使用上述方法将两个图像之间的差异标记为红色(“#F00”
)
//创建图像的副本作为画布
功能copyImage(图像){
const copy=document.createElement(“画布”);
copy.width=image.width;
copy.height=image.height;
copy.ctx=copy.getContext(“2d”);//为副本添加上下文以便于参考
copy.ctx.drawImage(图像,0,0);
返回副本;
}
//返回包含imageA和imageB之间差异的新画布
函数getDifference(imageA、imageB){
const dif=复制图像(imageA);
dif.ctx.globalCompositeOperation=“差异”;
dif.ctx.drawImage(imageB,0,0);
返回dif;
}
//将图像还原为黑白
函数makeBW(image){//color是有效的CSS颜色
image.ctx.globalCompositeOperation=“饱和”;
image.ctx.fillStyle=“#FFF”;
image.ctx.fillRect(0,0,image.width,image.height);
返回图像;
}
//如果超过值0,将所有通道设置为最大值(255)
函数MaxChannel(图像){
var i=8;//每次绘制时通道值加倍8倍,因此1*2^8得到255
image.ctx.globalCompositeOperation=“打火机”;
而(我--){
image.ctx.drawImage(图像,0,0)
}
返回图像;
}
//反转颜色通道resultRGB=255-imageRGB
函数反转(图像){
image.ctx.globalCompositeOperation=“差异”;
image.ctx.fillStyle=“#FFF”;
image.ctx.fillRect(0,0,image.width,image.height);
返回图像;
}
//保留遮罩中为白色的像素,如果遮罩中为黑色,则将像素设置为黑色。
函数maskOut(图像、掩码){
image.ctx.globalCompositeOperation=“乘法”;
image.ctx.drawImage(掩码,0,0);
返回图像;
}
//将通道从imageB添加到imageA。resultRGB=imageA_RGB+imageB_RGB
函数addChannels(imageA,imageB){//将imageB通道添加到imageA通道
imageA.ctx.globalCompositeOperation=“打火机”;
imageA.ctx.drawImage(imageB,0,0);
返回图像a;
}
//零通道是它的标志(红、绿、蓝)是真的
函数zeroChannels(图像、红色、绿色、蓝色){//将通道设置为零为真
image.ctx.fillStyle=`${red?'0:“F”}${green?'0:“F”}${blue?'0:“F”}`;
image.ctx.globalCompositeOperation=“乘法”;
image.ctx.fillRect(0,0,image.width,image.height);
返回图像;
}
//返回一个新画布,该画布是imageA的副本,其像素与标记为红色的imageB不同。
函数标记差异(imageA、imageB){
常量结果=复制图像(imageA);
const mask=invert(maxChannels(makeBW(getDifference(imageA,imageB)));
maskOut(结果、掩码);
返回addChannels(结果、零通道(反转(掩码)、false、true、true));
}
常量图像=[
"https://i.stack.imgur.com/ImeHB.jpg",
"https://i.stack.imgur.com/UrrnL.jpg"
];
var-imageCount=0;
函数onImageLoad(){
imageCount+=1;
如果(imageCount==2){
AddImagePage(标记差异(图像[0],图像[1]);
AddImagePage(图像[0]);
附加图像页(图像[1]);
}
}
函数AddImagePage(图像){
image.className=“images”;
document.body.appendChild(图像);
}
images.forEach((url,i)=>