C# bitmaps causes memory leak - c#

I have a simple application, which renders images and prints them out. In order to make it easy and convenient to design, I have used custom Control.
Now, I have a simple control of size 1800 x 2400 (yeah, pretty big):
public class PhotoControl : UserControl
{
public PhotoControl()
{
InitializeComponent();
}
}
And an extension class, which uses it for generating images:
public static class PhotoProcessor
{
private static readonly PhotoControl PhotoForm = new PhotoControl(); // Single instance
public static Image GenerateImage(this Photo photo)
{
PhotoForm.SetPhoto(photo); // Apply a photo
Image result;
using (Bitmap b = new Bitmap(PhotoForm.Width, PhotoForm.Height))
{
Rectangle r = new Rectangle(0, 0, b.Width, b.Height);
PhotoForm.DrawToBitmap(b, r); // Draw control to bitmap
using (MemoryStream ms = new MemoryStream())
{
b.Save(ms, ImageFormat.Png); // Save bitmap as PNG
result = Image.FromStream(ms);
}
}
// GC.Collect();
// GC.WaitForPendingFinalizers();
return result; // return
}
Now, I try to generate 30 photos using this:
myPhoto.GenerateImage().Save(#"Output1.png", ImageFormat.Png);
myPhoto.GenerateImage().Save(#"Output2.png", ImageFormat.Png);
I do not store the reference, I just save an image and expect that GC will collect these images after saving to a file.
It will take about 2 GB of memory, and finally throw an exception:
An unhandled exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll
Additional information: Out of memory.
If I look at Visual Studio diagnostic tools, it looks like:
Let's take a snapshot, and look at the contents of the heap, we will see that there are many MemoryStreams:
What causes MemoryStream to produce a memory leak? As far as I know, using() generates Dispose() call, which should take care of it.
P.S. If I remove comment and call GC.Collect(); GC.WaitForPendingFinalizers();, then it takes 2-3 times less memory, but it still grows - ~200 images will still kill an application.
Changing the image saving to the following:
Image img;
img = myPhoto.GenerateImage();
img.Save(#"Output1.png", ImageFormat.Png);
img.Dispose();
img = myPhoto.GenerateImage();
img.Save(#"Output2.png", ImageFormat.Png);
img.Dispose();
will produce the same result as using GC.Collect();. It will also take less memory, but not eliminate memory leakage.

Image implements IDisposable and thus must be disposed manually. You're currently returning an Image from GenerateImage but not disposing it after saving.

Related

Cant clear memory from using statement

I have tried to dispose and use the 'using' statement to prevent high memory usage. However, it perplexes me when my memory doesn't clear everything.
Below is the method where I save the bitmap pictures into an Excel worksheet.
I ran 2 simulations
I deleted the bitmap list before it comes in here and it uses 200mb+.
I loaded the bitmap list and it uses 600+mb (normal) before this method. After going into loop in this method, it adds another 600mb, totaling to 1.2GB. After exiting this method, it goes down to 600mb+. What am i missing out because I feel that the memory should be around 200mb - 300mb only.
In the code, I used 2 'using' statements to enable auto dispose of the image and the stream.
Thank you for helping!
private void SaveBitMapIntoExcelSht(ref List<Bitmap> bmpLst, IXLWorksheet wksheet, int pixH)
{
string inputCell;
using (MemoryStream stream = new MemoryStream())
{
for (int i = 0; i < bmpLst.Count; i++)
{
Console.WriteLine(GC.GetTotalMemory(true));
inputCell = "A" + (30).ToString();
using (Image image = Image.FromHbitmap(bmpLst[i].GetHbitmap()))//Convert bitmap to hbitmap and store as a stream to save directly into the excel file.
{
Console.WriteLine(GC.GetTotalMemory(true));
// Save image to stream.
image.Save(stream, ImageFormat.Png);
IXLPicture pic = wksheet.AddPicture(stream, XLPictureFormat.Png);
pic.MoveTo(wksheet.Cell(inputCell));
pic.Delete();
pic.Dispose();
image.Dispose();
GC.Collect();
}
}
}
foreach (var bmp in bmpLst)
{
bmp.Dispose();
}
bmpLst.Clear();
Dispose();
GC.Collect();
Console.WriteLine(GC.GetTotalMemory(true));
}
EDIT: ANSWER
For those who are interested, you may find the code below that works.
Previously I got the hbitmap in the using statement but there wasnt a reference to it, therefore I created a var handle so that I can delete it.
Add this in your class
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
using (MemoryStream stream = new MemoryStream())
{
for (int i = 0; i < bmpLst.Count; i++)
{
Console.WriteLine(GC.GetTotalMemory(true));
inputCell = "A" + (i * numberOfCells + 1).ToString();
//using (Image image = Image.FromHbitmap(bmpLst[i].GetHbitmap()))
var handle = bmpLst[i].GetHbitmap();
using (Image image = Image.FromHbitmap(handle))//Convert bitmap to hbitmap and store as a stream to save directly into the excel file.
{
// Save image to stream.
image.Save(stream, ImageFormat.Png);
pic = wksheet.AddPicture(stream, XLPictureFormat.Png);
pic.MoveTo(wksheet.Cell(inputCell));
try
{
var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(handle);
}
}
}
}
From the documentation of GetHBitmap
You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object. For more information about GDI bitmaps, see Bitmaps in the Windows GDI documentation.
and the documentation of FromHbitmap
The FromHbitmap method makes a copy of the GDI bitmap; so you can release the incoming GDI bitmap using the GDI DeleteObject method immediately after creating the new Image.
I.e. you are creating a GDI object you are never removing. So you need to call the pInvoke function DeleteObject on the IntPtr
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject([In] IntPtr hObject);
Or use some other way to convert a bitmap to an image.
As a rule of thumb, if you ever see anything returning IntPtr it may represent some unmanaged resource, and if so you need to be extra careful to check if you need to manually dispose/delete something.
Another rule of thumb is to use a memory profiler if you suspect a memory leak. It might not have found this issue since it involves pointers to unmanaged memory, but it should be the first step to investigate.

C# Waiting for an operation to complete prior to deleting a file [duplicate]

I have the Image of a PictureBox pointing to a certain file "A". At execution time I want to change the Image of the PictureBox to a different one "B" but I get the following error:
"A first chance exception of type 'System.IO.IOException' occurred in mscorlib.dll
Additional information: The process cannot access the file "A" because it is being used by another process."
I'm setting the Image as follows:
pbAvatar.Image = new Bitmap(filePath);
How can I unlock the first file?
Here is my approach to opening an image without locking the file...
public static Image FromFile(string path)
{
var bytes = File.ReadAllBytes(path);
var ms = new MemoryStream(bytes);
var img = Image.FromStream(ms);
return img;
}
UPDATE: I did some perf tests to see which method was the fastest. I compared it to #net_progs "copy from bitmap" answer (which seems to be the closest to correct, though does have some issues). I loaded the image 10000 times for each method and calculated the average time per image. Here are the results:
Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.
The results seem to make sense since you have to create the image twice using the copy from bitmap method.
UPDATE:
if you need a BitMap you can do:
return (Bitmap)Image.FromStream(ms);
This is a common locking question widely discussed over the web.
The suggested trick with stream will not work, actually it works initially, but causes problems later. For example, it will load the image and the file will remain unlocked, but if you try to save the loaded image via Save() method, it will throw a generic GDI+ exception.
Next, the way with per pixel replication doesn't seem to be solid, at least it is noisy.
What I found working is described here: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile--locks-file.aspx
This is how the image should be loaded:
Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp);
}
I was looking for a solution to this problem and this method works fine for me so far, so I decided to describe it, since I found that many people advise the incorrect stream approach here and over the web.
Using a filestream will unlock the file once it has been read from and disposed:
using (var fs = new System.IO.FileStream("c:\\path to file.bmp", System.IO.FileMode.Open))
{
var bmp = new Bitmap(fs);
pct.Image = (Bitmap) bmp.Clone();
}
Edit: Updated to allow the original bitmap to be disposed, and allow the FileStream to be closed.
THIS ANSWER IS NOT SAFE - See comments, and see discussion in net_prog's answer. The Edit to use Clone does not make it any safer - Clone clones all fields, including the filestream reference, which in certain circumstances will cause a problem.
You can't dispose / close a stream while a bitmap object is still using it. (Whether the bitmap object will need access to it again is only deterministic if you know what type of file you are working with and exactly what operations you will be performing. -- for example for SOME .gif format images, the stream is closed before the constructor returns.)
Clone creates an "exact copy" of the bitmap (per documentation; ILSpy shows it calling native methods, so it's too much to track down right now) likely, it copies that Stream data as well -- or else it wouldn't be an exact copy.
Your best bet is creating a pixel-perfect replica of the image -- though YMMV (with certain types of images there may be more than one frame, or you may have to copy palette data as well.) But for most images, this works:
static Bitmap LoadImage(Stream stream)
{
Bitmap retval = null;
using (Bitmap b = new Bitmap(stream))
{
retval = new Bitmap(b.Width, b.Height, b.PixelFormat);
using (Graphics g = Graphics.FromImage(retval))
{
g.DrawImage(b, Point.Empty);
g.Flush();
}
}
return retval;
}
And then you can invoke it like such:
using (Stream s = ...)
{
Bitmap x = LoadImage(s);
}
As far as I know, this is 100% safe, since the resulting image is 100% created in memory, without any linked resources, and with no open streams left behind in memory. It acts like any other Bitmap that's created from a constructor that doesn't specify any input sources, and unlike some of the other answers here, it preserves the original pixel format, meaning it can be used on indexed formats.
Based on this answer, but with extra fixes and without external library import.
/// <summary>
/// Clones an image object to free it from any backing resources.
/// Code taken from http://stackoverflow.com/a/3661892/ with some extra fixes.
/// </summary>
/// <param name="sourceImage">The image to clone</param>
/// <returns>The cloned image</returns>
public static Bitmap CloneImage(Bitmap sourceImage)
{
Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
Bitmap targetImage = new Bitmap(rect.Width, rect.Height, sourceImage.PixelFormat);
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
BitmapData sourceData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
BitmapData targetData = targetImage.LockBits(rect, ImageLockMode.WriteOnly, targetImage.PixelFormat);
Int32 actualDataWidth = ((Image.GetPixelFormatSize(sourceImage.PixelFormat) * rect.Width) + 7) / 8;
Int32 h = sourceImage.Height;
Int32 origStride = sourceData.Stride;
Boolean isFlipped = origStride < 0;
origStride = Math.Abs(origStride); // Fix for negative stride in BMP format.
Int32 targetStride = targetData.Stride;
Byte[] imageData = new Byte[actualDataWidth];
IntPtr sourcePos = sourceData.Scan0;
IntPtr destPos = targetData.Scan0;
// Copy line by line, skipping by stride but copying actual data width
for (Int32 y = 0; y < h; y++)
{
Marshal.Copy(sourcePos, imageData, 0, actualDataWidth);
Marshal.Copy(imageData, 0, destPos, actualDataWidth);
sourcePos = new IntPtr(sourcePos.ToInt64() + origStride);
destPos = new IntPtr(destPos.ToInt64() + targetStride);
}
targetImage.UnlockBits(targetData);
sourceImage.UnlockBits(sourceData);
// Fix for negative stride on BMP format.
if (isFlipped)
targetImage.RotateFlip(RotateFlipType.Rotate180FlipX);
// For indexed images, restore the palette. This is not linking to a referenced
// object in the original image; the getter of Palette creates a new object when called.
if ((sourceImage.PixelFormat & PixelFormat.Indexed) != 0)
targetImage.Palette = sourceImage.Palette;
// Restore DPI settings
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
return targetImage;
}
To call, simply use:
/// <summary>Loads an image without locking the underlying file.</summary>
/// <param name="path">Path of the image to load</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(String path)
{
using (Bitmap sourceImage = new Bitmap(path))
{
return CloneImage(sourceImage);
}
}
Or, from bytes:
/// <summary>Loads an image from bytes without leaving open a MemoryStream.</summary>
/// <param name="fileData">Byte array containing the image to load.</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(Byte[] fileData)
{
using (MemoryStream stream = new MemoryStream(fileData))
using (Bitmap sourceImage = new Bitmap(stream)) {
{
return CloneImage(sourceImage);
}
}
Here's the technique I'm currently using, and seems to work best. It has the advantage of producing a Bitmap object with the same pixel format (24-bit or 32-bit) and resolution (72 dpi, 96 dpi, whatever) as the source file.
// ImageConverter object used to convert JPEG byte arrays into Image objects. This is static
// and only gets instantiated once.
private static readonly ImageConverter _imageConverter = new ImageConverter();
This can be used as often as needed, as follows:
Bitmap newBitmap = (Bitmap)_imageConverter.ConvertFrom(File.ReadAllBytes(fileName));
Edit:
Here's an update of the above technique: https://stackoverflow.com/a/16576471/253938
(The accepted answer is wrong. When you try to LockBits(...) on the cloned bitmap eventually you will encounter GDI+ errors.)
I see only 3 ways to get out of this:
copy your file to a temporary file and open that the easy way new Bitmap(temp_filename)
open your file, read image, create a pixel-size-pixelformat copy (don't Clone()) and dispose the first bitmap
(accept the locked-file-feature)
I suggest to use PixelMap (available on NuGet)
or Github
Very easy to use and much faster than standard Bitmap from .NET
PixelMap pixelMap = new PixelMap(bild);
pictureBox1.Image = pixelMap.GetBitmap();
Read it into the stream, create bitmap, close the stream.

GDI+ Error converting 24bpp JPG to 8bpp Indexed format (PNG or GIF)

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

MemoryStream to BitmapImage

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

Convert a byte[] to Image without using a MemoryStream

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

Categories