处理大图像时垃圾收集器速度太慢
我正在使用Emgu OpenCV从网络摄像头抓取图像,并且想用WPF Image
来控制它们。
所以我需要将图像从Mat
转换为与Image
控件兼容的东西。所以我把从Emgu例子这个类:处理大图像时垃圾收集器速度太慢
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;
}
}
}
这就像为小图像(640×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(); //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;
}
}
当一个人使用错误的工具进行工作时会发生。视频并不是一组位图 - 有更好的方法来实现它。
我上次做了什么,我不得不这样做是使用Direct3d。有一个WPF集成,并很容易在那里建立一个位图。允许在视频流中进行大量操作;)将图像直接推入Direct3d表面。成品。
无代码示例 - 对不起。这是几年前,我没有准备好代码。
从哪里来的IImage参数?在完成之后处置它。
所以我试图手动启动垃圾回收器,在函数的某处添加 GC.Collect()。它再次运作。即使与 大图像。
图像实现终结器,如果你不处理它们。它将使这些实例生活多个GC循环。可能这是你的问题。
如果开发人员不调用Dispose,Finalizer是最后一点,它可以释放非托管(管理的)资源。当你打电话给Dispose时,它会压缩最终结果,并且可以让他们立即进入GC。
可以看内存消耗上去,一旦下去再往上去就是 异常抛出。感觉像垃圾收集器经常不足以释放所有空间。
这是不正确的。但是,当您经常打开/关闭图像并且定型队列正在成长时可能会出现这种情况。
这是一篇很好的文章:The Dangers of the Large Object Heap ...
谢谢@CharithJ。这篇文章很有趣,帮助我更好地理解我的问题。但由于我使用的图像总是相同的大小,我不认为这正是我的问题。我想出了一些东西,并会提供一个答案。 – Steffen
水晶球说,你永远不会调用IImage.Dispose()。是的,GC.Collect()会隐藏这个问题。 –