I understand, what this message means (need to do Dispose for unmanaged resources), but really don't understand why it happens in my case:
System.Drawing.Image imgAnimaha, imgNoanimaha;
using (System.IO.Stream file = thisExe.GetManifestResourceStream("WindowsApplication1.img.noanimaha135.gif"))
{
using (System.Drawing.Image img = Image.FromStream(file))
{
imgNoanimaha = (System.Drawing.Image)img.Clone();
}
}
using (System.IO.Stream file = thisExe.GetManifestResourceStream("WindowsApplication1.img.animaha135.gif"))
{
using (System.Drawing.Image img = Image.FromStream(file))
{
imgAnimaha = (System.Drawing.Image)img.Clone();
}
}
pbDiscovery.Image = imgAnimaha;
In this case I get "A generic error occurred in GDI+" Why and how to solve?
PS. If I write the following:
pbDiscovery.Image = imgNoanimaha;
It works correctly.
I really don't understand where and which unmanaged resource is not disposed...
The problem is that Image.Clone(), as in:
using (System.Drawing.Image img = Image.FromStream(file))
{
imgAnimaha = (System.Drawing.Image)img.Clone();
}
... does not create a deep copy of the image. It creates a copy of all the header information, but not the actual pixel data (it just points to the original pixel data). The original (and only) pixel data is disposed along with the original img object when the using goes out of scope.
So the question then becomes, what is the point of the using here? I would suggest there is none. Read the image into a System.Drawing.Image object and keep it alive as long as you need the pixel data (e.g. as long as the Image will need to be redrawn) and only Dispose it after it does not need to be displayed again.
Related
I need a generic function to convert images to a target format. (Format8bppIndexed in this case) The goal is to be able to handle a reasonable range of regular .NET supported images. We have many clients with hundreds of Terabytes of images of varying types and I plan to loop through them all with this code.
Here is an example Image I am trying to convert which throws the errors:
I realize this code has multiple inner try-catches, however I wanted to illustrate the problem.
Within each try below I have comments showing the exception and error I receive.
public static Bitmap ConvertToFormat(this Bitmap Source, PixelFormat TargetFormat)
{
try
{
//This throws OutOfMemoryException: "Out of memory."
return Source.Clone(new Rectangle(0, 0, Source.Width, Source.Height), TargetFormat);
}
catch (OutOfMemoryException)
{
try
{
MemoryStream ResultStream = new MemoryStream();
// This throws ExternalException: "A generic error occurred in GDI+"
Source.Save(ResultStream, ImageFormat.Gif);
ResultStream.Position = 0;
return new Bitmap(ResultStream);
}
catch (ExternalException)
{
// this is just an attempt to break the process down further to try and find the cause:
ImageCodecInfo myImageCodecInfo = GetCodecInfo(ImageFormat.Gif);
EncoderParameters myEncoderParameters = new EncoderParameters(2);
myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionLZW); ;
myEncoderParameters.Param[1] = new EncoderParameter(Encoder.Quality, 0L);
MemoryStream ResultStream = new MemoryStream();
// This throws ExternalException: "A generic error occurred in GDI+"
Source.Save(ResultStream, myImageCodecInfo, myEncoderParameters);
ResultStream.Position = 0;
return new Bitmap(ResultStream);
}
}
}
private static ImageCodecInfo GetCodecInfo(ImageFormat TargetFormat)
{
return ImageCodecInfo.GetImageEncoders().ToList().Find(
delegate (ImageCodecInfo codec)
{ return codec.FormatID == TargetFormat.Guid; });
}
I know the source image is good as I can read the pixels just fine using LockBits(). I am considering using a loop to create a new image pixel by pixel using this, but I would prefer to use the more direct clone option as it automatically handles color palette creation, color matching and dithering.
UPDATE:
I found the code causing the issue, but I am unsure why.
I did not want the file to be locked, so I was using the code below to load the image into memory and then unlock the file. Apparently this was causing issues, but only when calling the Clone() method. When I use that same image with LockBits() it allows me to access every pixel via the memory pointer just fine and also it displays just fine in a PictureBox. Does anyone know why this method is causing a .Clone() error and also how can I load an Image file into memory and then immediatly release the file?
using (Stream s = File.OpenRead(SourceFileName))
{
return (Bitmap)Bitmap.FromStream(s);
}
I am having a bit of a hard time converting MemoryStream into BitmapImage. There are a lot of questions on SO regarding similar situations, but after trying everything on them, I've been unable to fix this, so I turn to you. Note that I'm working with Magick.NET (ImageMagick.NET) and Tessnet2 -- that is what some of that code is.
I use Bitmap class to do most of the work in Magick.NET and Tessnet2. BitmapImage is used for displaying purposes.
First, I load up the PDF and extract a cropped bitmap from its first page:
public Task PdfToBmp(string path)
{
return Task.Run(() =>
{
using (var image = new MagickImage())
{
MagickNET.SetGhostscriptDirectory("./");
var settings = new MagickReadSettings
{
Density = new MagickGeometry(300, 300),
FrameCount = 1
};
image.Read(path, settings);
image.Crop(new MagickGeometry(1850, 200, 600, 140));
// ImageStream is a MemoryStream property.
ImageStream = new MemoryStream();
image.Write(ImageStream, MagickFormat.Bmp);
ImageStream.Position = 0;
}
});
}
That is when I save the bitmap into the MemoryStream. Once I have MemoryStream loaded up, I move onto working with it. I instantiate a Bitmap, so that I may use it for Tessnet2 related work and then try to instantiate a BitmapImage.
public Task DoOcr()
{
if (ImageStream == null)
{
return null;
}
TargetImage = new Bitmap(ImageStream);
ImageStream.Position = 0;
// ----------------------- Problem Area ----------------------- //
DisplayImage = new BitmapImage();
DisplayImage.BeginInit();
DisplayImage.StreamSource = ImageStream;
DisplayImage.CacheOption = BitmapCacheOption.OnLoad;
DisplayImage.EndInit();
//ImageStream.Close();
// ------------------------------------------------------------ //
return Task.Run(() =>
{
var ocr = new Tesseract();
ocr.Init("tessdata", "eng", false);
var results = ocr.DoOCR(TargetImage, Rectangle.Empty);
Dispatcher.Invoke(() =>
{
Results = new ObservableCollection<Word>(results);
});
});
}
This is where I'm having a problem. Without that DisplayImage block, the program runs fine and I just don't get the displayed image. I'm even able to save the Bitmap (TargetImage) to a file with no problems. However, with the DisplayImage block, I get System.NullReferenceException:
System.NullReferenceException occurred
_HResult=-2147467261
_message=Object reference not set to an instance of an object.
HResult=-2147467261
IsTransient=false
Message=Object reference not set to an instance of an object.
Source=System
StackTrace:
at System.Uri.CreateThisFromUri(Uri otherUri)
InnerException:
I'm unable to pinpoint where it occurs exactly, because the ImageStream object looks "fine" upon inspection. It contains data and is at position 0. If I try to close it, or do anything with it, after assigning it as the StreamSource to DisplayImage, I get a null exception on the line that attempts to perform such action. I even tried creating two different streams, to see if that's the problem; however, I was getting the exact same behavior. Debugging this is kind of a pain, considering it doesn't point to any one specific line. There's obviously an issue between this MemoryStream and BitmapImage. Could it be possible that there's some sort of format/conversion problem between the two, but not between MemoryStream and Bitmap in this particular situation?
I tried the file route, where I save MagickImage to a file and load it up into BitmapImage through Uri and it worked flawlessly; however, I would like to be able to perform this in-memory. By the way, setting position to 0 on the MemoryStream did not seem to affect either Bitmap (loads properly) or BitmapImage (same exception).
The temporary fix I currently use is to make DisplayImage a BitmapSource, rather than BitmapImage:
DisplayImage = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
TargetImage.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight(TargetImage.Width, TargetImage.Height));
The Magick.NET's Write() method has some bugs, so we have to use ToBitmap().
image.ToBitmap().Save(ImageStream, System.Drawing.Imaging.ImageFormat.Bmp);
I am having a problem exporting SQL images to files. I first initialize a List. MyRecord is a class with GraphicName, and Graphic properties. When I try to go through the list and save MyRecord.Graphic to disk I get a first chance exception of type 'System.ObjectDisposedException'. I realize this is because when I converted the bytes from the database to an Image I used a using statement with the MemoryStream. I can not use the using statement and it all works, but I am worried about memory usage / memory leaks on up to 6,000 records. Is there another way to convert the bytes to an image or is there a better design to do this?
... prior code
using (SqlDataReader reader = sqlCommand.ExecuteReader())
{
while (reader.Read())
{
MyRecord record = new MyRecord();
record.GraphicId = reader["GRAPHIC_ID"].ToString();
record.Graphic = !reader.IsDBNull(reader.GetOrdinal("IMAGE")) ? GetImage((byte[])reader["IMAGE"]) : null;
records.Add(record);
}
... more code
private Image GetImage(byte[] rawImage)
{
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(rawImage))
{
Image image = Image.FromStream(ms);
return image;
}
}
You shouldn't use a using statement with a stream that will be passed to Image.FromStream, as the Image class is basically responsible for the stream from then on. From the documentation:
You must keep the stream open for the lifetime of the Image.
Just change your code to:
private Image GetImage(byte[] rawImage)
{
var stream = new MemoryStream(rawImage);
return Image.FromStream(stream);
}
... but then make sure you dispose of your Image objects later. That will dispose of the stream, allowing the memory to be garbage collected. Then there shouldn't be any memory leaks - but you need to work out whether you can really load all 6000 images into memory at a time anyway.
(If you don't dispose of the Image objects, they're likely to be finalized anyway at some point - but it would be better to dispose of them deterministically.)
I'm having to fix a bug in some very old code that converts a Base64 string to a image using Memory Stream. Basically it loops through a list of images stored as base64 strings and converts them to images then draws them using ActiveReports.
The bug is that once it loads one image all following images will be a copy of the first image.
I found the code that is doing the conversion of string to image and immediately noticed it isn't disposing of the memory stream. If I wrap the memory stream in a using block I get a GDI exception. I'm guessing this is because the image isn't really read from memory yet or something, but I'd like to hear if anyone has a guess. Thanks in advance!
byte[] oGraphic = null;
try
{
oGraphic = Convert.FromBase64String(psGraphic);
DataDynamics.ActiveReports.Picture oImg = new Picture();
oImg.Top = this.Legend.Top + this.fTopFirst;
oImg.Visible = true;
oImg.Name = sLabelName;
oImg.PictureAlignment = PictureAlignment.Center;
oImg.Image = null;
if (oGraphic != null)
{
var oStream = new MemoryStream(oGraphic);
oImg.Image = System.Drawing.Image.FromStream(oStream);
oImg.Height = Convert.ToSingle(oImg.Image.Height)/(oImg.Image.VerticalResolution);
oImg.Width = Convert.ToSingle(oImg.Image.Width)/(oImg.Image.HorizontalResolution);
oImg.SizeMode = SizeModes.Zoom;
this.fGraphicHeight = oImg.Height;
this.fGraphicWidth = oImg.Width;
if (this.fConstantGraphic > this.fGraphicWidth)
oImg.Left = this.Legend.Left + this.fLeftFirst +
((this.fConstantGraphic - this.fGraphicWidth)/2);
else
oImg.Left = this.Legend.Left + this.fLeftFirst;
}
else
{
this.fGraphicHeight = 0f;
this.fGraphicWidth = 0f;
}
this.GHMap.Controls.Add(oImg);
}
catch (Exception oE)
{
.....
}
The only thing I can imagine is if there is a line of code missing from what you have here:
if (oGraphic == null) // missing line
oGraphic = Convert.FromBase64String(psGraphic);
There is no reason for this byte[] to be declared outside of the try { } block. That array gets wrapped into a MemoryStream which is then wrapped into an Image. That image is attached to a brand new Picture which is added to a Picture collection.
What is it we don't see?
Here's another guess (I'll leave the first guess alone for posterity):
I'm not familiar with Active Reports, but it looks like you're setting the Top and PictureAlignment properties of the Picture object to the same value and adding more than one Picture. Is it possible they are all there, but one on top of each other? So the result is a single picture?
Guess #3 (one of these is going to get a checkmark, I just know it!)
Everything looks OK in the code provided, therefore the problem is somewhere else (though it's still entirely possible I'm wrong about it being OK).
Are you certain that psGraphic is different each time this code is executed?
The cause of the problem is that the Picture control is a single control instance on a single section. So you're just overwriting the image on this single control over and over.
If the only thing you want to see in this report is the images, then the best thing to do is use ActiveReports' Unbound mode and treat each image as another "record". See this walkthrough for an example of using unbound mode (see the DataInitialize and FetchData events for the meat of the matter).
Using unbound mode, ActiveReports will render the image one after the other in sections treating each image like a new record. The code would be something like the following (sorry I don't have ActiveReports handy at the moment so I can't check this code, but this should be pretty close. Let me know if you run into any problems and I'll clean it up in the morning):
In ActiveReports' ReportStart Event:
DataDynamics.ActiveReports.Picture oImg = new Picture();
oImg.Top = 0;
oImg.Visible = true;
oImg.Name = sLabelName;
oImg.PictureAlignment = PictureAlignment.Center;
// setting DataField "binds" the Picture control to get it's data from the MyImageField field which we'll initialize and bind in the events below
oImg.DataField = "MyImageField";
this.Sections["Detail"].Controls.Add(oImg);
In ActiveReports' DataInitialize Event:
this.Fields.Add("MyImageField");
In ActiveReports FetchData Event:
var imageBytes = Convert.FromBase64String(_imageStrings.Current); // I'm not sure where the base64 image strings come from, some I'm assuming you can put them in an enumerator field in the report like "_imageStrings"
var imageStream = new MemoryStream(imageBytes);
var image = Image.FromStream(imageStream);
Fields["MyImageField"].Value = image;
// This tells ActiveReports if there are more records, and if it should raise the FetchData event again (allowing you to add another image).
eArgs.EOF = !_imageStrings.MoveNext();
If you need to resize the image control for each image, use the section's Format event for that. You could use something like the following:
In Detail_Format Event:
var pictureControl = this.Sections["Detail"].Controls["MyImageControl"] as DataDynamics.ActiveReports.Picture;
pictureControl.Width = Convert.ToSingle(pictureControl.Image.Width)/(pictureControl.Image.VerticalResolution);
pictureControl.Width = Convert.ToSingle(pictureControl.Image.Width)/(pictureControl.Image.HorizontalResolution);
Finally, ActiveReports will also just automatically bind to a set of POCO objects in an IEnumerable (or IList, I forget). So you could simply have a "MyImage" class with a property like "MyImage" and ActiveReports will read it and bind with it (you wouldn't have to write any code in DataInitialize and FetchData). I think you might also just be able to put the MemoryStream in there as the binding too and ActiveReports will read it, but I'm not positive on that.
BTW: The reason that GDI exception occurs when disposing the MemoryStream is because GDI attempts to just seek within that single MemoryStream for the image data rather than making a copy of it. So you'll need to supply each System.Drawing.Image instance with a new stream (don't worry MemoryStream will clean itself up when everything is released).
It turned out to be a problem with the way we were creating the image. Adding the code below fixed the problem.
oImg.Image = System.Drawing.Image.FromStream(oStream);
TO THIS
oImg.Image = ImageFromBase64String(psGraphic);
private Image ImageFromBase64String(string sBase64String)
{
using (var sStream = new MemoryStream(Convert.FromBase64String(sBase64String)))
using (var iSourceImage = Image.FromStream(sStream))
{
return new Bitmap(iSourceImage);
}
}
Im loading an image from a SQL CE db and then trying to load that into a PictureBox.
I am saving the image like this:
if (ofd.ShowDialog() == DialogResult.OK)
{
picArtwork.ImageLocation = ofd.FileName;
using (System.IO.FileStream fs = new System.IO.FileStream(ofd.FileName, System.IO.FileMode.Open))
{
byte[] imageAsBytes = new byte[fs.Length];
fs.Read(imageAsBytes, 0, imageAsBytes.Length);
thisItem.Artwork = imageAsBytes;
fs.Close();
}
}
and then saving to the Db using LINQ To SQL.
I load the image back like so:
using (FileStream fs = new FileStream(#"C:\Temp\img.jpg", FileMode.CreateNew ,FileAccess.Write ))
{
byte[] img = (byte[])encoding.GetBytes(ThisFilm.Artwork.ToString());
fs.Write(img, 0, img.Length);
}
but am getting an OutOfMemoryException. I have read that this is a slight red herring and that there is probably something wrong with the filetype, but i cant figure what.
Any ideas?
Thanks
picArtwork.Image = System.Drawing.Bitmap.FromFile(#"C:\Temp\img.jpg");
Based on the code you provided it seems like you are treating the image as a string, it might be that data is being lost with the conversion from byte[] to string and string to byte[].
I am not familiar with SQL CE, but if you can you should consider treating the data as a byte[] and not encoding to and from string.
I base my assumption in this line of code
byte[] img = (byte[])encoding.GetBytes(ThisFilm.Artwork.ToString());
The GDI has the bad behavior of throwing an OOM exception whenever it is not capable of understanding a certain image format. Use Irfanview to see what kind of image that really is, it is likely GDI can't handle it.
My advice would be to not use a FileStream to load images unless you need access to the raw bytes. Instead, use the static methods provided in Bitmap or Image objects:
Image img = Image.FromFile(#"c:\temp\img.jpg")
Bitmap bmp = Bitmap.FromFile(#"c:\temp\img.jpg")
To save the file use .Save method of the Image or Bitmap object:
img.Save(#"c:\temp\img.jpg")
bmp.Save(#"c:\temp\img.jpg")
Of course we don't know what type ArtWork is. Would you care to share that information with us?