2009-02-20 19 views
11

Sto provando a scrivere un'applicazione di visualizzazione immagini leggera. Tuttavia, esistono limitazioni della memoria di sistema con .NET.Come utilizzare i bitmap di grandi dimensioni in .NET?

Quando si tenta di caricare bitmap di grandi dimensioni (9000 x 9000 px o superiore, 24 bit), viene visualizzato System.OutOfMemoryException. Si tratta di un PC Windows 2000 con 2 GB di RAM (di cui 1,3 GB esauriti). Richiede anche molto tempo per tentare di caricare i file.

Il codice seguente genera questo errore:

Image image = new Bitmap(filename); 
using (Graphics gfx = this.CreateGraphics()) 
{ 
    gfx.DrawImage(image, new Point(0, 0)); 
} 

come fa questo codice:

Stream stream = (Stream)File.OpenRead(filename); 
Image image = Image.FromStream(stream, false, false); 
using (Graphics gfx = this.CreateGraphics()) 
{ 
    gfx.DrawImage(image, new Rectangle(0, 0, 100, 100), 4000, 4000, 100, 100, GraphicsUnit.Pixel); 
} 

Inoltre, è sufficiente fare proprio questo:

Bitmap bitmap = new Bitmap(filename); 
IntPtr handle = bitmap.GetHbitmap(); 

Quest'ultimo codice era destinato all'uso con GDI. Durante la ricerca di questo, ho scoperto che questo è in realtà un problema di memoria in cui .NET tenta di allocare il doppio di quanto è necessario in un singolo blocco contiguo di memoria.

http://bytes.com/groups/net-c/279493-drawing-large-bitmaps

so da altre applicazioni (Internet Explorer, MS Paint, ecc), che è possibile aprire immagini di grandi dimensioni, e piuttosto rapidamente. La mia domanda è, come faccio a utilizzare bitmap di grandi dimensioni con .NET?

Esiste comunque lo streaming o la mancata memoria li carica?

risposta

8

Questa è una domanda in due parti.La prima domanda è come caricare immagini di grandi dimensioni senza esaurire la memoria (1), la seconda è migliorare le prestazioni di caricamento (2).

(1) Concidete un'applicazione come Photoshop in cui è possibile lavorare con enormi immagini che consumano gigabites sul filesystem. Mantenere l'intera immagine in memoria e avere ancora abbastanza memoria libera per eseguire operazioni (filtri, elaborazione di immagini e così via, o anche solo aggiungere layer) sarebbe impossibile su molti sistemi (anche sistemi da 8GB x64).

Ecco perché applicazioni come questa utilizzano il concetto di file di scambio. Internamente suppongo che Photoshop utilizzi un formato di file proprietario, adatto per la progettazione dell'applicazione e costruito per supportare carichi parziali dallo swap, consentendo loro di caricare parti di un file in memoria per elaborarlo.

(2) Performande può essere migliorato (parecchio) scrivendo caricatori personalizzati per ogni formato di file. Ciò richiede di leggere le intestazioni e la struttura dei file con cui si desidera lavorare. Una volta che hai ottenuto la mano di esso non è **** così difficile, ma non è così banale come fare una chiamata di metodo.

Ad esempio, è possibile utilizzare google per FastBitmap per visualizzare esempi su come caricare un file bitmap (BMP) molto rapidamente, inclusa la decodifica dell'intestazione bitmap. Ciò ha comportato PInvoke e per darvi qualche idea su ciò che si è di fronte è necessario definire le structues bitmap quali

 [StructLayout(LayoutKind.Sequential, Pack = 1)] 
     public struct BITMAPFILEHEADER 
     { 
      public Int16 bfType; 
      public Int32 bfSize; 
      public Int16 bfReserved1; 
      public Int16 bfReserved2; 
      public Int32 bfOffBits; 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     public struct BITMAPINFO 
     { 
      public BITMAPINFOHEADER bmiHeader; 
      public RGBQUAD bmiColors; 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     public struct BITMAPINFOHEADER 
     { 
      public uint biSize; 
      public int biWidth; 
      public int biHeight; 
      public ushort biPlanes; 
      public ushort biBitCount; 
      public BitmapCompression biCompression; 
      public uint biSizeImage; 
      public int biXPelsPerMeter; 
      public int biYPelsPerMeter; 
      public uint biClrUsed; 
      public uint biClrImportant; 
     } 

Forse lavorare con la creazione di un DIB (http://www.herdsoft.com/ti/davincie/imex3j8i.htm) e stranezze come i dati vengano memorizzati "a testa in giù "in una bitmap che devi prendere in considerazione o vedrai un'immagine speculare quando la apri :-)

Ora è solo per bitmap. Diciamo che volevi fare PNG, quindi avresti bisogno di fare cose simili, ma decodificare l'intestazione PNG, che nella sua forma più semplice non è così difficile, ma se vuoi ottenere il pieno supporto per le specifiche PNG, allora sei pronto per una corsa divertente: -)

PNG è diverso da un bitmap poiché utilizza un formato basato sul blocco in cui ha "intestazioni" che è possibile individuare per trovare i dati differenti. Esempio di alcuni pezzi che ho usato durante il gioco con il formato era

string[] chunks = 
new string[] {"?PNG", "IHDR","PLTE","IDAT","IEND","tRNS", 
"cHRM","gAMA","iCCP","sBIT","sRGB","tEXt","zTXt","iTXt", 
"bKGD","hIST","pHYs","sPLT","tIME"}; 

Siete anche andando ad avere per conoscere checksum Adler32 per i file PNG. Quindi ogni formato di file che vorresti fare aggiungerebbe una serie diversa di sfide.

Vorrei davvero poter dare esempi di codice sorgente più completi nella mia risposta, ma è un argomento complesso, e ad essere onesto non ho implementato uno scambio da solo, quindi non sarei in grado di dare troppi consigli quella.

La risposta breve è che le capacità di elaborazione dell'immagine nel BCL non sono così calde. La risposta media sarebbe cercare di trovare se qualcuno ha scritto una libreria di immagini che potrebbe aiutarti e la lunga risposta sarebbe quella di tirarti su le maniche e scrivere tu stesso il nucleo della tua applicazione.

Dal momento che tu mi conosci nella vita reale si sa dove trovarmi;)

0

Che dire:

Image image = new Bitmap(filename); 
using (Graphics gfx = Graphics.FromImage(image)) 
{  
// Stuff 
} 
+0

non penso che funzionerà su un sistema a 32 bit basato sui limiti – WiiMaxx

0

solo una soluzione rapida, ho avuto lo stesso problema, ho creato una seconda istanza bitmap e superato la bitmap nel costruttore.

0

È possibile creare una bitmap nuova, vuota, con le stesse dimensioni e la stessa profondità di colore? In questo caso, almeno sai che il tuo ambiente può gestire l'immagine. Quindi il problema risiede nel sottosistema di caricamento dell'immagine, poiché ovviamente il tuo link potrebbe essere il caso.

Immagino che tu possa scrivere i tuoi caricatori di bitmap, ma quello è un sacco di lavoro per i formati non banali, quindi non lo suggerirei.

Forse ci sono librerie di sostituzione disponibili che aggirano questi problemi con i caricatori standard?

0

Quindi stai dicendo che non è il caricamento della bitmap ma il rendering che causa una memoria insufficiente?

In tal caso, è possibile utilizzare Bitmap.LockBits per ottenere i pixel e scrivere autonomamente un riduttore di immagini di base?

3

Per una risposta davvero completa, vorrei utilizzare Reflector per osservare il codice sorgente di Paint.NET (http://www.getpaint.net/); un programma di editing grafico avanzato scritto in C#.

(come indicato nel commento, Paint.NET era open source ma ora è closed source).

+0

Paint.net è un programma closed-source dalla fine del 2007 (http: //blog.getpaint.net/2007/12/04/freeware-authors-beware-of-% E2% 80% 9Cbackspaceware% E2% 80% 9D /) – zihotki

+0

Che peccato. Ho modificato per riflettere il tuo commento. – ine

0

Una cosa mi ha colpito. Stai disegnando l'intera immagine e non solo la parte visibile? Non si dovrebbe disegnare una porzione più grande dell'immagine di quella che si sta visualizzando nell'applicazione, utilizzare i parametri x, y, larghezza e altezza per limitare l'area disegnata.

Problemi correlati