Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   Show Off Your Open Source Projects (http://www.programmingforums.org/forum52.html)
-   -   Bitmap Comparison (RGB Averages and Histogram intersection distance) (http://www.programmingforums.org/showthread.php?t=12590)

Wizard1988 Feb 16th, 2007 9:08 PM

Bitmap Comparison (RGB Averages and Histogram intersection distance)
 
1 Attachment(s)
First of all I would like to thank both Arevos and DaWei for helping out with both understanding the algorithims and finding some of the bugs.:)

Here is the code, feel free to use it and/or modify it as long as you give credit where it belongs.

:

  1. using System;
  2. using System.IO;
  3. using System.Drawing;
  4. using System.Drawing.Imaging;
  5.  
  6. namespace BitmapSimilarity
  7. {
  8.     public interface IBitmapCompare
  9.     {
  10.         double GetSimilarity(Bitmap a, Bitmap b);
  11.     }
  12.  
  13.     class AveragingBitmapCompare : IBitmapCompare
  14.     {
  15.         public struct RGBdata
  16.         {
  17.             public int r;
  18.             public int g;
  19.             public int b;
  20.  
  21.             public int GetLargest()
  22.             {
  23.                 if(r>b)
  24.                 {
  25.                     if(r>g)
  26.                     {
  27.                         return 1;
  28.                     }
  29.                     else
  30.                     {
  31.                         return 2;
  32.                     }
  33.                 }
  34.                 else
  35.                 {
  36.                     return 3;
  37.                 }
  38.             }
  39.         }
  40.  
  41.         private RGBdata ProcessBitmap(Bitmap a)
  42.         {
  43.             BitmapData bmpData = a.LockBits(new Rectangle(0,0,a.Width,a.Height),ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb);
  44.             IntPtr ptr = bmpData.Scan0;
  45.             RGBdata data = new RGBdata();
  46.  
  47.             unsafe
  48.             {
  49.                 byte* p = (byte*)(void*)ptr;
  50.                 int offset = bmpData.Stride - a.Width * 3;
  51.                 int width = a.Width * 3;
  52.  
  53.                 for (int y = 0; y < a.Height; ++y)
  54.                 {
  55.                     for (int x = 0; x < width; ++x)
  56.                     {
  57.                         data.r += p[0];            //gets red values
  58.                         data.g += p[1];            //gets green values
  59.                         data.b += p[2];            //gets blue values
  60.                         ++p;
  61.                     }
  62.                     p += offset;
  63.                 }
  64.             }
  65.             a.UnlockBits(bmpData);
  66.             return data;
  67.         }
  68.  
  69.         public double GetSimilarity(Bitmap a, Bitmap b)
  70.         {
  71.             RGBdata dataA = ProcessBitmap(a);
  72.             RGBdata dataB = ProcessBitmap(b);
  73.             double result = 0;
  74.             int averageA = 0;
  75.             int averageB = 0;
  76.             int maxA = 0;
  77.             int maxB = 0;
  78.  
  79.             maxA = ((a.Width * 3) * a.Height);
  80.             maxB = ((b.Width * 3) * b.Height);
  81.  
  82.             switch (dataA.GetLargest())            //Find dominant color to compare
  83.             {
  84.                 case 1:
  85.                     {
  86.                         averageA = Math.Abs(dataA.r / maxA);
  87.                         averageB = Math.Abs(dataB.r / maxB);
  88.                         result = (averageA - averageB) / 2;
  89.                         break;
  90.                     }
  91.                 case 2:
  92.                     {
  93.                         averageA = Math.Abs(dataA.g / maxA);
  94.                         averageB = Math.Abs(dataB.g / maxB);
  95.                         result = (averageA - averageB) / 2;
  96.                         break;
  97.                     }
  98.                 case 3:
  99.                     {
  100.                         averageA = Math.Abs(dataA.b / maxA);
  101.                         averageB = Math.Abs(dataB.b / maxB);
  102.                         result = (averageA - averageB) / 2;
  103.                         break;
  104.                     }
  105.             }
  106.  
  107.             result = Math.Abs((result + 100) / 100);
  108.  
  109.             if (result > 1.0)
  110.             {
  111.                 result -= 1.0;
  112.             }
  113.  
  114.             return result;
  115.         }
  116.     }
  117.  
  118.     class HistogramBitmapCompare : IBitmapCompare
  119.     {
  120.         public struct RGBHistogram
  121.         {
  122.             public RGBHistogram(int s)
  123.             {
  124.                 size = s;
  125.                 data = new byte[size, size, size];
  126.                 Clear();
  127.             }
  128.  
  129.             public void Clear()
  130.             {
  131.                 for (int x = 0; x < size; x++)
  132.                 {
  133.                     for (int y = 0; y < size; y++)
  134.                     {
  135.                         for (int z = 0; z < size; z++)
  136.                         {
  137.                             data[x, y, z] = 0;
  138.                         }
  139.                     }
  140.                 }
  141.             }
  142.  
  143.             public int GetSize()
  144.             {
  145.                 return size;
  146.             }
  147.  
  148.             public int Sum()
  149.             {
  150.                 int sum = 0;
  151.                 for (int x = 0; x < size; x++)
  152.                 {
  153.                     for (int y = 0; y < size; y++)
  154.                     {
  155.                         for (int z = 0; z < size; z++)
  156.                         {
  157.                             sum += data[x, y, z];
  158.                         }
  159.                     }
  160.                 }
  161.                 return sum;
  162.             }
  163.             public byte[, ,] data;
  164.             int size;
  165.         }
  166.  
  167.         private int Minimum(int a, int b)
  168.         {
  169.             if (a > b)
  170.             {
  171.                 return b;
  172.             }
  173.             return a;
  174.         }
  175.  
  176.         private RGBHistogram ProcessBitmap(Bitmap img)
  177.         {
  178.             BitmapData bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
  179.             IntPtr ptr = bmpData.Scan0;
  180.             RGBHistogram histogram = new RGBHistogram(4);
  181.             int[] color = new int[3];
  182.             int lBound, uBound;
  183.             int a = 0;
  184.             int b = 0;
  185.             int c = 0;
  186.  
  187.  
  188.             unsafe
  189.             {
  190.                 byte* p = (byte*)(void*)ptr;
  191.                 int offset = bmpData.Stride - img.Width * 3;
  192.                 int width = img.Width * 3;
  193.  
  194.                 for (int y = 0; y < img.Height; ++y)
  195.                 {
  196.                     for (int x = 0; x < width; ++x)
  197.                     {
  198.                         color[0] = p[0];
  199.                         color[1] = p[1];
  200.                         color[2] = p[2];
  201.  
  202.                         for (int i = 0; i < histogram.GetSize(); i++)
  203.                         {
  204.                             lBound = (i * 64);
  205.                             uBound = (64 * i) + 63;
  206.  
  207.                             if ((lBound <= color[0]) && (uBound >= color[0]))
  208.                             {
  209.                                 a = i;
  210.                             }
  211.                             if ((lBound <= color[1]) && (uBound >= color[1]))
  212.                             {
  213.                                 b = i;
  214.                             }
  215.                             if ((lBound <= color[2]) && (uBound >= color[2]))
  216.                             {
  217.                                 c = i;
  218.                             }
  219.                             histogram.data[a, b, c]++;
  220.                         }
  221.                         ++p;
  222.                     }
  223.                     p += offset;
  224.                 }
  225.             }
  226.             img.UnlockBits(bmpData);
  227.             return histogram;
  228.         }
  229.  
  230.         public double GetSimilarity(Bitmap a, Bitmap b)
  231.         {
  232.             RGBHistogram histogramA = ProcessBitmap(a);
  233.             RGBHistogram histogramB = ProcessBitmap(b);
  234.             double minSum = 0;
  235.             double distance = 0;
  236.  
  237.             for (int x = 0; x < histogramA.GetSize(); x++)
  238.             {
  239.                 for (int y = 0; y < histogramA.GetSize(); y++)
  240.                 {
  241.                     for (int z = 0; z < histogramA.GetSize(); z++)
  242.                     {
  243.                         minSum += Minimum(histogramA.data[x, y, z], histogramB.data[x, y, z]);
  244.                     }
  245.                 }
  246.             }
  247.             distance = minSum / Minimum(histogramA.Sum(), histogramB.Sum());
  248.             return distance;
  249.         }
  250.     }
  251.  
  252.     class Program
  253.     {
  254.         static IBitmapCompare CompareBox;
  255.         static Bitmap searchImage;
  256.  
  257.         static private void Line()
  258.         {
  259.             for (int x = 0; x < Console.BufferWidth; x++)
  260.             {
  261.                 Console.Write("*");
  262.             }
  263.         }
  264.  
  265.         static void CheckDirectory(string directory,double percentage,int method,Bitmap sImage)
  266.         {
  267.             DirectoryInfo dir = new DirectoryInfo(directory);
  268.             FileInfo[] files = null;
  269.             double sim = 0;
  270.  
  271.             try
  272.             {
  273.                 files = dir.GetFiles("*.jpg");
  274.             }
  275.             catch (DirectoryNotFoundException)
  276.             {
  277.                 Console.WriteLine("Bad directory specified");
  278.                 return;
  279.             }
  280.  
  281.             foreach (FileInfo f in files)
  282.             {
  283.                 if (method == 1)
  284.                 {
  285.                     CompareBox = new AveragingBitmapCompare();
  286.                 }
  287.                 if (method == 2)
  288.                 {
  289.                     CompareBox = new HistogramBitmapCompare();
  290.                 }
  291.                 sim = CompareBox.GetSimilarity(sImage, new Bitmap(f.FullName));
  292.  
  293.                 if (sim >= percentage)
  294.                 {
  295.                     Console.WriteLine(f.Name);
  296.                     Console.WriteLine("Histogram Intersection");
  297.                     Console.WriteLine("Match of: {0}", sim);
  298.                     Line();
  299.                 }
  300.             }
  301.         }
  302.  
  303.         static void Main(string[] args)
  304.         {
  305.             int method = 0;
  306.             double percentage = 0;
  307.             Console.Write("Enter path to search image: ");
  308.             try
  309.             {
  310.                 searchImage = new Bitmap(Console.ReadLine());
  311.             }
  312.             catch (ArgumentException)
  313.             {
  314.                 Console.WriteLine("Error: bad input!");
  315.                 return;
  316.             }
  317.  
  318.             Console.Write("Enter directory to scan: ");
  319.             string dir = Console.ReadLine();
  320.             Console.Write("\nMethod of comparison:\n1 - Color percentages\n2 - Histogram intersection distance\nChoice: ");
  321.             method = int.Parse(Console.ReadLine());
  322.             if ((method != 1) && (method != 2))
  323.             {
  324.                 Console.WriteLine("Error: bad input!");
  325.                 return;
  326.             }
  327.             Console.Write("Minimum percentage of similarity: ");
  328.             percentage = double.Parse(Console.ReadLine())/100;
  329.  
  330.             Line();
  331.             CheckDirectory(dir,percentage,method, searchImage);
  332.         }
  333.     }
  334. }


If you find any bugs or ways to improve it please let me know.
Thanks.

Duck Feb 27th, 2007 5:19 AM

I'm not quite sure why I have to state a file and a directory (maybe just me being stupid)? I think you should add a more detailed description on how to use it.

Arevos Feb 27th, 2007 5:49 AM

Presumably the file is the image you want to find a match for, and the directory is the directory full of images you're searching through.


All times are GMT -5. The time now is 1:43 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC