Click here to Skip to main content
11,709,844 members (57,673 online)
Click here to Skip to main content

Comparing Images using GDI+

, 7 Jul 2005 229.1K 4.8K 99
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.

You may also be interested in...

Comments and Discussions

 
GeneralRe: Different file format, same picture Pin
harveyt1-Dec-12 3:57
memberharveyt1-Dec-12 3:57 
Generalhelp me Pin
arunbediya17-Feb-08 18:43
memberarunbediya17-Feb-08 18:43 
GeneralI found this article very helpful Pin
GuyThiebaut15-Nov-07 8:26
memberGuyThiebaut15-Nov-07 8:26 
GeneralRe: I found this article very helpful Pin
markrouse22-Nov-07 3:52
membermarkrouse22-Nov-07 3:52 
GeneralRe: I found this article very helpful Pin
GuyThiebaut22-Nov-07 3:56
memberGuyThiebaut22-Nov-07 3:56 
QuestionSame Images in a drawing Pin
kk_upadhyay15-Aug-07 22:04
memberkk_upadhyay15-Aug-07 22:04 
QuestionNeed help Pin
miopalmo8-Mar-07 3:03
membermiopalmo8-Mar-07 3:03 
GeneralComparing two identical images on two different computers Pin
Member #156497115-Jan-07 23:34
memberMember #156497115-Jan-07 23:34 
GeneralRe: Comparing two identical images on two different computers Pin
markrouse18-Jan-07 22:55
membermarkrouse18-Jan-07 22:55 
Generaltwo images captured in different screen resolution [modified] Pin
Mahmoud.Mady8-Jan-07 0:58
memberMahmoud.Mady8-Jan-07 0:58 
AnswerRe: two images captured in different screen resolution Pin
markrouse15-Jan-07 0:51
membermarkrouse15-Jan-07 0:51 
GeneralDegree of difference Pin
Jack Schitt9-Nov-06 18:18
memberJack Schitt9-Nov-06 18:18 
GeneralBitmap.GetHashCode(); Pin
5150@abrichardson.com24-Oct-06 4:55
member5150@abrichardson.com24-Oct-06 4:55 
AnswerRe: Bitmap.GetHashCode(); Pin
markrouse25-Oct-06 0:57
membermarkrouse25-Oct-06 0:57 
GeneralRe: Bitmap.GetHashCode(); Pin
genne225-May-07 7:44
membergenne225-May-07 7:44 
GeneralRe: Bitmap.GetHashCode(); Pin
John Brett22-Jan-10 0:39
memberJohn Brett22-Jan-10 0:39 
GeneralRe: Bitmap.GetHashCode(); Pin
ssheldon17-Mar-10 5:44
memberssheldon17-Mar-10 5:44 
QuestionWhy we say images are not identical if the file sizes are different Pin
albert arul prakash19-Aug-06 0:57
memberalbert arul prakash19-Aug-06 0:57 
AnswerRe: Why we say images are not identical if the file sizes are different Pin
markrouse25-Aug-06 1:21
membermarkrouse25-Aug-06 1:21 
GeneralFinding a subimage within a larger one... Pin
speedplane14-May-06 13:42
memberspeedplane14-May-06 13:42 
GeneralRe: Finding a subimage within a larger one... Pin
J Hoffa24-Jan-07 4:14
memberJ Hoffa24-Jan-07 4:14 
QuestionHash Collisions? Pin
DejaVudew8-Jul-05 12:55
memberDejaVudew8-Jul-05 12:55 
GeneralSpeed up with Hashtable Pin
Twink4-May-05 13:12
memberTwink4-May-05 13:12 
GeneralFaster and simpler Pin
SteveAbbott16-Jan-05 4:50
memberSteveAbbott16-Jan-05 4:50 
QuestionFaster Way? Pin
basementman14-Jan-05 4:12
memberbasementman14-Jan-05 4:12 
AnswerRe: Faster Way? Pin
Anthony_Yio14-Jan-05 15:17
memberAnthony_Yio14-Jan-05 15:17 
GeneralA very simple but even more efficient algorithm Pin
Anonymous13-Jan-05 23:46
sussAnonymous13-Jan-05 23:46 
GeneralRe: A very simple but even more efficient algorithm Pin
markrouse14-Jan-05 1:03
membermarkrouse14-Jan-05 1:03 
GeneralRe: A very simple but even more efficient algorithm Pin
Astorni17-Jun-07 22:39
memberAstorni17-Jun-07 22:39 
GeneralRe: An invitation to SimpleChart? Pin
G A McHale13-Jan-05 17:13
memberG A McHale13-Jan-05 17:13 
GeneralRe: An invitation to SimpleChart? Pin
G A McHale14-Jan-05 14:41
memberG A McHale14-Jan-05 14:41 
GeneralRe: An invitation to SimpleChart? Pin
Mark Focas13-Jan-05 17:52
memberMark Focas13-Jan-05 17:52 
GeneralRe: An invitation to SimpleChart? Pin
surgeproof18-Jan-05 6:09
membersurgeproof18-Jan-05 6:09 

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
Web04 | 2.8.150819.1 | Last Updated 8 Jul 2005
Article Copyright 2005 by markrouse
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid