10

我需要在 Web Worker 中以数组形式缩放图像。如果我在网络工作者之外,我可以使用画布和 drawImage 来复制图像的某些部分或缩放它。

看起来像在网络工作者中我不能使用画布所以我能做什么?有没有可以帮助我的纯 Javascript 库?

提前非常感谢。

4

3 回答 3

16

缩放可以通过多种方式完成,但它们都归结为从图像中删除或创建像素。由于图像本质上是像素值的矩阵(调整大小为数组),因此您可以将放大图像视为扩大该数组并填充空白,而缩小图像则视为通过省略值来缩小数组。

话虽如此,在 JavaScript 中编写自己的适用于数组的缩放函数通常并不难。由于我知道您已经拥有 JavaScript 数组形式的图像,您可以将该数组在消息中传递给 Web Worker,将其缩放为您的缩放函数并将缩放后的数组发送回主线程。

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

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

关于放大图像的缩放方法,基本上有两个组件需要实现。第一个是将已知像素(即您正在缩放的​​图像中的像素)划分为您创建的更大的新数组。一个明显的方法是在空间上平均划分所有像素。例如,如果您将图像的宽度设置为两倍宽,您只需在每个像素之后跳过一个位置,在其间留下空白。

然后第二个组件是填写这些空白,这可能稍微不那么简单。但是,有几个相当容易。(另一方面,如果您对计算机视觉或图像处理有一定的了解,您可能希望了解一些更高级的方法。)一种简单且有些明显的方法是使用其最近邻(即最近已知像素值)通过复制已知像素的颜色。当您过度缩放图像时,这通常会导致更大像素(相同颜色的更大块)的效果。除了复制最近像素的颜色,您还可以取附近几个已知像素的平均值。甚至可能与权重相结合,平均而言,您使较近的像素比较远的像素计数更多。其他方法包括使用高斯模糊图像。如果您想了解哪种方法最适合您的应用程序,请查看有关图像插值的一些页面。当然,请记住,扩大规模总是意味着填补实际上​​并不存在的东西。如果你做得太多,那看起来总是很糟糕。

就按比例缩小而言,通常只是通过仅将选择的像素从当前阵列转移到较小的阵列来移除像素。例如,如果您想让图像的 with 小一倍,则以 2 的步长粗略地迭代当前数组(这在一定程度上取决于图像的尺寸,偶数或奇数,以及您的表示使用)。有一些方法可以通过删除那些最容易错过的像素来更好地做到这一点。但我对他们的了解还不够。

顺便说一句,所有这些实际上都与网络工作者无关。如果你想在主线程上用 JavaScript 缩放图像,你可以用完全相同的方式来做。或任何其他语言。然而,Web Worker 是在单独的线程上而不是在 UI 线程上进行这些计算的一种非常好的方法,这意味着网站本身似乎不会无响应。然而,就像你说的,所有涉及画布元素的事情都需要在主线程上完成,但是缩放数组可以在任何地方完成。

此外,我确信有 JavaScript 库可以为您执行此操作,并且根据它们的方法,您还可以使用 importScripts 将它们加载到您的 Web Worker 中。但我会说,在这种情况下,尝试自己编写它并根据您的目的量身定制它可能会更容易,也更有趣。

根据您的编程技能有多先进以及您需要扩展的速度,您始终可以尝试在 GPU 上而不是使用 WebGL 的 CPU 上执行此操作。但在这种情况下,这似乎有点矫枉过正。此外,您可以尝试将图像分成几部分,并尝试在多个 Web Worker 上缩放单独的部分,使其成为多线程。尽管稍后将这些部分组合起来肯定不是一件容易的事。当您有大量需要在客户端缩放的图像时,多线程可能更有意义。

这完全取决于您的应用程序、图像以及您自己的技能和愿望。

无论如何,我希望能大致回答你的问题。

于 2012-06-14T20:16:02.230 回答
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})

收听已发布的消息。(在这种情况下,发布的事件来自网络工作者,而进行监听的事件在您的普通 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() 方法的存在。

ps 我不知道有任何图书馆可以帮助解决这个问题……但是。对不起。

于 2013-03-12T01:57:32.990 回答
2

我自己还没有测试过,但是这里有一个纯 JS 库可能有用:

https://github.com/taisel/JS-Image-Resizer

于 2014-10-30T02:45:42.107 回答