C# lower quality for JPEG image - c#

I'm trying to make an console application in .net core 3, this application can successfully get a bitmap from the computer and send it to another computer. But now I want to lower the quality of the image and send it as a JPEG file.
I have looked on the internet but I'm unable to get it to work. My latest attempt I tried to use the following MSDN website. But with this approach I get stuck at ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg); since my application cannot find GetEncoder. I have found GetEncoder in System.Text.Encoding.Getencoding but here getEncoder doesn't take any parameters.
So my question is which namespace do I have to use or is there another way to lower the quality of an 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();
}
}

Related

Image to byte array fails "A generic error occurred in GDI+."

let's assume I let the user choose an image from the computer. I load the file to a picture box. here is the conversion method:
public static Image LoadImageFromFile(string fileName)
{
Image result = null;
if (!File.Exists(fileName))
return result;
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
try
{
using (Image image = Image.FromStream(fs))
{
ImageManipulation.RotateImageByExifOrientationData(image);
result =(Image) image.Clone();
image.Dispose();
}
}
finally
{
fs.Close();
}
return result;
}
then, when the user clicks on the Save button, I convert the image into a byte array and save it into the database. here is the conversion code:
public static byte[] ImageToByteArray(Image image)
{
if (image == null)
return null;
using (var ms = new MemoryStream())
{
ImageFormat imageFormat = image.RawFormat;
ImageCodecInfo codec = ImageCodecInfo.GetImageDecoders().FirstOrDefault(c => c.FormatID == imageFormat.Guid);
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
if (codec != null)
image.Save(ms, codec, encoderParameters);
else
image.Save(ms, ImageFormat.Jpeg);
return ms.ToArray();
}
}
but the problem:
I have a jpg file on the disk. I can load it into my picture box without any problem. the picture is perfectly visible in it. but when I save it, the code gives me "A generic error occurred in GDI+." Error at 'image.Save(ms, codec,encoderParameters)' line.
more odd incident: I don't get this error all the time. it is happening with specific images. for example, I downloaded an image from the internet and crop it in "Paint" and saved it as jpg. error happened. open it in Paint again and save it as png. no error!!!! that is why I am really confused . and Yes I already have tried to don't dispose the image. not helping
I know it might be a stupid question but I am desperately stuck here :)
Thank you in Advanced
I didn't find out why my code is not working but I just substitute my loading method with the code below. it is releasing the file and at the same time works as it should.
public static Image LoadImageFromFile(string fileName)
{
using (Bitmap bmb = new Bitmap(fileName))
{
MemoryStream m = new MemoryStream();
bmb.Save(m, ImageFormat.Bmp);
return Image.FromStream(m);
}
}

Saving Bitmap to MemoryStream

I am trying to save a bitmap to a MemoryStream and then convert it to string. But the problem is, I am having an error that says the line img.Save(m, img.RawFormat); cannot be null. The error is this
The bitmap is from a fingerprint scan, that I converted to image. Now I want to convert its data to a string, by using MemoryStream. This is for the fingerprint data to be saved in the database. I don't know where I went wrong. You can find my code below:
Bitmap bitmap;
bitmap = ConvertSampleToBitmap(Sample);
Bitmap img = new Bitmap(bitmap, fingerprint.Size);
this.Invoke(new Function(delegate () {
fingerprint.Image = img; // fit the image into the picture box
}));
string ping;
using (MemoryStream m = new MemoryStream())
{
img.Save(m, img.RawFormat);
ping = m.ToString();
}
I hope for an accurate answer that can pin point the major error and what parts of the code should I change.
Though any help would be much appreciated.
Interesting; what happens here is:
public void Save(Stream stream, ImageFormat format)
{
if (format == null)
{
throw new ArgumentNullException("format");
}
ImageCodecInfo encoder = format.FindEncoder();
this.Save(stream, encoder, null);
}
with the inner Save doing this check:
public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
if (encoder == null)
{
throw new ArgumentNullException("encoder");
}
so; we can assume that format.FindEncoder(); is returning null here. As it happens, this is indeed the default if there is no matching codec:
internal ImageCodecInfo FindEncoder()
{
foreach (ImageCodecInfo info in ImageCodecInfo.GetImageEncoders())
{
if (info.FormatID.Equals(this.guid))
{
return info;
}
}
return null;
}
So basically, it isn't clear, but the problem is: there is no encoder found for the image format that you're using. Try saving as a well-known format, not necessarily the one that it loaded from. Maybe use ImageFormat.Png and save it as a png?
img.Save(m, ImageFormat.Png);
and as already mentioned in the comments, to get base-64 of that, you'll need:
ping = Convert.ToBase64String(m.GetBuffer(), 0, (int)m.Length);

Change resolution of jpeg image using c#

I am trying to change resolution of my jpg image using c#.
I have refer this code that written in this link How to change resolution (DPI) of an image? its work fine but I have getting another issue for grayscale images.
if I will try to save grayscale image using this code then it change the bit depth 8 to 24.
So I have try below code but I am getting error like "Bitmap region is already locked". I don't know how to pass guid in GetEncoderParameterList().
private static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
//Code
using (Image bitmap = Image.FromFile(pagePath))
{
using (Bitmap newBitmap = new Bitmap(bitmap))
{
ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);
EncoderParameters para = newBitmap.GetEncoderParameterList(jpgEncoder.Clsid);
imageResolution = (int)newBitmap.HorizontalResolution;
newBitmap.SetResolution(250, 250);
newBitmap.Save("file300.jpg", jpgEncoder, para);
}
}
Exception :

Images in Hero card inside carousel of bot framework rarely loads

I am using Bot Application Framework of C# to build a skype bot. I do some processing on image and then send it back to user in carousel of hero cards.
https://dev.skype.com/bots
I am following the constraints of the hero card, I am resizing image so that it's resolution is below 1024*1024, it's type is png/jpg and it's size is below 1MB. Also the link to the image is HTTPS://
Images loads in the web chat of https://dev.botframework.com every time, but when I use it on skype PC or Android it loads rarely.
This is how I am making the carousel.
var carousel = context.MakeMessage();
carousel.AttachmentLayout = AttachmentLayoutTypes.Carousel;
carousel.Attachments.Add(GetHeroCard(null, null, "Share this!", new CardImage(url: imageUrl, alt: imageUrl, tap: new CardAction(ActionTypes.ShowImage))));
This is how I am encoding Bitmap object to the png/jpeg format byte array to upload on the blob, from where I get the HTTPS link to the image.
public static byte[] BitmapEncode2Byte(Bitmap bitmap, ImageFormat format)
{
MemoryStream stream = new MemoryStream();
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bitmap.Save(stream, GetEncoder(ImageFormat.Png), encoderParameters);
return stream.ToArray();
}
public static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
Thanks in Advance!

Image compression is not working

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));
}
}
}
}

Categories