Laden...

Performance- und andere Fragen zu XXL-Bitmaps

Erstellt von MrSparkle vor 16 Jahren Letzter Beitrag vor 16 Jahren 4.100 Views
MrSparkle Themenstarter:in
5.658 Beiträge seit 2006
vor 16 Jahren
Performance- und andere Fragen zu XXL-Bitmaps

Hi Leute,

ich hab in der letzten Zeit ein bißchen mit extrem großen Bitmaps rumexperimentiert.
Wir hatten doch hier mal einen Beitrag, wo erwähnt wurde, wie groß ein Bitmap höchstens sein darf, um es mit der Bitmap-Klasse zu laden - kann sich da noch jemand erinnern?

Es geht bei mir darum, daß ich große Karten und Pläne mit DirectX anzeigen möchte. Dafür möchte ich ein großes Bitmap auf der CPU und mehrere kleinere (z.B. 512x512) Teile zerlegen, damit nur die Daten in den Speicher der Grafikkarte übertragen werden müssen, die gerade sichtbar sind.

Ich habe dabei übrigens festgestellt, daß die Größe scheinbar sehr beschränkt ist. Mit Paint.NET ist es nichtmal möglich, ein 16x16k großes Bitmap überhaupt zu erzeugen. Es kommt immer eine OutOfMemoryException, obwohl mehr als 700 MB freier RAM zur Verfügung steht. Gibt es da Beschränkungen durch Windows?

Ansonsten hab ich folgendes probiert, um die Bitmaps zu zerlegen:


// Großes bitmap laden:
Image srcBmp = Image.FromFile(@"c:\temp\debugmap8k.png");


// Erstes Teilstück als Datei speichern:
Image dstBmp = Image.FromHbitmap(new Bitmap(512, 512).GetHbitmap());
using (Graphics dstGfx = Graphics.FromImage(dstBmp))
{
	dstGfx.DrawImage(srcBmp,
		new Rectangle(0, 0, 512, 512),
		new Rectangle(0, 0, 512, 512),
		GraphicsUnit.Pixel);
} // using
dstBmp.Save(@"c:\temp\debugmap512.png");

Nun dauert das allerdings selbst für CPU-basierte Verarbeitung unglaublich lange. Von einer Echtzeitanwendung kann dabei nicht mehr die Rede sein.

Kann man evtl. auf einem anderen Wege das Bitmap zerlegen lassen? Irgendetwas das schneller geht als mit DrawImage()?

Schöne Grüße,
Christian

Weeks of programming can save you hours of planning

S
506 Beiträge seit 2006
vor 16 Jahren

ist die Bitmap doch erstma im speicher, solltest du mit DrawImage doch keine Probleme bekommen.

Denn selbst wenn du wenn die Bitmap 1024*786 groß ist, wirst du sie doch immer Flimmerfrei zeichnen können. Gerade wenn sie noch im Grafik speicher ist.

Wenn du natürlich für jeden Frame die Daten aus der Datei-hohlst, sieht es natürlich anderst aus.

Mach die doch einfach nen Offscreen DC oder Device, wo du dir die Bitmap zusammen puzzelst, und dann Blittest du immer die DC Teile um.

Die APi dazu heißt ja BitBlt... damit muss es gehen

MrSparkle Themenstarter:in
5.658 Beiträge seit 2006
vor 16 Jahren

Hi Stu42,

ich wollte GDI+ nur nutzen, um das große Bild in mehrere einzelne Bilder zu zerlegen.
Dazu hab ich mit Graphics.DrawImage() gezeichnet.
Ist BitBlt ein Ersatz für DrawImage()? Oder anders gefragt, ist es tatsächlich performanter, die Bitmaps mit einer API-Funktion zu zeichnen, als mit den .NET-Methoden?

Christian

Weeks of programming can save you hours of planning

S
506 Beiträge seit 2006
vor 16 Jahren

Also ich weiß, das BitBlt sehr performant ist. Ich bin mir auch sicher, das es performanter sein wird, als das DrawImage. Die frage ist nur, ob es performant genug ist...

Aber das verfahren von BitBlt ist ja auch anderst. Es verschiebt (Flippt) ja nur Daten von einen DC auf einen anderen.

Hast du dir das Bild denn schon auf Offscreen DC zusammen gepuzzelt?

Wenn ja ist es doch kein Problem das mal eben zu Testen....

Aber wie willst du das Bild denn wieder aus dem DC bekommen? Als PNG speichern? wenn du es so machst, ist es natürlich klar, das es langasm läuft.

Du darfst natürlich nur im Speicher arbeiten

MrSparkle Themenstarter:in
5.658 Beiträge seit 2006
vor 16 Jahren

Das Bild wird im Speicher zerlegt und in lauter andere Bilder, die auch im Speicher sind, aufgeteilt. Dann werden die kleineren Teil-Bitmaps als Direct3D.Texture an DirectX übergeben.

Hast du evtl. ein Beispiel für die BitBlt-Funktion? Wäre nett!

Schöne Grüße,
Christian

Weeks of programming can save you hours of planning

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo MrSparkle,

ich denke, das aufwändige ist nicht das eine DrawImage, sondern das Laden der großen Bitmap.

Hast du evtl. ein Beispiel für die BitBlt-Funktion?

Wie fast immer gibt es auch hier ein Beispiel in der (Win32-)Doku: BitBlt

herbivore

MrSparkle Themenstarter:in
5.658 Beiträge seit 2006
vor 16 Jahren

Hi herbivore!

Danke für das Beispiel. Hab allerdings in irgendeinem Forum gelesen, daß es nicht performanter ist als die DrawImageUnscaled-Methode.

Das Laden des Bitmaps ist selbstverständlich wesentlich langsamer als das Zerteilen, deshalb hab ich die Zeit nicht mit einbezogen. Trotzdem braucht GDI+ für das Anlegen eines Tiles von 512x512 noch 500 ms aus einem 8k Bitmap (das schon im Speicher liegt).

Das Problem ist wahrscheinlich, daß in den meisten Fällen das große Bitmap geladen wird und gleich wieder im Auslagerungsspeicher auf die Festplatte geschrieben wird. Deshalb dauert das Zerschneiden auch so lange.

Eigentlich bräuchte ich für sowas ein Dateiformat, das die Daten schon intern so aufgeteilt hat, daß man nur die entsprechenden Teile aus der Datei lesen braucht.

Sind nicht in einem JPEG die Daten in 8x8 Pixel-Blöcken angeordnet? Könnte man da nicht sequentiell auf die Datei zugreifen und sich alle Blöcke laden, die in dem gewünschten 512x512-Bereich liegen?

Christian

Weeks of programming can save you hours of planning

S
506 Beiträge seit 2006
vor 16 Jahren

die lade zeit ist doch egal, denn das machst du ja nur einmal am Start! dann ist es im speicher, und dann kannst du dir ja verschiedene Teile aus dem Bild (welches auf de, DC liegt) herauspicken...

Ich verstehe auch irgentwie dein Problem nicht.

Du willst eigentlich nur eine große bitmap darstellen, die man scrollen kann, oder?

Wo ist da denn nun das Problem, kannst du die Bitmap micht laden? Oder hast du sie schon geladen, aber kannst sie nicht flüssig darstellen?

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo MrSparkle,

Das Problem ist wahrscheinlich, daß in den meisten Fällen das große Bitmap geladen wird und gleich wieder im Auslagerungsspeicher auf die Festplatte geschrieben wird. Deshalb dauert das Zerschneiden auch so lange.

das ist durchaus möglich.

Eigentlich bräuchte ich für sowas ein Dateiformat, das die Daten schon intern so aufgeteilt hat, daß man nur die entsprechenden Teile aus der Datei lesen braucht.

Bei unkomprimierten Bitmaps (also BMPs) wäre das auch recht einfach.

Sind nicht in einem JPEG die Daten in 8x8 Pixel-Blöcken angeordnet?

Zwar nicht zwangsläufig 8x8, aber richtig, immer in Blöcken. Das "Problem" ist bloß, dass die Blöcke (unterschiedlich stark) komprimiert werden. Man kann also nicht per Seek den jeweils benötigten Block ansteuern, sondern muss die Datei sequentiell lesen. Aber möglich wäre es an sich schon. Gibt vielleicht sogar fertige Programme, die das tun.

herbivore

S
506 Beiträge seit 2006
vor 16 Jahren

Das Problem ist wahrscheinlich, daß in den meisten Fällen das große Bitmap geladen wird und gleich wieder im Auslagerungsspeicher auf die Festplatte geschrieben wird. Deshalb dauert das Zerschneiden auch so lange.

das ist durchaus möglich.

Halte ich hier für unwahrscheinlich

dann müsste das bild aber mehrere Hundert MB sein.... die Auslagerungsdatei wird ja erst geschrieben, wenn der speicher voll, oder nicht mehr benötigt wird.

Und vom ansatz her wäre es volkommen falsch, ein beispielsweise 800mb großs bild am start in den Speicher zu laden.

Da müsste man dann teile immer wieder neu laden.

MrSparkle Themenstarter:in
5.658 Beiträge seit 2006
vor 16 Jahren

Richtig. Allerdings müssen die Einzelteile erstmal erzeugt werden. Dazu muß das große Bild so oder so in den Speicher geladen werden.

Ich habe Tests gemacht mit einem 8k-Bild (8096x8096 Pixel * 32Bit). Das ist so ungefähr die Größenordnung, mit der ich für hochaufgelöste Geländemodelle/Geländetexturen rechne. Auf der Festplatte sind die Bilder als JPG oder PNG nur einige MB groß, aber im Speicher liegen sie entpackt. Das sind dann 8096² * 4 = 250 MB. Daß die Daten in die Auslagerungsdatei kommen, vermute ich weil während dem Zeichnen ständig von der Platte gelesen wird.

Deshalb suche ich momentan nach einem Beipiel, wo man Bilddateien stückchenweise (bzw. eher ausschnittsweise) von der Platte lesen kann.
Leider hab ich noch nichts brauchbares finden könne, einfach weil ich nicht weiß, wonach ich am besten suchen sollte. Falls jemand ein paar Stichwörter für mich hat, wäre ich sehr dankbar!

Schöne Grüße,
Christian

Weeks of programming can save you hours of planning

MrSparkle Themenstarter:in
5.658 Beiträge seit 2006
vor 16 Jahren

Hallo allerseits!

Ich hab in der Zwischenzeit zumindest herausbekommen, warum es nicht geht:

Let's imagine you are trying to load 8000x8000 JPEG image. Each pixel in JPEG takes 24 bits (it is stored in 24-bit RGB pixel format), or, in other words, 3 bytes. The memory buffer size for such bitmap should be 8000x8000x3=192000000, or about 183MB. This is quite block of memory and it is not always possible to allocate it.

Even if the system shows you much more free memory available, it is still may be lacking of memory. The problem is that this memory block should be linear (i.e. continuous). But memory is always fragmented (i.e. all the free memory comprises of a big number of memory portions of different sizes). It is not guarantied that the system will find a memory block of enough size when you try to allocate 183MB.

(Quelle: http://aurigma.com/128/section.aspx/83)
Edit: URL moved to http://forums.aurigma.com/yaf_postst2059_HOWTO-Processing-huge-bitmaps.aspx

Auf derselben Seite ist erwähnt, daß es eine Möglichkeit gibt, JPEG-Bilder stückchenweise in den Speicher zu lesen. Es gibt ja auch die Möglichkeiten JPEGs zu drehen oder zu spiegeln, ohne sie zu dekomprimieren. Scheinbar kann man die Bilder auch zuschneiden, ohne sie zu entpacken.

Kennt jemand eine Komponente oder Bibliothek, wo solche Bildoperationen für JPEGs schon implementiert sind?

Schöne Grüße,
Christian

Weeks of programming can save you hours of planning