Domanda

Sto ridimensionando alcune immagini in base alla risoluzione dello schermo dell'utente; se le proporzioni sono errate, l'immagine dovrebbe essere tagliata. Il mio codice è simile al seguente:

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, funziona bene. Ma per alcune immagini, il risultato ha una estremamente scarsa qualità. Sembra che sarebbe stato ridimensionato a qualcosa di molto piccolo (dimensioni in miniatura) e ingrandito di nuovo .. Ma la risoluzione dell'immagine è corretta. Cosa posso fare?

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

Esempio di immagine ridimensionata: alt text

Nota: ho un'applicazione WPF ma utilizzo la funzione WinForms per il ridimensionamento perché è più semplice e perché ho già bisogno di un riferimento a System.Windows.Forms per un'icona nella barra delle applicazioni.

È stato utile?

Soluzione

Cambia le ultime due righe del tuo metodo in 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();

Altri suggerimenti

Al momento non riesco a sbirciare nel sorgente .NET, ma molto probabilmente il problema è nel metodo Image.GetThumbnailImage . Anche MSDN afferma che "funziona bene quando l'immagine di anteprima richiesta ha una dimensione di circa 120 x 120 pixel, ma si richiede un'immagine di anteprima di grandi dimensioni (ad esempio 300 x 300) da un'immagine che ha un'anteprima incorporata, lì potrebbe essere una notevole perdita di qualità nell'immagine di anteprima ". Per un vero ridimensionamento (ovvero non miniature), è necessario utilizzare il metodo Graphics.DrawImage . Potrebbe anche essere necessario giocare con Graphics.InterpolationMode per ottenere una qualità migliore, se necessario.

Se non stai creando una miniatura, probabilmente non è una buona idea usare un metodo chiamato GetThumbnailImage ...

Per altre opzioni, dai un'occhiata a questo articolo di CodeProject . In particolare, crea una nuova immagine, crea un Graphics e imposta la modalità di interpolazione su HighQualityBicubic e disegna l'immagine originale sulla grafica. Vale la pena provare, almeno.

Come indicato su MSDN , GetThumbnailImage () non è progettato per eseguire il ridimensionamento arbitrario delle immagini. Qualunque cosa oltre 120x120 dovrebbe essere ridimensionata manualmente. Prova invece questo:

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 chiama Graphics.DrawImage , sebbene non si abbia alcun controllo sull'interpolazione.

invece di questo codice:

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

usa 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);

Ad esempio, l'immagine originale è JPG e l'immagine ridimensionata è PNG. Stai convertendo tra i formati di proposito? Il passaggio tra diversi schemi di compressione con perdita di dati può causare una perdita di qualità.

Stai aumentando o diminuendo la dimensione dell'immagine quando la ridimensioni? Se stai creando un'immagine più grande da una più piccola, questo tipo di degrado è prevedibile.

Le immagini saranno sicuramente degradate se le ingrandisci.

Alcune fotocamere hanno inserito una miniatura ridimensionata nel file stesso presumibilmente per scopi di anteprima sul dispositivo stesso.

Il metodo GetThumbnail ottiene effettivamente questa immagine Miniatura che è incorporata nel file immagine invece di ottenere il metodo di risoluzione più alta.

La soluzione semplice è ingannare .Net nel buttare via quelle informazioni in miniatura prima di fare il ridimensionamento o altre operazioni. così ....

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

Se stai tentando di ridimensionare le proporzioni vincolanti, ho scritto un metodo di estensione su System.Drawing.Immagine che potresti trovare utile.

/// <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)));
}

Questo varierà ampiamente in base ai seguenti fattori:

  1. Quanto la risoluzione di destinazione corrisponde a una "naturale" scala della risoluzione originale
  2. Profondità del colore dell'immagine sorgente
  3. I tipi di immagine - alcuni sono più in perdita di altri
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top