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/
private void btnCorrelation_Click(object sender, EventArgs e) { if (harrisPoints1 == null) { MessageBox.Show("Please, click Harris button first! :-)"); return; } // Step 2: Match feature points using a correlation measure CorrelationMatching matcher = new CorrelationMatching(9); IntPoint[][] matches = matcher.Match(img1, img2, harrisPoints1, harrisPoints2); // Get the two sets of points correlationPoints1 = matches[0]; correlationPoints2 = matches[1]; // Concatenate the two images in a single image (just to show on screen) Concatenate concat = new Concatenate(img1); Bitmap img3 = concat.Apply(img2); // Show the marked correlations in the concatenated image PairsMarker pairs = new PairsMarker( correlationPoints1, // Add image1's width to the X points to show the markings correctly correlationPoints2.Apply(p => new IntPoint(p.X + img1.Width, p.Y))); pictureBox.Image = pairs.Apply(img3); }
/// <summary> /// Maximum cross-correlation feature point matching algorithm. /// </summary> /// <param name="image1">First image.</param> /// <param name="image2">Second image.</param> /// <param name="points1">Points from the first image.</param> /// <param name="points2">Points from the second image.</param> /// <param name="windowSize">The size of the correlation window.</param> /// <param name="maxDistance">The maximum distance to consider points as correlated.</param> /// <returns>Matched point-pairs.</returns> public static Point[][] Match(Gray<byte>[,] image1, Gray<byte>[,] image2, Point[] points1, Point[] points2, int windowSize, int maxDistance) { Point[][] matches = null; using (var uImg1 = image1.Lock()) using(var uImg2 = image2.Lock()) { var correlationMatching = new CorrelationMatching(windowSize, maxDistance, uImg1.AsBitmap(), uImg2.AsBitmap()); matches = correlationMatching.Match(points1.ToPoints(), points2.ToPoints()).ToPoints(); } return matches; }
public void MatchTest() { int windowSize = 3; #pragma warning disable 0618 CorrelationMatching target = new CorrelationMatching(windowSize); #pragma warning restore 0618 Bitmap image1 = Properties.Resources.image1; Bitmap image2 = Properties.Resources.image1; IntPoint[] points1 = { new IntPoint( 3, 3), new IntPoint(14, 3), new IntPoint( 3, 14), new IntPoint(14, 14), }; IntPoint[] points2 = { new IntPoint( 3, 3), new IntPoint(14, 3), new IntPoint( 3, 14), new IntPoint(14, 14), }; IntPoint[][] expected = { new IntPoint[] { new IntPoint( 3, 3), new IntPoint(14, 3), new IntPoint( 3, 14), new IntPoint(14, 14) }, new IntPoint[] { new IntPoint( 3, 3), new IntPoint(14, 3), new IntPoint( 3, 14), new IntPoint(14, 14) }, }; #pragma warning disable 0618 IntPoint[][] actual = target.Match(image1, image2, points1, points2); #pragma warning restore 0618 Assert.IsTrue(actual.IsEqual(expected)); }
private void BtnCorrelation_OnClick(object sender, RoutedEventArgs e) { // Step 2: Match feature points using a correlation measure CorrelationMatching matcher = new CorrelationMatching(9); IntPoint[][] matches = matcher.Match(img1, img2, harrisPoints1, harrisPoints2); // Get the two sets of points correlationPoints1 = matches[0]; correlationPoints2 = matches[1]; // Concatenate the two images in a single image (just to show on screen) Concatenate concat = new Concatenate(img1); Bitmap img3 = concat.Apply(img2); // Show the marked correlations in the concatenated image PairsMarker pairs = new PairsMarker( correlationPoints1, // Add image1's width to the X points to show the markings correctly correlationPoints2.Apply(p => new IntPoint(p.X + img1.Width, p.Y))); PictureBox.Source = pairs.Apply(img3); }
public void MatchTest2() { for (int windowSize = 1; windowSize <= 15; windowSize += 2) { CorrelationMatching target = new CorrelationMatching(windowSize); Bitmap image1 = Properties.Resources.image1; Bitmap image2 = Properties.Resources.image1; Assert.AreEqual(16, image1.Height); Assert.AreEqual(16, image2.Height); Assert.AreEqual(16, image1.Width); Assert.AreEqual(16, image2.Width); // will test every possible point in the image // (and also some points outside just to make sure) List<IntPoint> points = new List<IntPoint>(); for (int i = -5; i < 20; i++) for (int j = -5; j < 20; j++) points.Add(new IntPoint(i, j)); // Assert that no exception if thrown IntPoint[][] actual = target.Match(image1, image2, points.ToArray(), points.ToArray()); Assert.IsNotNull(actual); Assert.AreEqual(2, actual.Length); var p1 = actual[0]; var p2 = actual[1]; Assert.AreEqual(p1.Length, p2.Length); for (int i = 0; i < p1.Length; i++) { // As the images are the same, assert that // each point correlates with itself. Assert.AreEqual(p1[i], p2[i]); // Also assert we have no bogus values Assert.IsTrue(p1[i].X >= 0 && p1[i].X < 16); Assert.IsTrue(p1[i].Y >= 0 && p1[i].Y < 16); } } }
/// <summary> /// Matches detected keypoints. /// </summary> private void MatchKeypoints() { // matching keypoints step needs at least two images if (input_images.Count < 2) return; // using RANSAC homography estimator RansacHomographyEstimator ransac = new RansacHomographyEstimator(0.001, 0.99); CorrelationMatching matcher = new CorrelationMatching(9); Bitmap mergedImage = new Bitmap(input_images.Count * input_images[0].Width, input_images[0].Height); //it is assumed the images are of same size! Graphics graphics = Graphics.FromImage(mergedImage); IntPoint[][] matches; int cumulativeWidth = 0; int keypoints_cntr = 0; // draw all images for (int i = 0; i < input_images.Count; i++) { graphics.DrawImage(input_images[i], cumulativeWidth, 0, input_images[i].Width, input_images[i].Height); cumulativeWidth += input_images[i].Width; } // iterate through all images for (int i = 0; i < input_images.Count; i++) { if (i != input_images.Count - 1) { // match detected keypoints with maximum cross-correlation algorithm matches = matcher.Match(new Bitmap(input_images[i]), new Bitmap(input_images[i + 1]), keypoints[keypoints_cntr].ToArray(), keypoints[keypoints_cntr + 1].ToArray()); IntPoint[] correlationPoints1 = matches[0]; IntPoint[] correlationPoints2 = matches[1]; // Plot RANSAC results against correlation results homography = ransac.Estimate(correlationPoints1, correlationPoints2); // take only inliers - good matches IntPoint[] inliers1 = correlationPoints1.Submatrix(ransac.Inliers); IntPoint[] inliers2 = correlationPoints2.Submatrix(ransac.Inliers); PairsMarker pairs = new PairsMarker(correlationPoints1, correlationPoints2.Apply(p => new IntPoint(p.X + input_images[i].Width, p.Y))); // draw all matching pairs for (int j = 0; j < pairs.Points1.Count(); j++) { if (i % 2 == 0) graphics.DrawLine(new Pen(Color.Red, 1.5f), new System.Drawing.Point(correlationPoints1[j].X + i * input_images[i].Width, correlationPoints1[j].Y), new System.Drawing.Point(correlationPoints2[j].X + (i + 1) * input_images[i].Width, correlationPoints2[j].Y)); } // draw inliers for (int j = 0; j < inliers1.Count(); j++) { if (i % 2 == 0) graphics.DrawLine(new Pen(Color.GreenYellow, 1.5f), new System.Drawing.Point(inliers1[j].X + i * input_images[i].Width, inliers1[j].Y), new System.Drawing.Point(inliers2[j].X + (i + 1) * input_images[i].Width, inliers2[j].Y)); else graphics.DrawLine(new Pen(Color.Blue, 1.5f), new System.Drawing.Point(inliers1[j].X + i * input_images[i].Width, inliers1[j].Y), new System.Drawing.Point(inliers2[j].X + (i + 1) * input_images[i].Width, inliers2[j].Y)); } keypoints_cntr += 2; } } ShowThumbnail(mergedImage, true, PanoramaPhase.MatchKeypoints); }
private bool GenerateKeypointMatches(Bitmap target, Bitmap frame, Rectangle prevTarget) { HarrisCornersDetector harrisDetector = new HarrisCornersDetector(HarrisCornerMeasure.Harris, 1000f, 1f, 2); targetKeyPoints=harrisDetector.ProcessImage(target).ToArray(); frameKeyPoints=harrisDetector.ProcessImage(frame).ToArray(); //Console.WriteLine("tr={0} fr={1}", targetKeyPoints.Length, frameKeyPoints.Length); if (targetKeyPoints.Length==0||frameKeyPoints.Length==0) { return false; } CorrelationMatching matcher=new CorrelationMatching(15); IntPoint[][] matches=matcher.Match(target, frame, targetKeyPoints, frameKeyPoints); targetMacthes=matches[0]; frameMatches=matches[1]; if (targetMacthes.Length<4||frameMatches.Length<4) { return false; } RansacHomographyEstimator ransac=new RansacHomographyEstimator(0.1, 0.50); MatrixH estiment = new MatrixH(); try { estiment = ransac.Estimate(targetMacthes, frameMatches); } catch { return false; } IntPoint[] targetGoodMatch=targetMacthes.Submatrix(ransac.Inliers); IntPoint[] frameGoodMatch=frameMatches.Submatrix(ransac.Inliers); CalculatePosChange(targetGoodMatch, frameGoodMatch, prevTarget); return true; }
// PANORAMIC STICHING CODE // Accord.NET and AForge.NET frameworks and code examples provided from // http://www.codeproject.com/KB/recipes/automatic_panoramas.aspx private void panoramicStitchingToolStripMenuItem_Click(object sender, EventArgs e) { // Save a copy of the current image, ask user to open another image to merge with Bitmap img2 = img; openToolStripMenuItem_Click(sender, e); // Check whether the current loaded image is different // (If the user cancelled the open image operation, the current image in the viewer // is still the same image object) if (img2 != img) { Bitmap img1 = img; AForge.IntPoint[] harrisPoints1; AForge.IntPoint[] harrisPoints2; AForge.IntPoint[] correlationPoints1; AForge.IntPoint[] correlationPoints2; MatrixH homography; // Use Harris Corners Detector to find points of interest HarrisCornersDetector harris = new HarrisCornersDetector(0.04f, 1000f); harrisPoints1 = harris.ProcessImage(img1).ToArray(); harrisPoints2 = harris.ProcessImage(img2).ToArray(); // This check fixes an out of bounds exception generated by matcher.Match() below when a // monocolour image (0 harris points) is stitched with a non-monocolour image if (harrisPoints1.Length == 0 || harrisPoints2.Length == 0) { MessageBox.Show("Panoramic stitching cannot continue because at least one of the images does not contain any Harris points.", "Warning"); } else { // Match detected points using correlation CorrelationMatching matcher = new CorrelationMatching(9); AForge.IntPoint[][] matches = matcher.Match(img1, img2, harrisPoints1, harrisPoints2); // Separate the two arrays correlationPoints1 = matches[0]; correlationPoints2 = matches[1]; // Find homography matrix using RANSAC algorithm RansacHomographyEstimator ransac = new RansacHomographyEstimator(0.001, 0.99); // This check is to handle the ransac.Estimate() function // which throws an exception if the array parameters do not contain at least 4 elements if (correlationPoints1.Length < 4 || correlationPoints2.Length < 4) { MessageBox.Show("Panoramic stitching cannot continue because at least one of the images does not contain at least 4 correlation points.", "Warning"); } else { homography = ransac.Estimate(correlationPoints1, correlationPoints2); // Merge the images Blend blend = new Blend(homography, img1); img = blend.Apply(img2); //save the image properly and resize main form origImg.Dispose(); origImg = new Bitmap(img); pictureBox.Image = img; mainForm.ActiveForm.Width = img.Width + widthPad; mainForm.ActiveForm.Height = img.Height + heightPad; } } } }