繁体   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