Access Violation Exception in System.Drawing.dll when saving jpeg - c#

I have a camera which uses Emgucv Package to take photo in some parts of my code.
I randomly get AccessViolationException in System.Drawing.dll when trying to save a Jpeg file. As a result I have a corrupted Jpeg file.
My code is run on windows 10 and is written in Microsoft visual studio 2015.
This is my code that throws an exception randomly:
public Boolean SavePhoto(Bitmap image)
{try
{
MyGate.LogCamera("Start Save Photo");
string photoTime = PersianDateTime.Now.ToString("HHmmss") + "_"
+ DateTime.Now.Millisecond;
String photoPath = System.IO.Path.Combine(Path.UserImagePath , PersianDateTime.Now.ToString("yyyyMMdd"));
if (System.IO.Directory.Exists(photoPath)==false)
{
System.IO.Directory.CreateDirectory(photoPath);
}
photoPath = System.IO.Path.Combine(photoPath, photoTime + ".jpg");
MyGate.LogCamera("Just Before Save Photo");
SaveJpeg(photoPath, image, 90);
MyGate.LogCamera("End Save Photo");
return true;
}
catch(Exception e)
{
new NDCBaseClass().LogError(e.ToString());
return false;
}
}
public void SaveJpeg(string path, Image img, int quality)
{
try
{
// Encoder parameter for image quality
EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
// JPEG image codec
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
img.Save(path, jpegCodec, encoderParams);
}
catch(Exception e)
{
new NDCBaseClass().LogError(e.ToString());
}
}

calling GC.Collect after img.save method solves the problem.

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

Tiff Image :How to set metadata for the mulltipage tiff programatically in .Net?

I am trying to set metadata for multi-page tiff image. Below is code snippet setting metadata for single-page tiff image.
Bitmap imageSource = img as Bitmap; // img is an original singlepage tiff image
Bitmap tempBitmap = new Bitmap(imageSource.Width, imageSource.Height);
try
{
ImageCodecInfo encoderInfo = GetEncoderInfo("image/tiff");
EncoderParameters encoderParams = new EncoderParameters(2);
EncoderParameter parameter = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionCCITT3);
encoderParams.Param[0] = parameter;
parameter = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag,(long)EncoderValue.Flush);
encoderParams.Param[1] = parameter;
//Set the resolution as of the image file
tempBitmap.SetResolution(imageSource.HorizontalResolution, imageSource.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(tempBitmap))
{
//Draw the image in the Empty bitmap by passing original imageSource
graphics.DrawImage(imageSource, 0, 0, (float)imageSource.Width, (float)imageSource.Height);
}
string strSign = "I am author";
PropertyItem propItem = img.PropertyItems[0];
propItem.Id = 315; // this is called 'Author'
propItem.Type = 2;
propItem.Value = System.Text.Encoding.UTF8.GetBytes(strSign + "\0");//signature;
propItem.Len = propItem.Value.Length;
((Image)tempBitmap).SetPropertyItem(propItem);
img.Dispose();
((Image)tempBitmap).Save("sample.tif", encoderInfo, encoderParams);
}
catch (Exception ex)
{
}
This above code works fine for single-page image, but cant achieve setting metadata for multi-page image. I am bit new to this, any useful links or libraries available to achieve setting matadata for multi-page tiff image would be helpful.

C# Rotating JPG without losing too much quality

So I'm reading in files from a directory, figuring out which way they need to be rotated. Rotating and then saving. That part works... The issue is, after it saves the file it gets recompressed and I go from 1.5meg images to 250k images. I need to keep the file size around the original. I tried using jhead.exe and calling it from a command line but couldn't get any of my arguments to pass in correctly. Here's my code snipit to detect, rotate, and save.
foreach (FileInfo f in dir.GetFiles("*.jpg"))
{
try
{
string ExportName = "";
Bitmap originalImage = new Bitmap(f.FullName.ToString());
Info inf = new Info(originalImage);
gma.Drawing.ImageInfo.Orientation orientation = gma.Drawing.ImageInfo.Orientation.TopLeft;
try
{
orientation = inf.Orientation;
}
catch
{
orientation = gma.Drawing.ImageInfo.Orientation.TopLeft;
}
originalImage = CheckRotation(originalImage, orientation);
progressBar.Value = progressBar.Value + 1;
originalImage.Save(f.FullName.ToString(), ImageFormat.Jpeg);
Application.DoEvents();
}
private Bitmap CheckRotation(Bitmap inputImage, gma.Drawing.ImageInfo.Orientation orientation)
{
Bitmap rotatedImage = inputImage;
switch (orientation)
{
case gma.Drawing.ImageInfo.Orientation.LeftBottom:
rotatedImage.RotateFlip(RotateFlipType.Rotate90FlipXY);
break;
case gma.Drawing.ImageInfo.Orientation.RightTop:
rotatedImage.RotateFlip(RotateFlipType.Rotate90FlipNone);
break;
default:
break;
}
return rotatedImage;
}
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo ici = null;
foreach (ImageCodecInfo codec in codecs)
{
if (codec.MimeType == "image/jpeg")
ici = codec;
}
EncoderParameters ep = new EncoderParameters();
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)100);
originalImage.Save(f.FullName.ToString(), ici, ep);
This will use 100% quality - but beware, jpegs are still lossy compression - try using a png if you need loss-less quality.
The key for lossless jpeg edit is to use always same QualityLevel and BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile with BitmapCacheOption.None.
And be aware that even if you use QualityLevel 100, quality will go down. With this method goes down just first time, because it goes from unknown QualityLevel to 80, but every other jpeg edit is lossless.
RotateJpeg(#"d:\!test\TestInTest\20160209_143609.jpg", 80, Rotation.Rotate90);
public bool RotateJpeg(string filePath, int quality, Rotation rotation) {
var original = new FileInfo(filePath);
if (!original.Exists) return false;
var temp = new FileInfo(original.FullName.Replace(".", "_temp."));
const BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
try {
using (Stream originalFileStream = File.Open(original.FullName, FileMode.Open, FileAccess.Read)) {
JpegBitmapEncoder encoder = new JpegBitmapEncoder {QualityLevel = quality, Rotation = rotation};
//BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile and BitmapCacheOption.None
//is a KEY to lossless jpeg edit if the QualityLevel is the same
encoder.Frames.Add(BitmapFrame.Create(originalFileStream, createOptions, BitmapCacheOption.None));
using (Stream newFileStream = File.Open(temp.FullName, FileMode.Create, FileAccess.ReadWrite)) {
encoder.Save(newFileStream);
}
}
}
catch (Exception) {
return false;
}
try {
temp.CreationTime = original.CreationTime;
original.Delete();
temp.MoveTo(original.FullName);
}
catch (Exception) {
return false;
}
return true;
}
Easy...
public static void Rotate90(string fileName)
{
Image Pic;
string FileNameTemp;
Encoder Enc = Encoder.Transformation;
EncoderParameters EncParms = new EncoderParameters(1);
EncoderParameter EncParm;
ImageCodecInfo CodecInfo = GetEncoderInfo("image/jpeg");
// load the image to change
Pic = Image.FromFile(fileName);
// we cannot store in the same image, so use a temporary image instead
FileNameTemp = fileName + ".temp";
// for rewriting without recompression we must rotate the image 90 degrees
EncParm = new EncoderParameter(Enc,(long)EncoderValue.TransformRotate90);
EncParms.Param[0] = EncParm;
// now write the rotated image with new description
Pic.Save(FileNameTemp,CodecInfo,EncParms);
Pic.Dispose();
Pic = null;
// delete the original file, will be replaced later
System.IO.File.Delete(fileName);
System.IO.File.Move(FileNameTemp, fileName);
}
See reference at:
https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-transforming-a-jpeg-image-without-loss-of-information-use

Bmp to jpg/png in C#

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.

Categories