High quality JPEG compression with c#
It looks like you're setting the quality to 100%. That means that there will be no compression.
If you change the compression level (80, 50, etc.) and you're unsatisifed with the quality, you may want to try a different image library. LEADTools has a good (non-free) engine.
UPDATE: As a commenter mentioned, 100% quality still does not mean lossless compression when using JPEG. Loading the image, doing something to it, and then saving it again will ultimately result in image degradation. If you need to alter and save an image without losing any of the data you need to use a lossless format such as TIFF, PNG or BMP. I'd go with compressed TIFF (since it's still lossless even though it's compressed) or PNG.
What quality level does Image.Save() use for jpeg files?
Using reflector, it turns out Image.Save()
boils down to the GDI+ function GdipSaveImageToFile, with the encoderParams
NULL. So I think the question is what the JPEG encoder does when it gets a null encoderParams
. 75% has been suggested here, but I can't find any solid reference.
EDIT You could probably find out for yourself by running your program above for quality values of 1..100 and comparing them with the jpg saved with the default quality (using, say, fc.exe /B)
Does resizing jpeg images affect their compression?
When you call "bmpOut.Save" you have to pass in some EncoderParameters to tell the method what quality level you would like to save it with.
http://msdn.microsoft.com/en-us/library/system.drawing.imaging.encoderparameters(VS.80).aspx
Quality of a saved JPG in 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
JPEG Compression produces bigger size
What is happening is that your JPEGs are compressed. When you load them into memory they are uncompressed. This uncompressed size is definitely larger than the original JPG.
When you save the image, you are not compressing from the original size but from the larger uncompressed image in memory. The recompression process is not aware of the fact that the image it is compressing came from a compressed image originally. It just runs the algorithm on the image in memory at the compression setting you give.
This is why lossy formats like JPEG show compression artifacts after a few rounds of open/save.
C# lower quality for JPEG image
Here is my functions to compress image as jpeg
public static System.Drawing.Imaging.ImageCodecInfo GetEncoder(System.Drawing.Imaging.ImageFormat format)
{
System.Drawing.Imaging.ImageCodecInfo[] codecs = System.Drawing.Imaging.ImageCodecInfo.GetImageDecoders();
foreach (System.Drawing.Imaging.ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
public static byte[] GetCompressedImage(System.Drawing.Image img, long quality)
{
System.Drawing.Imaging.ImageCodecInfo ici = GetEncoder(System.Drawing.Imaging.ImageFormat.Jpeg);
System.Drawing.Imaging.EncoderParameters eps = new System.Drawing.Imaging.EncoderParameters(2);
eps.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
eps.Param[1] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)System.Drawing.Imaging.EncoderValue.CompressionLZW);
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, ici, eps);
return ms.ToArray();
}
}
Lossless optimization of JPEG with FreeImage
I think FreeImage only supports lossless transformations (ie FreeImage_JPEGTransform
).
This can save to different files, but unfortunately there doesn't appear to be a way to set any save flags on the new file to allow stripping of metadata etc.
I can only suggest you have a look at the source to see if there is anything you can utilise yourself.
How to compress the image size using .NET Core?
I have used Magick.NET library which is available for both .NET and .NET Core to compress images. Refer to its documentation for more details.
In your case you either need to save the image temporarily somewhere and use the path (similar to my example), or convert it to an array of byte
in memory and read that since it accept Byte[]
using (MagickImage image = new MagickImage(@"YourImage.jpg"))
{
image.Format = image.Format; // Get or Set the format of the image.
image.Resize(40, 40); // fit the image into the requested width and height.
image.Quality = 10; // This is the Compression level.
image.Write("YourFinalImage.jpg");
}
Related Topics
Why Appdomain.Currentdomain.Basedirectory Not Contains "Bin" in ASP.NET App
.Net (C#): Getting Child Windows When You Only Have a Process Handle or Pid
Identityserver4 Role Based Authorization for Web API with ASP.NET Core Identity
Accessing UI Control from Backgroundworker Thread
Singleton by Jon Skeet Clarification
Passing Data to Master Page in ASP.NET MVC
JSON.Net Serialize Specific Private Field
Server Execution Failed (Exception from Hresult: 0X80080005 (Co_E_Server_Exec_Failure))
Passing Object in Redirecttoaction
How to Make the Value of a Variable Track the Value of Another
How to Change the Background Color of All Other Forms from One Form
How to Pass Parameters to the Custom Action
Should You Implement Idisposable.Dispose() So That It Never Throws
Webbrowser Documentcompleted Event Fired More Than Once