来自LockBits消耗内存的BitmapData

时间:2014-03-25 05:40:44

标签: c# .net bitmap gdi+

我有一个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次,并且位图通过引用传递)。< / p>

单步执行代码,我看到RGB.LockBits增加了RAM的使用量。我每十次通话(每秒一次)添加一个GC.Collect(),现在情况还不错。

我在这里做错了吗?不应该UnlockBits自己清理?

2 个答案:

答案 0 :(得分:0)

  

单步执行代码,我看到RGB.LockBits增加了RAM的使用量。我每十次调用(每秒一次)添加一个GC.Collect(),现在情况很好。

你所说的基本上是普通的垃圾收集会回收所有东西,问题是垃圾收集器没有按你预期的那样执行。

你的机器内存不足吗?你是否有实际的内存压力会迫使垃圾收集器触发并回收空间?或者你有几千兆字节的可用内存,以至于只是浪费cpu资源来暂停所有线程来回收内存?这听起来像你的机器只是耸了耸肩并且说'呃,我在这里有更多的记忆。照常营业。&#34;

答案 1 :(得分:0)

Hans Passant是对的。问题是一个重复的位图,虽然我仍然不清楚为什么这会导致垃圾收集器显然无法完成其工作。

我在这个程序中有一些后端类,它从相机中获取图像并将位图转换为适当的格式(例如,我发布的YUV422到RGB转换)。传递的位图被重用。

然后我有一些UI类,其中一个是一个窗口,它在一个以大约10赫兹运行的计时器上显示一个新帧。在这里你必须复制Bitmap,因为后端的东西将用新的框架等替换它。

更新显示的例程很简单:

    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位(黑白)图像;在后端我还使用lockbits将我从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,而彩色相机只有几十倍。我甚至不会开始猜测为什么前者与垃圾收集器没有问题,后者也没有问题。