OpenCv: Finding multiple matches

CaffGeek picture CaffGeek · Sep 8, 2012 · Viewed 7.2k times · Source

I have the following, but I can't figure out how to find ALL the matches in a source image.

    static void Main()
    {
        using (var template = Cv.LoadImage(@"images\logo.png", LoadMode.GrayScale))
        using (var source = Cv.LoadImage(@"images\manyLogos.png", LoadMode.GrayScale))
        using (var sourceColour = Cv.LoadImage(@"images\manyLogos.png", LoadMode.Color))
        {
            var width = source.Width - template.Width + 1;
            var height = source.Height - template.Height + 1;
            using (var result = Cv.CreateImage(Cv.Size(width, height), BitDepth.F32, 1))
            {
                Cv.MatchTemplate(source, template, result, MatchTemplateMethod.SqDiff);
                var THRESHOLD = 0.08D;

                double minVal, maxVal;
                CvPoint minLoc, maxLoc;                    
                Cv.MinMaxLoc(result, out minVal, out maxVal, out minLoc, out maxLoc);

                var outlineColor = (minVal > THRESHOLD) ? CvColor.Green : CvColor.Red;
                Cv.Rectangle(sourceColour, Cv.Point(minLoc.X, minLoc.Y), Cv.Point(minLoc.X + template.Width, minLoc.Y + template.Height), outlineColor, 1, 0, 0);
            }

            using (var window = new CvWindow("Test"))
            {
                while (CvWindow.WaitKey(10) < 0)
                {
                    window.Image = sourceColour;
                }
            }
        }
    }

I can outline the best match, just not all the matches. I need to get all the matches somehow.

Answer

remi picture remi · Sep 8, 2012

Using matchTemplate method, your output image will give you pixels values which represents how well your template is matched at this specific location. In you case, the lower the value, the best the match, since you used MatchTemplateMethod.SqDiff.

You problem is that when you use the minMaxLoc function, you get what you asks for, which is the best match in this case, the min).

All matches are the pixels whose value are under the threshold that you set up. Since I'm not used to csharp, here is how it would go in C++, you can do the translation:

// after your call to MatchTemplate
float threshold = 0.08;
cv::Mat thresholdedImage;
cv::threshold(result, thresholdedImage, threshold, 255, CV_THRESH_BINARY);
// the above will set pixels to 0 in thresholdedImage if their value in result is lower than the threshold, to 255 if it is larger.
// in C++ it could also be written cv::Mat thresholdedImage = result < threshold;
// Now loop over pixels of thresholdedImage, and draw your matches
for (int r = 0; r < thresholdedImage.rows; ++r) {
  for (int c = 0; c < thresholdedImage.cols; ++c) {
    if (!thresholdedImage.at<unsigned char>(r, c)) // = thresholdedImage(r,c) == 0
      cv::circle(sourceColor, cv::Point(c, r), template.cols/2, CV_RGB(0,255,0), 1);
  }
}