-1

I have one Bitmap A and one array of Bitmap, in the array there is a Bitmap that looks the same as Bitmap A. I'm using the code below but it sometimes doesnt work, it iterates the entire array without finding it, it seems there are some minor differences, is there a way to change the function to return true if its 90% similar or pick the most similar image in the array? The array has only 6 images.

 for(int i = 0; i < list.Count;i++)
    {
        if(ImageCompareString(image,list[i])
        {
            answerIndex = i;
            break;
        }
    }

private static bool ImageCompareString(Bitmap firstImage, Bitmap secondImage)
{
    MemoryStream ms = new MemoryStream();
    firstImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
    String firstBitmap = Convert.ToBase64String(ms.ToArray());
    ms.Position = 0;

    secondImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
    String secondBitmap = Convert.ToBase64String(ms.ToArray());

    if (firstBitmap.Equals(secondBitmap))
    {
        return true;
    }
    else
    {
        return false;
    }
}
Carlos Siestrup
  • 1,031
  • 2
  • 13
  • 33

1 Answers1

0

Of course there is such way... But you have to code it yourself.

First you shoud not compare the base64 data... You'll loose direct pixel value access and increase the size of the data to compare by more then 150% (Originaly 200% but corrected thanks to PeterDuniho's comment) in C# due to UTF16.
Second I assume that all pictures have the same fixed size. Before comparing, reduce the image size to something really small, but keep the width/height aspect. This will speed up the comparsion and also eliminates noise.
Third Iterate both pictures and compare their grayscaled pixel values. I Assume that you have resized the picture to 16x16. Since we're comparing their grayscale-values the value of one pixel is between 0 and 255. So the maximum distance between both pictures will be 16 * 16 * 256 = 65536. If both pictures are black, the distance between the pictures will be zero (100% similarity). If one picture is black and the other is white the distance will be 65535 (0% similarity).

To compare the images iterate the picture-pixels and subtract the grayscale-pixel-value-from-picture-a from the grayscale-pixel-value-of-picture-b at the point x,y and add the absolute difference value to the counter. This counter will be the total distance between both pictures. Lets assume this counter has a value of 1000 after the comparison loop, you get the percentage-similarity by 1000 / 65535 ~ 1.5% difference (or 98.5% similarity) between both pictures.

pseudo-compare-code

long counter = 0;
long total = image.Width * image.Height * (Color.White - Color.Black);
for(int x = 0; x < image.Width; x++)
{
    for(int y = 0; y < image.Height; y++)
    {
        var p1 = image.GetPixel(x, y);
        var p2 = otherImage.GetPixel(x, y);
        var g1 = ((p1.R + p1.G + p1.B) / 3);
        var g2 = ((p2.R + p2.G + p2.B) / 3);
        var distance = Math.Abs(g1 - g2);
        counter += distance;
    }
}
var similarity = 100 - ((counter / total) * 100);

This is an more or less easy approach, but you have to test this with you scenario/images. Instead of comparing grayscale-values you could also compare rgb-values. Look for distance definitions like the euclidean distance... Start and keep reading :)

EDIT
This is just a really basic approach that should explain how you can start comparing images. It does not take into account that there might be different image formats (jpeg, png, gif), color formats (indexed, 16bit, 24bit, 32bit) or images with different resolutions.

Michael
  • 1,931
  • 2
  • 8
  • 22
  • 1
    Converting to base64 doesn't "loose [sic]" pixel values. They just get represented differently. It's the conversion to PNG first that will prevent effective comparisons. And the increase in data size for base64 is "only" 33%. Even accounting for the fact that six bits in binary gets converted to sixteen (because C# strings are UTF16), that's "only" a 167% increase in size, not "more then [sic] 200%". And while reducing image size can improve speed, it really will lose pixel information, as will converting to greyscale. Consider e.g. comparing a solid-color image with other solid-color images. – Peter Duniho Nov 09 '17 at 03:08
  • @PeterDuniho You're right, the pixel values are represented differently - but this makes it more difficult to access the pixel value at point x,y and I (personally) see only disadvantages in the base64 converesion. The lost pixel information by down- and greyscaling is correct (and might be wanted) and 16x16 is surely to small for most cases, but makes it easier to explain the steps on how to start comparing images and getting first results. With this first results he can start adjusting parameters like size or rgb/grayscale or used distances... – Michael Nov 09 '17 at 08:11