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