Is there any way to convert a bmp image to jpg/png without losing the quality in C#? Using Image class we can convert bmp to jpg but the quality of output image is very poor. Can we gain the quality level as good as an image converted to jpg using photoshop with highest quality?
var qualityEncoder = Encoder.Quality;
var quality = (long)<desired quality>;
var ratio = new EncoderParameter(qualityEncoder, quality );
var codecParams = new EncoderParameters(1);
codecParams.Param[0] = ratio;
var jpegCodecInfo = <one of the codec infos from ImageCodecInfo.GetImageEncoders() with mime type = "image/jpeg">;
bmp.Save(fileName, jpegCodecInfo, codecParams); // Save to JPG
public static class BitmapExtensions
{
public static void SaveJPG100(this Bitmap bmp, string filename)
{
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bmp.Save(filename, GetEncoder(ImageFormat.Jpeg), encoderParameters);
}
public static void SaveJPG100(this Bitmap bmp, Stream stream)
{
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bmp.Save(stream, GetEncoder(ImageFormat.Jpeg), encoderParameters);
}
public static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
}
Provided BitmapExtensions by jestro are great, I used them. However would like to show the corrected version - works for Image parent class which is more convenient as I think and provides a way to supply quality:
public static class ImageExtensions
{
public static void SaveJpeg(this Image img, string filePath, long quality)
{
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
img.Save(filePath, GetEncoder(ImageFormat.Jpeg), encoderParameters);
}
public static void SaveJpeg(this Image img, Stream stream, long quality)
{
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
img.Save(stream, GetEncoder(ImageFormat.Jpeg), encoderParameters);
}
static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
return codecs.Single(codec => codec.FormatID == format.Guid);
}
}
Fundamentally you won't be able to keep the same quality because jpg is (so far as I'm aware) always lossy even with the highest possible quality settings.
If bit-accurate quality is really important, consider using png, which has some modes which are lossless.
Just want to say that JPEG is by nature a lossy format. So in thoery even at the highest settings you are going to have some information loss, but it depends a lot on the image.But png is lossless.
I am working on an expense report app, and I am really pleased with the default quality settings for JPG (and PNG) when saving from a Bitmap object.
https://msdn.microsoft.com/en-us/library/9t4syfhh%28v=vs.110%29.aspx
Bitmap finalBitmap = ....; //from disk or whatever
finalBitmap.Save(xpsFileName + ".final.jpg", ImageFormat.Jpeg);
finalBitmap.Save(xpsFileName + ".final.png", ImageFormat.Png);
I'm on .NET 4.6...perhaps the quality has improved in subsequent framework releases.
You can try:
Bitmap.InterpolationMode = InterpolationMode.HighQualityBicubic;
and
Bitmap.CompositingQuality = CompositingQuality.HighQuality;
Which does keep the quality fairly high, but not the highest possible.
Related
I have a BMP image object which contains a BMP image, how can I convert it to JPEG format without saving to file?
All I need to know is the size of the JPEG file after the conversion, so If I have several BMP images, I want to convert each one of them into a JPEG and then see the size of each one.
How can I do that?
Thanks.
Just save it stream. and look size.
private ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
private void Form1_Load(object sender, EventArgs e)
{
Bitmap bmp1 = new Bitmap(#"C:\Users\ercan.acar\Pictures\Current CR Sticker.PNG");
ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);
System.Drawing.Imaging.Encoder myEncoder =System.Drawing.Imaging.Encoder.Quality;
EncoderParameters myEncoderParameters = new EncoderParameters(1);
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder,
50L);
myEncoderParameters.Param[0] = myEncoderParameter;
Stream stream=new MemoryStream();
bmp1.Save(stream, jpgEncoder,myEncoderParameters);
//bmp1.Save(#"C:\Users\ercan.acar\Documents\TestPhotoQualityFifty.jpg",);
var size = stream.Length;
}
How about using a nuget package like Magick.NET or similar.
Why reinventing the wheel and care about encoding, conversion adjustments, optimization if other have done this already for you.
Hello I have this tutorial to try to get an image compressed, however, when I use a profile photo with my phone(selfie) it just rotates 45 degrees.
What could be happening?
EDIT
I added the code to show you, so it is not necessary to click the link
private void VaryQualityLevel()
{
// Get a bitmap. The using statement ensures objects
// are automatically disposed from memory after use.
using (Bitmap bmp1 = new Bitmap(#"C:\TestPhoto.jpg"))
{
ImageCodecInfo jpgEncoder = 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);
// Save the bitmap as a JPG file with 10 quality level compression.
myEncoderParameter = new EncoderParameter(myEncoder, 10L);
myEncoderParameters.Param[0] = myEncoderParameter;
bmp1.Save(#"C:\TestPhotoQualityZero.jpg", jpgEncoder, myEncoderParameters);
}
}
This is the other portion of code:
private ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
I have an operation on the site that takes crops an image, however the resultant, cropped image is coming out significantly larger in terms of file size (original is 24k and the cropped image is like 650k). So I found that I need to apply some compression to the image before saving it. I came up with the following:
public static System.Drawing.Image CropImage(System.Drawing.Image image, Rectangle cropRectangle, ImageFormat format)
{
var croppedImage = new Bitmap(cropRectangle.Width, cropRectangle.Height);
using (var g = Graphics.FromImage(croppedImage))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(
image,
new Rectangle(new Point(0,0), new Size(cropRectangle.Width, cropRectangle.Height)),
cropRectangle,
GraphicsUnit.Pixel);
return CompressImage(croppedImage, format);
}
}
public static System.Drawing.Image CompressImage(System.Drawing.Image image, ImageFormat imageFormat)
{
var bmp = new Bitmap(image);
var codecInfo = EncoderFactory.GetEncoderInfo(imageFormat);
var encoder = System.Drawing.Imaging.Encoder.Quality;
var parameters = new EncoderParameters(1);
var parameter = new EncoderParameter(encoder, 10L);
parameters.Param[0] = parameter;
using (var ms = new MemoryStream())
{
bmp.Save(ms, codecInfo, parameters);
var resultImage = System.Drawing.Image.FromStream(ms);
return resultImage;
}
}
I set the quality low just to see if there was any change at all. There isn't. The crop is being saved correctly appearance-wise but compression is a no joy. If I bypass CompressImage() altogether, neither the file size nor the image quality appear to be any different.
So, 2 questions. Why is nothing happening? Is there a simpler way to compress the resultant image to "web-optimize" similar to how photoshop saves web images (I thought it just stripped a lot of info out of it to reduce the size).
Your problem is you must 'compress' (really encode) the image as you save it, not before you save it. An Image object in your program is always uncompressed.
By saving to the MemoryStream and reading back out from the stream will encode the image and then decode it back to the same size again (with some quality loss in the process if you are using JPEG). However, if you save it to a file with the compression parameters, you will get a compressed image file.
Using this routine with JPEG quality level 90 on a 153 KB source image gives an output image of 102 KB. If you want a smaller file size (with more encoding artifacts) change the encoder parameter to something smaller than 90.
public static void SaveJpegImage(System.Drawing.Image image, string fileName)
{
ImageCodecInfo codecInfo = ImageCodecInfo.GetImageEncoders()
.Where(r => r.CodecName.ToUpperInvariant().Contains("JPEG"))
.Select(r => r).FirstOrDefault();
var encoder = System.Drawing.Imaging.Encoder.Quality;
var parameters = new EncoderParameters(1);
var parameter = new EncoderParameter(encoder, 90L);
parameters.Param[0] = parameter;
using (FileStream fs = new FileStream(fileName, FileMode.Create))
{
image.Save(fs, codecInfo, parameters);
}
}
I believe you shouldn't dispose of the MemoryStream while you are using an image created using Image.FromStream that refers to the stream. Creating a Bitmap directly from the stream also doesn't work.
Try this:
private static Image CropAndCompressImage(Image image, Rectangle rectangle, ImageFormat imageFormat)
{
using(Bitmap bitmap = new Bitmap(image))
{
using(Bitmap cropped = bitmap.Clone(rectangle, bitmap.PixelFormat))
{
using (MemoryStream memoryStream = new MemoryStream())
{
cropped.Save(memoryStream, imageFormat);
return new Bitmap(Image.FromStream(memoryStream));
}
}
}
}
I need to get a picture from the user, with different format extensions, and I want to always save it as "jpg", for easy handling.
is there a good way do that in c# without arming the quality?
From: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.encoderparameter.aspx
private void VaryQualityLevel()
{
// Get a bitmap.
Bitmap bmp1 = new Bitmap(#"c:\TestPhoto.jpg");
ImageCodecInfo jpgEncoder = 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", jpgEncoder,
myEncoderParameters);
myEncoderParameter = new EncoderParameter(myEncoder, 100L);
myEncoderParameters.Param[0] = myEncoderParameter;
bmp1.Save(#"c:\TestPhotoQualityHundred.jpg", jpgEncoder,
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", jpgEncoder,
myEncoderParameters);
}
...
private ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
I use this simple extension to convert a stream, all it does is convert it though and does nothing for quality.
public static Stream ConvertImage(this Stream originalStream, ImageFormat format)
{
var image = Image.FromStream(originalStream);
var stream = new MemoryStream();
image.Save(stream, format);
stream.Position = 0;
return stream;
}
usage:
var outputStream = gifStream.ConvertImage(ImageFormat.Png);
Windows GDI+ provides the Image class and the Bitmap class for storing
images in memory and manipulating images in memory. GDI+ writes images
to disk files with the help of image encoders and loads images from
disk files with the help of image decoders.
Check Image.Save Method overloads to implement your functionality. which support various image format of type ImageFormat Class.
follow example on Image.Save Method (String, ImageCodecInfo, EncoderParameters), which will let you save Jpg file with encoding etc.
code snippet:
using System;
using System.Drawing;
using System.Drawing.Imaging;
class Example_SetJPEGQuality
{
public static void Main()
{
Bitmap myBitmap;
ImageCodecInfo myImageCodecInfo;
Encoder myEncoder;
EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;
// Create a Bitmap object based on a BMP file.
myBitmap = new Bitmap("Shapes.bmp");
// Get an ImageCodecInfo object that represents the JPEG codec.
myImageCodecInfo = GetEncoderInfo("image/jpeg");
// Create an Encoder object based on the GUID
// for the Quality parameter category.
myEncoder = 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.
myEncoderParameters = new EncoderParameters(1);
// Save the bitmap as a JPEG file with quality level 25.
myEncoderParameter = new EncoderParameter(myEncoder, 25L);
myEncoderParameters.Param[0] = myEncoderParameter;
myBitmap.Save("Shapes025.jpg", myImageCodecInfo, myEncoderParameters);
// Save the bitmap as a JPEG file with quality level 50.
myEncoderParameter = new EncoderParameter(myEncoder, 50L);
myEncoderParameters.Param[0] = myEncoderParameter;
myBitmap.Save("Shapes050.jpg", myImageCodecInfo, myEncoderParameters);
// Save the bitmap as a JPEG file with quality level 75.
myEncoderParameter = new EncoderParameter(myEncoder, 75L);
myEncoderParameters.Param[0] = myEncoderParameter;
myBitmap.Save("Shapes075.jpg", myImageCodecInfo, myEncoderParameters);
}
// Codec info
private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for(j = 0; j < encoders.Length; ++j)
{
if(encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
}
Hope this help..
Try this:
public static class BitmapExtension
{
public static void Save(this Bitmap bitmap, String fileName, ImageFormat imageFormat, long quality = 75L)
{
using (var encoderParameters = new EncoderParameters(1))
using (encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality))
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
bitmap.Save(fileName, codecs.Single(codec => codec.FormatID == imageFormat.Guid), encoderParameters);
}
}
}
...
Bitmap bitmap = new Bitmap("myImage.bmp");
bitmap.Save("myImage.jpg", ImageFormat.Jpeg, 50L);
I'm trying to send a bitmap screenshot over network, so I need to compress it before sending it. Is there a library or method for doing this?
When you save an Image to a stream, you have to select a format. Almost all bitmap formats (bmp, gif, jpg, png) use 1 or more forms of compression. So just select an appropriate format, and make make sure that sender and receiver agree on it.
If you are looking for something to compress the image in quality, here it is-
private Image GetCompressedBitmap(Bitmap bmp, long quality)
{
using (var mss = new MemoryStream())
{
EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
ImageCodecInfo imageCodec = ImageCodecInfo.GetImageEncoders().FirstOrDefault(o => o.FormatID == ImageFormat.Jpeg.Guid);
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = qualityParam;
bmp.Save(mss, imageCodec, parameters);
return Image.FromStream(mss);
}
}
Use it -
var compressedBmp = GetCompressedBitmap(myBmp, 60L);
Try the System.IO.DeflateStream class.
May be you can use:
private Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//
int options = 100;
while ( baos.toByteArray().length / 1024>100) { //
baos.reset();
image.compress(Bitmap.CompressFormat.JPEG, options, baos);//
options -= 10;// 10
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//
return bitmap;
}