High Quality Image Scaling Library

138,136

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:

Solution 5

Use this library: http://imageresizing.net

Have a read of this article by the library author: 20 Image Sizing Pitfalls with .NET

Share:
138,136
Ramesh Soni
Author by

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, 2020

Comments

  • Ramesh Soni
    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
    ilija veselica almost 14 years
    ImageCodecInfo jpegCodec = getEncoderInfo("image/jpeg"); - where did you define getEncoderInfo cause I can't compile it
  • Doctor Jones
    Doctor Jones almost 14 years
    It should read GetEncoderInfo and not getEncoderInfo. I fixed the typo and the class compiles now.
  • ilija veselica
    ilija veselica almost 14 years
    How to use this code to show thumbnail on fly (without saving thumbnail to a file)?
  • Doctor Jones
    Doctor Jones almost 14 years
    Use the ResizeImage function, it returns the resized image as a System.Drawing.Bitmap object that you can use.
  • James
    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
    SirDarius over 13 years
    The SDK with this feature is now available for free for common photo formats (JPEG, PNG, etc) atalasoft.com/photofree
  • Oskar Austegard
    Oskar Austegard almost 13 years
    /Lou Franco: can the common format free version be used in production deployments, also for free?
  • Kirk Woll
    Kirk Woll over 12 years
    Not sure if code has changed, but I had to omit the new Size in the declaration of scaled: new Bitmap(original.Width * 4, original.Height * 4);
  • SirDarius
    SirDarius over 12 years
    Yes, DotImage Photo Free is free to deploy.
  • Solaiman Mansyur
    Solaiman Mansyur almost 12 years
    1: Nice article. Couldn't access the link but found this another: local.wasp.uwa.edu.au/~pbourke/texture_colour/imageprocess
  • Jaap
    Jaap almost 12 years
    Add 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
    Oskar Austegard over 11 years
    I corrected the link in the original post as Thomas' link was also broken... paulbourke.net/texture_colour/imageprocess
  • KatieK
    KatieK over 11 years
    This answer would be better if it explained the pertinent parts of the answer, rather than relying on the link.
  • dana
    dana over 11 years
    The 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
    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
    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
    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
    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
    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
    Learning over 10 years
    IT 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
    Doctor Jones over 10 years
    @KnowledgeSeeker I've added some example usage that will hopefully help
  • mpen
    mpen almost 10 years
    This solution produces ghost-borders. See here for solution.
  • Jane Panda
    Jane Panda over 8 years
    Recommend incorporating one of these concurrency changes into this answer in order to prevent concurrency issues I ran into: stackoverflow.com/questions/35068294/…
  • Doctor Jones
    Doctor Jones over 8 years
    Hi 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
    Shaiju T over 7 years
    what about graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; is it not use full ?
  • Furkan Ekinci
    Furkan Ekinci over 6 years
    After 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.