处理大图像时垃圾收集器太慢

Garbage Collector too slow when working with large images

我正在使用 Emgu OpenCV 从网络摄像头抓取图像,并希望使用 WPF Image 控件将它们可视化。
所以我需要将图像从 Mat 转换为与 Image 控件兼容的图像。所以我从 Emgu 示例中获取了这个 class:

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 x 480)来说很有用。使用任务管理器时(我在 Windows 8),我看到使用的内存在增加和减少。工作正常。

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

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

我认为手动调用垃圾收集器既不是好的风格也不是高效的。任何人都可以在不调用 GC.Collect() 的情况下给出如何解决这个问题的提示吗?

在工作中使用错误的工具时发生。视频实际上并不是一组位图 - 有更好的方法。

我上次必须做的是使用 Direct3d。有一个 WPF 集成,在那里设置位图非常容易。也允许在视频流中进行大量操作;)然后将图像直接推入 Direct3d 表面。已完成。

没有代码示例 - 抱歉。这是几年前的事了,我还没有准备好代码。

IImage参数从哪里来?用完就扔掉。

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.

图像实现终结器,如果你不处理它们的话。它将使这些实例存在多个 GC 周期。可能是你的问题。

如果开发人员不调用 Dispose,Finalizer 是它可以释放非托管(也是托管)资源的最后一点。当您调用 Dispose it Supress 完成时,它将使它们立即可用于 GC。

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.

这通常不太正确。但是当您 open/close 图像频繁并且最终确定队列不断增长时,这可能是可能的。

这是一篇适合您的好文章:The Dangers of the Large Object Heap...

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

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