2012-04-18 80 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.我不知道有任何圖書館可以幫助... ...。抱歉。