Question

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.

Was it helpful?

Solution

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

OTHER TIPS

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.

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.

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.

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

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.

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.

Images will definitely be degraded if you enlarge them.

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

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top