2009-04-29 20 views
6

Sto ridimensionando alcune immagini alla risoluzione dello schermo dell'utente; se le proporzioni sono sbagliate, l'immagine dovrebbe essere tagliata. Il mio codice è simile al seguente:Ridimensionamento delle immagini - a volte qualità molto scarsa?

protected void ConvertToBitmap(string filename) 
    { 
     var origImg = System.Drawing.Image.FromFile(filename); 
     var widthDivisor = (double)origImg.Width/(double)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width; 
     var heightDivisor = (double)origImg.Height/(double)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; 
     int newWidth, newHeight; 

     if (widthDivisor < heightDivisor) 
     { 
      newWidth = (int)((double)origImg.Width/widthDivisor); 
      newHeight = (int)((double)origImg.Height/widthDivisor); 
     } 
     else 
     { 
      newWidth = (int)((double)origImg.Width/heightDivisor); 
      newHeight = (int)((double)origImg.Height/heightDivisor); 
     } 

     var newImg = origImg.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero); 
     newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp); 
    } 

Nella maggior parte dei casi, questo funziona bene. Ma per alcune immagini, il risultato è uno estremamente scadente. Sembra che sarebbe stato ridimensionato a qualcosa di molto piccolo (dimensione della miniatura) e ingrandito di nuovo .. Ma la risoluzione dell'immagine è corretta. Cosa posso fare?

Esempio orig immagine: alt text http://img523.imageshack.us/img523/1430/naturaerowoods.jpg

Esempio immagine ridimensionata: alt text http://img523.imageshack.us/img523/2531/naturaerowoods.png

Nota: Ho un'applicazione WPF, ma io uso la funzione WinForms per il ridimensionamento perché è più facile e perché ho già bisogno di un riferimento alla System.Windows.Forms per un'icona nella barra delle applicazioni.

+0

Grazie per avermelo chiesto questo! Questo problema è ancora presente al giorno d'oggi ... – Andrew

risposta

7

Cambiare le ultime due righe del tuo metodo a questo:

var newImg = new Bitmap(newWidth, newHeight); 
Graphics g = Graphics.FromImage(newImg); 
g.DrawImage(origImg, new Rectangle(0,0,newWidth,newHeight)); 
newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp); 
g.Dispose(); 
+2

È necessario anche impostare le impostazioni di qualità, che hanno un valore predefinito basso. Vedere http://nathanaeljones.com/163/20-image-resizing-pitfalls/ –

+0

Cheers per entrambi Bfree e Computer Linguist, ora risolto i miei problemi ... –

+0

Se si sta facendo questo sul server, come in un'applicazione ASP.NET, è meglio usare [un wrapper] (http://imageresizing.net) che corregge i memleaks System.Drawing. –

7

Al momento non riesco a dare un'occhiata alla fonte .NET, ma molto probabilmente il problema è nel metodo Image.GetThumbnailImage. Anche MSDN dice che "funziona bene quando l'immagine di anteprima richiesta ha una dimensione di circa 120 x 120 pixel, ma richiede una grande immagine di anteprima (ad esempio, 300 x 300) da un'immagine che ha una miniatura incorporata, potrebbe essere una notevole perdita di qualità nell'immagine in miniatura ". Per il ridimensionamento vero (cioè non la miniatura), è necessario utilizzare il metodo Graphics.DrawImage. Potrebbe anche essere necessario giocare con lo Graphics.InterpolationMode per ottenere una qualità migliore, se necessario.

2

Come indicato su MSDN, GetThumbnailImage() non è progettato per il ridimensionamento arbitrario dell'immagine. Tutto ciò che va oltre 120x120 dovrebbe essere ridimensionato manualmente. Prova a modificare:

using(var newImg = new Bitmap(origImg, newWidth, newHeight)) 
{ 
    newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp); 
} 

Modifica

Come punto di chiarimento, questo sovraccarico del costruttore Bitmap chiamate Graphics.DrawImage, anche se non si ha alcun controllo sul interpolazione.

0

Per esempi, l'immagine originale è JPG e l'immagine ridimensionata è PNG. Stai effettuando la conversione tra i formati di proposito? Il passaggio tra diversi schemi di compressione lossey può causare perdite di qualità.

+0

Voglio usare l'immagine come sfondo, e per quello scopo devo convertirlo in BMP perché Win XP non accetta altri formati per gli sfondi. – eWolf

3

Se non si sta creando una miniatura, utilizzando un metodo chiamato GetThumbnailImage probabilmente non è una buona idea ...

Per le altre opzioni, uno sguardo ai this CodeProject article. In particolare, crea una nuova immagine, crea un Graphics per esso e imposta la modalità di interpolazione su HighQualityBicubic e disegna l'immagine originale sulla grafica. Vale la pena provare, almeno.

+3

Downvoters: per favore spieghi i motivi ... –

+0

sono semplicemente gelosi della tua conoscenza epica jon li ignora –

-1

questo sta andando a variare notevolmente in base ai seguenti fattori:

  1. Come da vicino la risoluzione di destinazione corrisponde a una scala "naturale" della risoluzione originale
  2. La profondità fonte immagine a colori
  3. L'immagine tipo (s) - alcuni sono più lossy di altri
0

Aumenta o diminuisci la dimensione dell'immagine quando la ridimensiona? Se stai creando un'immagine più grande da una più piccola, ci si può aspettare questo tipo di degrado.

+0

Le sue due immagini sembrano indicare che l'originale è più grande. –

0

Le immagini saranno sicuramente degradate se le ingrandisci.

0

Alcune telecamere mettono una miniatura ridimensionata nel file stesso presumibilmente per scopi di anteprima sul dispositivo stesso.

Il metodo GetThumbnail ottiene in realtà questa anteprima che è incorporata nel file immagine invece di ottenere il metodo di risoluzione più alta.

La soluzione facile è ingannare .Net nel buttare via le informazioni di anteprima prima di eseguire il ridimensionamento o altre operazioni. in questo modo ....

img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX); 
//removes thumbnails from digital camera shots 
img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX); 

Se si sta tentando di ridimensionare le proporzioni vincolanti ho scritto un metodo di estensione su System.Drawing.Image che potreste trovare a portata di mano.

/// <summary> 
/// 
/// </summary> 
/// <param name="img"></param> 
/// <param name="size">Size of the constraining proportion</param> 
/// <param name="constrainOnWidth"></param> 
/// <returns></returns> 
public static System.Drawing.Image ResizeConstrainProportions(this System.Drawing.Image img, 
    int size, bool constrainOnWidth, bool dontResizeIfSmaller) 
{ 
    if (dontResizeIfSmaller && (img.Width < size)) 
     return img; 
    img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX); 
    img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX); 
    float ratio = 0; 
    ratio = (float)img.Width/(float)img.Height; 

    int height, width = 0; 
    if (constrainOnWidth) 
    { 
     height = (int)(size/ratio); 
     width = size; 
    } 
    else 
    { 
     width = (int)(size * ratio); 
     height = size; 
    } 
    return img.GetThumbnailImage(width, height, null, (new System.IntPtr(0))); 
} 
2

al posto di questo codice:

newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp); 

uso questo:

System.Drawing.Imaging.ImageCodecInfo[] info = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders(); 
System.Drawing.Imaging.EncoderParameters param = new System.Drawing.Imaging.EncoderParameters(1); 
param.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L); 
newImg.Save(dest_img, info[1], param); 
Problemi correlati