2

我正在使用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()?

+2

水晶球说,你永远不会调用IImage.Dispose()。是的,GC.Collect()会隐藏这个问题。 –

回答

3

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

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

当一个人使用错误的工具进行工作时会发生。视频并不是一组位图 - 有更好的方法来实现它。

我上次做了什么,我不得不这样做是使用Direct3d。有一个WPF集成,并很容易在那里建立一个位图。允许在视频流中进行大量操作;)将图像直接推入Direct3d表面。成品。

无代码示例 - 对不起。这是几年前,我没有准备好代码。

+0

谢谢TomTom。我认为你是正确的抓住和可视化。但是在这里我想用OpenCV对帧进行一些图像处理。所以我需要他们作为“垫子”。 – Steffen

+0

不清楚你的问题。无论如何,没有必要创建garbate。他们都是一样的大小。重新使用它们。不要创建新的位图;) – TomTom

2

从哪里来的IImage参数?在完成之后处置它。

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

图像实现终结器,如果你不处理它们。它将使这些实例生活多个GC循环。可能这是你的问题。

如果开发人员不调用Dispose,Finalizer是最后一点,它可以释放非托管(管理的)资源。当你打电话给Dispose时,它会压缩最终结果,并且可以让他们立即进入GC。

可以看内存消耗上去,一旦下去再往上去就是 异常抛出。感觉像垃圾收集器经常不足以释放所有空间。

这是不正确的。但是,当您经常打开/关闭图像并且定型队列正在成长时可能会出现这种情况。

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

+0

谢谢@CharithJ。这篇文章很有趣,帮助我更好地理解我的问题。但由于我使用的图像总是相同的大小,我不认为这正是我的问题。我想出了一些东西,并会提供一个答案。 – Steffen