Fast Bitmap comparison - C#
Recently i have done bitmap comparison using Bitmap.GetPixel() method to check pixel by pixel using the following code snippet and it works functionality wise pretty well.
After spending lot of time, i am able to reduce this comparison time to almost 40 ms using Bitmap.LockBits() and Bitmap.UnlockBits() using the following code.
If you feel this is helpful or you like it, Please share this using share buttons available on page.private bool CompareBitmaps(Image left, Image right) { if (object.Equals(left, right)) return true; if (left == null || right == null) return false; if (!left.Size.Equals(right.Size) || !left.PixelFormat.Equals(right.PixelFormat)) return false; Bitmap leftBitmap = left as Bitmap; Bitmap rightBitmap = right as Bitmap; if (leftBitmap == null || rightBitmap == null) return true; #region Code taking more time for comparison for (int col = 0; col < left.Width; col++) { for (int row = 0; row < left.Height; row++) { if (!leftBitmap.GetPixel(col, row).Equals(rightBitmap.GetPixel(col, row))) return false; } } #endregion return true; }But, here the problem is we are accessing memory directly using Bitmap.GetPixel() and it is taking lot of time for comparison. Finally performance of comparison is becoming very very slow for larger images. Say for 1.5MB size images it is taking around 2.5 sec which is not acceptable.
After spending lot of time, i am able to reduce this comparison time to almost 40 ms using Bitmap.LockBits() and Bitmap.UnlockBits() using the following code.
private bool CompareBitmaps(Image left, Image right) { if (object.Equals(left, right)) return true; if (left == null || right == null) return false; if (!left.Size.Equals(right.Size) || !left.PixelFormat.Equals(right.PixelFormat)) return false; Bitmap leftBitmap = left as Bitmap; Bitmap rightBitmap = right as Bitmap; if (leftBitmap == null || rightBitmap == null) return true; #region Optimized code for performance int bytes = left.Width * left.Height * (Image.GetPixelFormatSize(left.PixelFormat) / 8); bool result = true; byte[] b1bytes = new byte[bytes]; byte[] b2bytes = new byte[bytes]; BitmapData bmd1 = leftBitmap.LockBits(new Rectangle(0, 0, leftBitmap.Width - 1, leftBitmap.Height - 1), ImageLockMode.ReadOnly, leftBitmap.PixelFormat); BitmapData bmd2 = rightBitmap.LockBits(new Rectangle(0, 0, rightBitmap.Width - 1, rightBitmap.Height - 1), ImageLockMode.ReadOnly, rightBitmap.PixelFormat); Marshal.Copy(bmd1.Scan0, b1bytes, 0, bytes); Marshal.Copy(bmd2.Scan0, b2bytes, 0, bytes); for (int n = 0; n <= bytes - 1; n++) { if (b1bytes[n] != b2bytes[n]) { result = false; break; } } leftBitmap.UnlockBits(bmd1); rightBitmap.UnlockBits(bmd2); #endregion return result; }That's all it is!
Great pure, simple .Net solution to this problem.
ReplyDeleteIt's a good idea to wrap the LockBits in try-finally block and call UnLockBits in the finally block.
Thanks a lot!
ReplyDeleteNice sir,
ReplyDeleteYou can probably increase the speed of this even more if instead of marshal.Copy, you could just make the method unsafe and use pointers. additionally, if you don't care about the actual color data, read the file as UIn32 in the loop, instead of bytes.
ReplyDeleteThe first change would remove the need to copy both bitmaps to arrays, just use them where they already are. The second change reduces the number of cycles in your loop.
I will advice against using Uint or int witout the following consideration.
DeleteUsing Uint/Int will not give the correct result every time.
it will compile and it will not give runtime errors, but.. the result may vary
The method is fine as long as raw byte width is a multiple of 4
check that Abs(stride) == width
it can fail for example under the following conditions:
DataType: 8bit Indexed or 8bit Monochrome
size = 3x3
in this case, doing Int comparison the last 8 of the 32bits that is compared will have an undefined value.
Consider yourself warned :)
BitmapData bmd1 = leftBitmap.LockBits(new Rectangle(0, 0, leftBitmap.Width - 1, leftBitmap.Height - 1), ImageLockMode.ReadOnly, leftBitmap.PixelFormat);
ReplyDeleteBitmapData bmd2 = rightBitmap.LockBits(new Rectangle(0, 0, rightBitmap.Width - 1, rightBitmap.Height - 1), ImageLockMode.ReadOnly, rightBitmap.PixelFormat);
It gives exception when Width or Height equals to 0.
it should be: when Width or Height equals to 1.
DeleteThank you for sharing this.
ReplyDeleteCould you please give me a contact email address, as I couldn't find any on this page.
My question would be related to the type of license you apply for the code you've shared.
Thanks in advance,
Alexandra
alexutaseptembrie@gmail.com
تمتعوا الآن بأقوي العروض التي تقدمها لكم شركة تسليك مجارى بالدمام و التي تعتبر واحدة من أهم و أكبر الشركات بالسعودية تقدم أفضل مستوي خدمات من خلال فريق متخصص ذو كفاءة .
ReplyDeleteللتواصل الآن عبر :-
http://abraj-dubai.net/%D8%B4%D8%B1%D9%83%D8%A9-%D8%AA%D8%B3%D9%84%D9%8A%D9%83-%D9%85%D8%AC%D8%A7%D8%B1%D9%89-%D8%A8%D8%A7%D9%84%D8%AF%D9%85%D8%A7%D9%85-%D9%88%D8%A7%D9%84%D9%82%D8%B7%D9%8A%D9%81/
خدمات علي مدار 24 ساعة من خلال فريق صيانة ال جي المختص حيث يعتبر توكيل صيانة ال جي من أكبر التوكيلات في مصر لذا يمكنكم الإتصال عبر رقم ال جي المباشر.
ReplyDeleteللتواصل الآن عبر :-
http://www.maintenanceg.com/Lg-Agent-Service-Egypt-Center.html