Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Feb 16th, 2007, 9:08 PM   #1
Wizard1988
Professional Programmer
 
Wizard1988's Avatar
 
Join Date: Oct 2005
Location: Chitown
Posts: 422
Rep Power: 4 Wizard1988 is on a distinguished road
Thumbs up Bitmap Comparison (RGB Averages and Histogram intersection distance)

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.

csharp Syntax (Toggle Plain Text)
  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.
Attached Files
File Type: zip BitmapSimilarity.zip (4.0 KB, 3 views)
__________________

Wizard1988 is offline   Reply With Quote
Old Feb 27th, 2007, 5:19 AM   #2
Duck
Programmer
 
Join Date: Jun 2006
Location: England London
Posts: 72
Rep Power: 3 Duck is on a distinguished road
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.
Duck is offline   Reply With Quote
Old Feb 27th, 2007, 5:49 AM   #3
Arevos
Programming Guru
 
Arevos's Avatar
 
Join Date: Aug 2005
Location: England
Posts: 1,499
Rep Power: 5 Arevos is on a distinguished road
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.
Arevos is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 9:40 PM.

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