我需要在我的 UI 中包含相机图像。我为 View
提出的解决方案和 ViewModel
下面给出。在 ViewModel
我正在使用 BitmapSource
每当相机发出新帧信号时,它就会保存相机图像并且不断更新。对此BitmapSource
我绑定(bind)View
.
这段代码大部分都能找到。但是,我遇到的问题是应用程序会定期消耗大量内存。视频尚未启动时,消耗量约为 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
不是为这种情况创建的,而是在您访问外部/非托管资源时创建的。
这是代码:
摄像头图像查看 :
<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;
}
最佳答案
替换创建新 BitmapSource
每UpdateCameraImage
使用单个 WriteableBitmap
您创建一次并经常更新。这样,您将避免在内存中创建图像副本。
此代码假定 ImageWidth
和 ImageHeight
不随时间变化,提前知晓。如果不是这种情况,则必须在尺寸更改时动态重新创建图像。
private cameraImageSource = new WriteableBitmap(
ImageWidth,
ImageHeight,
96,
96,
PixelFormats.Gray8,
null);
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);
}
也可以调用
currentImage.Bitmap
在 UMat
没有必要。我不知道EmguCV
您似乎正在使用的库,但 UMat 还有其他属性,例如 Ptr
可以直接传递给 CopyPixels
.
关于c# - 对于这种使用 BitmapSource 不断更新相机图像的情况,如何避免显式调用 GC.Collect(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39122503/