我需要在Web Worker中以数组形式缩放图像。如果我在网络工作者之外,我可以使用画布和drawImage复制图像的某些部分或缩放它。
在网络工作者看来我不能使用画布,我该怎么办?有没有可以帮助我的纯Javascript库?
提前多多感谢。
答案 0 :(得分:16)
缩放可以通过各种方式完成,但它们都归结为从图像中删除或创建像素。由于图像基本上是像素值的矩阵(调整为数组),因此您可以将图像放大为放大该数组并填充空白并缩小图像,因为通过保留值来缩小数据。
话虽这么说,在用于数组的JavaScript中编写自己的缩放函数通常并不困难。由于我了解您已经拥有JavaScript数组形式的图像,因此您可以将该数组传递给Web Worker,将其缩放为您的缩放函数并将缩放的数组发送回主线程。
在表示方面,我建议你使用专为RGBA(颜色,带alpha通道)编码图像设计的Uint8ClampedArray,它比普通的JavaScript数组效率更高。您还可以轻松地将消息中的Uint8ClampedArray对象发送给Web Worker,这样就不会有问题。另一个好处是Uint8ClampedArray用于Canvas API的ImageData数据类型(在替换CanvasPixelArray之后)。这意味着很容易将缩放的图像绘制回Canvas(如果这是您想要的),只需获取画布的当前ImageData即可。 2D上下文使用ctx.getImageData()并将其数据属性更改为缩放的Uint8ClampedArray对象。
顺便说一句,如果您还没有将图像作为数组,那么您可以使用相同的方法。首先在画布上绘制图像,然后使用当前ImageData对象的data属性在Uint8ClampedArray中检索图像。
关于缩放图像的缩放方法,基本上需要实现两个组件。第一个是将已知像素(即正在缩放的图像中的像素)除以您创建的较大的新数组。一种显而易见的方法是均匀地划分空间上的所有像素。例如,如果您将图像的宽度设置为宽度的两倍,则只需在每个像素之后跳过一个位置,在其间留下空白。
然后第二个组件填写那些空白,这可能会稍微简单一些。但是,有几个相当容易。 (另一方面,如果您对计算机视觉或图像处理有一些了解,您可能需要查看一些更高级的方法。)一种简单而明显的方法是使用其最近邻居(即最近的)插入每个未知像素位置通过复制已知像素的颜色已知的像素值。当您过度缩放图像时,这通常会导致较大像素(相同颜色的较大块)的影响。您也可以采用附近几个已知像素的平均值,而不是复制最近像素的颜色。可能甚至与权重相结合,你使得更接近的像素在平均值上比在更远的像素中更多。其他方法包括使用高斯模糊图像。如果您想了解哪种方法最适合您的应用,请查看有关图像插值的一些页面。当然,请记住,扩大规模总是意味着填补那些并不存在的东西。如果你做得太多,这总会很糟糕。
就缩小而言,通常只是通过将当前阵列中的一些像素选择转移到较小的阵列来移除像素。例如,如果您想要将图像的两倍小,则大致以2为步长迭代当前数组(这取决于图像的尺寸,偶数或奇数,以及您的表示使用)。有些方法可以通过删除那些可能错过最多的像素来做到这一点。但我对他们的了解不够。
顺便说一句,所有这一切实际上与网络工作者无关。如果您想在主线程上使用JavaScript缩放图像,则可以完全相同的方式执行此操作。或者用其他任何语言表达。然而,Web Workers是在单独的线程上而不是在UI线程上进行这些计算的一种非常好的方式,这意味着网站本身似乎没有响应。但是,就像你说的那样,涉及canvas元素的所有内容都需要在主线程上完成,但是可以在任何地方完成扩展数组。
此外,我确定有一些JavaScript库可以为您执行此操作,并且根据他们的方法,您还可以使用importScripts将它们加载到Web Worker中。但我要说的是,在这种情况下,尝试自己编写它并使其为您的目的量身定做可能会更容易也更有趣。
根据您的编程技巧有多高级以及您需要扩展的速度,您总是可以尝试在GPU上而不是使用WebGL在CPU上执行此操作。但在这种情况下,这似乎有点矫枉过正。此外,您可以尝试将图像分成几部分,并尝试在多个Web Workers上缩放单独的部分,使其成为多线程。虽然稍后将这些部分组合起来当然不是一件容易的事。当你有很多需要在客户端扩展的图像时,也许多线程更有意义。
这一切都取决于你的应用,图像以及你自己的技能和欲望。
无论如何,我希望大致回答你的问题。
答案 1 :(得分:5)
我觉得需要一些关于mslatour答案的细节,因为我花了6个小时试图找出如何“......简单地......将其数据属性更改为缩放的Uint8ClampedArray对象”。要做到这一点:
①从网络工作者发回您的阵列。使用表格:
self.postMessage(bufferToReturn, [bufferToReturn]);
如果您不愿意,可以将您的缓冲区传递给Web工作者和从Web工作者传递缓冲区,而无需复制它。 (这种方式更快。)(有some MDN documentation,但我无法链接到它,因为我没有代表。抱歉。)无论如何,你也可以把第一个bufferToReturn内部列表或映射,如下所示:
self.postMessage({buffer:bufferToReturn, width:500, height:500}, [bufferToReturn]);
您使用类似
的内容webWorker.addEventListener('message', function(event) {your code here})
收听发布的消息。 (在这种情况下,发布的事件来自Web工作者,正在进行监听的事件在您的普通JS代码中。它以相同的方式工作,只需切换'self'和'webWorker'变量。)
②在浏览器端Javascript(而不是工作者端),您可以使用imageData。data.set()来“简单地”更改数据属性并将其放回画布中。 / p>
var imageData = context2d.createImageData(width, height);
imageData.data.set(new Uint8ClampedArray(bufferToReturn));
context2d.putImageData(imageData, x_offset, y_offset);
我要感谢hacks.mozilla.org提醒我存在data.set()方法。
P.S。我还不知道有任何图书馆可以帮助解决这个问题。遗憾。
答案 2 :(得分:2)
我还没有自己测试一下,但是有一个纯JS库可能会在这里使用: