2012-04-18 14 views
9

Ich muss Bilder in Array-Form in einem Web Worker skalieren. Wenn ich außerhalb eines Web-Arbeiters bin, könnte ich eine Leinwand und drawImage verwenden, um bestimmte Teile eines Bildes zu kopieren oder es zu skalieren.Web Worker und Skalierung Bilder

Sieht aus wie in einem Web-Arbeiter kann ich keine Leinwand so verwenden, was kann ich tun? Gibt es eine reine Javascript-Bibliothek, die mir helfen kann?

Vielen Dank im Voraus.

+0

Hey Mann, ich brauche das gleiche zu tun, haben findf Sie eine Imagedata/ByteArray RGBA überlappen und skalieren hoch/runter Bibliotheken oder Funktionen? – Noitidart

Antwort

15

Die Skalierung kann auf verschiedene Arten durchgeführt werden, aber alle reduzieren sich auf das Entfernen oder Erstellen von Pixeln aus dem Bild. Da Bilder im Wesentlichen Matrizen (in Form von Arrays) von Pixelwerten sind, können Sie Bilder skalieren, indem Sie das Array vergrößern und die Leerstellen ausfüllen und die Bilder verkleinern, indem Sie das Array schrumpfen lassen, indem Sie Werte auslassen.

Das gesagt, es ist normalerweise nicht so schwierig, Ihre eigene Skalierungsfunktion in JavaScript zu schreiben, das auf Arrays funktioniert. Da ich weiß, dass Sie die Bilder bereits in Form eines JavaScript-Arrays haben, können Sie dieses Array in einer Nachricht an den Web Worker übergeben, Ihre Skalierungsfunktion skalieren und das skalierte Array zurück an den Hauptthread senden.

In Bezug auf die Darstellung würde ich Ihnen empfehlen, die Uint8ClampedArray, die für RGBA (Farbe, mit Alphakanal) kodierte Bilder entwickelt wurde und ist effizienter als normale JavaScript-Arrays. Sie können auch einfach Uint8ClampedArray-Objekte in Nachrichten an Ihren Web Worker senden, so dass dies kein Problem darstellt. Ein weiterer Vorteil ist, dass ein Uint8ClampedArray im ImageData-Datentyp (nach dem Ersetzen von CanvasPixelArray) der Canvas-API verwendet wird. Dies bedeutet, dass es ziemlich einfach ist, das skalierte Bild auf einen Canvas-Bereich zurück zu ziehen (wenn Sie das wollten), indem Sie einfach die aktuellen ImageData des Canvas-2D-Kontexts mithilfe von ctx.getImageData() abrufen und sein Datenattribut auf skaliert ändern Uint8ClampedArray-Objekt.

Übrigens, wenn Sie Ihre Bilder noch nicht als Arrays haben, können Sie die gleiche Methode verwenden. Zeichnen Sie zuerst das Bild auf der Zeichenfläche und verwenden Sie dann das Datenattribut des aktuellen ImageData-Objekts, um das Bild in einem Uint8ClampedArray abzurufen.

In Bezug auf Skalierungsmethoden, um ein Bild zu skalieren, gibt es im Grunde zwei Komponenten, die Sie implementieren müssen. Die erste besteht darin, die bekannten Pixel (d. H. Die Pixel aus dem Bild, das Sie skalieren) über das größere neue Array zu teilen, das Sie erstellt haben. Ein naheliegender Weg ist, alle Pixel gleichmäßig über den Raum zu verteilen. Wenn Sie beispielsweise die Breite eines Bilds doppelt so groß machen, sollten Sie einfach eine Position nach jedem Pixel überspringen und dabei Leerzeichen dazwischen lassen.

Die zweite Komponente ist dann, diese Leerzeichen zu füllen, die etwas weniger direkt sein können. Es gibt jedoch einige, die ziemlich einfach sind. (Auf der anderen Seite, wenn Sie etwas über Computer Vision oder Bildverarbeitung wissen, können Sie sich einige fortgeschrittene Methoden ansehen.) Eine einfache und naheliegende Methode ist es, jede unbekannte Pixelposition mit ihrem nächsten Nachbarn (dh dem nächsten) zu interpolieren Pixelwert, der bekannt ist), indem die bekannte Pixelfarbe dupliziert wird. Dies führt in der Regel zu größeren Pixeln (größeren Blöcken derselben Farbe), wenn Sie die Bilder zu stark skalieren. Anstatt die Farbe des nächstgelegenen Pixels zu duplizieren, können Sie auch den Durchschnitt mehrerer bekannter Pixel in der Nähe ermitteln. Möglicherweise sogar kombiniert mit Gewichten wurden Sie dazu gebracht, dass näher liegende Pixel im Durchschnitt mehr zählen als Pixel, die weiter entfernt sind. Andere Methoden umfassen das Verwischen des Bildes mit Hilfe von Gaussianen. Wenn Sie herausfinden möchten, welche Methode für Ihre Anwendung am besten geeignet ist, sehen Sie sich einige Seiten zur Bildinterpolation an. Denken Sie daran, dass Scaling-Up immer bedeutet, dass man Dinge füllt, die nicht wirklich da sind. Das wird immer schlecht aussehen, wenn du es zu viel tust.

Was das Herunterskalieren angeht, entfernt man typischerweise nur Pixel, indem nur eine Auswahl von Pixeln vom aktuellen Array auf das kleinere Array übertragen wird.Wenn Sie zum Beispiel das Bing eines Bildes doppelt so klein machen möchten, durchlaufen Sie grob das aktuelle Array mit den Schritten 2 (Dies hängt ein wenig von den Dimensionen des Bildes, gerade oder ungerade, und der Darstellung ab, die Sie sind verwenden). Es gibt Methoden, die das noch besser machen, indem sie diejenigen Pixel entfernen, die am meisten übersehen werden könnten. Aber ich weiß nicht genug über sie.

Übrigens, all dies hat praktisch nichts mit Web-Arbeitern zu tun. Sie würden es auf die gleiche Weise tun, wenn Sie Bilder in JavaScript im Hauptthread skalieren möchten. Oder in irgendeiner anderen Sprache. Web Workers sind jedoch eine sehr nette Möglichkeit, diese Berechnungen in einem separaten Thread statt im UI-Thread durchzuführen, was bedeutet, dass die Website selbst nicht reagiert. Wie Sie bereits gesagt haben, muss alles, was das canvas-Element betrifft, auf dem Haupt-Thread ausgeführt werden, aber das Skalieren von Arrays kann überall durchgeführt werden.

Ich bin mir sicher, dass es JavaScript-Bibliotheken gibt, die das für Sie tun können und je nach ihren Methoden können Sie sie auch in Ihrem Web Worker mit Hilfe von importScripts laden. Aber ich würde sagen, dass es in diesem Fall einfach einfacher und viel lustiger ist, zu versuchen, es selbst zu schreiben und es für Ihren Zweck maßgeschneidert zu machen.

Und je nachdem wie fortgeschritten Ihre Programmierkenntnisse sind und wie schnell Sie skalieren müssen, können Sie immer versuchen, dies auf der GPU statt auf der CPU mit WebGL zu tun. Aber das scheint in diesem Fall ein leichter Overkill zu sein. Sie können auch versuchen, Ihr Bild in mehrere Teile zu zerlegen und die einzelnen Teile auf mehreren Web Workern zu skalieren, um es multi-threaded zu machen. Es ist sicherlich nicht trivial, die Teile später zu kombinieren. Vielleicht macht Multi-Threading mehr Sinn, wenn Sie viele Bilder haben, die auf der Client-Seite skaliert werden müssen.

Es hängt alles von Ihrer Anwendung, den Bildern und Ihren eigenen Fähigkeiten und Wünschen ab.

Wie auch immer, ich hoffe, dass in etwa Ihre Frage beantwortet.

+0

Vielen Dank für alle Details. Und eine einfache Javascript-Implementierung allein reicht in meinem Fall aus. –

+1

Mann ... das ist eine Antwort – Jeremythuff

5

Ich glaube, einige Besonderheiten auf Mslatours Antwort sind erforderlich, da ich gerade 6 Stunden damit verbracht habe herauszufinden, wie man "... einfach ... sein Datenattribut in Ihr skaliertes Uint8ClampedArray-Objekt ändert". Um dies zu tun:

① Senden Sie das Array vom Web-Mitarbeiter zurück. Benutzen Sie das Formular:

self.postMessage(bufferToReturn, [bufferToReturn]); 

passieren Ihre Puffer zu und von dem Web-Arbeiter ohne eine Kopie davon zu machen, wenn Sie nicht wollen. (Es ist schneller so.) (Es gibt some MDN documentation, , aber ich kann nicht verlinken, da ich nicht rep. Sorry.) Wie auch immer, Sie können auch die erste bufferToReturn in Listen oder Karten, wie folgt:

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

verwenden Sie so etwas wie

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

für eine gesendete Nachricht zu hören. (In diesem Fall stammen die geposteten Ereignisse vom Web-Worker und das Ereignis, das das Abhören durchführt, ist in Ihrem normalen JS-Code. Es funktioniert auf die gleiche andere Weise. Wechseln Sie einfach die Variablen 'self' und 'webWorker'.)

② In Ihrem browserseitigen JavaScript (im Gegensatz zur Arbeiterseite) können Sie imageData .data.set() verwenden, um "einfach" das Datenattribut zu ändern und es wieder in den Erstellungsbereich zu setzen.

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

würde Ich mag hacks.mozilla.org danken für mich auf die Existenz des data.set() -Methode zu alarmieren.

p.s. Ich kenne keine Bibliotheken, um damit zu helfen ... noch nicht. Es tut uns leid.