8

I'm resizing some images to the screen resolution of the user; if the aspect ratio is wrong, the image should be cut. My code looks like this:

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 most cases, this works fine. But for some images, the result has an extremely poor quality. It looks like the would have been resized to something very small (thumbnail size) and enlarged again.. But the resolution of the image is correct. What can I do?

Example orig image: alt text http://img523.imageshack.us/img523/1430/naturaerowoods.jpg

Example resized image: alt text

Note: I have a WPF application but I use the WinForms function for resizing because it's easier and because I already need a reference to System.Windows.Forms for a tray icon.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
eflorico
  • 3,589
  • 2
  • 30
  • 41

10 Answers10

8

Change the last two lines of your method to this:

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();
BFree
  • 102,548
  • 21
  • 159
  • 201
  • 2
    You need to set the quality settings also - they default to low. See http://nathanaeljones.com/163/20-image-resizing-pitfalls/ – Lilith River Jan 04 '10 at 19:23
  • If you're doing this on the server, such as in an ASP.NET app, it's best to use [a wrapper](http://imageresizing.net) that fixes the System.Drawing memleaks. – Lilith River Jun 07 '12 at 22:24
7

I cannot peek into the .NET source at the moment, but most likely the problem is in the Image.GetThumbnailImage method. Even MSDN says that "it works well when the requested thumbnail image has a size of about 120 x 120 pixels, but it you request a large thumbnail image (for example, 300 x 300) from an Image that has an embedded thumbnail, there could be a noticeable loss of quality in the thumbnail image". For true resizing (i.e. not thumbnailing), you should use the Graphics.DrawImage method. You may also need to play with the Graphics.InterpolationMode to get a better quality if needed.

Jan Zich
  • 14,993
  • 18
  • 61
  • 73
3

If you're not creating a thumbnail, using a method called GetThumbnailImage probably isn't a good idea...

For other options, have a look at this CodeProject article. In particular, it creates a new image, creates a Graphics for it and sets the interpolation mode to HighQualityBicubic and draws the original image onto the graphics. Worth a try, at least.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

instead of this code:

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

use this one :

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);
nmokkary
  • 1,219
  • 3
  • 14
  • 24
2

As indicated on MSDN, GetThumbnailImage() is not designed to do arbitrary image scaling. Anything over 120x120 should be scaled manually. Try this instead:

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

Edit

As a point of clarification, this overload of the Bitmap constructor calls Graphics.DrawImage, though you do not have any control over the interpolation.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
0

Images will definitely be degraded if you enlarge them.

0

Some camera's put a resized thumbnail into the file itself presumably for preview purposes on the device itself.

The GetThumbnail method actually gets this Thumbnail image which is embedded within the image file instead of getting the higher res method.

The easy solution is to trick .Net into throwing away that thumbnail information before doing your resize or other operation. like so....

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

If you are attempting to resize constraining proportions I wrote an extension method on System.Drawing.Image that you might find handy.

/// <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)));
}
Martin Murphy
  • 1,775
  • 2
  • 16
  • 24
0

For examples, the original image is JPG and the resized image is PNG. Are you converting between formats on purpose? Switching between different lossey compression schemes can cause quality loss.

James McMahon
  • 48,506
  • 64
  • 207
  • 283
  • I want to use the picture as wallpaper, and for that purpose I have to convert it to BMP because Win XP does not accept any other formats for wallpapers. – eflorico Apr 30 '09 at 11:27
0

Are you increasing or decreasing the size of the image when you resize it? If you are creating a larger image from a smaller one, this sort of degradation is to be expected.

TwentyMiles
  • 4,063
  • 3
  • 30
  • 37
-1

This is going to vary widely based on the following factors:

  1. How closely the destination resolution matches a "natural" scale of the original resolution
  2. The source image color depth
  3. The image type(s) - some are more lossy than others
GalacticCowboy
  • 11,663
  • 2
  • 41
  • 66