There are a few possible test cases for how "different" images can be, and the approach that you need to take to "match" them become progressively harder.
- The simple case is where you expect the images to be the same size and contain the same features, except for pixels being slightly lighter or darker as you state. For example, if you were to save a bitmap image once as 24-bit, then again as 8-bit (without dithering). The two images would have the same features in the same place, but slightly different colours for each pixel.
- Another possibility is where you take a bitmap image, and crop 1 line of pixels of the left to create the first image, and crop a line off the right for the second image.
- A third possibility is where you save a bitmap image, then double the image size in both directions to create the second (so one pixel in the source becomes four pixels in the output). I accept that you have size checking code to detect this case already.
- A fourth possibility (more of a test case) is to have a large image of just white, and a large image of just black. We expect this to return
ciPixelMismatch
, but without throwing an exception.
If we consider only the first case, then what you really want is a function to compare two Color
s and return a value for how different they are. A simple way to compare two colours is to calculate the Pythagorean distance between the Red, Green and Blue components, such as
static int CompareColours(Color x, Color y)
{
return (int)(Math.Pow((int)x.R - y.R, 2) + Math.Pow((int)x.B - y.B, 2) + Math.Pow((int)x.G - y.G, 2));
}
this will return an number between 0 (when the Color
s are identical) and 198608 (between Black and White, which is Math.Pow(256, 2) * 3
).
With this, you can apply the function to each pair of pixels (one from each image) and accumulate the error. Average this error across the number of pixels to determine the average pixel error across the whole image, then compare this to a threshold to determine if they are "the same":
const decimal errorThreshold = 0.0001D
decimal totalError = 0;
for (int x = 0; x < bmp1.Width; x++)
{
for (int y = 0; y < bmp1.Height; y++)
{
totalError += CompareColours(bmp1.GetPixel(x, y), bmp2.GetPixel(x, y)) / 198608D;
}
}
decimal averageError = totalError / (bmp1.Width * bmp1.Height);
if ( averageError > errorThreshold ) cr = CompareResult.ciPixelMismatch;
(I divide by 198608D
to avoid the possibility of an Integer Overflow while adding. averageError
is then a value between 0D
for identical and 1D
for completely different.)
I'd also recommend you look at some of the other questions here on StackOverflow. While this pixel-colour-matching works for the simplest case, it won't work for the others. The approaches given in answers to other questions will be useful if you need something more complex:
Hope this helps