Click here to Skip to main content
Click here to Skip to main content

Comparing Images using GDI+

, 7 Jul 2005
Rate this:
Please Sign up or sign in to vote.
An article on comparing two images by computing and comparing their hash values.

Introduction

.NET provides some great methods for working with images and bitmaps using the managed GDI+ methods. However, I found myself a bit stuck even with GDI+ when I wanted to compare two images to see if they were identical. I was trying to run some automated tests on our charting component, SimpleChart, and I needed to know if the charts being produced were identical to those in the test specification. To do this, I needed to compare each image being generated by SimpleChart in the test with a reference image that was known to be good. If the two were identical then the test had passed.

Comparing Images

First Attempts

The first step in comparing two images to see if they were identical was to check the size of each. If they don't match then we know almost immediately that the images are not identical. Once that quick test was complete, we needed to look at the actual image content to see if it matched up. Initially, I decided to use GetPixel method of the GDI+ Bitmap class to compare each pixel in the first image with the corresponding pixel in the second image. If at any point, the two pixels did not match then we can safely say that the images are different. If, however, we got to the end of the comparison tests without any mismatches then we can conclude that the two images are indeed identical.

public static CompareResult Compare(Bitmap bmp1, Bitmap bmp2)
{
    CompareResult cr = CompareResult.ciCompareOk;

    //Test to see if we have the same size of image
    if (bmp1.Size != bmp2.Size)
    {
        cr = CompareResult.ciSizeMismatch;
    }
    else
    {
        //Sizes are the same so start comparing pixels
        for (int x = 0; x < bmp1.Width 
             && cr == CompareResult.ciCompareOk; x++)
        {
            for (int y = 0; y < bmp1.Height 
                         && cr == CompareResult.ciCompareOk; y++)
            {
                if (bmp1.GetPixel(x, y) != bmp2.GetPixel(x, y))
                    cr = CompareResult.ciPixelMismatch;
            }
        }
    }
    return cr;
}

This method worked fine but with one major drawback, speed, or rather the lack of it. Comparing two 2000 x 1500 pixel images using this method took over 17 seconds! With over 200 images to compare, this meant that my tests would take nearly an hour to complete and I wasn't prepared to wait that long.

Hash in a Flash

What I needed was a faster method to compare the images to allow the tests to complete in a timely manner. Rather than comparing the individual pixels in each image using GetPixel, I decided that it would be quicker if I could some how compare a 'hash' of each image to see if they were identical. As we know, a hash is a unique value of a fixed size representing a large amount of data, in this case our image data. Hashes of two images should match if and only if the corresponding images also match. Small changes to the image result in large unpredictable changes in the hash.

There are many different hashing algorithms provided by .NET in the System.Security.Cryptography namespace such as SHA1 and MD5 but I decided to use the SHA256Managed class. The ComputeHash method of this class takes a byte array of data as an input parameter and produces a 256 bit hash of that data. By computing and then comparing the hash of each image, I would be quickly able to tell if the images were identical or not.

The only problem now remaining was how to convert the image data stored in the GDI+ Bitmap objects to a suitable form for passing to the ComputeHash method, namely a byte array. Initially, I looked at the LockBits method of the Bitmap class which allowed me access to the individual pixel bytes but it would have meant a journey into the land of unmanaged code and that was somewhere I really didn't want to visit. Instead, GDI+ kindly provides an ImageConvertor class to allow us to convert Image (or Bitmap) objects from one data type to another, such as a byte array.

The final step to see if the images are identical is to compare the two hash values (also stored in byte arrays) to see if they match. Here is the final code:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Security.Cryptography;

namespace Imagio
{
    public class ComparingImages
    {
        public enum CompareResult
        {
            ciCompareOk,
            ciPixelMismatch,
            ciSizeMismatch
        };

        public static CompareResult Compare(Bitmap bmp1, Bitmap bmp2)
        {
            CompareResult cr = CompareResult.ciCompareOk;

            //Test to see if we have the same size of image
            if (bmp1.Size != bmp2.Size)
            {
                cr = CompareResult.ciSizeMismatch;
            }
            else
            {
                //Convert each image to a byte array
                System.Drawing.ImageConverter ic = 
                       new System.Drawing.ImageConverter();
                byte[] btImage1 = new byte[1];
                btImage1 = (byte[])ic.ConvertTo(bmp1, btImage1.GetType());
                byte[] btImage2 = new byte[1];
                btImage2 = (byte[])ic.ConvertTo(bmp2, btImage2.GetType());
                
                //Compute a hash for each image
                SHA256Managed shaM = new SHA256Managed();
                byte[] hash1 = shaM.ComputeHash(btImage1);
                byte[] hash2 = shaM.ComputeHash(btImage2);

                //Compare the hash values
                for (int i = 0; i < hash1.Length && i < hash2.Length 
                                  && cr == CompareResult.ciCompareOk; i++)
                {
                    if (hash1[i] != hash2[i])
                        cr = CompareResult.ciPixelMismatch;
                }
            }
            return cr;
        }
    }
}

Conclusion

Running this new compare method on a 2000 x 1500 pixel bitmap resulted in a comparison time of 0.28 seconds which meant that my automated testing of 200 SimpleChart images now takes only 56 seconds to complete.

Hashes are normally used as a security tool to see if credentials such as passwords match. Using the same hashing approach, we can also quickly compare two images to see if they too are identical.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

markrouse
Web Developer
United Kingdom United Kingdom
Mark Rouse works as a software developer for Imagio Technology in Yorkshire, UK
 
When not working on his favourite charting component, SimpleChart, Mark is providing custom application development for his clients using ASP.NET, C# and SQL Server and in particular Web Services.

Comments and Discussions

 
Questionremoving similarities from given images Pinmembermughalshahzad@gmail.com10-Mar-14 19:03 
QuestionWant giudence PinmemberPrathap Kp24-Jan-14 17:48 
SuggestionSuggestion Pinmemberharveyt28-Nov-12 6:39 
Questionpls anyone help me.... Pinmemberkalpesh badjate31-Jan-12 19:35 
GeneralMy vote of 5 PinmemberH.C.V.A14-Oct-11 23:04 
Questioncomparing images Pinmembersachinthapa7-Sep-11 22:06 
GeneralExllent PinmemberTejuKrishna10-Jan-11 2:40 
GeneralMy vote of 5 PinmemberTejuKrishna10-Jan-11 2:37 
QuestionWhy use hash code? PinmemberJoe Schulte14-Jun-10 19:22 
AnswerRe: Why use hash code? Pinmembermegahurts_gr14-Jun-11 19:46 
Generalurgently please Pingroupmetyouba18-Mar-10 2:08 
GeneralA generic error occurred in GDI+. Pinmembersuleh11-Sep-09 3:30 
GeneralRe: A generic error occurred in GDI+. Pinmemberjra7325-Jul-10 15:05 
GeneralUnsafe hashing PinmemberMember 31523124-Apr-09 9:36 
GeneralRe: Unsafe hashing Pinmemberharveyt1-Dec-12 4:48 
Generalis more faster like that, check it PinmemberShargon_858-Feb-09 11:54 
GeneralComparing one image to many others speeded up PinmemberJan W.6-Feb-09 2:17 
QuestionNot working for me Pinmemberpravin dingore31-Dec-08 2:11 
Questionerror :( ??? Pinmembercheng_j_zhang22-Nov-08 18:47 
AnswerRe: error :( ??? Pinmembercheng_j_zhang22-Nov-08 19:07 
GeneralObject leak Pinmemberthelawnet3-Nov-08 13:03 
QuestionHow to compare images for similarity Pinmemberlexstyles14-Apr-08 10:48 
AnswerRe: How to compare images for similarity Pinmembermarkrouse20-Apr-08 23:53 
GeneralDifferent file format, same picture PinmemberScareCr0w25-Mar-08 15:18 
GeneralRe: Different file format, same picture PinmemberTenTrinacty21-Jul-09 9:34 
GeneralRe: Different file format, same picture Pinmemberharveyt1-Dec-12 4:57 
Generalhelp me Pinmemberarunbediya17-Feb-08 19:43 
GeneralI found this article very helpful PinmemberGuyThiebaut15-Nov-07 9:26 
GeneralRe: I found this article very helpful Pinmembermarkrouse22-Nov-07 4:52 
GeneralRe: I found this article very helpful PinmemberGuyThiebaut22-Nov-07 4:56 
QuestionSame Images in a drawing Pinmemberkk_upadhyay15-Aug-07 23:04 
QuestionNeed help Pinmembermiopalmo8-Mar-07 4:03 
GeneralComparing two identical images on two different computers PinmemberMember #156497116-Jan-07 0:34 
GeneralRe: Comparing two identical images on two different computers Pinmembermarkrouse18-Jan-07 23:55 
Generaltwo images captured in different screen resolution [modified] PinmemberMahmoud.Mady8-Jan-07 1:58 
AnswerRe: two images captured in different screen resolution Pinmembermarkrouse15-Jan-07 1:51 
GeneralDegree of difference PinmemberJack Schitt9-Nov-06 19:18 
GeneralBitmap.GetHashCode(); Pinmember5150@abrichardson.com24-Oct-06 5:55 
AnswerRe: Bitmap.GetHashCode(); Pinmembermarkrouse25-Oct-06 1:57 
GeneralRe: Bitmap.GetHashCode(); Pinmembergenne225-May-07 8:44 
GeneralRe: Bitmap.GetHashCode(); PinmemberJohn Brett22-Jan-10 1:39 
GeneralRe: Bitmap.GetHashCode(); Pinmemberssheldon17-Mar-10 6:44 
QuestionWhy we say images are not identical if the file sizes are different Pinmemberalbert arul prakash19-Aug-06 1:57 
AnswerRe: Why we say images are not identical if the file sizes are different Pinmembermarkrouse25-Aug-06 2:21 
GeneralFinding a subimage within a larger one... Pinmemberspeedplane14-May-06 14:42 
GeneralRe: Finding a subimage within a larger one... PinmemberJ Hoffa24-Jan-07 5:14 
QuestionHash Collisions? PinmemberDejaVudew8-Jul-05 13:55 
GeneralSpeed up with Hashtable PinmemberTwink4-May-05 14:12 
GeneralFaster and simpler PinmemberSteveAbbott16-Jan-05 5:50 
QuestionFaster Way? Pinmemberbasementman14-Jan-05 5:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411023.1 | Last Updated 8 Jul 2005
Article Copyright 2005 by markrouse
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid