namespace VisualMath.Accord.Imaging
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using VisualMath.Accord.Math;
using AForge;
using AForge.Imaging.Filters;
///
/// Maximum cross-correlation feature point matching algorithm.
///
///
///
/// This class matches feature points by using a maximum cross-correlation measure.
///
/// References:
///
/// -
/// P. D. Kovesi. MATLAB and Octave Functions for Computer Vision and Image Processing.
/// School of Computer Science and Software Engineering, The University of Western Australia.
/// Available in:
/// http://www.csse.uwa.edu.au/~pk/Research/MatlabFns/Match/matchbycorrelation.m
///
/// -
///
/// http://www.instructor.com.br/unesp2006/premiados/PauloHenrique.pdf
///
/// -
///
/// http://siddhantahuja.wordpress.com/2010/04/11/correlation-based-similarity-measures-summary/
///
///
///
///
///
///
public class CorrelationMatching
{
private int windowSize;
private double dmax;
///
/// Gets or sets the maximum distance to consider
/// points as correlated.
///
public double DistanceMax
{
get { return dmax; }
set { dmax = value; }
}
///
/// Gets or sets the size of the correlation window.
///
public int WindowSize
{
get { return windowSize; }
set { windowSize = value; }
}
///
/// Constructs a new Correlation Matching algorithm.
///
public CorrelationMatching(int windowSize)
: this(windowSize, 0)
{
}
///
/// Constructs a new Correlation Matching algorithm.
///
public CorrelationMatching(int windowSize, double maxDistance)
{
if (windowSize % 2 == 0)
throw new ArgumentException("Window size should be odd", "windowSize");
this.windowSize = windowSize;
this.dmax = maxDistance;
}
///
/// Matches two sets of feature points computed from the given images.
///
public IntPoint[][] Match(Bitmap image1, Bitmap image2,
IntPoint[] points1, IntPoint[] points2)
{
// Make sure we are dealing with grayscale images.
Bitmap grayImage1, grayImage2;
if (image1.PixelFormat == PixelFormat.Format8bppIndexed)
{
grayImage1 = image1;
}
else
{
// create temporary grayscale image
grayImage1 = Grayscale.CommonAlgorithms.BT709.Apply(image1);
}
if (image2.PixelFormat == PixelFormat.Format8bppIndexed)
{
grayImage2 = image2;
}
else
{
// create temporary grayscale image
grayImage2 = Grayscale.CommonAlgorithms.BT709.Apply(image2);
}
// Generate correlation matrix
double[,] correlationMatrix =
computeCorrelationMatrix(grayImage1, points1, grayImage2, points2, windowSize, dmax);
// Free allocated resources
if (image1.PixelFormat != PixelFormat.Format8bppIndexed)
grayImage1.Dispose();
if (image2.PixelFormat != PixelFormat.Format8bppIndexed)
grayImage2.Dispose();
// Select points with maximum correlation measures
int[] colp2forp1; Matrix.Max(correlationMatrix, 1, out colp2forp1);
int[] rowp1forp2; Matrix.Max(correlationMatrix, 0, out rowp1forp2);
// Construct the lists of matched point indices
int rows = correlationMatrix.GetLength(0);
List p1ind = new List();
List p2ind = new List();
// For each point in the first set of points,
for (int i = 0; i < rows; i++)
{
// Get the point j in the second set of points with which
// this point i has a maximum correlation measure. (i->j)
int j = colp2forp1[i];
// Now, check if this point j in the second set also has
// a maximum correlation measure with the point i. (j->i)
if (rowp1forp2[j] == i)
{
// The points are consistent. Ensure they are valid.
if (correlationMatrix[i, j] != Double.NegativeInfinity)
{
// We have a corresponding pair (i,j)
p1ind.Add(i); p2ind.Add(j);
}
}
}
// Extract matched points from original arrays
var m1 = points1.Submatrix(p1ind.ToArray());
var m2 = points2.Submatrix(p2ind.ToArray());
// Create matching point pairs
return new IntPoint[][] { m1, m2 };
}
///
/// Constructs the correlation matrix between selected points from two images.
///
///
/// Rows correspond to points from the first image, columns correspond to points
/// in the second.
///
private static double[,] computeCorrelationMatrix(
Bitmap image1, IntPoint[] points1,
Bitmap image2, IntPoint[] points2,
int windowSize, double maxDistance)
{
// Create the initial correlation matrix
double[,] matrix = Matrix.Create(points1.Length, points2.Length, Double.NegativeInfinity);
// Gather some information
int width1 = image1.Width;
int width2 = image2.Width;
int height1 = image1.Height;
int height2 = image2.Height;
int r = (windowSize - 1) / 2; // 'radius' of correlation window
double m = maxDistance * maxDistance; // maximum considered distance
double[,] w1 = new double[windowSize, windowSize]; // first window
double[,] w2 = new double[windowSize, windowSize]; // second window
// Lock the images
BitmapData bitmapData1 = image1.LockBits(new Rectangle(0, 0, width1, height1),
ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
BitmapData bitmapData2 = image2.LockBits(new Rectangle(0, 0, width2, height2),
ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
int stride1 = bitmapData1.Stride;
int stride2 = bitmapData2.Stride;
// We will ignore points at the edge
int[] idx1 = Matrix.Find(points1,
p => p.X >= r && p.X < width1 - r &&
p.Y >= r && p.Y < height1 - r);
int[] idx2 = Matrix.Find(points2,
p => p.X >= r && p.X < width2 - r &&
p.Y >= r && p.Y < height2 - r);
// For each index in the first set of points
foreach (int n1 in idx1)
{
// Get the current point
var p1 = points1[n1];
unsafe // Create the first window for the current point
{
byte* src = (byte*)bitmapData1.Scan0 + (p1.X - r) + (p1.Y - r) * stride1;
for (int j = 0; j < windowSize; j++)
{
for (int i = 0; i < windowSize; i++)
w1[i, j] = (byte)(*(src + i));
src += stride1;
}
}
// Normalize the window
double sum = 0;
for (int i = 0; i < windowSize; i++)
for (int j = 0; j < windowSize; j++)
sum += w1[i, j] * w1[i, j];
sum = System.Math.Sqrt(sum);
for (int i = 0; i < windowSize; i++)
for (int j = 0; j < windowSize; j++)
w1[i, j] /= sum;
// Identify the indices of points in p2 that we need to consider.
int[] candidates;
if (maxDistance == 0)
{
// We should consider all points
candidates = idx2;
}
else
{
// We should consider points that are within
// distance maxDistance apart
// Compute distances from the current point
// to all points in the second image.
double[] distances = new double[idx2.Length];
for (int i = 0; i < idx2.Length; i++)
{
double dx = p1.X - points2[idx2[i]].X;
double dy = p1.Y - points2[idx2[i]].Y;
distances[i] = dx * dx + dy * dy;
}
candidates = idx2.Submatrix(Matrix.Find(distances, d => d < m));
}
// Calculate normalized correlation measure
foreach (int n2 in candidates)
{
var p2 = points2[n2];
unsafe // Generate window in 2nd image
{
byte* src = (byte*)bitmapData2.Scan0 + (p2.X - r) + (p2.Y - r) * stride2;
for (int j = 0; j < windowSize; j++)
{
for (int i = 0; i < windowSize; i++)
w2[i, j] = (byte)(*(src + i));
src += stride2;
}
}
double sum1 = 0, sum2 = 0;
for (int i = 0; i < windowSize; i++)
{
for (int j = 0; j < windowSize; j++)
{
sum1 += w1[i, j] * w2[i, j];
sum2 += w2[i, j] * w2[i, j];
}
}
matrix[n1, n2] = sum1 / System.Math.Sqrt(sum2);
}
}
// Release the images
image1.UnlockBits(bitmapData1);
image2.UnlockBits(bitmapData2);
return matrix;
}
}
}