Quality of a saved JPG in C#

C#Image Processing

C# Problem Overview


I made a small C# app to create an image in .jpg format.

pictureBox.Image.Save(name,ImageFormat.Jpeg);

The image is succesfully created. I input an original pic, do some stuff with it and save it. The quality of this new pic however, is lower than that of the original.

Is there any way to set the desired quality?

C# Solutions


Solution 1 - C#

The following code example demonstrates how to create a EncoderParameter using the EncoderParameter constructor. To run this example, paste the code and call the VaryQualityLevel method.

This example requires an image file named TestPhoto.jpg located at c:.

private void VaryQualityLevel()
{
    // Get a bitmap.
    Bitmap bmp1 = new Bitmap(@"c:\TestPhoto.jpg");
    ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg);

    // Create an Encoder object based on the GUID
    // for the Quality parameter category.
    System.Drawing.Imaging.Encoder myEncoder =
        System.Drawing.Imaging.Encoder.Quality;

    // Create an EncoderParameters object.
    // An EncoderParameters object has an array of EncoderParameter
    // objects. In this case, there is only one
    // EncoderParameter object in the array.
    EncoderParameters myEncoderParameters = new EncoderParameters(1);

    EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 
        50L);
    myEncoderParameters.Param[0] = myEncoderParameter;
    bmp1.Save(@"c:\TestPhotoQualityFifty.jpg", jgpEncoder, 
        myEncoderParameters);

    myEncoderParameter = new EncoderParameter(myEncoder, 100L);
    myEncoderParameters.Param[0] = myEncoderParameter;
    bmp1.Save(@"c:\TestPhotoQualityHundred.jpg", jgpEncoder, 
        myEncoderParameters);

    // Save the bitmap as a JPG file with zero quality level compression.
    myEncoderParameter = new EncoderParameter(myEncoder, 0L);
    myEncoderParameters.Param[0] = myEncoderParameter;
    bmp1.Save(@"c:\TestPhotoQualityZero.jpg", jgpEncoder, 
        myEncoderParameters);

}

private ImageCodecInfo GetEncoder(ImageFormat format)
{
    ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
    foreach (ImageCodecInfo codec in codecs)
    {
        if (codec.FormatID == format.Guid)
        {
            return codec;
        }
    }
    return null;
}

Ref: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.encoderparameter.aspx

Solution 2 - C#

Here's an even more compact chunk of code for saving as JPEG with a specific quality:

var encoder = ImageCodecInfo.GetImageEncoders().First(c => c.FormatID == ImageFormat.Jpeg.Guid);
var encParams = new EncoderParameters() { Param = new[] { new EncoderParameter(Encoder.Quality, 90L) } };
image.Save(path, encoder, encParams);

Or, if 120 character wide lines are too long for you:

var encoder = ImageCodecInfo.GetImageEncoders()
                            .First(c => c.FormatID == ImageFormat.Jpeg.Guid);
var encParams = new EncoderParameters(1);
encParams.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
image.Save(path, encoder, encParams);

Make sure the quality is a long or you will get an ArgumentException!

Solution 3 - C#

This is an old thread, but I have rewritten the Microsoft (as per Dustin Getz answer) to be a little more useful - shrinking GetEncoderInfo and making an extension on Image. Anyway nothing really new, but may be of use:

    /// <summary>
    /// Retrieves the Encoder Information for a given MimeType
    /// </summary>
    /// <param name="mimeType">String: Mimetype</param>
    /// <returns>ImageCodecInfo: Mime info or null if not found</returns>
    private static ImageCodecInfo GetEncoderInfo(String mimeType)
    {
        var encoders = ImageCodecInfo.GetImageEncoders();
        return encoders.FirstOrDefault( t => t.MimeType == mimeType );
    }

    /// <summary>
    /// Save an Image as a JPeg with a given compression
    ///  Note: Filename suffix will not affect mime type which will be Jpeg.
    /// </summary>
    /// <param name="image">Image: Image to save</param>
    /// <param name="fileName">String: File name to save the image as. Note: suffix will not affect mime type which will be Jpeg.</param>
    /// <param name="compression">Long: Value between 0 and 100.</param>
    private static void SaveJpegWithCompressionSetting(Image image, string fileName, long compression)
    {
        var eps = new EncoderParameters(1);
        eps.Param[0] = new EncoderParameter(Encoder.Quality, compression);
        var ici = GetEncoderInfo("image/jpeg");
        image.Save(fileName, ici, eps);
    }

    /// <summary>
    /// Save an Image as a JPeg with a given compression
    /// Note: Filename suffix will not affect mime type which will be Jpeg.
    /// </summary>
    /// <param name="image">Image: This image</param>
    /// <param name="fileName">String: File name to save the image as. Note: suffix will not affect mime type which will be Jpeg.</param>
    /// <param name="compression">Long: Value between 0 and 100.</param>
    public static void SaveJpegWithCompression(this Image image, string fileName, long compression)
    {
        SaveJpegWithCompressionSetting( image, fileName, compression );
    }

Solution 4 - C#

The community wiki answer, which is accepted, referrs to an example from Microsoft.

However, in order to save some of you time, I boiled it down to an essence and

  • Packed it into a proper method
  • Implemented IDisposable. I haven't seen using (...) { in any other answers. In order to avoid memory leakage, it is best practice to dispose everything that implements IDisposable.

public static void SaveJpeg(string path, Bitmap image)
{
	SaveJpeg(path, image, 95L);
}
public static void SaveJpeg(string path, Bitmap image, long quality)
{
	using (EncoderParameters encoderParameters = new EncoderParameters(1))
	using (EncoderParameter encoderParameter = new EncoderParameter(Encoder.Quality, quality))
	{
		ImageCodecInfo codecInfo = ImageCodecInfo.GetImageDecoders().First(codec => codec.FormatID == ImageFormat.Jpeg.Guid);
		encoderParameters.Param[0] = encoderParameter;
		image.Save(path, codecInfo, encoderParameters);
	}
}

Solution 5 - C#

Using typeless GDI+ style ( https://msdn.microsoft.com/en-us/library/windows/desktop/ms533845(v=vs.85).aspx ) attributes for setting JPEG Quality looks overkilling.

A direct way should look like this:

FileStream stream = new FileStream("new.jpg", FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.QualityLevel = 100;   // "100" for maximum quality (largest file size).
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);

Ref: https://msdn.microsoft.com/en-us/library/system.windows.media.imaging.jpegbitmapencoder.rotation(v=vs.110).aspx#Anchor_2

Solution 6 - C#

Check out MSDN's article on how to set JPEG Compression level.

You need to use the other Save() overload that takes an ImageEncoder and its parameters.

Solution 7 - C#

If you are using the .NET Compact Framework, an alternative might be to use the PNG lossless format ie:

image.Save(filename, ImageFormat.Png);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKdgDevView Question on Stackoverflow
Solution 1 - C#Dustin GetzView Answer on Stackoverflow
Solution 2 - C#Roman StarkovView Answer on Stackoverflow
Solution 3 - C#Wolf5370View Answer on Stackoverflow
Solution 4 - C#bytecode77View Answer on Stackoverflow
Solution 5 - C#epoxView Answer on Stackoverflow
Solution 6 - C#JoshJordanView Answer on Stackoverflow
Solution 7 - C#AlainDView Answer on Stackoverflow