简体   繁体   中英

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). This means a bitmap is being converted to BitmapImage multiple times per second. 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. I've tried writing to the BitmapImage instead of creating a new one, But once frozen it becomes Readonly. And if I unfreeze it I get Error: "Must create DependencySource on same Thread as the DependencyObject".

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. 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. 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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