2009-11-16 16 views
10

Ho una situazione in cui è necessario ridimensionare un numero elevato di immagini. Queste immagini sono attualmente archiviate come file .jpg sul file system, ma mi aspetto di avere byte [] in memoria più avanti nel progetto. La dimensione dell'immagine sorgente è variabile, ma l'output dovrebbe essere di 3 dimensioni predeterminate differenti. I rapporti di aspetto dovrebbero essere preservati, riempendo l'immagine originale con uno spazio bianco (ad esempio, un'immagine molto alta verrà ridimensionata per adattarsi alle dimensioni dell'immagine target quadrata, con ampie aree di bianco a sinistra ea destra).Ridimensionamento delle prestazioni delle immagini: System.Drawing vs System.Windows.Media

Inizialmente ho creato il progetto con targeting per .NET 2.0 e utilizzando le classi System.Drawing per eseguire il caricamento/ridimensionamento/salvataggio. relativo codice comprende:

original = Image.FromFile(inputFile); //NOTE: Reused for each of the 3 target sizes 
Bitmap resized = new Bitmap(size, size); 
//Draw the image to a new image of the intended size 
Graphics g = Graphics.FromImage(resized); 
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; 
g.Clear(Color.White); 
g.DrawImage(original, center - width/2f, center - height/2f, width, height); 
g.Dispose(); 
//Save the new image to the output path 
resized.Save(outputFile, ImageFormat.Jpeg); 

ho voluto porto questo progetto NET 3.5, quindi provato utilizzando le classi System.Windows.Media per eseguire la stessa funzione. L'ho fatto funzionare, tuttavia le prestazioni sono terribili; il tempo di elaborazione per immagine è di circa 50 volte più lungo. La maggior parte del tempo viene impiegata per caricare l'immagine. Il codice rilevante include:

BitmapImage original = new BitmapImage(); //Again, reused for each of the 3 target sizes 
original.BeginInit(); 
original.StreamSource = new MemoryStream(imageData); //imageData is a byte[] of the data loaded from a FileStream 
original.CreateOptions = BitmapCreateOptions.None; 
original.CacheOption = BitmapCacheOption.Default; 
original.EndInit(); //Here's where the vast majority of the time is spent 
original.Freeze(); 

// Target Rect for the resize operation 
Rect rect = new Rect(center - width/2d, center - height/2d, width, height); 

// Create a DrawingVisual/Context to render with 
DrawingVisual drawingVisual = new DrawingVisual(); 
using (DrawingContext drawingContext = drawingVisual.RenderOpen()) 
{ 
    drawingContext.DrawImage(original, rect); 
} 

// Use RenderTargetBitmap to resize the original image 
RenderTargetBitmap resizedImage = new RenderTargetBitmap(
    size, size,       // Resized dimensions 
    96, 96,        // Default DPI values 
    PixelFormats.Default);    // Default pixel format 
resizedImage.Render(drawingVisual); 

// Encode the image using the original format and save the modified image 
SaveImageData(resizedImage, outputFile); 

Sto facendo qualcosa di sbagliato qui, per prendere così tanto tempo? Ho provato a utilizzare solo il costruttore su BitmapImage che prende un URI, lo stesso problema di prestazioni. Qualcuno ha fatto qualcosa di simile prima, sai se c'è un modo più performante per farlo? O ho solo bisogno di usare ancora System.Drawing? Grazie!

risposta

10

E dopo aver digitato tutto ciò, mi è venuto in mente che potevo caricare i simboli da MS per le classi System.Windows.Media e passare dove era lento. Trovato immediatamente la causa e la soluzione. Le immagini di input sono state salvate con un profilo colore e stava tentando di caricare quel profilo colore (dal file system) di ogni immagine. Passando da BitmapCreateOptions.None a BitmapCreateOptions.IgnoreColorProfile nel codice qui sopra, non lo fa più e si comporta altrettanto velocemente di quanto fatto da System.Drawing.

Spero che questo aiuti chiunque altro che si imbatte in questo problema!

+0

Grazie per il suggerimento! – Jan

0

penso che questo dalla pagina System.Drawing su MSDN potrebbero essere rilevanti:

Lo spazio dei nomi System.Drawing fornisce l'accesso alle funzionalità GDI + grafica di base. Le funzionalità più avanzate sono fornite negli spazi dei nomi System.Drawing.Drawing2D, System.Drawing.Imaging e System.Drawing.Text. La classe Graphics fornisce metodi per disegnare sul dispositivo di visualizzazione. Classi come Rectangle e Point incapsulano primitive GDI +. La classe Pen viene utilizzata per disegnare linee e curve, mentre le classi derivate dalla classe astratta Brush vengono utilizzate per riempire gli interni delle forme.

Utilizzando System.Drawing si sono più vicini alla funzionalità grafica di base reale che se si va via System.Windows.Media cui:

definisce gli oggetti che consentono l'integrazione di contenuti multimediali, tra cui disegni, testo e audio/video contenuto nelle applicazioni Windows Presentation Foundation (WPF).

System.Drawing è ancora supportato, quindi rimango con quello.

0

Ho trovato una situazione interessante nel codice.Rimuovereusing dalla seguente riga:

using(DrawingContext drawingContext = drawingVisual.RenderOpen()) 

Non sono sicuro che il motivo per cui questa velocità Codice, ma si può fare un tentativo.

+2

Questo accelera il codice perché quando lo fai, non è in realtà il rendering. Il contesto di disegno in realtà non disegna ciò che gli dici, alla visuale sottostante, finché non chiami drawingContext.Close(), o il disegnoContext esce dall'ambito (che è ciò che fa l'uso). –

+0

bella informazione, ty! –

5

Sembra che tu stia facendo questo nel modo più difficile. Puoi lasciare che WPF lavori per te semplicemente impostando DecodePixelHeight e DecodePixelWidth. Questo farà sì che il ridimensionamento avvenga durante il caricamento dell'immagine:

BitmapImage resizedImage = new BitmapImage 
{ 
    StreamSource = new MemoryStream(imageData), 
    CreateOptions = BitmapCreateOptions.IgnoreColorProfile, 
    DecodePixelHeight = height, 
    DecodePixelWidth = width, 
} 
resizedImage.BeginInit(); // Needed only so we can call EndInit() 
resizedImage.EndInit(); // This does the actual loading and resizing 

imageSaveImageData(resizedImage, outputFile); 

ho inserito anche la soluzione IgnoreColorProfile hai trovato nel mio codice.

Aggiornamento Ho riletto la tua domanda e ho capito che la ragione per cui stai usando DrawingVisual è che hai bisogno di spazi bianchi attorno all'immagine per renderla quadrata. DecodePixelHeight e DecodePixelWidth non raggiungerebbero questo obiettivo, quindi la mia soluzione non risponde alla tua domanda.

Lascerò qui la mia risposta nel caso in cui qualcuno che ha appena bisogno di un ridimensionamento senza spazio bianco si imbatta in questa domanda.

Problemi correlati