简体   繁体   English

将 Bitmap 转换为 BitmapImage 的持续垃圾收集

[英]Constant garbage collection from converting Bitmap to BitmapImage

My code is essentially attempting to create a video feed by constantly updating a BitmapImage that is binded to the UI (WPF).我的代码本质上是尝试通过不断更新绑定到 UI (WPF) 的 BitmapImage 来创建视频源。 This means a bitmap is being converted to BitmapImage multiple times per second.这意味着每秒将多次将位图转换为 BitmapImage。 However this is causing constant garbage collection (multiple times per second) which seems like a bad sign?然而,这会导致持续的垃圾收集(每秒多次),这似乎是一个坏兆头?

The bitmaps are being disposed of correctly its just the BitmapImage part which is causing the problem.位图被正确处理,它只是导致问题的 BitmapImage 部分。 I've tried writing to the BitmapImage instead of creating a new one, But once frozen it becomes Readonly.我尝试写入 BitmapImage 而不是创建一个新的,但是一旦冻结它就变成了只读的。 And if I unfreeze it I get Error: "Must create DependencySource on same Thread as the DependencyObject".如果我解冻它,我会得到错误:“必须在与 DependencyObject 相同的线程上创建 DependencySource”。

Below is the method I'm using to create a bitmap Image下面是我用来创建位图图像的方法

        //CapturedBitmapmapImage bound to the UI
        CapturedBitmapImage = BitMap2BitMapImage(bitmap);

        //Method for converting Bitmap to BitmapImage
        public static BitmapImage BitMap2BitMapImage(Bitmap bitmap)
        {
            MemoryStream ms = new MemoryStream();           
            bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
            BitmapImage image = new BitmapImage();
            image.BeginInit();
            ms.Seek(0, SeekOrigin.Begin);
            image.StreamSource = ms;
            image.EndInit();
            image.Freeze();
            return image;
        }

This method will save the image and decode it for each frame, probably allocating the entire image buffer multiple times.此方法将保存图像并为每一帧解码,可能会多次分配整个图像缓冲区。 So it should be no surprise that it will cause a lot of memory allocation, and probably on the Large object heap to make things even worse.所以也就不足为奇了,它会造成大量的内存分配,并且可能在大对象堆上让事情变得更糟。

To fix this I would recommend using a WritableBitmap instead.要解决此问题,我建议改用WritableBitmap Assuming you have the same size/color space of both bitmaps and are running unsafe code:假设您有两个位图的相同大小/颜色空间并且正在运行不安全的代码:

var bitmapData = sourceBitmap.LockBits(
    new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),
    System.Drawing.Imaging.ImageLockMode.ReadOnly,
    sourceBitmap.PixelFormat);
var sourcePtr = bitmapData.Scan0;
myWriteableBitmap.Lock();
var destPtr = bitmap.BackBuffer;
 var totalBytes = bitmapData.Stride * bitmapData.Height * myWriteableBitmap.Format.BitsPerPixel / 8;
Buffer.MemoryCopy((byte*)sourcePtr, (byte*)destPtr, totalBytes, totalBytes);
myWriteableBitmap.Unlock();
sourceBitmap.UnlockBits(bitmapData);

But you might be able to call the C memcopy function to avoid any actual pointers.但是您也许可以调用 C memcopy 函数来避免任何实际的指针。 Also note that you should add error handling whenever locking/unlocking data.另请注意,您应该在锁定/解锁数据时添加错误处理。

This should allow you to avoid allocations, except for creating the bitmap in the first place.这应该允许您避免分配,除了首先创建位图。 Ideally you would want to avoid creating any new bitmaps at all, and reuse any buffers for image data.理想情况下,您希望完全避免创建任何新位图,并为图像数据重用任何缓冲区。 But it is difficult to tell how to do this since you do not show how the bitmap is created.但是很难说出如何执行此操作,因为您没有显示位图是如何创建的。

Instead of creating a new BitmapImage for each frame, you should use a WriteableBitmap that is once assigned to the Source property of an Image element.您应该使用曾经分配给 Image 元素的 Source 属性的 WriteableBitmap,而不是为每个帧创建一个新的 BitmapImage。

int frameWidth = ...
int frameHeight = ...
var pixelFormat = PixelFormats.Bgr24; // must match Bitmap.PixelFormat

var writeableBitmap = new WriteableBitmap(
    frameWidth, frameHeight, 96, 96, pixelFormat, null);

image.Source = writeableBitmap;

You would then cyclically update the WriteableBitmap (and hence also the Image element) from a Bitmap like this:然后,您将像这样从 Bitmap 循环更新 WriteableBitmap(以及 Image 元素):

Bitmap bitmap = ... // a frame

var bitmapData = bitmap.LockBits(
    new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
    System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);

image.Dispatcher.Invoke(() => writeableBitmap.WritePixels(
    new Int32Rect(0, 0, bitmap.Width, bitmap.Height),
    bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride));

bitmap.UnlockBits(bitmapData);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM