Search code examples
c#wpfmemory-managementbitmapgarbage-collection

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

Solution

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