Click here to Skip to main content
Click here to Skip to main content

Resize Images to Size Limit

, 3 Jun 2013
Rate this:
Please Sign up or sign in to vote.
This tip describes how to resize an existing image (e.g. uploaded by a user) so it does not exceed a defined size limit.

Introduction

To enable customers to upload images which exceed the maximum upload size given by the system, it became necessary to scale down large images until the given size limit was no longer exceeded. Since I did not find any articles about this problem, I decided to write one myself to maybe help others struggling with a similar problem.

Initialization

Before we can start the resizing process, we need to obtain the source path of the uploaded image, the destination path (which is very likely the same path) and the maximum allowed size given by your system. All these values are initialized in the constructor:

private int allowedFileSizeInByte;
private string sourcePath;
private string destinationPath;

public ImageResizer(int allowedSize, string sourcePath, string destinationPath)
{
	allowedFileSizeInByte = allowedSize;
	this.sourcePath = sourcePath;
	this.destinationPath = destinationPath;
}

Scaling Images

Let's start with the function which does the scaling. I use a Bitmap as the source image and a scale factor which is applied to change the width and height of the image. Later, we will see how to calculate this scale factor. Since the following code is pretty straight forward, I won't go into further details:

public Bitmap ScaleImage(Bitmap image, double scale)
{
	int newWidth = (int)(image.Width * scale);
	int newHeight = (int)(image.Height * scale);

	Bitmap result = new Bitmap(newWidth, newHeight, PixelFormat.Format24bppRgb);
	result.SetResolution(image.HorizontalResolution, image.VerticalResolution);

	using (Graphics g = Graphics.FromImage(result))
	{
		g.InterpolationMode = InterpolationMode.HighQualityBicubic;
		g.CompositingQuality = CompositingQuality.HighQuality;
		g.SmoothingMode = SmoothingMode.HighQuality;
		g.PixelOffsetMode = PixelOffsetMode.HighQuality;

		g.DrawImage(image, 0, 0, result.Width, result.Height);
	}
	return result;
}   

Calculating the Scale Factor

The scale factor for the file size can be approximated by dividing the allowed file size by the file size of the image. This simply describes by which amount the file size has to decrease to reach the size limit. Since we generally don't know anything about the compression of the uploaded image, we cannot exactly predict by which factor we have to scale the width and height of our image. However, we know that the file size of an image will increment quadratically if we increase its width and height, since the file size is approximately calculated by multiplying width and height. Since we want to decrease the file size of the picture, we simply have to calculate the square root of the scale factor calculated above and use this value as a parameter for our ScaleImage function. To clarify this process, take a look at the following function which will be called by the client to perform the resizing:

public void ScaleImage()
{
	using (MemoryStream ms = new MemoryStream())
	{
		using (FileStream fs = new FileStream(sourcePath, FileMode.Open))
		{
			Bitmap bmp = (Bitmap)Image.FromStream(fs);
			SaveTemporary(bmp, ms, 100);

			while (ms.Length > allowedFileSizeInByte)
			{
				double scale = Math.Sqrt
				((double)allowedFileSizeInByte / (double)ms.Length);
				ms.SetLength(0);
				bmp = ScaleImage(bmp, scale);
				SaveTemporary(bmp, ms, 100);
			}

			if (bmp != null)
				bmp.Dispose();
			SaveImageToFile(ms);
		}
	}
}	

As you can see, we use a while MemoryStream to save the uploaded image in memory instead of the hard disk during the resizing process. The Bitmap variable bmp contains the image to be resized which is obtained through a FileStream. Inside the while loop, the image is resized using the scale factor and the new image is saved back into the MemoryStream using the SaveTemporary function we will see in a minute. This while loop is applied until the size limit is not exceeded anymore. We need to iterate here, because our scale factor is really only an approximation and depending on the image it might not provide enough scaling when calling ScaleImage for the first time.

Other Helper Functions

Finally, let's take a look at the helper functions used by the scaling process:

private void SaveTemporary(Bitmap bmp, MemoryStream ms, int quality)
{
	EncoderParameter qualityParam = new EncoderParameter
		(System.Drawing.Imaging.Encoder.Quality, quality);
	var codec = GetImageCodecInfo();
	var encoderParams = new EncoderParameters(1);
	encoderParams.Param[0] = qualityParam;
	bmp.Save(ms, codec, encoderParams);
}

private void SaveImageToFile(MemoryStream ms)
{
	byte[] data = ms.ToArray();

	using (FileStream fs = new FileStream(destinationPath, FileMode.Create))
	{
		fs.Write(data, 0, data.Length);
	}
}

private ImageCodecInfo GetImageCodecInfo()
{
	FileInfo fi = new FileInfo(sourcePath);

	switch (fi.Extension)
	{
		case ".bmp": return ImageCodecInfo.GetImageEncoders()[0];
		case ".jpg":
		case ".jpeg": return ImageCodecInfo.GetImageEncoders()[1];
		case ".gif": return ImageCodecInfo.GetImageEncoders()[2];
		case ".tiff": return ImageCodecInfo.GetImageEncoders()[3];
		case ".png": return ImageCodecInfo.GetImageEncoders()[4];
		default: return null;
	}
}

The SaveTemporary function is used to temporarily save the image into the provided MemoryStream during each iteration. It uses .NET EncoderParameters and ImageCodecInfo to get high quality compression rates which are nearly as good as the compression rates provided by Image editing software. Using the EncoderParameters and ImageCodecInfo in the call to bmp.Save provides better result than using the save function which takes a stream and an ImageFormat instance as its only parameters, so I advise you to use this version to get the best results. The SaveImageToFile function is finally used to write the MemoryStream, which contains the resized image, to its final location on the hard disk.

Summary

Hopefully you find this code useful and can apply it to solve similar problems. I appreciate any feedback tips and suggestions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Philipp_Engelmann
Software Developer (Junior)
Germany Germany
No Biography provided

Comments and Discussions

 
QuestionReduce image size without affecting the image dimension PinmemberMember 992590810-Mar-14 1:46 
AnswerRe: Reduce image size without affecting the image dimension PinmemberPhilipp100010-Mar-14 2:03 
QuestionThanks - exactly what I was looking for! Pinmemberkmf15-Feb-14 0:14 
GeneralMy vote of 5 PinmemberYuukanOO23-Jul-13 3:57 
GeneralRe: My vote of 5 PinmemberPhilipp Engelmann23-Jul-13 4:18 
GeneralMy vote of 5 PinmemberVolynsky Alex6-Jun-13 9:32 
QuestionVery Useful Pinmemberryanoc3334-Jun-13 4:45 
I might use this for my personal image directory since the size is getting out of control. I think I may use this for a utility to apply it to a directory of images with the ability to exclude certain image by some type of filter. Thanks for sharing!

AnswerRe: Very Useful PinmemberPhilipp Engelmann4-Jun-13 5:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 3 Jun 2013
Article Copyright 2013 by Philipp_Engelmann
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid