2010-03-15 4 views
13

Ich arbeite an einem Silverlight-Projekt, bei dem Benutzer ihre eigenen Collagen erstellen können.Wie Bilder angezeigt werden, ohne riesige Mengen an RAM zu belegen

Das Problem

Wenn mithilfe der Bitmap Klasse, Silverlight Schweine bis große unvernünftig Mengen an RAM ein paar Bilder zu laden. 150 Bilder, bei denen einzelne maximal 4,5 MB auffüllen, nehmen etwa 1,6 GB RAM in Anspruch, was zu Speicherausnahmen führt.

Ich lade sie durch Streams, da der Benutzer ihre eigenen Fotos auswählt.

Was ich suche

Eine Klasse, Methode oder ein Verfahren der riesige Menge an RAM wird zu beseitigen gesaugt. Geschwindigkeit ist ein Problem, deshalb möchte ich nicht zwischen Bildformaten oder ähnlichem konvertieren. Eine schnelle Größenänderungslösung könnte funktionieren.

Ich habe versucht, eine WriteableBitmap zu verwenden, um die Bilder zu rendern, aber ich finde diese Methode zwingt mich, das Rad neu zu erfinden, wenn es Drag/Drop und andere Dinge, die Benutzer mit den Bildern tun soll.

+0

Also ... wollen Sie eine effiziente Lösung, aber Sie wollen keine Methoden verwenden, die die Effizienz verbessern, d. H. Mit den Bildern auf einer niedrigeren Ebene arbeiten? Warum nicht? Ich kenne Silverlight nicht und hoffe, dass Sie eine Lösung finden, aber manchmal müssen Sie tatsächlich die Ärmel hochkrempeln und ein wenig arbeiten. –

+0

Das Problem ist nur die Speichereffizienz. Ich brauche kein schnelles Rendering oder schnelle Modifikation der Bilddaten selbst - ich muss einfach nur die tatsächlichen JPEG-Daten im Speicher verwenden, um sie darzustellen und später in einer PDF zu kombinieren. Das PDF-Framework, das ich verwende, erfordert, dass ich Ströme von JPEG-Daten einwerfe, aber ich sehe keinen guten Weg, um ein BitmapImage in einen JPEG-Stream zu bekommen. –

+1

Können Sie Code hinzufügen, wie Ihr Programm funktioniert und wie groß sind Ihre Bilder allgemein? Wie viele werden Sie gleichzeitig anzeigen? –

Antwort

6

Was ich versuchen würde ist, jeden Strom zu laden und die Größe zu einem Thumbnail zu ändern (zB 640x480), bevor der nächste geladen wird. Lassen Sie den Benutzer dann mit den kleineren Bildern arbeiten. Sobald Sie bereit sind, die PDF-Datei zu generieren, laden Sie die JPEG-Dateien nacheinander einzeln aus den ursprünglichen Streams, und entsorgen Sie jedes Bitmap, bevor Sie das nächste laden.

+0

Dies würde Ihre 4,5 Megabyte Bilder in ~ .9 Megabyte Bilder verwandeln. Sie könnten das auf 0,6 Megabyte reduzieren, indem Sie auf 16-Bit-Farbe gehen. Jetzt würden Ihre 150 Bilder etwa 60 Megabyte benötigen. Immer noch viel, aber viel plausibler. Solange Sie die Originalbilder für das endgültige Rendering verwendet haben, wäre es Ihnen gut. –

+0

Ich habe es bereits versucht, und es geht wirklich Prozessorleistung. Was ich sehe ist, dass Silverlight das irgendwie schon macht, wenn es die Bilder rendert. Wäre es nicht möglich, die Bitmap-Bilder dazu zu zwingen, ihre Ressourcen auszugeben, während das fertige gerenderte Bild im Speicher bleibt? –

+0

WPF tut dies zumindest als Teil des Compositing-Prozesses. Die Skalierung erfolgt somit im laufenden Betrieb durch die Grafikhardware. Ich vermute, dass Silverlight etwas Ähnliches macht. In diesem Fall lautet die Antwort nein, es tut dies noch nicht, zumindest nicht auf eine Weise, die für Sie zugänglich ist. –

1

Was Ihnen passieren könnte, ist eine wenig bekannte Tatsache über die Müllsammlung, die mich auch bekommen hat. Wenn ein Objekt groß genug ist (ich erinnere mich nicht, wo die Linie genau ist), entscheidet Garbage Collection darüber, dass, obwohl nichts im Bereich mit dem Objekt verknüpft ist (in beiden sind die Objekte die Bilder), das Bild erhalten bleibt im Speicher, weil es entschieden hat, dass, falls Sie dieses Bild jemals wieder wollen, es billiger ist, es herumzuhalten, anstatt es zu löschen und es später neu zu laden.

+0

Ja, ich habe über thesee erfahren, während ich nach Antworten gesucht habe. Es gibt keine Problemumgehung für jetzt oder was? –

+0

System.GC.Collect() und System.GC.WaitForPendingFinalizers() war, wie ich zu helfen schien, aber es gibt wahrscheinlich einen besseren Weg. – Alex

1

Dies ist keine vollständige Lösung, aber wenn Sie Bitmaps und JPEGs konvertieren (und umgekehrt), müssen Sie in die FJCore image library suchen. Es ist relativ einfach zu verwenden und ermöglicht beispielsweise die Größenänderung von JPEG-Bildern oder das Verschieben in eine andere Qualität. Wenn Sie Silverlight für die clientseitige Bildverarbeitung verwenden, wird diese Bibliothek wahrscheinlich nicht ausreichen, aber es ist sicherlich notwendig.

Sie sollten auch prüfen, wie Sie die Bilder dem Benutzer präsentieren. Wenn Sie Collagen mit Silverlight erstellen, können Sie vermutlich keine Virtualisierungssteuerelemente verwenden, da die Benutzer alle 150 Bilder gleichzeitig bearbeiten. Aber wie andere Leute gesagt haben, sollten Sie auch sicherstellen, dass Sie keine Bitmaps basierend auf JPEG-Dateien in voller Größe präsentieren. Ein 1MB komprimiertes JPEG wird wahrscheinlich auf eine 10MB Bitmap erweitert, von der wahrscheinlich viele Ihrer Probleme herrühren. Stellen Sie sicher, dass Sie die Bilder, die Sie dem Benutzer präsentieren, auf viel kleineren JPEG-Dateien (in geringerer Qualität und in der Größe) basieren.

+0

Danke für die Antwort, ich bin auch auf FJCore gestoßen - hatte eine Menge Probleme damit, zum Beispiel, dass die JPEGs beim Dekodieren tatsächlich größer wurden als beim Laden durch BitmapImage. Der untere Teil Ihrer Antwort wird oben besprochen. –

3

Ich vermute, Sie etwas liek dies tun:

Bitmap bitmap = new Bitmap (filename of jpeg); 

und dann tun:

OnPaint (...) 
{ 
    Graphics g = ....; 
    g.DrawImage (bitmap, ...); 
} 

Dies wird die große JPEG-Bild auf die Größe auf dem Bildschirm angezeigt werden, Ändern der Größe jedes Mal, du zeichnest es. Ich nehme an, dass Ihr JPEG etwa 2500x2000 Pixel groß ist.Wenn Sie ein JPEG in eine Bitmap laden, dekomprimiert der Bitmap - Ladecode die Daten und speichert sie entweder als RGB - Daten in einem Format, das leicht zu rendern ist (dh im selben Pixelformat wie das Display) oder als etwas, das als bekannt ist Geräteunabhängige Bitmap (alias DIBitmap). Diese Bitmaps erfordern mehr RAM zum Speichern als ein komprimiertes JPEG.

Ihre aktuelle Implementierung macht bereits Formatkonvertierung und Größenanpassung, aber dies geschieht auf unschöne Weise, d. H. Die Größe eines riesigen Bilds wird jedes Mal auf die Bildschirmgröße geändert, wenn es gerendert wird.

Idealerweise möchten Sie das Bild laden und verkleinern. .Net verfügt über ein entsprechendes System:

wobei Breite und Höhe der Größe des erforderlichen Miniaturbildes entsprechen. Nun könnten dadurch Bilder erzeugt werden, die nicht so gut aussehen, wie Sie es vielleicht möchten (aber es werden alle eingebetteten Miniaturbilddaten verwendet, wenn sie vorhanden sind, damit sie schneller sind). In diesem Fall nehmen Sie eine Bitmap-> Bitmap-Größenänderung vor .

Wenn Sie die PDF-Datei erstellen, müssen Sie die JPEG-Daten neu laden, aber aus der Sicht des Benutzers ist das OK. Ich bin mir sicher, dass es dem Benutzer nichts ausmacht, kurz darauf zu warten, die Daten in ein PDF zu exportieren, solange Sie ein Feedback haben, um den Benutzer wissen zu lassen, dass daran gearbeitet wird. Sie können dies auch in einem Hintergrund-Thread tun und den Benutzer an einer anderen Collage arbeiten lassen.

+1

Sie haben Recht, aber in Silverlight verwende ich Klassen, die von diesen abgeleitet sind. Ich bin verpflichtet, BitmapImage zu verwenden, da es kein anderes Äquivalent gibt. Das bedeutet, ich habe keine direkte Kontrolle über die Formatkonvertierung, und ich habe keine Methoden, um Thumbnails von JPEGs zu erhalten. Also meine einzige Option ist ein Bitmap-Bild aus dem Original zu machen, und verwenden Sie das, um ein Thumbnail aus - aber dann laufen Sie auf das Problem von Alex oben hingewiesen. Vielen Dank für Ihre Antwort. –

+0

@Sheeo: Ich denke, die Image.Dispose wird das von Alex angesprochene Problem lösen, da du dem System sagst, dass du die Ressourcen nicht mehr möchtest - die Docs geben an, dass die Dispose-Methode alle nicht gemanagten Ressourcen freigibt (und das meiste davon) nicht verwaltet werden - die DIBitmap – Skizz

0

Die Lösung, die schließlich für mich gearbeitet wurde mit WriteableBitmapEX folgendes zu tun:

Natürlich nur ich Thumbnails verwenden, wenn das Bild nicht bereits klein genug ist, im Speicher zu speichern.

Die gotch'a ich hatte, war die Tatsache, dass WriteableBitmap hat keinen parameterlosen Konstruktor, aber initialisiert es mit 0,0 als Größe und lädt dann die Quelle setzt diese automatisch. Das war für mich nicht selbstverständlich.

Danke für die Hilfe alle!

private WriteableBitmap getThumbnailFromBitmapStream(Stream bitmapStream, PhotoFrame photoFrame) 
    { 
     WriteableBitmap inputBitmap = new WriteableBitmap(0,0); 
     inputBitmap.SetSource(bitmapStream); 

     Size thumbnailSize = getThumbnailSizeFromWriteableBitmap(inputBitmap, photoFrame.size); 

     WriteableBitmap thumbnail = new WriteableBitmap(0,0); 
     thumbnail = inputBitmap.Resize((int)thumbnailSize.Width, (int)thumbnailSize.Height, WriteableBitmapExtensions.Interpolation.NearestNeighbor); 

     return thumbnail; 
    } 
0

Eine weitere Variante zu reduzieren ram mit: Dont laden Bilder, die in diesem Moment unsichtbar ar, und sie laden, während Benutzer die Seite zu scrollen. Diese Methode wird von Webentwicklern verwendet, um die Seitenladegeschwindigkeit zu verbessern. Für Sie ist es die Möglichkeit, die Anzahl der Bilder im RAM nicht zu speichern.

Und ich denke, der bessere Weg, um Thumbnails auf Run nicht zu machen, sondern speichern Sie sie in der Nähe der Fullsize-Bilder und erhalten Sie nur Links für sie. Wenn es benötigt wird, können Sie immer den Link zum Vollbild bekommen und es laden.

Verwandte Themen