High Quality Image Scaling Library
Solution 1
Here's a nicely commented Image Manipulation helper class that you can look at and use. I wrote it as an example of how to perform certain image manipulation tasks in C#. You'll be interested in the ResizeImage function that takes a System.Drawing.Image, the width and the height as the arguments.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
namespace DoctaJonez.Drawing.Imaging
{
/// <summary>
/// Provides various image untilities, such as high quality resizing and the ability to save a JPEG.
/// </summary>
public static class ImageUtilities
{
/// <summary>
/// A quick lookup for getting image encoders
/// </summary>
private static Dictionary<string, ImageCodecInfo> encoders = null;
/// <summary>
/// A lock to prevent concurrency issues loading the encoders.
/// </summary>
private static object encodersLock = new object();
/// <summary>
/// A quick lookup for getting image encoders
/// </summary>
public static Dictionary<string, ImageCodecInfo> Encoders
{
//get accessor that creates the dictionary on demand
get
{
//if the quick lookup isn't initialised, initialise it
if (encoders == null)
{
//protect against concurrency issues
lock (encodersLock)
{
//check again, we might not have been the first person to acquire the lock (see the double checked lock pattern)
if (encoders == null)
{
encoders = new Dictionary<string, ImageCodecInfo>();
//get all the codecs
foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
{
//add each codec to the quick lookup
encoders.Add(codec.MimeType.ToLower(), codec);
}
}
}
}
//return the lookup
return encoders;
}
}
/// <summary>
/// Resize the image to the specified width and height.
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
{
//a holder for the result
Bitmap result = new Bitmap(width, height);
//set the resolutions the same to avoid cropping due to resolution differences
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}
/// <summary>
/// Saves an image as a jpeg image, with the given quality
/// </summary>
/// <param name="path">Path to which the image would be saved.</param>
/// <param name="quality">An integer from 0 to 100, with 100 being the
/// highest quality</param>
/// <exception cref="ArgumentOutOfRangeException">
/// An invalid value was entered for image quality.
/// </exception>
public static void SaveJpeg(string path, Image image, int quality)
{
//ensure the quality is within the correct range
if ((quality < 0) || (quality > 100))
{
//create the error message
string error = string.Format("Jpeg image quality must be between 0 and 100, with 100 being the highest quality. A value of {0} was specified.", quality);
//throw a helpful exception
throw new ArgumentOutOfRangeException(error);
}
//create an encoder parameter for the image quality
EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
//get the jpeg codec
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
//create a collection of all parameters that we will pass to the encoder
EncoderParameters encoderParams = new EncoderParameters(1);
//set the quality parameter for the codec
encoderParams.Param[0] = qualityParam;
//save the image using the codec and the parameters
image.Save(path, jpegCodec, encoderParams);
}
/// <summary>
/// Returns the image codec with the given mime type
/// </summary>
public static ImageCodecInfo GetEncoderInfo(string mimeType)
{
//do a case insensitive search for the mime type
string lookupKey = mimeType.ToLower();
//the codec to return, default to null
ImageCodecInfo foundCodec = null;
//if we have the encoder, get it to return
if (Encoders.ContainsKey(lookupKey))
{
//pull the codec from the lookup
foundCodec = Encoders[lookupKey];
}
return foundCodec;
}
}
}
Update
A few people have been asking in the comments for samples of how to consume the ImageUtilities class, so here you go.
//resize the image to the specified height and width
using (var resized = ImageUtilities.ResizeImage(image, 50, 100))
{
//save the resized image as a jpeg with a quality of 90
ImageUtilities.SaveJpeg(@"C:\myimage.jpeg", resized, 90);
}
Note
Remember that images are disposable, so you need to assign the result of your resize to a using declaration (or you could use a try finally and make sure you call dispose in your finally).
Solution 2
When you draw the image using GDI+ it scales quite well in my opinion. You can use this to create a scaled image.
If you want to scale your image with GDI+ you can do something like this:
Bitmap original = ...
Bitmap scaled = new Bitmap(new Size(original.Width * 4, original.Height * 4));
using (Graphics graphics = Graphics.FromImage(scaled)) {
graphics.DrawImage(original, new Rectangle(0, 0, scaled.Width, scaled.Height));
}
Solution 3
Tested libraries like Imagemagick and GD are available for .NET
You could also read up on things like bicubic interpolation and write your own.
Solution 4
CodeProject articles discussing and sharing source code for scaling images:
- Two Pass Scaling using Filters
- Matrix Transformation of Images in C#, using .NET GDI+
- Resizing a Photographic image with GDI+ for .NET
- Fast Dyadic Image Scaling with Haar Transform
Solution 5
Use this library: http://imageresizing.net
Have a read of this article by the library author: 20 Image Sizing Pitfalls with .NET
Ramesh Soni
I am fun loving guy and a techie working as a engineer in a software development firm. My hobbies and interests are all around my profession. These includes programming, computer games, technology, internet..
Updated on May 12, 2020Comments
-
Ramesh Soni almost 4 years
I want to scale an image in C# with quality level as good as Photoshop does. Is there any C# image processing library available to do this thing?
-
ilija veselica almost 14 yearsImageCodecInfo jpegCodec = getEncoderInfo("image/jpeg"); - where did you define getEncoderInfo cause I can't compile it
-
Doctor Jones almost 14 yearsIt should read GetEncoderInfo and not getEncoderInfo. I fixed the typo and the class compiles now.
-
ilija veselica almost 14 yearsHow to use this code to show thumbnail on fly (without saving thumbnail to a file)?
-
Doctor Jones almost 14 yearsUse the ResizeImage function, it returns the resized image as a System.Drawing.Bitmap object that you can use.
-
James over 13 years+1 this works brilliantly! One issue you need to correct in this code is to convert the quality variable to a long prior to passing it to the encoder param or you will get a invalid parameter runtime exception.
-
SirDarius over 13 yearsThe SDK with this feature is now available for free for common photo formats (JPEG, PNG, etc) atalasoft.com/photofree
-
Oskar Austegard almost 13 years/Lou Franco: can the common format free version be used in production deployments, also for free?
-
Kirk Woll over 12 yearsNot sure if code has changed, but I had to omit the
new Size
in the declaration ofscaled
:new Bitmap(original.Width * 4, original.Height * 4);
-
SirDarius over 12 yearsYes, DotImage Photo Free is free to deploy.
-
Solaiman Mansyur almost 12 years1: Nice article. Couldn't access the link but found this another: local.wasp.uwa.edu.au/~pbourke/texture_colour/imageprocess
-
Jaap almost 12 yearsAdd
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
to make sure you don't run into weird differences between the source image's resolution and the default bitmap resolution. -
Oskar Austegard over 11 yearsI corrected the link in the original post as Thomas' link was also broken... paulbourke.net/texture_colour/imageprocess
-
KatieK over 11 yearsThis answer would be better if it explained the pertinent parts of the answer, rather than relying on the link.
-
dana over 11 yearsThe
Encoders
property is not thread safe, but could pretty easily be made so by using double-checked locking (en.wikipedia.org/wiki/Double-checked_locking). -
Behzad about 11 years@DoctorJones When I re-size an image with 300*308 dimensions and 16KB size to an image with 150*120 dimensions, the re-sized image that is smaller than original one has a bigger size, its size is 30KB! I have changed the three re-sized method properties to the lowest, but no difference
-
Doctor Jones about 11 years@Behzad are you saving the image as a jpeg? If so you need to set the quality parameter. I'd presume that your original image was encoded at a lower quality than what you're trying to save as now.
-
Behzad about 11 years@DoctorJones, so, if the original picture is encoded at a lower quality, what should I do to re-size it with that quality? Thanks :)
-
Doctor Jones about 11 years@Behzad, if you look, the SaveJpeg function takes an int parameter called quality. You need to call that and specify the correct value for the quality parameter (it accepts a value between 0 and 100).
-
Behzad about 11 years@DoctorJones, Thanks a lot. If the file isn't Jpeg file, I should save it with the default Image.save method without format parameter?
-
Learning over 10 yearsIT would be great to have the complete & final code also posted so to help others as i am also struggling with similar issue.
-
Doctor Jones over 10 years@KnowledgeSeeker I've added some example usage that will hopefully help
-
mpen almost 10 yearsThis solution produces ghost-borders. See here for solution.
-
Jane Panda over 8 yearsRecommend incorporating one of these concurrency changes into this answer in order to prevent concurrency issues I ran into: stackoverflow.com/questions/35068294/…
-
Doctor Jones over 8 yearsHi Bob, thanks for the comment. I've added concurrency protection as per your request. I've used a double check lock pattern instead of Lazy, to maintain compatibility with earlier versions of the framework.
-
Shaiju T over 7 yearswhat about
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
is it not use full ? -
Furkan Ekinci over 6 yearsAfter a long search, this answer's sizing part (didn't use whole code) worked for qrcode resizing without quality lost. Right settings are important for result quality.