문제

I have a C# routine to take a YUV422 bitmap and convert to 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);
    }

After the classic "hey, why does my program always crash after 30 seconds" I realized the garbage collector wasn't keeping up (I call this function 10 times a second, and the bitmaps are passed by reference throughout).

Stepping through the code I saw that RGB.LockBits was the one increasing the RAM usage. I added a GC.Collect() every ten calls (once per second) and now things are fine.

Am I doing something wrong here? Shouldn't UnlockBits clean up after itself?

도움이 되었습니까?

해결책 2

Hans Passant was correct. The issue was a bitmap that was being duplicated, though it's still not clear to me why this was causing the garbage collector to apparently fail to do its job.

I have some back-end classes in this program which take images from cameras and convert the bitmaps to the appropriate format (for example, the YUV422 to RGB conversion I posted). The bitmap that gets passed around is reused.

Then I have some UI classes, one of which is a window which displays a fresh frame on a timer running at more or less 10 Hz. Here you must copy the Bitmap since the back end stuff will be replacing it with a new frame, etc.

The routine to update the display was simple:

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

One camera gives 640x480x8 bit (black and white) images; on the back end I also use lockbits to copy the array of pixels I get from the API calls to a .NET bitmap. I had no issues with memory even with two of these cameras (and display windows) running at once.

The 1920x1080 YUV422 image was obviously "saturating" (or however you'd call it) the garbage collector so that this version of the display routine fixed the problem:

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

What threw me is that watching the memory usage with the black and white cameras only revealed it was nailed around some low number, while the color camera alone was skyrocketing toward 2 GB with no signs of stopping. I've seen borderline garbage collector cases where you see the memory creep up and then suddenly come back down when the garbage collector kicks in.

The clear difference here is of course that even two black and white cameras are only 614kB x 10 fps, whereas the color camera is some 10 times that. I am not even going to begin to conjecture why the former had no issues with the garbage collector and the latter did.

다른 팁

Stepping through the code I saw that RGB.LockBits was the one increasing the RAM usage. I added a GC.Collect() every ten calls (once per second) and now things are fine.

What you're saying is basically that a normal garbage collection will reclaim everything, and the issue is that the garbage collector isn't executed when you expect it to.

Are your machine low on memory? Do you have an actual memory pressure present that would force the garbage collector to trigger and reclaim space? Or do you have several gigabyte of free memory, so much that it's just a waste of cpu resource to pause all your threads to reclaim memory? It sounds like your machine just shrugs and says "Eh, I've got a lot more memory over here. Business as usual."

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top