簡體   English   中英

來自LockBits的BitmapData占用內存

[英]BitmapData from LockBits consuming memory

我有一個C#例程來獲取YUV422位圖並轉換為RGB:

    private unsafe void YUV422toRGB(byte[] YUV422, int YUVstride, ref Bitmap RGB)
    {
        //Found http://pastebin.com/MFsDnUCq after I wrote this.

        int row, col, index;
        byte y1, y2, u, v;
        int r1, r2, g1, g2, b1, b2;
        int c1, c2, d, e;
        byte* RGBbytes;
        int RGBindex;

        //http://bobpowell.net/lockingbits.aspx
        //It looks as though this bmp guy is consuming memory to the point
        //where I must force the garbage collector or the program will crash.
        //Why?
        System.Drawing.Imaging.BitmapData bmp =
            RGB.LockBits(
            new Rectangle(0, 0, RGB.Width, RGB.Height),
            System.Drawing.Imaging.ImageLockMode.WriteOnly,
            RGB.PixelFormat);
        RGBbytes = (byte*)bmp.Scan0;
        RGBindex = 0;
        index = 0;
        for (row = 0; row < RGB.Height; row++)
        {
            for (col = 0; col < YUVstride; col += 4)
            {
                u = YUV422[index + 0];
                y1 = YUV422[index + 1];
                v = YUV422[index + 2];
                y2 = YUV422[index + 3];
                index += 4;

                c1 = y1 - 16;
                c2 = y2 - 16;
                d = u - 128;
                e = v - 128;

                int c298 = 298 * c1;
                r1 = (c298 + 409 * e + 128) >> 8;
                g1 = (c298 - 100 * d - 208 * e + 128) >> 8;
                b1 = (c298 + 516 * d + 128) >> 8;

                c298 = 298 * c2;
                r2 = (c298 + 409 * e + 128) >> 8;
                g2 = (c298 - 100 * d - 208 * e + 128) >> 8;
                b2 = (c298 + 516 * d + 128) >> 8;

                //Now for clamping.
                //From http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
                //min(x, y) = y ^ ((x ^ y) & -(x < y))
                //max(x, y) = x ^ ((x ^ y) & -(x < y))
                //We want min(x, 255) followed by max(x, 0).
                //The problem is that x < y in C# is a bool which cannot be converted to int.
                //But effectively, -(x < y) is -1 if x < y and 0 otherwise.
                //we can do this by looking at the first bit of x-y
                //min(x, y) = y ^ ((x ^ y) & ((x - y) >> 31))
                //max(x, y) = x ^ ((x ^ y) & ((x - y) >> 31))
                //There appears to be 10% or so speed increase with the bithack.

                //r1 = Math.Max(0, Math.Min(r1, 255));
                //r2 = Math.Max(0, Math.Min(r2, 255));
                //g1 = Math.Max(0, Math.Min(g1, 255));
                //g2 = Math.Max(0, Math.Min(g2, 255));
                //b1 = Math.Max(0, Math.Min(b1, 255));
                //b2 = Math.Max(0, Math.Min(b2, 255));

                r1 = 255 ^ ((r1 ^ 255) & ((r1 - 255) >> 31));
                g1 = 255 ^ ((g1 ^ 255) & ((g1 - 255) >> 31));
                b1 = 255 ^ ((b1 ^ 255) & ((b1 - 255) >> 31));
                r2 = 255 ^ ((r2 ^ 255) & ((r2 - 255) >> 31));
                g2 = 255 ^ ((g2 ^ 255) & ((g2 - 255) >> 31));
                b2 = 255 ^ ((b2 ^ 255) & ((b2 - 255) >> 31));

                r1 = r1 ^ ((r1 ^ 0) & ((r1 - 0) >> 31));
                g1 = g1 ^ ((g1 ^ 0) & ((g1 - 0) >> 31));
                b1 = b1 ^ ((b1 ^ 0) & ((b1 - 0) >> 31));
                r2 = r2 ^ ((r2 ^ 0) & ((r2 - 0) >> 31));
                g2 = g2 ^ ((g2 ^ 0) & ((g2 - 0) >> 31));
                b2 = b2 ^ ((b2 ^ 0) & ((b2 - 0) >> 31));

                RGBbytes[RGBindex + 0] = (byte)b1;
                RGBbytes[RGBindex + 1] = (byte)g1;
                RGBbytes[RGBindex + 2] = (byte)r1;

                RGBbytes[RGBindex + 3] = (byte)b2;
                RGBbytes[RGBindex + 4] = (byte)g2;
                RGBbytes[RGBindex + 5] = (byte)r2;

                RGBindex += 6;
            }
        }

        RGB.UnlockBits(bmp);
    }

在經典的“嘿,為什么我的程序為什么在30秒后總是崩潰”之后,我意識到垃圾收集器沒有跟上(我每秒調用此函數10次,並且位圖始終通過引用傳遞)。

RGB.LockBits代碼,我看到RGB.LockBits是增加RAM使用率的代碼。 我每十個調用(每秒一次GC.Collect()添加一個GC.Collect() ),現在一切正常。

我在這里做錯什么了嗎? UnlockBits不應該UnlockBits清理嗎?

逐步查看代碼,我看到RGB.LockBits是增加RAM使用率的代碼。 我每十個調用(每秒一次)添加一個GC.Collect(),現在一切正常。

您的意思基本上是,正常的垃圾回收將回收所有內容,問題是,垃圾回收器沒有按預期執行。

您的機器內存不足嗎? 您是否存在實際的內存壓力,該壓力會迫使垃圾收集器觸發並回收空間? 還是您有幾GB的可用內存,以至於暫停所有線程來回收內存只是浪費cpu資源? 聽起來您的計算機只是聳了聳肩,說:“嗯,我的存儲空間更大了。一切照舊。”

漢斯·帕桑(Hans Passant)是正確的。 問題是一個位圖正在被復制,盡管我仍然不清楚為什么這導致垃圾收集器顯然無法完成其工作。

我在該程序中有一些后端類,這些類從相機拍攝圖像並將位圖轉換為適當的格式(例如,我發布的YUV422到RGB轉換)。 被傳遞的位圖被重用。

然后,我有一些UI類,其中一個是窗口,該窗口在以大約10 Hz或更低的頻率運行的計時器上顯示一個新幀。 在這里,您必須復制位圖,因為后端材料將用新框架替換它,等等。

更新顯示的例程很簡單:

    public void SetImage(Bitmap Image)
    {
        if (this.IsDisposed) return;
        if (this.InvokeRequired)
            this.Invoke((MethodInvoker)delegate { SetImage(Image); });
        else
            picFrame.Image = (Bitmap)Image.Clone();
    }

一台相機可提供640x480x8位(黑白)圖像; 在后端,我還使用鎖定位將從API調用獲得的像素數組復制到.NET位圖。 即使其中兩個相機(和顯示窗口)同時運行,我的內存也沒有問題。

1920x1080 YUV422圖像顯然是垃圾收集器“飽和”了(或者您可以稱呼它),因此此版本的顯示例程解決了該問題:

    private Bitmap DisplayImage = null;
    public void SetImage(Bitmap Image)
    {
        if (this.IsDisposed) return;
        if (this.InvokeRequired)
            this.Invoke((MethodInvoker)delegate { SetImage(Image); });
        else
        {
            if (DisplayImage != null)
                DisplayImage.Dispose();
            DisplayImage = (Bitmap)Image.Clone();
            picFrame.Image = DisplayImage;
        }
    }

讓我感到震驚的是,用黑白相機觀察內存使用情況時,只能發現它的數量很少,而僅彩色相機就已飆升至2 GB,沒有停止的跡象。 我已經看到了邊緣垃圾收集器的情況,在這種情況下,您看到內存逐漸增大,然后在垃圾收集器啟動時突然下降。

當然,這里的明顯區別是,即使是兩個黑白攝像機也只有614kB x 10 fps,而彩色攝像機大約是后者的10倍。 我什至不會開始猜測為什么前者對垃圾收集器沒有問題,而后者卻沒有。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM