'EmguCV MatchTemplate get all occurrences

I am having trouble trying to return all match occurrences with the EmguCV MatchTemplate function. As another requirement, I need it to be color sensitive (red square is not blue square). edit: I think it actually is color-sensitive already if my tests are not incorrect.

This is the code I have come up with, but I am unsure how to modify it to meet my needs:

Image<Bgr, byte> template = new Image<Bgr, byte>(pathNeedle);
Image<Bgr, byte> source = new Image<Bgr, byte>(pathHaystack);
Image<Bgr, byte> imageToShow = source.Copy();

Stopwatch watch = Stopwatch.StartNew();
using (Image<Gray, float> result = source.MatchTemplate(template, TemplateMatchingType.CcoeffNormed))
{
    double[] minValues, maxValues;
    Point[] minLocations, maxLocations;
    result.MinMax(out minValues, out maxValues, out minLocations, out maxLocations);

    watch.Stop();
    if (maxValues[0] > threshold)
    {
        // Match success
        Rectangle match = new Rectangle(maxLocations[0], template.Size);

        imageToShow.Draw(match, new Bgr(Color.Red), 3);

        // do stuff with match
        // etc..
    }

}


Solution 1:[1]

You need to get the local max's

private static List<Point> GetLocalMaxs(Image<Bgr, byte> image, Image<Bgr, byte> template)
        {
            Image<Gray, float> result = image.MatchTemplate(template, TemplateMatchingType.CcoeffNormed);
            var listOfMaxs = new List<Point>();
            var resultWithPadding = new Image<Gray, float>(image.Size);
            var heightOfPadding = (image.Height - result.Height) / 2;
            var widthOfPadding = (image.Width - result.Width) / 2;
            resultWithPadding.ROI = new Rectangle() { X = heightOfPadding, Y = widthOfPadding, Width = result.Width, Height = result.Height };
            result.CopyTo(resultWithPadding);
            resultWithPadding.ROI = Rectangle.Empty;

            for (int i = 0; i < resultWithPadding.Width; i++)
            {
                for (int j = 0; j < resultWithPadding.Height; j++)
                {
                    var centerOfRoi = new Point() { X = i + template.Width / 2, Y = j + template.Height / 2 };
                    var roi = new Rectangle() { X = i, Y = j, Width = template.Width, Height = template.Height };
                    resultWithPadding.ROI = roi;
                    double[] minValues, maxValues;
                    Point[] minLocations, maxLocations;
                    resultWithPadding.MinMax(out minValues, out maxValues, out minLocations, out maxLocations);
                    resultWithPadding.ROI = Rectangle.Empty;
                    var maxLocation = maxLocations.First();
                    if (maxLocation.X == roi.Width / 2 && maxLocation.Y == roi.Height / 2)
                    {
                        var point = new Point() { X = centerOfRoi.X, Y = centerOfRoi.Y };
                        listOfMaxs.Add(point);
                    }

                }
            }

            return listOfMaxs;
        }

Solution 2:[2]

Solution posted by Jake_2 works, but it's pretty slow since it needs to check every pixel for match.

Here is an example from my app where I'm looking for matches, what it does is basically like that:

  1. Find best match
  2. Draw object in the spot that was found on top of copy of source image
  3. Check if treshold wasn't too low
  4. Go back to point 1

It works much faster because it needs to run MinMax function only as many times as total amount of objects onto the screen (if you set up min treshold properly)

Keep in mind that if you have large template image you probably should draw a filled rectangle onto your "imageToShow" instead of just a border because if you don't the treshold won't change as much and you will go into infinite loop.

To counter infinite loop you can just create an variable of Point type and check if same point was found 2 times in a row. If that statement is true then just exit while loop (you probably messed up with treshold in that situation).

I hope that it will help someone.

Here's the code:

   private void SearchForImageButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                // CharacterStash hold path to my source image
                CharacterStash item = CharactersListView.SelectedItem as CharacterStash;
    
    
                Emgu.CV.Image<Emgu.CV.Structure.Bgr, byte> source = new Emgu.CV.Image<Emgu.CV.Structure.Bgr, byte>(item.ImageSrc); // Bigger image
    
                Emgu.CV.Image<Emgu.CV.Structure.Bgr, byte> template = new Emgu.CV.Image<Emgu.CV.Structure.Bgr, byte>("template.png"); // smaller image
                Emgu.CV.Image<Emgu.CV.Structure.Bgr, byte> imageToShow = source.Copy();
    
    
                const double minTreshold = 0.9;
                double actualTreshold = 1;
                
    
                while (actualTreshold > minTreshold)
                {
                    Emgu.CV.Image<Emgu.CV.Structure.Gray, float> result = imageToShow.MatchTemplate(template, Emgu.CV.CvEnum.TemplateMatchingType.CcoeffNormed);
                    double[] minValues, maxValues;
                    System.Drawing.Point[] minLocations, maxLocations;
                    result.MinMax(out minValues, out maxValues, out minLocations, out maxLocations);
                    actualTreshold = maxValues[0];
                    if (maxValues[0] > minTreshold)
                    {
                        // This is a match. Do something with it, for example draw a rectangle around it.
                        System.Drawing.Rectangle match = new System.Drawing.Rectangle(maxLocations[0], template.Size);
                        imageToShow.Draw(match, new Emgu.CV.Structure.Bgr(System.Drawing.Color.Red), 3);
                        
                    }
                }
                // StashImage.Source is an System.Windows.Controls.Image object in my app
                StashImage.Source = getImage(imageToShow.ToJpegData());
            }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jake_2
Solution 2 Morph21