141

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?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Ramesh Soni
  • 15,867
  • 28
  • 93
  • 113
  • 49
    This is in C#, that other question is C++, so it's not a duplicate at all. – Doctor Jones Dec 09 '08 at 16:14
  • 7
    The http://imageresizing.net/ library offers the highest-quality and highest-performance image resizing you can get. The accepted answer falls victim [to one of the many GDI+ pitfalls](http://nathanaeljones.com/163/20-image-resizing-pitfalls/) and will cause a 1px wide border artifact around each image it generates. That's fixed by using an ImageAttributes instance with TileModeXY set for the last parameter to the DrawImage call. – Lilith River Jan 09 '12 at 21:32
  • 2
    @Computer Linguist -- is TileModeXY a typo? You have copy pasted this comment across several answers and a google search for exactly "TileModeXY" only turns up your posts. The following link for System.Drawing.Drawing2D.WrapMode only shows 5 possible values: Tile, TileFlipX, TileFlipY, TileFlipXY, Clamp http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.wrapmode.aspx – JasDev Oct 27 '12 at 16:58
  • 1
    Yes, it should be TileFlipXY, thanks for the correction! – Lilith River Oct 28 '12 at 17:07

14 Answers14

236

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

Doctor Jones
  • 21,196
  • 13
  • 77
  • 99
  • ImageCodecInfo jpegCodec = getEncoderInfo("image/jpeg"); - where did you define getEncoderInfo cause I can't compile it – ilija veselica May 18 '10 at 14:41
  • 3
    It should read GetEncoderInfo and not getEncoderInfo. I fixed the typo and the class compiles now. – Doctor Jones May 18 '10 at 15:30
  • How to use this code to show thumbnail on fly (without saving thumbnail to a file)? – ilija veselica May 27 '10 at 10:38
  • Use the ResizeImage function, it returns the resized image as a System.Drawing.Bitmap object that you can use. – Doctor Jones May 27 '10 at 19:31
  • 5
    +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. – James Aug 04 '10 at 18:00
  • 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. – Jaap Jul 22 '12 at 20:42
  • The `Encoders` property is not thread safe, but could pretty easily be made so by using double-checked locking (http://en.wikipedia.org/wiki/Double-checked_locking). – dana Jan 14 '13 at 20:15
  • @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 – Behzad Apr 03 '13 at 16:13
  • @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. – Doctor Jones Apr 04 '13 at 09:09
  • @DoctorJones, so, if the original picture is encoded at a lower quality, what should I do to re-size it with that quality? Thanks :) – Behzad Apr 05 '13 at 07:04
  • 1
    @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). – Doctor Jones Apr 05 '13 at 17:05
  • @DoctorJones, Thanks a lot. If the file isn't Jpeg file, I should save it with the default Image.save method without format parameter? – Behzad Apr 06 '13 at 08:56
  • IT would be great to have the complete & final code also posted so to help others as i am also struggling with similar issue. – Learning Sep 11 '13 at 07:42
  • @KnowledgeSeeker I've added some example usage that will hopefully help – Doctor Jones Sep 11 '13 at 10:36
  • This solution produces ghost-borders. See [here](http://stackoverflow.com/q/1890605/65387) for solution. – mpen Jun 13 '14 at 04:59
  • Recommend incorporating one of these concurrency changes into this answer in order to prevent concurrency issues I ran into: http://stackoverflow.com/questions/35068294/what-would-cause-this-property-to-occasionally-throw-a-nullreferenceexception/35068626#35068626 – Jane Panda Jan 28 '16 at 19:39
  • 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. – Doctor Jones Jan 29 '16 at 09:41
  • what about `graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;` is it not use full ? – Shaiju T Jan 10 '17 at 09:10
  • 1
    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. – Furkan Ekinci Aug 14 '17 at 12:46
15

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));
}
Hallgrim
  • 15,143
  • 10
  • 46
  • 54
  • 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);` – Kirk Woll Oct 29 '11 at 23:20
10

Tested libraries like Imagemagick and GD are available for .NET

You could also read up on things like bicubic interpolation and write your own.

kitsune
  • 11,516
  • 13
  • 57
  • 78
8

CodeProject articles discussing and sharing source code for scaling images:

Robin Rodricks
  • 110,798
  • 141
  • 398
  • 607
6

Use this library: http://imageresizing.net

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

superlogical
  • 14,332
  • 9
  • 66
  • 76
4

Try the different values for Graphics.InterpolationMode. There are several typical scaling algorithms available in GDI+. If one of these is sufficient for your need, you can go this route instead of relying on an external library.

OregonGhost
  • 23,359
  • 7
  • 71
  • 108
3

You can try dotImage, one of my company's products, which includes an object for resampling images that has 18 filter types for various levels of quality.

Typical usage is:

// BiCubic is one technique available in PhotoShop
ResampleCommand resampler = new ResampleCommand(newSize, ResampleMethod.BiCubic);
AtalaImage newImage = resampler.Apply(oldImage).Image;

in addition, dotImage includes 140 some odd image processing commands including many filters similar to those in PhotoShop, if that's what you're looking for.

plinth
  • 48,267
  • 11
  • 78
  • 120
2

This might help

    public Image ResizeImage(Image source, RectangleF destinationBounds)
    {
        RectangleF sourceBounds = new RectangleF(0.0f,0.0f,(float)source.Width, (float)source.Height);
        RectangleF scaleBounds = new RectangleF();

        Image destinationImage = new Bitmap((int)destinationBounds.Width, (int)destinationBounds.Height);
        Graphics graph = Graphics.FromImage(destinationImage);
        graph.InterpolationMode =
            System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

        // Fill with background color
        graph.FillRectangle(new SolidBrush(System.Drawing.Color.White), destinationBounds);

        float resizeRatio, sourceRatio;
        float scaleWidth, scaleHeight;

        sourceRatio = (float)source.Width / (float)source.Height;

        if (sourceRatio >= 1.0f)
        {
            //landscape
            resizeRatio = destinationBounds.Width / sourceBounds.Width;
            scaleWidth = destinationBounds.Width;
            scaleHeight = sourceBounds.Height * resizeRatio;
            float trimValue = destinationBounds.Height - scaleHeight;
            graph.DrawImage(source, 0, (trimValue / 2), destinationBounds.Width, scaleHeight);
        }
        else
        {
            //portrait
            resizeRatio = destinationBounds.Height/sourceBounds.Height;
            scaleWidth = sourceBounds.Width * resizeRatio;
            scaleHeight = destinationBounds.Height;
            float trimValue = destinationBounds.Width - scaleWidth;
            graph.DrawImage(source, (trimValue / 2), 0, scaleWidth, destinationBounds.Height);
        }

        return destinationImage;

    }

Note the InterpolationMode.HighQualityBicubic -> this is generally a good tradeoff between performance and results.

Tom van der Woerdt
  • 29,532
  • 7
  • 72
  • 105
Leslie Marshall
  • 171
  • 2
  • 3
1

Try This basic code snippet:

private static Bitmap ResizeBitmap(Bitmap srcbmp, int width, int height )
{
    Bitmap newimage = new Bitmap(width, height);
    using (Graphics g = Graphics.FromImage(newimage))
           g.DrawImage(srcbmp, 0, 0, width, height);
    return newimage;
}
DareDevil
  • 5,249
  • 6
  • 50
  • 88
0

This is an article I spotted being referenced in Paint.NET's code for image resampling: Various Simple Image Processing Techniques by Paul Bourke.

Oskar Austegard
  • 4,599
  • 4
  • 36
  • 50
Igor Brejc
  • 18,714
  • 13
  • 76
  • 95
  • 1: Nice article. Couldn't access the link but found this another: http://local.wasp.uwa.edu.au/~pbourke/texture_colour/imageprocess/ – Thomas Bratt Jun 26 '12 at 12:42
  • I corrected the link in the original post as Thomas' link was also broken... http://paulbourke.net/texture_colour/imageprocess/ – Oskar Austegard Nov 02 '12 at 18:21
  • This answer would be better if it explained the pertinent parts of the answer, rather than relying on the link. – KatieK Nov 02 '12 at 18:24
0

You could try the magic kernel. It produces less pixelation artifacts than bicubic resample when upscaling and it also gives very good results when downscaling. The source code is available in c# from the web site.

rold2007
  • 1,297
  • 1
  • 12
  • 25
0

I have some improve for Doctor Jones's answer.

It works for who wanted to how to proportional resize the image. It tested and worked for me.

The methods of class I added:

public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, Size size)
{
    return ResizeImage(image, size.Width, size.Height);
}


public static Size GetProportionedSize(Image image, int maxWidth, int maxHeight, bool withProportion)
{
    if (withProportion)
    {
        double sourceWidth = image.Width;
        double sourceHeight = image.Height;

        if (sourceWidth < maxWidth && sourceHeight < maxHeight)
        {
            maxWidth = (int)sourceWidth;
            maxHeight = (int)sourceHeight;
        }
        else
        {
            double aspect = sourceHeight / sourceWidth;

            if (sourceWidth < sourceHeight)
            {
                maxWidth = Convert.ToInt32(Math.Round((maxHeight / aspect), 0));
            }
            else
            {
                maxHeight = Convert.ToInt32(Math.Round((maxWidth * aspect), 0));
            }
        }
    }

    return new Size(maxWidth, maxHeight);
}

and new available using according to this codes:

using (var resized = ImageUtilities.ResizeImage(image, ImageUtilities.GetProportionedSize(image, 50, 100)))
{
    ImageUtilities.SaveJpeg(@"C:\myimage.jpeg", resized, 90);
}
bafsar
  • 1,080
  • 2
  • 16
  • 16
0

you could try this one if it's a lowres cgi 2D Image Filter

Cryx
  • 1
0

There's an article on Code Project about using GDI+ for .NET to do photo resizing using, say, Bicubic interpolation.

There was also another article about this topic on another blog (MS employee, I think), but I can't find the link anywhere. :( Perhaps someone else can find it?