Garbage Collector too slow when working with large images - c#

I am using Emgu OpenCV to grab images from a webcam and want to visualize them with WPF Image Control.
So I need to convert the image from Mat to something compatible with Image control. So I took this class from the Emgu examples:
public static class BitmapSourceConvert
{
/// <summary>
/// Delete a GDI object
/// </summary>
/// <param name="o">The poniter to the GDI object to be deleted</param>
/// <returns></returns>
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
/// <summary>
/// Convert an IImage to a WPF BitmapSource. The result can be used in the Set Property of Image.Source
/// </summary>
/// <param name="image">The Emgu CV Image</param>
/// <returns>The equivalent BitmapSource</returns>
public static BitmapSource ToBitmapSource(IImage image)
{
using (System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap(); //obtain the Hbitmap
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
ptr,
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr); //release the HBitmap
return bs;
}
}
}
This works like a charm for small images (640 x 480 for instance). When using the task Manager (I am on Windows 8), I see the used memory increasing and decreasing. Works fine.
But when using larger images like 1920x1080 the application crashes after a short period of time with an exception saying no more memory. When looking at the task manager again, I can see the memory consumption go up, once go down and then go up till the exception is thrown.
It feels like the garbage collector works not often enough to free all the space.
So I tried to start the garbage collector manually by adding GC.Collect() somewhere in the function. And it works again. Even with the large images.
I think calling the garbage collector manually is neither good style nor performant. Can anyone please give hints on how to solve this without calling GC.Collect()?

Finally, I think the problem is, that garbage collector has no idea of how big the images are and therefore is not able to plan a reasonable schedule. I found the Methods
GC.AddMemoryPreasure(long bytesAllocated)
GC.RemoveMemoryPreasure(long bytesAllocated)
These Methods tell the garbage collector when large unmanaged objects are allocated and released so the garbage collector can plan his schedule in a better way.
The following code works without any memory problems:
public static BitmapSource ToBitmapSource(IImage image)
{
using (System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap(); //obtain the Hbitmap
long imageSize = image.Size.Height*image.Size.Width*4; // 4 bytes per pixel
GC.AddMemoryPressure(imageSize);
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
ptr,
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr); //release the HBitmap
GC.RemoveMemoryPressure(imageSize);
return bs;
}
}

From where IImage parameter come? Dispose it after you finish with it.
So I tried to start the garbage collector manually by adding
GC.Collect() somewhere in the function. And it works again. Even with
the large images.
Image implements finalizer, if you don't dispose them. It will make those instances to live more than one GC cycles. Probably that's your issue.
Finalizer is the last point it can release unmanaged (managed too) resources if developer don't call the Dispose. When you call the Dispose it Supress the finalization and it will make them reachable for GC straight away.
can see the memory consumption go up, once go down and then go up till
the exception is thrown. It feels like the garbage collector works not
often enough to free all the space.
This is not quite right normally. But might be possible when you open/close images frequently and finalization queue is growing up.
Here is a good article for you : The Dangers of the Large Object Heap...

Happens when one uses the wrong tool for the job. A video is not really a set of bitmaps - there are better ways to do it.
What I did last time I had to do that was using Direct3d. There is a WPF integration and it is quite easy to set up a bitmap there. Allows a ton of manipulation in the video stream, too ;) THen you push the image directly into the Direct3d surface. Finished.
No code examples - sorry. It is a couple of years ago and I don't have the code ready.

Related

Image Resource Memory

Since I had a really nasty problem with an not too obvious unmanaged resource last month, I got a little hypernervous regarding memory leaking problems.
I was just coding on a very simple test app with a button with two different pictures on it and noticed I am not quite sure if I have a "problem" here or not...
If I have 2 picture resources Pic1 and Pic2 and an ImageButton-Object, which is just some object inherited from UserControl and with a changed OnPaint:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
//stuff
if (this.keyStatus))
{ this.imageButton.DefaultImage = Resource1.Pic1; }
else
{ this.imageButton.DefaultImage = Resource1.Pic2; }
e.Graphics.DrawImage(this.defaultImage, this.ClientRectangle);
}
Beside OnPaint not being a good place for assigning DefaultImage (its just here to show you what I mean in a short piece of code), I am just assinging a reference to my precompiled resource here, am I? I am not creating a copy as I would if I would call it with new Bitmap(Resource1.Pic1).
So if I change keyStatus every 5 seconds, I would have a very annoying picture on my screen with a lot of changing, but no problems that the picture changing or turning it invisible from time to time leaks memory. Correct?
Thanks a lot!
How object references work
Say you have a random object. The object is a class type (not a value type) and not IDisposable. Basically that means the following:
// y is some object
var x = y;
Now, x doesn't copy all the data from y, but simply makes a new reference to the contents of y. Simple.
To ensure that there won't be memory leaks, the GC keeps track of all objects and (periodically) checks which objects are reachable. If an object is still reachable, it won't be deleted - if it's not, it will be removed.
And then there was unmanaged code
As long as you stick to managed code, everything is fine. The moment you run into unmanaged code (say: GDI+, which is the native counterpart of a lot of System.Drawing stuff) you need to do extra book-keeping to get rid of the code. After all, the .NET runtime doesn't know much about the unmanaged data - it merely knows that there is a pointer. Therefore, the GC will cleanup the pointer, but not the data -- which would result in memory leaks.
Therefore, the guys from .NET added IDisposable. By implementing IDisposable, you can implement extra (unmanaged) cleanup, such as releasing unmanaged memory, closing files, closing sockets, etc.
Now, the GC knows about finalizers, which are implemented as part of the Disposable pattern (details: https://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx ). However, you usually don't want to wait for a GC run to clean up unmanaged resources. So, it's generally a good idea to call Dispose() when an object can be cleaned up and has unmanaged resources.
As is the case with System.Drawing.Bitmap, which implements IDisposable.
In most cases, you can simply wrap IDisposable in a using statement, which will call 'Dispose()' for you in a nice try/finally clause. e.g.:
using (var myBitmap = new Bitmap(...))
{
// use myBitmap
}
// myBitmap including all resources are gone.
What about resource bitmaps
#HansPassant pointed out that resource bitmaps generate a new Bitmap every time you access a bitmap property. This basically means that the bitmaps are copied and need to be disposed.
In other words:
// Free the old bitmap if it exists:
if (this.imageButton.DefaultImage != null)
{
this.imageButton.DefaultImage.Dispose();
this.imageButton.DefaultImage = null;
}
// assign new imageButton.DefaultImage
So, this solves the memory leak, but will give you a lot of data that is copied around.
If you don't want to dispose
Here comes the part why I was surprised by the remark from Hans :) Basically you assign a Bitmap to a button every time, so you don't want to copy the data over and over again - that makes little sense.
Therefore, you might get the idea to wrap the resource into a 'static' container and simply don't deallocate it at all:
static Bitmap myPic1 = Resource1.Pic1;
static Bitmap myPic2 = Resource1.Pic2;
...
if (this.keyStatus))
{
this.imageButton.DefaultImage = myPic1;
}
else
{
this.imageButton.DefaultImage = myPic2;
}
This works, but will give you issues if you at some point decide to generate images as well. To illustrate, say we change the code like this::
if (this.keyStatus))
{
this.imageButton.DefaultImage = myPic1; // #1 don't dispose
}
else
{
Bitmap myPic3 = CreateFancyBitmap(); // #2 do dispose
this.imageButton.DefaultImage = myPic3;
}
Now, the issue here is with the combination. myPic1 is a static object and shouldn't be disposed. On the other hand, myPic3 is not, and should be disposed. If you do call Dispose(), you'll get a nasty exception at #1, because the data is no longer there. There's no proper way to distinguish the two.

loading lots of images causes out of memory

I swear I knew the answer to this one but I forgot.
I have this function. It loads bitmaps and draws them. it can be called in rapid succession. After about 300 or so bitmaps the application crashes with a System.OutOfMemoryException.
Please tell me what am I doing wrong again :)
private void PaintPicture()
{
string FullPath = Global.RunttimePath + EditType.FilePath;
if (File.Exists(FullPath))
{
Image i = Image.FromFile(FullPath);
//DrawImage(i, pnlPicture, pbColor.BackColor); //I disabled this so the problem is not here
i.Dispose();
//GC.Collect(); //I know I know... I should never call GC. So disabled it :)
}
else
{
//DrawImage(Properties.Resources.Fail800, pnlPicture, Color.White, true);
}
}
According to the documentation of Image.FromFile you can get an OutOfMemoryException if the bitmap is in an unknown format. Make sure your application can safely load all images you're trying to use and see if it always crashes on the same image.
If it's always the same image then you can try re-saving the image in a supported pixel format (using Photoshop or Paint.Net or some other free tool) - this should fix the particular image that breaks your application.
Also, add an exception handler around your drawing logic to make sure your application doesn't crash when it runs into a bad image - GDI+ only supports a relatively low number of image formats.
To verify if you're actually running out of memory (that is, if there is a leak), monitor memory use while your application is running. If you see signs of a memory leak, your problem is likely elsewhere.
Edit:
Read these questions / answers for advice about using Image.FromStream instead of FromFile() - doing so avoids locking the file for a long time:
File.Delete failing when Image.FromFile was called prior it, despite making copy of loaded image and destroying original one
out of memory Image.FromFile
This likely won't resolve your problem, but the Image class implements IDisposable. That means you can wrap it in a USING statement, which causes the objects inside to go out of scope faster/less objects surviving to L2 garbage collection (it shouldn't make a difference between wrapping things in a using vs. calling dispose, but we found through memory profiling that it actually does).
if (File.Exists(FullPath))
{
using(Image i = Image.FromFile(FullPath))
{
DrawImage(i, pnlPicture, pbColor.BackColor); //I disabled this so the problem is not here
//GC.Collect(); //I know I know... I should never call GC. So disabled it :)
}
}
else
{
//DrawImage(Properties.Resources.Fail800, pnlPicture, Color.White, true);
}
}

Workaround for memory leak when using WriteableBitmap.AddDirtyRect()

There appears to be a memory leak with WriteableBitmaps when writing to the backbuffer directly and using the AddDirtyRect function multiple times within a single Lock/Unlock. The rectangles need to define different regions within the bitmap. The memory will then leak when you try to discard the WriteableBitmap.
You can recreate it by inserting the following code into a new WPF application. When the application starts, resize the window to create new WriteableBitmaps and watch the memory rise.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Image m = new Image();
m.Stretch = Stretch.Fill;
this.Content = m;
this.SizeChanged += OnSizeChanged;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs args)
{
WriteableBitmap bm = new WriteableBitmap((int)args.NewSize.Width, (int)args.NewSize.Height, 96, 96, PixelFormats.Bgra32, null);
bm.Lock();
bm.AddDirtyRect(new Int32Rect(1, 1, 1, 1));
bm.AddDirtyRect(new Int32Rect(2, 2, 1, 1));
bm.Unlock();
((Image)this.Content).Source = bm;
}
}
We need to be able to discard the bitmap so keeping the same one around and reusing it is not an option. We could also not write to the backbuffer directly and instead use WritableBitmap.WritePixels() but it's slower and speed is an issue.
UPDATE:
I've tested the WritePixels method and it leaks all the same. It may be an issue of calling too many writes too quickly in different regions.
We've contacted Microsoft on this issue and it appears to be a problem with the underlying c++ library backing WPF. We haven't been given an promise of when (or if) a fix will come but it is still a bug as of .NET 4.5.1.
There are currently only two ways we have found to work around this problem and they are mutually exclusive. You can either:
Never dirty any subregion of the bitmap, only dirty the full bitmap
The problem with this method is performance. You can try to counteract this by making your bitmaps smaller but there likely many situations where this isn't possible.
Never discard your bitmap
If you're going to dirty multiple subsections of the bitmap then you must ensure it will never be garbage collected unless you're about to close the application. This comes with it's own host of problems as you have to make sure the bitmap is large enough the first time you create it. If users are allowed to resize the window then you'll have to make it fit the entire desktop, but even this is problematic as users can change their desktop resolution or add/remove monitors meaning you'll either have to leak memory or not have enough bitmap to cover the entire possible size of the window.
Hopefully Microsoft will release a fix for this in the future but for the mean time be very careful with WriteableBitmap as it's very prone to leaking memory.

Bitmap constructor with scan0 and unmanaged resources

I have written some code to retrieve frames from a camera, along with information obtained from these frames, and to display them on a form.
All the data that I get is unmanaged as it comes form a library of my own written in c++ and working with OpenCv.
I prefer getting all the data at once with a single function call and not using a wrapper to OpenCv that would PInvoke several times to get the same result. Furthermore for me the code is easier to maintain and I have much more control on everything that is going on and I have many other reason to prefer this approach.
Everything is ok, (seemingly) perfectly working and I’m happy, but… there is something I would like to understand better with your help.
At a certain point I create a bitmap with the unmanaged pixel data with the method
public Bitmap(int width,int height,int stride,PixelFormat format, IntPtr scan0);
My question are the following (I have some idea, but just tell me if I’m right) :
1) I don’t release the data pointed by scan0 as I think that, once the data is owned by the bitmap object, it will do the job for me via its Garbage Collection. Am I right?
2) I don’t like the fact that a new instance of bitmap is created and allocated every time (apart from the pixel data), but I suppose that there is no better way of getting a Bitmap out of unmanaged data.
3) I think that there is no need to tell the Garbage Collector that there is a big amount of data to clean up with GC.AddMemoryPressure(…) as it knows it, estimating from the information provided with the initialization.
EDIT
I've found on the documentation
The caller is responsible for allocating and freeing the block of memory specified by the scan0 parameter. However, the memory should not be released until the related Bitmap is released.
The only way to do this is that the Bitmap object created in such a way leaves the data untouched and doesn't change its position in memory.
1) I don’t release the data pointed by scan0 as I think that, once the
data is owned by the bitmap object, it will do the job for me via its
Garbage Collection. Am I right?
No, the garbage collector knows nothing about the object, which you've initialized on the unmanaged side, that is why it is unmanaged. So you have to call delete in the unmanaged code to release the allocated memory.
2) I don’t like the fact that a new instance of bitmap is created and
allocated every time (apart from the pixel data), but I suppose that
there is no better way of getting a Bitmap out of unmanaged data.
There is a way and a keyword is unsafe. You can run the c++ code inside of the unsafe block, but you must allow this in the c# project settings. So you can reuse every pixel of once initialized bitmap
unsafe
{
byte stlThres = 115;
Bitmap myBmp = ...; // init the bitmap
var data = myBmp.LockBits(new Rectangle(0, 0, myBmp.Width, myBmp.Height), ImageLockMode.WriteOnly, myBmp.PixelFormat);
for (int y = 0; y < data.Height; y++)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
//...
}
3) I think that there is no need to tell the Garbage Collector that
there is a big amount of data to clean up with GC.AddMemoryPressure(…)
as it knows it, estimating from the information provided with the
initialization.
If you created a managed Bitmap object (with new), it will be released automatically after it gets out of scope or will not be referenced any longer.

AND two bitmaps in C#

I'm trying to AND two bitmaps like this:
[DllImport("gdi32.dll")]
public static extern int SetROP2(IntPtr hDC, int nDrawMode);
const int SRCAND = 0x008800C6; // AND raster op.
lock (g.Clip)
{
IntPtr pDC = g.GetHdc ();
SetROP2 (pDC, SRCAND);
g.DrawImageUnscaled (currentBitmap, bound.Location);
g.ReleaseHdc (pDC);
}
But I get an "Object is currently in use elsewhere" exception from the Draw statement. Moving the ReleaseHdc statement before the Draw statement runs, but doesn't use the specified raster op.
The LockBits approach is too slow, since it copies the whole bitmap twice, one of the bitmaps is huge, and this has to happen many times per second.
Any ideas how I can trick .NET into ANDing bitmaps?
lock (g.Clip)
This cannot work. You are getting this exception because you use the bitmap in more than one thread. I'd guess at another thread that's drawing the bitmap. To make that work, you have to make sure that the two threads cannot use the bitmap at the same time. And that indeed requires the lock keyword. But on the same lock object. The Graphics instance you use won't be the same. The lock doesn't work.
Create a dedicated locking object that both threads use.
Though you found a workaround, it is worth noting the actual source of the exception. GDI and GDI+ operations cannot be interleaved - either one or the other can operate at once, but not both.
In your code, calling g.GetHdc() switches the Graphics object into a state where the newly created HDC can be used for GDI rendering. The Graphics object will then be "in use" until calling g.ReleaseHdc(). At this point, the HDC is destroyed, and the Graphics object can then be used again for rendering.
Noting that the HDC returned by the call to GetHdc() was ney created, and only exists until the call to ReleaseHdc(), where it is destroyed, it is apparent why the ROP is not applied to later operations performed by the Graphics object.
If you needed to use GDI ROPs, you would have to do all associated rendering in a pure GDI context - using Bitmap.GetHbitmap() to get the necessary handles. Be aware that similar to Graphics.GetHdc(), the HBITMAP is newly created from the Bitmap, but shares no state with it.
More details about GDI/GDI+ interop are given in KB 311221

Categories