Strategien zum Arbeiten mit großen Bilddatenmengen

8

Technologie-Stack: C # / .NET 4 / WinForms

Hintergrund:

Das Projekt, an dem ich arbeite, ist eine Visualisierungsanwendung für eine Reihe von Bildstapeln. Insbesondere ist jeder Bildstapel zu einem Gitter ausgerichtet, zeigt das gleiche Bild zu jeder Zeit, und Verarbeitungsfunktionen werden auf die gegenwärtig angezeigten Bilder angewendet. Die Bildstapel selbst sind 150-300 MB und jedes Bild ist 512 KB-1 MB. Ein typischer Datensatz besteht aus ~ 100 Bildstapeln.

Frage:

Um zu versuchen, mit dieser Datenmenge zu arbeiten, verwende ich mehrere Techniken:

  • Speicherabbilddateien: Bildstapel werden beim Start der Anwendung von der Festplatte geladen
  • Kompilierung unter x64 mit unsicherem Code erlaubt: Offensichtlich brauche ich 64-bit Adressraum für Dateien dieser Größe. Ich verschiebe das aktuell angezeigte Bild aus der Memory-Mapped-Datei in eine Methode, die über Marshal.Copy eine Bitmap mit unsicheren Zeigern erzeugt.
  • System.Threading.Tasks: Ich verwende parallele Schleifen für die Verarbeitung wo möglich
  • System.Drawing.BufferedGraphicsContext: Jeder Bildstapel hat ein aktives Bild, das auf einen BufferedGraphicsContext zusammengesetzt wird, bevor es an eine PictureBox zur Anzeige an den Benutzer übergeben wird.
  • High-End-Systemanforderungen: Quad-Core-CPU oder besser, SSD, 12 GB Speicher, etc

Aber selbst wenn man all das oben genannte verwendet, lässt die Reaktionsfähigkeit viel zu wünschen übrig. Bei Verwendung des Prozess-Explorers von SysInternals ist die CPU-Auslastung niedrig (& lt; 25%), während die Speicherbelegung den Grenzwert erreicht, bevor die Speicherbereinigung stattfindet.

Die Profilerstellung zeigt, dass die meiste Ausführungszeit dafür aufgewendet wird, Daten aus den im Speicher abgelegten Dateien abzurufen. Ich nehme an, es wartet, wie das Betriebssystem den angeforderten Speicher in den aktiven Speicher zurücksendet?

Was könnte ich noch tun, um die Leistung zu verbessern?

Hinweis:

  • Die meisten, wenn nicht alle Bildstapel können gleichzeitig angezeigt werden, sodass das Beschneiden im aktuellen Ansichtsfenster möglicherweise nicht viel Geschwindigkeit ergibt.
  • Die Größenanpassung für die Anzeige ist eine Option, aber die vollständigen Originaldaten müssen immer noch für die Verarbeitung verfügbar sein, so dass dies nur ein zusätzlicher Schritt zu sein scheint.

Update 1:

  • Für den Speicher hat meine Entwicklungsbox nur 6 GB (und ich versuche, weniger Dateien zu laden), aber das Bereitstellungssystem hat 24 GB.
  • Ich untersuche SSE-Optimierungen durch die Intel Performance Primitives und GPU-Beschleunigung über CUDA.
  • Der Grund, warum ich versuche, alle Daten in den Speicher zu laden, liegt darin, dass ein wichtiger Visualisierungsschritt mit 15-60 Hz durch die Bildstapel läuft und ich Angst hatte zu dreschen.
Noren 01.02.2012, 21:42
quelle

5 Antworten

5

Zunächst einmal ist es nicht sehr hilfreich, unsicheren Code und gespeicherte Dateien zu verwenden. Sie müssen rund 20 GB Daten von der Festplatte lesen. Das Lesen von der Festplatte dauert viel länger als eine zusätzliche Kopie im Speicher, wenn Sie nur Streams verwenden - Sie haben sie an der falschen Stelle optimiert.

Ich denke, du solltest es aus einem anderen Blickwinkel betrachten. Sie zeigen Stapel von Bildern mit einem Wert von 20 GB auf einem Bildschirm, der weniger als 10 MB Daten anzeigen kann. Sie müssen nicht 20 GB Daten lesen, um alle Bildstapel anzuzeigen und während der Verarbeitung dieser Bilder eine ansprechende Benutzeroberfläche bereitzustellen. Sie müssen nur das oberste Bild von jedem Stapel laden - das wird viel viel schneller sein.

Was die eigentliche Verarbeitung betrifft, so können Sie, sofern Sie die GPU nicht irgendwie verwenden können, nicht schneller vorgehen, als Bilder parallel zu verarbeiten. Ich denke, es hängt von der Verarbeitung ab, die du tatsächlich tust.

    
zmbq 01.02.2012, 21:55
quelle
2

Sie können immer noch vorgenerierte Thumb-Bilder pro Bild erstellen und nur diese in Grid laden, wenn alle Bilder verfügbar sind. In dem Moment, in dem der Benutzer einen Effekt / eine Transformation auf das Bild anwendet, können Sie nur dieses Bild laden. Und auch bei der Beladung von nur das Bild, das Sie es zu Clipping-Sektoren Laden devide können und sie in asynchroner Weise laden. Wenn Sie schauen auf Google Street View, wie es lädt, nach Zoom, wil Sie herausfinden, dass nie gesamtes Bild (auch wenn es von Ihnen angeforderte wurde) sofort geladen, aber es belastet durch Sektoren.

Eine weitere sehr interessante Technologie Ich denke, Deep Zoom kann sein, wenn nicht eine Antwort auf Ihre Probleme, aber kann zumindest einen guten Hinweis geben.

Ein weiteres Beispiel auf Deep Zoom von Scott Hanselman

Viel Glück.

    
Tigran 01.02.2012 21:57
quelle
0

So pervers dies klingt, Sie sollten versuchen, alle Daten in den Speicherabbilddateien beim Start Ihrer Anwendung zu berühren (d. h. ein Byte bei jedem Vielfachen von 4 KB ab dem Beginn jeder Datei zu lesen). Da Sie genug RAM haben, ist das Problem wahrscheinlich nicht, dass das Betriebssystem Ihre Bilder ausblättert, sondern dass es sie nicht zuerst einblendet. Speicherkarten sind ladend geladen, so dass das Betriebssystem die Festplatte erst trifft, wenn Sie es tatsächlich versuchen Greifen Sie auf die Daten in den Memory-Mapped-Dateien zu. Als Ergebnis führt das Berühren des Speichers beim Laden der Anwendung dazu, dass die Plattenlesevorgänge dann statt der Zeit stattfinden, wenn der Benutzer Bildstapel betrachtet.

    
Adam Mihalcin 01.02.2012 21:55
quelle
0

Verringern Sie die Anzahl parallel verarbeiteter Bilder, um die Speicherlokalität zu erhöhen. Ihre 4 Kerne sollten also ein Bild zur selben Zeit verarbeiten. Dies ist gut für den Prozessor-Cache.

    
Andrew 01.02.2012 21:56
quelle
0

Je nachdem, wie groß die Anzeige und die Bilder sind, können Sie versuchen, die Bilder so zu verkleinern, dass sie der Auflösung des Quadrats entsprechen. Wenn Sie dann auf einen Punkt des Bildes fokussieren möchten, können Sie das Bild erneut laden Originalbild.

Wenn Sie WPF verwenden, können Sie versuchen, DecodePixelWidth und DecodePixelHeight zu verwenden. Vielleicht gibt es ein Äquivalent in winforms

    
Vincent Hubert 02.02.2012 16:20
quelle