1
votes

I need to display 8k RGB image from a RAW byte array which I get from a framegrabber in wpf control at High fps speed. I am able to successfully convert and the byte array to BitmapSource and display it in the image windows in WPF. But since the grabber is generating images at around 5 FPS after display i am releasing the hold to the object but GC takes time collect the memory within which my application memory increases and freezes the application and my system. How to properly dispose the BitmapSource(CachedBitmap) created using the below method. Please help me out in disposing the object after use.

I tried GC.Collect() while image change but it doesn't work.

Here is my byte array to bitmap conversion code,

    int Width = 7680;
    int Height = 4320;
    PixelFormat PixelFormat = PixelFormats.Bgr24;
    int BytesPerPixel = 3;

    public BitmapSource ByteToImage(byte[] imageData)
    {            
        var stride = BytesPerPixel * Width;
        BitmapSource bitmapSource = BitmapSource.Create(Width, Height, 96d, 96d, PixelFormat, null, imageData, stride);
        bitmapSource.Freeze();
        return bitmapSource;
    }
2
Maybe the issue is lying in freezing the object. Maybe try without freezing it. It should still be no problem though - lorenzw
Thanks , But it doesn't work. Same issue - Tamil Prakash
Do you really need the bitmaps to be that large? Maybe the frame grabber is able to produce frames with a smaller resolution. - Clemens
Yes frame grabber able to produce 8k images - Tamil Prakash

2 Answers

0
votes

Probably you should try Writable bitmap:

        WriteableBitmap writeableBitmap = new WriteableBitmap(
            (int)width,
            (int)height,
            96,
            96,
            PixelFormats.Bgr32,
            null);

        int stride = width * writeableBitmap.Format.BitsPerPixel / 8

Now when you receive a frame you can write it into writable bitmap:

        writeableBitmap.Lock();
        writeableBitmap.WritePixels(new Int32Rect(0, 0, width, height), imageData, stride, 0);
        writeableBitmap.Unlock();

In this way you do not need to create new image. You just overwrite bitmap data. But be careful to always grab data from source into the same buffer array.

0
votes

This sounds like a possible candidate for composition target rendering, try adding this to your MainWindow constructor:

CompositionTarget.Rendering += CompositionTarget_Rendering;

And then implement the function itself:

void CompositionTarget_Rendering(object sender, EventArgs e)
{
    // dispose previous image, create and assign the new one
}

Do it properly and it should also handle cases where your target machine isn't able to keep up with your desired frame rate.

UPDATE: BitmapSource just uses regular memory, so the correct behavior is to allow the GC to clean it up. If that's not happening in your case then it means something is holding on to that memory for some reason, probably display. Here's an example that uses CompositionTarget.Rendering, I'm dumping the GC memory both before and after collection so you can see it's quite clearly being collected properly:

    public MainWindow()
    {
        InitializeComponent();
        CompositionTarget.Rendering += CompositionTarget_Rendering;
    }       

    private void CompositionTarget_Rendering(object sender, EventArgs e)
    {
        // remove reference to old image
        this.theImage.Source = null;

        // invoke GC
        Debug.WriteLine(GC.GetTotalMemory(false));
        GC.Collect();
        Debug.WriteLine(GC.GetTotalMemory(false));

        // create and assign new image
        const int width = 1000;
        const int height = 1000;
        var pixels = new uint[width * height];
        uint color = (uint)((new Random((int)DateTime.Now.Ticks)).Next(0x1000000) | 0xff000000);
        for (int i = 0; i < width * height; i++)
            pixels[i] = color;
        this.theImage.Source = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, pixels, width * 4);
    }