CorrelationMatching.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. namespace VisualMath.Accord.Imaging
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Drawing;
  6. using System.Drawing.Imaging;
  7. using VisualMath.Accord.Math;
  8. using AForge;
  9. using AForge.Imaging.Filters;
  10. /// <summary>
  11. /// Maximum cross-correlation feature point matching algorithm.
  12. /// </summary>
  13. /// <remarks>
  14. /// <para>
  15. /// This class matches feature points by using a maximum cross-correlation measure.</para>
  16. /// <para>
  17. /// References:
  18. /// <list type="bullet">
  19. /// <item><description>
  20. /// P. D. Kovesi. MATLAB and Octave Functions for Computer Vision and Image Processing.
  21. /// School of Computer Science and Software Engineering, The University of Western Australia.
  22. /// Available in: <a href="http://www.csse.uwa.edu.au/~pk/Research/MatlabFns/Match/matchbycorrelation.m">
  23. /// http://www.csse.uwa.edu.au/~pk/Research/MatlabFns/Match/matchbycorrelation.m</a>
  24. /// </description></item>
  25. /// <item><description>
  26. /// <a href="http://www.instructor.com.br/unesp2006/premiados/PauloHenrique.pdf">
  27. /// http://www.instructor.com.br/unesp2006/premiados/PauloHenrique.pdf</a>
  28. /// </description></item>
  29. /// <item><description>
  30. /// <a href="http://siddhantahuja.wordpress.com/2010/04/11/correlation-based-similarity-measures-summary/">
  31. /// http://siddhantahuja.wordpress.com/2010/04/11/correlation-based-similarity-measures-summary/</a>
  32. /// </description></item>
  33. /// </list></para>
  34. /// </remarks>
  35. ///
  36. /// <seealso cref="RansacHomographyEstimator"/>
  37. ///
  38. public class CorrelationMatching
  39. {
  40. private int windowSize;
  41. private double dmax;
  42. /// <summary>
  43. /// Gets or sets the maximum distance to consider
  44. /// points as correlated.
  45. /// </summary>
  46. public double DistanceMax
  47. {
  48. get { return dmax; }
  49. set { dmax = value; }
  50. }
  51. /// <summary>
  52. /// Gets or sets the size of the correlation window.
  53. /// </summary>
  54. public int WindowSize
  55. {
  56. get { return windowSize; }
  57. set { windowSize = value; }
  58. }
  59. /// <summary>
  60. /// Constructs a new Correlation Matching algorithm.
  61. /// </summary>
  62. public CorrelationMatching(int windowSize)
  63. : this(windowSize, 0)
  64. {
  65. }
  66. /// <summary>
  67. /// Constructs a new Correlation Matching algorithm.
  68. /// </summary>
  69. public CorrelationMatching(int windowSize, double maxDistance)
  70. {
  71. if (windowSize % 2 == 0)
  72. throw new ArgumentException("Window size should be odd", "windowSize");
  73. this.windowSize = windowSize;
  74. this.dmax = maxDistance;
  75. }
  76. /// <summary>
  77. /// Matches two sets of feature points computed from the given images.
  78. /// </summary>
  79. public IntPoint[][] Match(Bitmap image1, Bitmap image2,
  80. IntPoint[] points1, IntPoint[] points2)
  81. {
  82. // Make sure we are dealing with grayscale images.
  83. Bitmap grayImage1, grayImage2;
  84. if (image1.PixelFormat == PixelFormat.Format8bppIndexed)
  85. {
  86. grayImage1 = image1;
  87. }
  88. else
  89. {
  90. // create temporary grayscale image
  91. grayImage1 = Grayscale.CommonAlgorithms.BT709.Apply(image1);
  92. }
  93. if (image2.PixelFormat == PixelFormat.Format8bppIndexed)
  94. {
  95. grayImage2 = image2;
  96. }
  97. else
  98. {
  99. // create temporary grayscale image
  100. grayImage2 = Grayscale.CommonAlgorithms.BT709.Apply(image2);
  101. }
  102. // Generate correlation matrix
  103. double[,] correlationMatrix =
  104. computeCorrelationMatrix(grayImage1, points1, grayImage2, points2, windowSize, dmax);
  105. // Free allocated resources
  106. if (image1.PixelFormat != PixelFormat.Format8bppIndexed)
  107. grayImage1.Dispose();
  108. if (image2.PixelFormat != PixelFormat.Format8bppIndexed)
  109. grayImage2.Dispose();
  110. // Select points with maximum correlation measures
  111. int[] colp2forp1; Matrix.Max(correlationMatrix, 1, out colp2forp1);
  112. int[] rowp1forp2; Matrix.Max(correlationMatrix, 0, out rowp1forp2);
  113. // Construct the lists of matched point indices
  114. int rows = correlationMatrix.GetLength(0);
  115. List<int> p1ind = new List<int>();
  116. List<int> p2ind = new List<int>();
  117. // For each point in the first set of points,
  118. for (int i = 0; i < rows; i++)
  119. {
  120. // Get the point j in the second set of points with which
  121. // this point i has a maximum correlation measure. (i->j)
  122. int j = colp2forp1[i];
  123. // Now, check if this point j in the second set also has
  124. // a maximum correlation measure with the point i. (j->i)
  125. if (rowp1forp2[j] == i)
  126. {
  127. // The points are consistent. Ensure they are valid.
  128. if (correlationMatrix[i, j] != Double.NegativeInfinity)
  129. {
  130. // We have a corresponding pair (i,j)
  131. p1ind.Add(i); p2ind.Add(j);
  132. }
  133. }
  134. }
  135. // Extract matched points from original arrays
  136. var m1 = points1.Submatrix(p1ind.ToArray());
  137. var m2 = points2.Submatrix(p2ind.ToArray());
  138. // Create matching point pairs
  139. return new IntPoint[][] { m1, m2 };
  140. }
  141. /// <summary>
  142. /// Constructs the correlation matrix between selected points from two images.
  143. /// </summary>
  144. /// <remarks>
  145. /// Rows correspond to points from the first image, columns correspond to points
  146. /// in the second.
  147. /// </remarks>
  148. private static double[,] computeCorrelationMatrix(
  149. Bitmap image1, IntPoint[] points1,
  150. Bitmap image2, IntPoint[] points2,
  151. int windowSize, double maxDistance)
  152. {
  153. // Create the initial correlation matrix
  154. double[,] matrix = Matrix.Create(points1.Length, points2.Length, Double.NegativeInfinity);
  155. // Gather some information
  156. int width1 = image1.Width;
  157. int width2 = image2.Width;
  158. int height1 = image1.Height;
  159. int height2 = image2.Height;
  160. int r = (windowSize - 1) / 2; // 'radius' of correlation window
  161. double m = maxDistance * maxDistance; // maximum considered distance
  162. double[,] w1 = new double[windowSize, windowSize]; // first window
  163. double[,] w2 = new double[windowSize, windowSize]; // second window
  164. // Lock the images
  165. BitmapData bitmapData1 = image1.LockBits(new Rectangle(0, 0, width1, height1),
  166. ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
  167. BitmapData bitmapData2 = image2.LockBits(new Rectangle(0, 0, width2, height2),
  168. ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
  169. int stride1 = bitmapData1.Stride;
  170. int stride2 = bitmapData2.Stride;
  171. // We will ignore points at the edge
  172. int[] idx1 = Matrix.Find(points1,
  173. p => p.X >= r && p.X < width1 - r &&
  174. p.Y >= r && p.Y < height1 - r);
  175. int[] idx2 = Matrix.Find(points2,
  176. p => p.X >= r && p.X < width2 - r &&
  177. p.Y >= r && p.Y < height2 - r);
  178. // For each index in the first set of points
  179. foreach (int n1 in idx1)
  180. {
  181. // Get the current point
  182. var p1 = points1[n1];
  183. unsafe // Create the first window for the current point
  184. {
  185. byte* src = (byte*)bitmapData1.Scan0 + (p1.X - r) + (p1.Y - r) * stride1;
  186. for (int j = 0; j < windowSize; j++)
  187. {
  188. for (int i = 0; i < windowSize; i++)
  189. w1[i, j] = (byte)(*(src + i));
  190. src += stride1;
  191. }
  192. }
  193. // Normalize the window
  194. double sum = 0;
  195. for (int i = 0; i < windowSize; i++)
  196. for (int j = 0; j < windowSize; j++)
  197. sum += w1[i, j] * w1[i, j];
  198. sum = System.Math.Sqrt(sum);
  199. for (int i = 0; i < windowSize; i++)
  200. for (int j = 0; j < windowSize; j++)
  201. w1[i, j] /= sum;
  202. // Identify the indices of points in p2 that we need to consider.
  203. int[] candidates;
  204. if (maxDistance == 0)
  205. {
  206. // We should consider all points
  207. candidates = idx2;
  208. }
  209. else
  210. {
  211. // We should consider points that are within
  212. // distance maxDistance apart
  213. // Compute distances from the current point
  214. // to all points in the second image.
  215. double[] distances = new double[idx2.Length];
  216. for (int i = 0; i < idx2.Length; i++)
  217. {
  218. double dx = p1.X - points2[idx2[i]].X;
  219. double dy = p1.Y - points2[idx2[i]].Y;
  220. distances[i] = dx * dx + dy * dy;
  221. }
  222. candidates = idx2.Submatrix(Matrix.Find(distances, d => d < m));
  223. }
  224. // Calculate normalized correlation measure
  225. foreach (int n2 in candidates)
  226. {
  227. var p2 = points2[n2];
  228. unsafe // Generate window in 2nd image
  229. {
  230. byte* src = (byte*)bitmapData2.Scan0 + (p2.X - r) + (p2.Y - r) * stride2;
  231. for (int j = 0; j < windowSize; j++)
  232. {
  233. for (int i = 0; i < windowSize; i++)
  234. w2[i, j] = (byte)(*(src + i));
  235. src += stride2;
  236. }
  237. }
  238. double sum1 = 0, sum2 = 0;
  239. for (int i = 0; i < windowSize; i++)
  240. {
  241. for (int j = 0; j < windowSize; j++)
  242. {
  243. sum1 += w1[i, j] * w2[i, j];
  244. sum2 += w2[i, j] * w2[i, j];
  245. }
  246. }
  247. matrix[n1, n2] = sum1 / System.Math.Sqrt(sum2);
  248. }
  249. }
  250. // Release the images
  251. image1.UnlockBits(bitmapData1);
  252. image2.UnlockBits(bitmapData2);
  253. return matrix;
  254. }
  255. }
  256. }