2009-04-29 9 views
6

Ich skaliere einige Bilder auf die Bildschirmauflösung des Benutzers; Wenn das Seitenverhältnis falsch ist, sollte das Bild geschnitten werden. Mein Code sieht wie folgt aus:Bildgrößenanpassung - manchmal sehr schlechte Qualität?

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

In den meisten Fällen funktioniert das gut. Aber für einige Bilder hat das Ergebnis eine extrem schlechte Qualität. Es sieht so aus als wäre die Größe auf etwas sehr klein (thumbnail size) und wieder vergrößert worden. Aber die Auflösung des Bildes ist korrekt. Was kann ich tun?

Beispiel orig Bild: alt text http://img523.imageshack.us/img523/1430/naturaerowoods.jpg

Beispiel skalierte Bild: alt text http://img523.imageshack.us/img523/2531/naturaerowoods.png

Anmerkung: Ich habe eine Anwendung WPF aber ich benutze die Funktion WinForms zum Ändern der Größe, weil es einfacher ist, und weil ich schon einen Verweis auf benötigen System.Windows.Forms für ein Taskleistensymbol.

+0

Vielen Dank für diese fragen! Dieses Problem ist immer noch vorhanden ... – Andrew

Antwort

7

ändern, um die letzten beiden Zeilen Ihrer Methode dazu:

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

Sie müssen auch die Qualität einstellen - sie Standard zu niedrig. Siehe http://nathanaeljones.com/163/20-image-resizing-pitfalls/ –

+0

Cheers zu Bfree und Computer Linguist, jetzt gelöst meine Probleme ... –

+0

Wenn Sie dies auf dem Server tun, wie In einer ASP.NET-App empfiehlt es sich, [einen Wrapper] (http://imageresizing.net) zu verwenden, der die System.Drawing-Memleaks behebt. –

7

Ich kann im Moment nicht in die .NET-Quelle schauen, aber am wahrscheinlichsten ist das Problem in der Image.GetThumbnailImage Methode. Sogar MSDN sagt, dass "es gut funktioniert, wenn das angeforderte Miniaturbild eine Größe von etwa 120 x 120 Pixeln hat, aber Sie können ein großes Miniaturbild (z. B. 300 x 300) von einem Bild mit einem eingebetteten Miniaturbild anfordern ein deutlicher Qualitätsverlust im Thumbnail-Bild sein ". Für die echte Größenanpassung (d. H. Nicht Thumbnailing) sollten Sie die Methode Graphics.DrawImage verwenden. Möglicherweise müssen Sie auch mit der Graphics.InterpolationMode spielen, um bei Bedarf eine bessere Qualität zu erhalten.

2

Wie auf MSDN angegeben, ist GetThumbnailImage() nicht für die Bildskalierung gedacht. Alles über 120x120 sollte manuell skaliert werden. Versuchen Sie stattdessen:

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

bearbeiten

Als Ausgangspunkt der Klärung dieser Überlastung des Bitmap Konstruktor ruft Graphics.DrawImage, wenn Sie keine Kontrolle über die Interpolation haben.

0

Zum Beispiel ist das Originalbild JPG und das Bild in der Größe ist PNG. Konvertieren Sie absichtlich zwischen verschiedenen Formaten? Der Wechsel zwischen verschiedenen Lossey-Komprimierungsschemata kann zu Qualitätsverlusten führen.

+0

Ich möchte das Bild als Hintergrundbild verwenden, und zu diesem Zweck muss ich es in BMP konvertieren, da Win XP keine anderen Formate für Hintergrundbilder akzeptiert. – eWolf

3

Wenn Sie nicht ein Miniaturbild, mit einer Methode namens GetThumbnailImage wahrscheinlich keine gute Idee ...

Für andere Optionen haben einen Blick auf this CodeProject article zu schaffen. Insbesondere erzeugt es ein neues Bild, erzeugt eine Graphics dafür und setzt den Interpolationsmodus auf HighQualityBicubic und zeichnet das Originalbild auf die Grafik. Einen Versuch wert, zumindest.

+3

Downwoters: Bitte geben Sie Gründe ... –

+0

Sie sind nur eifersüchtig auf Ihr episches Wissen Jon ignorieren sie –

-1

Dies wird allgemein auf der Grundlage der folgenden Faktoren variieren:

  1. Wie eng die Zielauflösung
  2. Die Quellbild-Farbtiefe
  3. Das Bild einen „natürlichen“ Maßstab der ursprünglichen Auflösung Spiele Art (en) - einige sind verlustbehaftete als andere
0

Vergrößern oder verkleinern Sie das Bild, wenn Sie die Größe ändern? Wenn Sie ein größeres Bild von einem kleineren erstellen, ist diese Art der Verschlechterung zu erwarten.

+0

Seine zwei Bilder scheinen anzuzeigen, dass das Original größer ist. –

0

Bilder werden definitiv verschlechtert, wenn Sie sie vergrößern.

0

Einige Kameras haben ein verkleinertes Thumbnail in die Datei selbst eingefügt, vermutlich für Vorschauzwecke auf dem Gerät selbst.

Die GetThumbnail-Methode erhält dieses Thumbnail-Bild, das in die Bilddatei eingebettet ist, anstatt die höhere res-Methode zu erhalten.

Die einfache Lösung besteht darin, .Net dazu zu bringen, diese Miniaturansichtsinformationen wegzuwerfen, bevor Sie die Größe ändern oder andere Operationen ausführen. wie so ....

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

Wenn Sie beschränke Proportionen, um die Größe versuchen ich eine Erweiterungsmethode auf System.Drawing.Image schrieb, dass Sie praktisch finden könnte.

/// <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

anstelle dieser Code:

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

Verwendung dieses:

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