Web Worker和图片缩放

10

我需要在Web Worker中按比例缩放图像数组。如果我在Web Worker之外,可以使用Canvas和drawImage来复制图像的某些部分或将其缩放。

看起来在Web Worker中不能使用Canvas,那么我该怎么办?是否有任何纯JavaScript库可以帮助我?

非常感谢提前。


兄弟,我也需要做同样的事情,你找到任何ImageData/ByteArray RGBA重叠和缩放库或函数了吗? - Noitidart
3个回答

16

缩放图像可以通过不同的方式实现,但它们都可以归结为从图像中删除或创建像素。由于图像本质上是像素值的矩阵(调整大小为数组),因此您可以将图像放大看作扩展该数组并填充空白,将图像缩小看作通过留出值来缩小数组。

话虽如此,通常编写适用于数组的自己的JavaScript比例函数并不难。由于我理解您已经将图像以JavaScript数组的形式获取,因此您可以将该数组传递到Web Worker中的消息中,使用您的比例函数进行缩放,并将缩放后的数组发送回主线程。

在表示方面,建议您使用Uint8ClampedArray,该数组专为RGBA(带有alpha通道的颜色)编码图像而设计,比普通JavaScript数组更有效。您还可以轻松将Uint8ClampedArray对象发送到Web Worker中的消息中,因此这不会成为问题。另一个好处是Uint8ClampedArray在Canvas API的ImageData数据类型中使用(替换CanvasPixelArray)。这意味着,如果您想要,在画布上绘制缩放后的图像非常容易,只需使用ctx.getImageData()获取画布2D上下文的当前ImageData,然后将其数据属性更改为缩放后的Uint8ClampedArray对象即可。

顺便说一下,如果您还没有将图像转换为数组,可以使用相同的方法。首先在画布上绘制图像,然后使用当前ImageData对象的数据属性以Uint8ClampedArray形式检索图像。

关于放大图像的缩放方法,基本上需要实现两个组件。第一个组件是将已知像素(即要缩放的图像中的像素)分配到您创建的较大新数组中。一种明显的方法是均匀地将所有像素分配到空间中。例如,如果将图像的宽度增加两倍,则只需在每个像素之后简单地跳过一个位置,留下空白。

第二个组件是填充这些空白,这可能有点不太直接。然而,有一些方法是相当容易的。(另一方面,如果您对计算机视觉或图像处理有一些了解,您可能需要查看一些更高级的方法。)一种简单而明显的方法是使用最近邻插值法来插入每个未知的像素位置(即最接近已知像素值的最近像素),通过复制已知像素的颜色来进行。当你将图像缩放得太大时,这通常会导致更大的像素效果(更大的同色块)。除了复制最接近像素的颜色之外,您还可以取几个附近已知像素的平均值。可能甚至与权重相结合,使较近的像素在平均值中占据更多比较远的像素。其他方法包括使用高斯模糊来模糊图像。如果您想找出哪种方法最适合您的应用程序,请查看一些关于图像插值的页面。当然,记住,放大总是意味着填充一些实际上并不存在的东西。如果您这样做得太多,这总是会看起来很糟糕。
至于缩小比例,则通常只需通过从当前数组中仅传输一部分像素到较小的数组来删除像素。例如,如果您想将图像的宽度减少两倍,则大致通过步长为2迭代当前数组(这在一定程度上取决于图像的尺寸,奇数或偶数以及您使用的表示方式)。有一些方法可以通过删除可能会被最错过的那些像素来更好地完成此操作。但是我对它们了解不够。
顺便说一句,所有这些都与Web Workers实际上没有关系。如果您想在JavaScript主线程中缩放图像,您将完全以同样的方式进行操作。或者在任何其他语言中为此目的进行编程。然而,Web Workers是在单独的线程上执行这些计算的非常好的方式,而不是在UI线程上执行,这意味着网站本身似乎不会无响应。但是,正如您所说,涉及画布元素的所有内容都需要在主线程上完成,但是可以在任何地方缩放数组。

此外,我相信有一些JavaScript库可以为您完成此操作,取决于它们的方法,您也可以使用importScripts在Web Worker中加载它们。但我认为,在这种情况下,自己编写并量身定制更容易且更有趣。

根据您的编程技能和需要扩展的速度,您可以尝试在WebGL上使用GPU而不是CPU来执行此操作。但在这种情况下,可能会有点过度。此外,您可以尝试将图像分成几个部分,并尝试在多个Web Worker上对各个部分进行缩放,使其变为多线程。虽然后面组合部分并不容易。也许当您需要在客户端缩放许多图像时,多线程更有意义。

所有这一切都取决于您的应用程序、图像以及您自己的技能和需求。

总之,希望这大致回答了您的问题。


非常感谢您提供的所有细节。在我的情况下,仅一个简单的JavaScript实现就足够了。 - Rafa de Castro

5
我觉得需要对mslatour的答案进行一些具体说明,因为我刚刚花了6个小时尝试弄清楚如何“…简单地…将其数据属性更改为您的缩放Uint8ClampedArray对象”。要做到这一点:
① 从Web Worker中发送回您的数组。使用以下格式:
self.postMessage(bufferToReturn, [bufferToReturn]);

将您的缓冲区传递到和从 Web Worker 中传递而不进行复制(如果您不想进行复制)。这样做速度更快。还有一些 MDN 文档,但我不能链接到它,因为我的声望不够。抱歉。无论如何,您也可以将第一个 bufferToReturn 放入列表或映射中,例如:
self.postMessage({buffer:bufferToReturn, width:500, height:500}, [bufferToReturn]);

您可以使用类似以下的内容
webWorker.addEventListener('message', function(event) {your code here})

监听已发布的消息。(在这种情况下,被发布的事件来自 Web Worker,而进行监听的事件则在普通 JS 代码中。方式相同,只需交换 'self' 和 'webWorker' 变量即可)

② 在浏览器端的 Javascript 中(与 worker 端不同),您可以使用 imageData.data.set() 来“简单地”更改数据属性并将其放回画布中。

var imageData = context2d.createImageData(width, height);
imageData.data.set(new Uint8ClampedArray(bufferToReturn));
context2d.putImageData(imageData, x_offset, y_offset);

我想感谢hacks.mozilla.org提醒我数据集合(data.set())方法的存在。
附注:我目前还不知道有哪些库可以帮助这个问题。抱歉。

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接