繁体   English   中英

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

[英]Constant garbage collection from converting Bitmap to BitmapImage

我的代码本质上是尝试通过不断更新绑定到 UI (WPF) 的 BitmapImage 来创建视频源。 这意味着每秒将多次将位图转换为 BitmapImage。 然而,这会导致持续的垃圾收集(每秒多次),这似乎是一个坏兆头?

位图被正确处理,它只是导致问题的 BitmapImage 部分。 我尝试写入 BitmapImage 而不是创建一个新的,但是一旦冻结它就变成了只读的。 如果我解冻它,我会得到错误:“必须在与 DependencyObject 相同的线程上创建 DependencySource”。

下面是我用来创建位图图像的方法

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

此方法将保存图像并为每一帧解码,可能会多次分配整个图像缓冲区。 所以也就不足为奇了,它会造成大量的内存分配,并且可能在大对象堆上让事情变得更糟。

要解决此问题,我建议改用WritableBitmap 假设您有两个位图的相同大小/颜色空间并且正在运行不安全的代码:

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);

但是您也许可以调用 C memcopy 函数来避免任何实际的指针。 另请注意,您应该在锁定/解锁数据时添加错误处理。

这应该允许您避免分配,除了首先创建位图。 理想情况下,您希望完全避免创建任何新位图,并为图像数据重用任何缓冲区。 但是很难说出如何执行此操作,因为您没有显示位图是如何创建的。

您应该使用曾经分配给 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;

然后,您将像这样从 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