处理大图像时垃圾收集器速度太慢 [英] Garbage Collector too slow when working with large images

查看:101
本文介绍了处理大图像时垃圾收集器速度太慢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Emgu OpenCV从网络摄像头抓取图像,并想用WPF Image 控制。>
来显示它们。因此,我需要将图像从 Mat Image 控件兼容。所以我从Emgu的例子中学习了这个类:

  public static class BitmapSourceConvert 
{
/// <总结>
///删除一个GDI对象
///< / summary>
///< param name =o>要删除的GDI对象的poniter< / param>
///<返回>< /返回>
[DllImport(gdi32)]
private static extern int DeleteObject(IntPtr o);

///< summary>
///将IImage转换为WPF BitmapSource。结果可用于Image.Source的
///< / summary>
///< param name =image> Emgu CV Image< / param>
///< returns>等价的BitmapSource< / returns>
public static BitmapSource ToBitmapSource(IImage image)
{
using(System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap( ); //获取Hbitmap
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
ptr,
IntPtr.Zero,
Int32Rect.Empty,
System。 Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

DeleteObject(ptr); //释放HBitmap
return bs;





$ b这个工程就像一个魅力小图像(例如640 x 480)。当使用任务管理器(我在Windows 8上)时,我看到使用的内存在增加和减少。工作正常。



但是,当使用像1920x1080这样的大图像时,应用程序会在短时间内崩溃,但不会有更多内存。当再次查看任务管理器时,我可以看到内存消耗增加,一旦下降,然后上升,直到抛出异常。
感觉垃圾收集器的工作不足以释放所有空间。

所以我尝试通过添加GC.Collect来手动启动垃圾回收器)在函数的某处。它再次运作。即使是大图像。

我认为手动调用垃圾收集器既不是好的风格,也不是高性能的。任何人都可以提供关于如何解决这个问题,而无需调用GC.Collect()?

解决方案

最后,我认为问题是,垃圾收集器不知道图像有多大,因此无法计划合理的时间表。我找到了方法

  GC.AddMemoryPreasure(long bytesAllocated)
GC.RemoveMemoryPreasure(long bytesAllocated)

这些方法告诉垃圾回收器何时分配和释放大的非托管对象,以便垃圾回收器可以更好地规划他的日程安排

下面的代码没有任何内存问题:

  public static BitmapSource ToBitmapSource(IImage image)
{
using(System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap(); //获取Hbitmap
long imageSize = image.Size.Height * image.Size.Width * 4; //每像素4个字节
GC.AddMemoryPressure(imageSize);
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
ptr,
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging .BitmapSizeOptions.FromEmptyOptions());

DeleteObject(ptr); //释放HBitmap
GC.RemoveMemoryPressure(imageSize);
return bs;
}
}


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

这篇关于处理大图像时垃圾收集器速度太慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆