2016-08-24 42 views
1

我需要在我的UI中包含摄像机图像。下面给出了我为ViewViewModel提出的解决方案。在ViewModel中,我使用的是一个BitmapSource,用于保存相机图像,并且每当相机发出一个新帧时就会持续更新。对此BitmapSource我绑定View如何避免显式调用GC.Collect(),这种情况下使用BitmapSource来连续更新摄像机图像

这段代码大部分都适用。不过,我有问题,应用程序会周期性地消耗大量的内存。当视频尚未启动时,消费约为245MB。但是当视频开始播放时,它会在约4秒内迅速攀升至〜1GB,这是Garbage-Collector开始播放时的值,并将该值降低至约245MB。在这一点上,视频会简短的口吃(我怀疑是由于CPU征税)。这种情况会定期发生,每4秒钟左右一次。某些时候,GC在4秒后未启动时,内存使用率甚至会达到2GB,并且还导致了内存不足异常。

我发现可以解决这个问题的方法是在每次更新帧时明确调用GC。看到两条评论线。这样做时,视频启动后,内存将继续悬停在〜245MB。

但是,这会导致CPU使用率从〜20%显着增加到〜35%。

我不明白GC是如何工作的,但我怀疑,GC为时已晚,因为更新BitmapSource的线程忙于更新视频(运行速度为25FPS)以及因此没有时间运行GC,除非明确告知。

所以我的问题是:这种行为的原因是什么,有没有更好的方式来实现,我正在尝试做什么,同时避免明确调用GC

我试图在using语句来包裹这一点,但BitmapSource没有实现IDisponsable,并从我明白了什么using没有为这种情况下创建的,但是当你访问外部/非托管资源。

下面是代码:

CameraImageView

<Image Source="{Binding CameraImageSource}"/> 

CameraImageViewModel

public class CameraImageViewModel : ViewModelBase 
{ 
    private ICamera camera; 
    private UMat currentImage; 

    public BitmapSource CameraImageSource 
    { 
     get { return cameraImageSource; } 
     set 
     { 
      cameraImageSource = value; 
      RaisePropertyChanged("CameraImageSource"); 
     } 
    } 
    private BitmapSource cameraImageSource; 

    public CameraImageViewModel(ICamera camera) 
    { 
     this.camera = camera; 
     camera.EventFrame += new EventHandler(UpdateCameraImage); 
    } 

    private void UpdateCameraImage(object s, EventArgs e) 
    { 
     camera.GetMatImage(out currentImage); 
     // commenting from here on downward, will also remove the described memory usage, but then we do not have an image anymore 
     BitmapSource tmpBitmap = ImageProcessing.UMatToBitmapSource(currentImage); 
     tmpBitmap.Freeze(); 
     DispatcherHelper.CheckBeginInvokeOnUI(() => CameraImageSource = tmpBitmap); 
     //GC.Collect(); // without these lines I have the memory issue 
     //GC.WaitForPendingFinalizers(); 
    } 
} 

ImageProcessing.UMatToBitmapSource

public static BitmapSource UMatToBitmapSource(UMat image) 
    { 
     using (System.Drawing.Bitmap source = image.Bitmap) 
     { 
      return Convert(source); 
     } 
    } 

    /* 
    * REF for implementation of 'Convert': http://stackoverflow.com/questions/30727343/fast-converting-bitmap-to-bitmapsource-wpf/30729291#30729291 
    */ 
    public static BitmapSource Convert(System.Drawing.Bitmap bitmap) 
    { 
     var bitmapData = bitmap.LockBits(
      new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), 
      System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat); 

     var bitmapSource = BitmapSource.Create(
      bitmapData.Width, bitmapData.Height, 96, 96, PixelFormats.Gray8, null, 
      bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); 

     bitmap.UnlockBits(bitmapData); 
     return bitmapSource; 
    } 
+0

如果工作正常,如果内存超过xmb,而不是每一帧,则可以每x毫秒运行一次GC。 – null

回答

1

取代与中您创建一次,并经常更新单WriteableBitmap使用创造新BitmapSourceUpdateCameraImage。这样你将避免在内存中创建图像的副本。

此代码假定ImageWidthImageHeight不随时间变化并且事先已知。如果不是这种情况,当尺寸改变时,您将不得不重新创建图像。

  1. 替换为您cameraImageSource:

    private cameraImageSource = new WriteableBitmap(
         ImageWidth, 
         ImageHeight, 
         96, 
         96, 
         PixelFormats.Gray8, 
         null); 
    
  2. 更改UpdateCameraImage到:

    private void UpdateCameraImage(object s, EventArgs e) 
    { 
        camera.GetMatImage(out currentImage); 
        System.Drawing.Bitmap bitmap = currentImage.Bitmap; 
    
        var bitmapData = bitmap.LockBits(
         new System.Drawing.Rectangle(0, 0, ImageWidth, ImageHeight), 
         System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat); 
    
    
        cameraImageSource.CopyPixels(new Int32Rect(0, 0, ImageWidth, 
         ImageHeight), bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); 
    
        bitmap.UnlockBits(bitmapData); 
    } 
    

这也有可能是在UMat调用currentImage.Bitmap是没有必要的。我不知道你使用的EmguCV库,但UMat也有其他属性,如Ptr,可以直接传递给CopyPixels

+0

感谢您的回答。你介意写一个简短的例子吗?我已经在这里看到'WritableBitmap'的用法(http://stackoverflow.com/questions/30727343/fast-converting-bitmap-to-bitmapsource-wpf/30729291),但我真的不明白我会怎么做我的代码,因为我正在使用'BitmapSource'(?)。 – packoman