I'm having a hard time trying to figure out how to prevent memory leak when repainting PictureBox
That's how my drawing method looks like:
Bitmap image;
image = new Bitmap((Bitmap)baseImage.Clone());
Graphics g = Graphics.FromImage(image);
//here I'm drawing using created "g"
//reason why am doing it on new bitmap, not on paintBox.Image is that..
//..I don't want this drawings to be permanently added to my base image
g.Dispose();
paintBox.Image = image;
Then I'm using method which is changing baseImage and refreshing paintBox many times (hundreds).
Calling this method gives me 'System.OutOfMemoryException' in System.Drawing.dll
This method is recursive, however I'm pretty sure it's not causing this exception, because when I'm changing this method to only modify base image without refreshing paint box it works fine (however I would like to see changes it's making up to date).
So, what's the best method to prevent memory leak in this case?
I was trying something like this:
paintBoxx.Image.Dispose();
paintBox.Image = image;
but it is giving me 'System.NullReferenceException' (because I'm trying to use disposed image).
Any advice would be appreciated.
Change your code to this:
Bitmap image;
image = new Bitmap((Bitmap)baseImage.Clone());
using (Graphics g = Graphics.FromImage(image) )
{
// I am drawing on the bitmap so I don't permanently change my base image
// do your draw stuff here..
g.FillEllipse(Brushes.Yellow, 3, 3, 9, 9);
// ..
}
// don't leak the image and..
// ..don't Dispose without checking for null
if (paintBox.Image != null) paintBox.Image.Dispose();
paintBox.Image = image;
Note the using clause, which will dispose of the Graphics object, even if the drawing runs into problems.
Have you tried using MemoryStream?
Take a look at my example code:
image = new Bitmap((Bitmap)baseImage.Clone());
using (MemoryStream imageStream = new MemoryStream())
{
// put iimagem in memory stream
image.Save(imageStream, System.Drawing.Imaging.ImageFormat.Gif);
// create an array of bytes with image length
byte[] imageContent = new Byte[imageStream.Length];
// reset memory stream
imageStream.Position = 0;
// load array of bytes with the imagem
imageStream.Read(imageContent, 0, (int)imageStream.Length);
// change header page "content-type" to "image/jpeg" and print the image.
Response.ContentType = "image/gif";
Response.BinaryWrite(imageContent);
}
Related
I'm a newby in C#. I have to repeatedly refresh a GUI picture box in a worker thread. The image is acquired from a camera polling a driver with a GetImage method that retrives the image to be displayed. Even if I allocate the bitmap using directive "using" and explicitly call G.C, memory seems to be never deallocated.
The worker thread is something like this:
while (true)
{
// request image with IR signal values (array of UInt16)
image = axLVCam.GetImage(0);
lut = axLVCam.GetLUT(1);
DrawPicture(image, lut);
//GC.Collect();
}
While the DrawPicture method is something like
public void DrawPicture(object image, object lut)
{
[...]
// We have an image - cast it to proper type
System.UInt16[,] im = image as System.UInt16[,];
float[] lutTempConversion = lut as float[];
int lngWidthIrImage = im.GetLength(0);
int lngHeightIrImage = im.GetLength(1);
using (Bitmap bmp = new Bitmap(lngWidthIrImage, lngHeightIrImage)) {
[...many operation on bitmap pixel...]
// Bitmap is ready - update image control
//SetControlPropertyThreadSafe(tempTxtBox, "Text", string.Format("{0:0.#}", lutTempConversion[im[160, 100]]));
//tempTxtBox.Text = string.Format("{0:00000}", im[160, 100]);
//System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
}
}
Problems arises with the
pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
In fact commenting that line of code, garbage collection works as it would.
Better, the problem seems to be with
System.Drawing.Image.FromHbitmap(bmp.GetHbitmap())
Any advice to solve this memory leak?
Thanks a lot!
Image implements IDisposable, so you should call Dispose on each Image instance that you create, when it is no longer needed. You could try to replace this line in your code:
pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
With this:
if (pic.Image != null)
{
pic.Image.Dispose();
}
pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
This will dispose the previous image (if any) before the new one is assigned.
The thing is, you're making a GDI bitmap of bmp with GetHbitmap, which according to msdn:
You are responsible for calling the
GDI DeleteObject method to free the
memory used by the GDI bitmap object.
Then 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.
So basically I would add:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
...
IntPtr gdiBitmap = bmp.GetHbitmap();
// Release the copied GDI bitmap
if (pic.Image != null)
{
pic.Image.Dispose();
}
pic.Image = System.Drawing.Image.FromHbitmap(gdiBitmap);
// Release the current GDI bitmap
DeleteObject(gdiBitmap);
I am unsure if you need the GDI bitmap to perform some kind of transformation. In case you don't you can just assign the bitmap to the Image property of your PictureBox, and ignore the former solution:
// Since we're not using unmanaged resources anymore, explicitly disposing
// the Image only results in more immediate garbage collection, there wouldn't
// actually be a memory leak if you forget to dispose.
if (pic.Image != null)
{
pic.Image.Dispose();
}
pic.Image = bmp;
There are several ways to release an image from pbox. I strongly recommend the way is that do not use pbox.Image = Image.FromFile.... If you do not use FileStream and you want to read it from file use BitMap class. Like this:
Bitmap bmp = new Bitmap(fileName);
pbox.Image = bmp; // notice that we used bitmap class to initialize pbox.
... and then you want to release the image file bmp.Dispose();
Now you can delete, move or whatever you want to the file.
I am using EmguCV to get a live stream from a high-resolution webcam. (full HD)
The issue is that there is a significant lag in the video.
I compared with the Windows camera app and my application is much more delayed.
Here is the code snippet I am using to get the live stream and show on the canvas.
I am wondering if I am missing anything important to minimize the lag.
If anybody is experienced, please help me.
void init_camera()
{
m_capture= new VideoCapture(0);
m_capture.ImageGrabbed += ProcessFrame;
}
void ProcessFrame()
{
if (m_capture_in_progress)
return;
m_capture_in_progress = true;
if (m_capture != null && m_capture.Ptr != IntPtr.Zero)
{
m_capture.Retrieve(m_frame, 0);
if (m_frame != null && m_frame.Bitmap != null)
{
if (IsRecording && m_video_writer != null && m_video_writer.IsOpened)
{
try
{
m_video_writer.Write(m_frame);
}
catch (Exception ex)
{
log(ex.Message + "\n" + ex.StackTrace);
}
}
this.Dispatcher.Invoke(() =>
{
using (var stream = new MemoryStream())
{
m_frame.Bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
using (var outstream = new MemoryStream(stream.ToArray()))
{
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = new MemoryStream(stream.ToArray());
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
ui_canvas.Background = new ImageBrush(bitmap);
}
};
});
}
}
m_capture_in_progress = false;
}
OP is referring some kind of laggs while drawing a camera capture stream from EmguCv into canvas wpf control. OP also states that lagg disappears when streaming with third party software.
This is caused by low performance in OP's code.
After wataching the posted code sample I would suggest the following improvements:
Increasing FPS rate from EmguCV parameters
Maybe your device is supporting higher fps rates, try to increase this value.
Drawing the bitmap over another control like image
As far as I know, drawing a bitmap over a canvas is slower than drawing it over an imagecontrol. A good solution would be to overlap both controls so you can paint the frames over the imageand draw your shapes over the canvasas they are well overlapped.
Cleaning up the rendering method
Keep simple and try to avoid so much control structures when you are inside a critical program block. Specially in program blocks that are executed when an external devices fires an event.
Fixing possible program lock
As far as I can see, you are locking the ProcessFrame event with a bool variable called m_capture_in_progress which is set to true while you are drawing the frame and it is freed after you finish the drawing. This can cause that next incoming frames are not painted because the method is still blocked by the previous frame. This can cause a low performance issue as many frames get lost and not painted.
Use the Image control but set its Source Property to a WriteableBitmap instead of a BitmapImage. Then lock the bitmap, copy the pixel data from your EmguCV Mat to the backbuffer of the WriteableBitmap, call AddDirtyRect method and finally unlock the Bitmap. The final unlock call in combination with the AddDirtyRect will trigger a redraw of the UI image.
Adavantages:
It does not generate a new BitmapImage object each time you want to draw a new frame.
You copy the pixel data only once
Your copy, encode and decode data to often:
// Copies and encodes pixel data to jpeg stream
m_frame.Bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
// Copies jpeg stream
using (var outstream = new MemoryStream(stream.ToArray()))
{
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
//Copies jpeg stream again
bitmap.StreamSource = new MemoryStream(stream.ToArray());
bitmap.CacheOption = BitmapCacheOption.OnLoad;
// Triggers jpeg stream decoding
bitmap.EndInit();
ui_canvas.Background = new ImageBrush(bitmap);
}
I'm creating a Windows Forms Application in C#.
I have some buttons on the form and I used the BackgroundImage property to set bitmap images on them.
I wanted to change those images dynamiclly so:
I used using (bitmap = new Bitmap...) and saved a new bitmap picture.
I assign the new bitmap to be on the button.
Then, I tried to delete the old bitmap by calling Dispose() and then System.IO.File.Delete(nameToDelete);
The problem: the File.Delete function throws an exception: "file is being used by another process" and I can't delete it.
I also tried calling GC.Collect() and assigning the bitmap to null but it didn't work.
The only "solution" I find is to call Thread.Sleep(2) before deleting, But it's a patch and doesn't work each time.
Here is the code (layerPics[z] is an array that holds all the bitmaps, one for each button):
string name = "new name";
Bitmap bitmap;
using (bitmap = new Bitmap(x, y))
{
Form.DrawToBitmap(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));
bitmap.Save(name);
button.BackgroundImageLayout = ImageLayout.Stretch;
button.BackgroundImage = Image.FromFile(name);
}
layerPics[z].Dispose();
layerPics[z] = null;
layerPics[z] = bitmap;
What can I do?
Thanks!
It's not the Bitmap that causes the problem, but this line:
Buttons.BackgroundImage = Image.FromFile(name);
Here, you create an instance of an Image class using the factory method FromFile(). The file remains locked until the Image is disposed.
This is the reference.
I'm a newby in C#. I have to repeatedly refresh a GUI picture box in a worker thread. The image is acquired from a camera polling a driver with a GetImage method that retrives the image to be displayed. Even if I allocate the bitmap using directive "using" and explicitly call G.C, memory seems to be never deallocated.
The worker thread is something like this:
while (true)
{
// request image with IR signal values (array of UInt16)
image = axLVCam.GetImage(0);
lut = axLVCam.GetLUT(1);
DrawPicture(image, lut);
//GC.Collect();
}
While the DrawPicture method is something like
public void DrawPicture(object image, object lut)
{
[...]
// We have an image - cast it to proper type
System.UInt16[,] im = image as System.UInt16[,];
float[] lutTempConversion = lut as float[];
int lngWidthIrImage = im.GetLength(0);
int lngHeightIrImage = im.GetLength(1);
using (Bitmap bmp = new Bitmap(lngWidthIrImage, lngHeightIrImage)) {
[...many operation on bitmap pixel...]
// Bitmap is ready - update image control
//SetControlPropertyThreadSafe(tempTxtBox, "Text", string.Format("{0:0.#}", lutTempConversion[im[160, 100]]));
//tempTxtBox.Text = string.Format("{0:00000}", im[160, 100]);
//System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
}
}
Problems arises with the
pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
In fact commenting that line of code, garbage collection works as it would.
Better, the problem seems to be with
System.Drawing.Image.FromHbitmap(bmp.GetHbitmap())
Any advice to solve this memory leak?
Thanks a lot!
Image implements IDisposable, so you should call Dispose on each Image instance that you create, when it is no longer needed. You could try to replace this line in your code:
pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
With this:
if (pic.Image != null)
{
pic.Image.Dispose();
}
pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
This will dispose the previous image (if any) before the new one is assigned.
The thing is, you're making a GDI bitmap of bmp with GetHbitmap, which according to msdn:
You are responsible for calling the
GDI DeleteObject method to free the
memory used by the GDI bitmap object.
Then 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.
So basically I would add:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
...
IntPtr gdiBitmap = bmp.GetHbitmap();
// Release the copied GDI bitmap
if (pic.Image != null)
{
pic.Image.Dispose();
}
pic.Image = System.Drawing.Image.FromHbitmap(gdiBitmap);
// Release the current GDI bitmap
DeleteObject(gdiBitmap);
I am unsure if you need the GDI bitmap to perform some kind of transformation. In case you don't you can just assign the bitmap to the Image property of your PictureBox, and ignore the former solution:
// Since we're not using unmanaged resources anymore, explicitly disposing
// the Image only results in more immediate garbage collection, there wouldn't
// actually be a memory leak if you forget to dispose.
if (pic.Image != null)
{
pic.Image.Dispose();
}
pic.Image = bmp;
There are several ways to release an image from pbox. I strongly recommend the way is that do not use pbox.Image = Image.FromFile.... If you do not use FileStream and you want to read it from file use BitMap class. Like this:
Bitmap bmp = new Bitmap(fileName);
pbox.Image = bmp; // notice that we used bitmap class to initialize pbox.
... and then you want to release the image file bmp.Dispose();
Now you can delete, move or whatever you want to the file.
Is it possible to load a picture from memory (byte[] or stream or Bitmap) without saving it to disk?
This is the code I use to turn the byte[] array into a Bitmap:
unsafe
{
fixed (byte* ptr = Misc.ConvertFromUInt32Array(image))
{
Bitmap bmp = new Bitmap(200, 64, 800, PixelFormat.Format32bppRgb, new IntPtr(ptr));
bmp.RotateFlip(RotateFlipType.Rotate180FlipX);
bmp.MakeTransparent(Color.Black);
bmp.Save("test.bmp");
}
}
Instead of using Bmp.save(), can I put the Bitmap in the picture box on my form?
Have you tried this?
pictureBox.Image = bmp;
I had some code resembling the accepted answer that caused a memory leak. Problem is that when you set the picture box image to the bitmap, you're still referring to the bitmap, rather than creating a copy. If you need to set the image multiple times you need to make sure you're disposing all the old bitmaps.
This is for anyone who's looking to clone a bitmap to an image box. Try this:
if (pictureBox.Image != null) pictureBox.Image.Dispose();
pictureBox.Image = myBitmap.Clone(
new Rectangle(0, 0, myBitmap.Width, myBitmap.Height),
System.Drawing.Imaging.PixelFormat.DontCare);
If you are working with C++ programming language, it can be done like this:
void backGroundImage()
{
Image^ back = gcnew Bitmap("C:\\Users\\User\\Documents\\image.bmp");
pictureBox1->BackGroundImage = back;
};
Then you can call backGroundImage when you need to load a bitmap.