Search code examples
c#performancebitmapsystem.drawing

How can I capture bitmap data very quickly in C#


My goal is to be able to take a row of 70 pixels, analyze all 70 pixels for a certain kind of color, and then throw to another function if criteria are met. This needs to happen at least once every 50 milliseconds, and preferably even faster than that.

My current code looks like so

    public void CaptureArea()
    {
        using (Bitmap capture = new Bitmap(70, 35))
        {
            using (Graphics g = Graphics.FromImage(capture))
            {
                for (int i = 0; i < 10; i++)
                {
                    g.CopyFromScreen(copyPoint, pastePoint, new Size(70, 35));
                    evaluteBitmap(capture);
                }
            }
        }
    }
    public void evaluteBitmap(Bitmap scanArea)
    {
        Rectangle rect = new Rectangle(0, 0, 70, 35);
        BitmapData data = scanArea.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

        IntPtr ptr = data.Scan0;

        int bytes = data.Stride * scanArea.Height;
        byte[] rgbValues = new byte[bytes];
        byte[] r = new byte[bytes / 3];
        byte[] g = new byte[bytes / 3];
        byte[] b = new byte[bytes / 3];

        Marshal.Copy(ptr, rgbValues, 0, bytes);

        int count = 0;
        int stride = data.Stride;

        for (int column = 0; column < 30; column++)
        {
            b[count] = (byte)(rgbValues[(column * stride) + (34 * 3)]);
            g[count] = (byte)(rgbValues[(column * stride) + (34 * 3) + 1]);
            r[count++] = (byte)(rgbValues[(column * stride) + (34 * 3) + 2]);
        }

        scanArea.UnlockBits(data);
    }

Just CaptureArea() can drop about 60 bitmaps into memory every second, which is fine, but it currently takes about 600 milliseconds for EvaluateBitmap() to grab a pixel and split it out into RGB values 70 times.

The end result is that a single frame of data takes well over 500 milliseconds to process when it needs to be much closer to 50 milliseconds. This kind of problem-solving is beyond me and I'm not experienced enough with code of this nature to be able to eyeball solutions and know what will end up being faster or slower and by how much.

Is there a way I can get performance quicker by an order of magnitude or am I asking the impossible?


Solution

  • Profiling session for the given code gives an unambiguous result:

    CopyFromScreen - 40.00% exclusive samples
    Bitmap..ctor - 15.00% exclusive samples
    clr.dll - 11.67% exclusive samples
    KernelBase.dll - 8.33% exclusive samples
    GdiPlus.dll - 6.67% exclusive samples
    LockBits - 6.67% exclusive samples
    Image.Dispose - 5.00% exclusive samples
    ....
    ....
    EvaluateBitmap - 1.67% exclusive samples
    ....
    ....
    CaptureArea - 0.0% exclusive samples

    The biggest portion of the time is spent on .NET methods which cannot be improved.

    Conclusion: there no possible significant improvement of the code. You can probably use multithreading to process more frames during the given time period, though.