2012-04-18 78 views
9

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

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

非常感谢。

+0

嗨,老兄,我需要做同样的,你findf任何的ImageData/ByteArray RGBA重叠和放大/缩小库或函数? – Noitidart 2015-09-09 12:57:14

回答

15

缩放可以通过各种方式完成,但它们都归结为从图像中删除或创建像素。由于图像基本上是像素值的矩阵(调整为数组的大小),因此您可以查看放大图像的方法,即放大数组,填充空白并缩小图像,使数组缩小并将数组缩小。这就是说,在JavaScript中编写自己的数组函数通常并不困难。由于我知道您已经拥有JavaScript数组形式的图像,因此可以将该数组的消息传递给Web Worker,将其缩放至缩放函数并将缩放后的数组发送回主线程。

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

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

关于缩放方法来放大图像,基本上有两个组件需要实现。第一种方法是将已知像素(即缩放图像中的像素)分配到您创建的较大的新阵列上。一个显而易见的方法是将空间上的所有像素均匀分割。例如,如果将图像的宽度设置为宽度的两倍,则只需在每个像素之间保留空白后略过一个位置即可。

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

就缩小而言,通常只是通过将当前数组中的像素选择转移到较小数组来删除像素。例如,如果您想要使图像的两倍小,则可以粗略地遍历当前数组,步长为2(这取决于图像尺寸上的一点,偶数或奇数,以及您所表示的图像使用)。有些方法通过去除那些可能错失最多的像素来做到更好。但我对他们的了解不够。

顺便说一下,所有这些与网络工作者几乎无关。如果您想要在主线程中使用JavaScript缩放图像,您可以按照完全相同的方式进行操作。或者就此而言,使用任何其他语言。然而,网络工作者是在单独的线程而不是在UI线程上完成这些计算的一种非常好的方式,这意味着网站本身似乎没有反应。然而,正如你所说,涉及canvas元素的所有事情都需要在主线程上完成,但是可以在任何地方完成缩放数组。

另外,我确定有JavaScript库可以为你做这件事,根据他们的方法,你也可以使用importScripts将它们加载到Web Worker中。但我想说,在这种情况下,尝试自己编写并根据自己的目的量身定做可能会更容易,更有趣。

并且取决于编程技能的先进程度以及您需要扩展的速度,您可以始终尝试在GPU上执行此操作,而不是使用WebGL在CPU上执行此操作。但在这种情况下,这似乎有点矫枉过正。此外,您可以尝试将图片分成几部分,并尝试在多个Web Worker上对多个部分进行缩放,使其成为多线程。尽管稍后组合这些部分当然不是微不足道的。也许多线程更有意义,当你有很多图像需要在客户端进行缩放时。

这一切都取决于你的应用程序,图像和你自己的技能和愿望。

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

+0

非常感谢所有细节。在我的情况下,一个简单的JavaScript实现就足够了。 – 2012-06-16 19:40:41

+1

男人......这是一个答案 – Jeremythuff 2013-10-15 19:00:16

5

我觉得mslatour的答案需要一些细节,因为我只花了6个小时试图弄清楚如何“......简单地......将其数据属性更改为缩放的Uint8ClampedArray对象”。要做到这一点:

①从网络工作者发回您的阵列。使用形式:

self.postMessage(bufferToReturn, [bufferToReturn]); 

你的缓冲区传递,并从网络工作者未做它的副本,如果你不想。 (这是更快的方式。)(有some MDN documentation,,但我不能链接到它,因为我没有代表。对不起。)无论如何,你也可以把第一个bufferToReturn放在列表或地图内,像这样:

self.postMessage({buffer:bufferToReturn, width:500, height:500}, [bufferToReturn]); 

你使用类似

webWorker.addEventListener('message', function(event) {your code here}) 

监听张贴的消息。 (在这种情况下,发布的事件来自网络工作者,并且正在进行侦听的事件在您的正常JS代码中,它以同样的方式工作,只需切换“自我”和“webWorker”变量即可。)

②在您的浏览器端JavaScript(与工作端相反)中,您可以使用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()方法的存在。

p.s.我不知道有任何图书馆可以帮助... ...。抱歉。