Click here to Skip to main content
15,867,594 members
Articles / Multimedia / GDI+
Article

Comparing Images using GDI+

Rate me:
Please Sign up or sign in to vote.
4.73/5 (33 votes)
7 Jul 20053 min read 344.9K   6.4K   104   61
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.

C#
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:

C#
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


Written By
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

 
Questioncompare with hash or byte data Pin
chen1595918-Dec-15 3:52
chen1595918-Dec-15 3:52 
AnswerRe: compare with hash or byte data Pin
chen1595918-Dec-15 6:07
chen1595918-Dec-15 6:07 
Questionremoving similarities from given images Pin
mughalshahzad@gmail.com10-Mar-14 18:03
mughalshahzad@gmail.com10-Mar-14 18:03 
QuestionWant giudence Pin
Prathap Kp24-Jan-14 16:48
Prathap Kp24-Jan-14 16:48 
SuggestionSuggestion Pin
harveyt28-Nov-12 5:39
harveyt28-Nov-12 5:39 
Questionpls anyone help me.... Pin
kalpesh badjate31-Jan-12 18:35
kalpesh badjate31-Jan-12 18:35 
GeneralMy vote of 5 Pin
H.C.V.A14-Oct-11 22:04
H.C.V.A14-Oct-11 22:04 
Questioncomparing images Pin
sachinthapa7-Sep-11 21:06
sachinthapa7-Sep-11 21:06 
GeneralExllent Pin
TejuKrishna10-Jan-11 1:40
TejuKrishna10-Jan-11 1:40 
GeneralMy vote of 5 Pin
TejuKrishna10-Jan-11 1:37
TejuKrishna10-Jan-11 1:37 
QuestionWhy use hash code? PinPopular
Joe Schulte14-Jun-10 18:22
Joe Schulte14-Jun-10 18:22 
AnswerRe: Why use hash code? Pin
Mike Nakis14-Jun-11 18:46
Mike Nakis14-Jun-11 18:46 
Generalurgently please Pin
metyouba18-Mar-10 1:08
metyouba18-Mar-10 1:08 
GeneralA generic error occurred in GDI+. Pin
suleh11-Sep-09 2:30
suleh11-Sep-09 2:30 
GeneralRe: A generic error occurred in GDI+. Pin
jra7325-Jul-10 14:05
jra7325-Jul-10 14:05 
GeneralUnsafe hashing Pin
marr4t4-Apr-09 8:36
marr4t4-Apr-09 8:36 
GeneralRe: Unsafe hashing Pin
harveyt1-Dec-12 3:48
harveyt1-Dec-12 3:48 
Generalis more faster like that, check it Pin
Shargon_858-Feb-09 10:54
Shargon_858-Feb-09 10:54 
GeneralComparing one image to many others speeded up Pin
Jan W.6-Feb-09 1:17
Jan W.6-Feb-09 1:17 
QuestionNot working for me Pin
pravin dingore31-Dec-08 1:11
pravin dingore31-Dec-08 1:11 
Questionerror :( ??? Pin
James_Zhang22-Nov-08 17:47
James_Zhang22-Nov-08 17:47 
AnswerRe: error :( ??? Pin
James_Zhang22-Nov-08 18:07
James_Zhang22-Nov-08 18:07 
GeneralObject leak Pin
thelawnet3-Nov-08 12:03
thelawnet3-Nov-08 12:03 
QuestionHow to compare images for similarity Pin
lexstyles14-Apr-08 9:48
lexstyles14-Apr-08 9:48 
AnswerRe: How to compare images for similarity Pin
markrouse20-Apr-08 22:53
markrouse20-Apr-08 22:53 

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

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