0

I have a class ImageCompare with 2 methods (CompareBitmapsFast and CompareBitmapsSlow) that each take 2 bitmaps as parameters and compare them one using a slow and one using a fast algorithm. The one with LockBits being much faster despite having a higher for loop iteration counter. Why is it so?

public static class ImageCompare {
    public static bool CompareBitmapsFast(Bitmap bmp1, Bitmap bmp2)
    {
        if (bmp1 == null || bmp2 == null)
            return false;
        if (object.Equals(bmp1, bmp2))
            return true;
        if (!bmp1.Size.Equals(bmp2.Size) || !bmp1.PixelFormat.Equals(bmp2.PixelFormat))
            return false;

        int bytes = bmp1.Width * bmp1.Height * (Image.GetPixelFormatSize(bmp1.PixelFormat) / 8);

        bool result = true;
        byte[] b1bytes = new byte[bytes];
        byte[] b2bytes = new byte[bytes];

        BitmapData bitmapData1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height), ImageLockMode.ReadOnly, bmp1.PixelFormat);
        BitmapData bitmapData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadOnly, bmp2.PixelFormat);

        Marshal.Copy(bitmapData1.Scan0, b1bytes, 0, bytes);
        Marshal.Copy(bitmapData2.Scan0, b2bytes, 0, bytes);
        int fastCounter = 0;
        for (int n = 0; n <= bytes - 1; n++) {
            fastCounter++;
            if (b1bytes[n] != b2bytes[n])
            {
                result = false;
                break;
            }
        }

        bmp1.UnlockBits(bitmapData1);
        bmp2.UnlockBits(bitmapData2);
        Console.WriteLine("Number of for iterations in fast compare: " + fastCounter);
        return result;
    }
    public static bool CompareBitmapsSlow(Bitmap bmp1, Bitmap bmp2)
    {
        if (bmp1 == null || bmp2 == null)
            return false;
        if (object.Equals(bmp1, bmp2))
            return true;
        if (!bmp1.Size.Equals(bmp2.Size) || !bmp1.PixelFormat.Equals(bmp2.PixelFormat))
            return false;

        //Compare bitmaps using GetPixel method
        int slowCounter = 0;
        for (int column = 0; column < bmp1.Width; column++)
        {
            for (int row = 0; row < bmp1.Height; row++) {
                slowCounter++;
                if (!bmp1.GetPixel(column, row).Equals(bmp2.GetPixel(column, row)))
                    return false;
            }
        }

        Console.WriteLine("Number of for iterations in slow compare: " + slowCounter);
        return true;
    }
}
class Program {
    static void Main(string[] args) {
       var bmp1 = ScreenUtil.Capture(1000, 725, 1825, 800);
       var bmp2 = ScreenUtil.Capture(1000, 725, 1825, 800);

       Stopwatch sw = new Stopwatch();
       sw.Start();
       bool res1 = ImageCompare.CompareBitmapsSlow(bmp1, bmp2);
       sw.Stop();
       Console.WriteLine("CompareBitmapsSlow Time: {0} ms", sw.ElapsedMilliseconds);

       sw.Restart();
       bool res2 = ImageCompare.CompareBitmapsFast(bmp1, bmp2);
       sw.Stop();
       Console.WriteLine("CompareBitmapsFast Time: {0} ms", sw.ElapsedMilliseconds);
    }
}

The main program creates 2 screenshots of the same area and uses that to compare both pictures.

Soleil
  • 6,404
  • 5
  • 41
  • 61
miran80
  • 945
  • 7
  • 22
  • 3
    What do the docs say `LockBits` does? Is looping over arrays fast? Is `GetPixel` fast? See also https://stackoverflow.com/questions/24701703/c-sharp-faster-alternatives-to-setpixel-and-getpixel-for-bitmaps-for-windows-f . – mjwills Nov 17 '19 at 11:30
  • Locks a Bitmap into system memory. https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.lockbits?view=netcore-3.0 Comparing bytes is much faster than using GetPixel and compare that, 10x faster. – miran80 Nov 17 '19 at 11:31
  • 5
    The problem is that LockBits is slow. It is used by practically any method that does anything with a bitmap, it takes care of essential internal house-keeping. GetPixel() uses it. The big, big difference is that the slow method gets called only once in the first snippet but called many times in the second snippet. – Hans Passant Nov 17 '19 at 11:46
  • `https://en.morzel.net/post/Why-the-use-of-GetPixel-and-SetPixel-is-so-inefficient!` – mjwills Nov 17 '19 at 12:16

0 Answers0