Login


Programmatically Resizing an Image

By Jonathan Wood on 3/8/2011
Language: C#
Technology: .NET
Platform: Windows
License: CPOL
Views: 28,373
General Programming » Graphics » General » Programmatically Resizing an Image

Screenshot of Demo Project

Download Source Code Download Source Code

Introduction

I was recently working on a website that allows the user to upload an image to be used as their avatar. Since the website requires avatar images of a certain size, I needed to write code to resize the image.

While the .NET frameworks make programmatically resizing an image easy, there were a couple of issues I had to address. So I've decided to wrap some of the code up in a class and share it with you here.

Resizing An Image

The first issue has to do with the image's aspect ratio, which is the ratio of the width of the image to its height. Obviously, I don't want to distort the image in any way. So my code needed to resize the image to keep it within the required size, without stretching the image.

Let's say my avatar size is 150 x 150 pixels. That's a square. However, most photos will not be square. So I have two choices: I can either size the image small enough so that it fits within the 150 x 150 square in its widest dimension, or I can size the image small enough so that it fits within the 150 x 150 square in its narrowest dimension and then trim the widest dimension to fit.

In the first case, the image maintains it's original aspect ratio, but the image generally needs to be resized smaller to fit. In the second case, the image takes on the aspect ratio of my avatar square, but the image will generally be larger. Neither case distorts the image but the second approach could potentially trim off an important part of the image.

In my testing, the second approach generally produces the best results. It provides a larger image that exactly fits the area allocated for it. And, as long as the trimming occurs equally on each end of the trimmed dimension, it's very rare that the removed portion of the image is critical.

The ImageResizer Class

Listing 1 shows my ImageResizer class. The MaxX and MaxY properties specify the maximum width and height, respectively, of the resized image. If TrimImage is true, the image is trimmed to exactly fit within the MaxX and MaxY dimensions. Otherwise, the image is sized small enough so that it fits within the maximum dimensions without trimming.

The SaveFormat property specify the image format used when saving the resized image.

Once the properties are set, call the Resize() method to resize an image. This method takes two arguments: The names of the input and output files. Ideally, the output file should have the correct extension for the specified SaveFormat value.

The Resize() method loads the image from the input file, resizes it according to the current property values, and then writes the result to the output file. Note that in no case does this method make the image larger. If the image is smaller than the maximum dimensions (or the same size), the resulting image is equal to the input image.

Listing 1: The ImageResizer Class

/// <summary>
/// Class to resize an image.
/// </summary>
public class ImageResizer
{
    /// <summary>
    /// Maximum width of resized image.
    /// </summary>
    public int MaxX { get; set; }

    /// <summary>
    /// Maximum height of resized image.
    /// </summary>
    public int MaxY { get; set; }

    /// <summary>
    /// If true, resized image is trimmed to exactly fit
    /// maximum width and height dimensions.
    /// </summary>
    public bool TrimImage { get; set; }

    /// <summary>
    /// Format used to save resized image.
    /// </summary>
    public ImageFormat SaveFormat { get; set; }

    /// <summary>
    /// Constructor.
    /// </summary>
    public ImageResizer()
    {
        MaxX = MaxY = 150;
        TrimImage = false;
        SaveFormat = ImageFormat.Jpeg;
    }

    /// <summary>
    /// Resizes the image from the source file according to the
    /// current settings and saves the result to the targe file.
    /// </summary>
    /// <param name="source">Path containing image to resize</param>
    /// <param name="target">Path to save resized image</param>
    /// <returns>True if successful, false otherwise.</returns>
    public bool Resize(string source, string target)
    {
        using (Image src = Image.FromFile(source, true))
        {
            // Check that we have an image
            if (src != null)
            {
                int origX, origY, newX, newY;
                int trimX = 0, trimY = 0;

                // Default to size of source image
                newX = origX = src.Width;
                newY = origY = src.Height;

                // Does image exceed maximum dimensions?
                if (origX > MaxX || origY > MaxY)
                {
                    // Need to resize image
                    if (TrimImage)
                    {
                        // Trim to exactly fit maximum dimensions
                        double factor = Math.Max((double)MaxX / (double)origX,
                            (double)MaxY / (double)origY);
                        newX = (int)Math.Ceiling((double)origX * factor);
                        newY = (int)Math.Ceiling((double)origY * factor);
                        trimX = newX - MaxX;
                        trimY = newY - MaxY;
                    }
                    else
                    {
                        // Resize (no trim) to keep within maximum dimensions
                        double factor = Math.Min((double)MaxX / (double)origX,
                            (double)MaxY / (double)origY);
                        newX = (int)Math.Ceiling((double)origX * factor);
                        newY = (int)Math.Ceiling((double)origY * factor);
                    }
                }

                // Create destination image
                using (Image dest = new Bitmap(newX - trimX, newY - trimY))
                {
                    Graphics graph = Graphics.FromImage(dest);
                    graph.InterpolationMode =
                        System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    graph.DrawImage(src, -(trimX / 2), -(trimY / 2), newX, newY);
                    dest.Save(target, SaveFormat);
                    // Indicate success
                    return true;
                }
            }
        }
        // Indicate failure
        return false;
    }
}

Using the ImageResizer Class

Using the ImageResizer class is then straight forward enough. Listing 2 shows some sample code that allocates an instance of the ImageResizer class. It then sets the properties and calls the Resize() method.

The attached download includes a test project that allows you to set the class' properties through the project's user interface.

Listing 2: Using the ImageResizer Class

// Resize image
ImageResizer resizer = new ImageResizer();
resizer.MaxX = MyWidth;
resizer.MaxY = MyHeight;
resizer.TrimImage = true;
resizer.Resize(sourceFile, destFile);
pictureBox1.Image = Image.FromFile(destFile);

Conclusion

When working with JPG images, you may notice that the image quality is not as good as you'd like. The JPG format can cause undesirable artifacts when compression is too aggressive. For more information about controlling the quality of JPG images, please see my article Controlling JPEG Compression.

Obviously, you'll need some additional work if you are allowing users to upload an image to a website, as I am. But, again, the .NET frameworks make the task of resizing an image very easy. But you need to decide exactly how you want your images to be resized, and then get the logic worked out to get the optimum results.

Variations on this code have worked well for me. So perhaps some of you may also find it helpful.

End-User License

Use of this article and any related source code or other files is governed by the terms and conditions of The Code Project Open License.

Author Information

Jonathan Wood

I'm a software/website developer working out of the greater Salt Lake City area in Utah. I've developed many websites including Black Belt Coder, Insider Articles, and others.