2

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.

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
CaffGeek
  • 21,856
  • 17
  • 100
  • 184

3 Answers3

2

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);
  }
}
remi
  • 3,914
  • 1
  • 19
  • 37
2

Translating from C++ and using OpenCvSharp wrapper, the above code, replacing minMaxLoc lines, worked for me:

double threshold=0.9
var thresholdImage=Cv.CreateImage(newImageSize, BitDepth.F32,1);
Cv.Threshold(result, thresholdImage, threshold, 255, ThresholdType.Binary);
for (int r = 0; r < thresholdImage.GetSize().Height; r++)
{
    for (int c = 0; c < thresholdImage.GetSize().Width; c++)
    {
        if (thresholdImage.GetRow(r)[c].Val0 > 0)
        {
            Cv.Rectangle(soruceColour, Cv.Point(c, r), Cv.Point(c + template.Width, r + template.Height), CvColor.Red, 1, 0, 0);
        }
    }
}
edumen
  • 171
  • 1
  • 1
  • 11
1

here is the solution using Min_Max and Match_Template methods. hope it will help.

 public void multipleTemplateMatch(string SourceImages, string tempImage)
    {            
        Image<Bgr, byte> image_source = new Image<Bgr, byte>(SourceImages);
        Image<Bgr, byte> image_partial1 = new Image<Bgr, byte>(tempImage);

        double threshold = 0.9;
        ImageFinder imageFinder = new ImageFinder(image_source, image_partial1, threshold);
        imageFinder.FindThenShow();

    }   

and here is the class which will help.

class ImageFinder
{
    private List<Rectangle> rectangles;  
    public Image<Bgr, byte> BaseImage { get; set; }
    public Image<Bgr, byte> SubImage { get; set; }
    public Image<Bgr, byte> ResultImage { get; set; }
    public double Threashold { get; set; }        

    public List<Rectangle> Rectangles
    {
        get { return rectangles; }
    }

    public ImageFinder(Image<Bgr, byte> baseImage, Image<Bgr, byte> subImage, double threashold)
    {
        rectangles = new List<Rectangle>();            
        BaseImage = baseImage;
        SubImage = subImage;
        Threashold = threashold;            
    }

    public void FindThenShow()
    {
        FindImage();
        DrawRectanglesOnImage();
        ShowImage();
    }

    public void DrawRectanglesOnImage()
    {
        ResultImage = BaseImage.Copy();
        foreach (var rectangle in this.rectangles)
        {
            ResultImage.Draw(rectangle, new Bgr(Color.Blue), 1);
        }
    }

    public void FindImage()
    {          
        rectangles = new List<Rectangle>();           

        using (Image<Bgr, byte> imgSrc = BaseImage.Copy())
        {
            while (true)
            {
                using (Image<Gray, float> result = imgSrc.MatchTemplate(SubImage, TemplateMatchingType.CcoeffNormed))
                {
                    double[] minValues, maxValues;
                    Point[] minLocations, maxLocations;
                    result.MinMax(out minValues, out maxValues, out minLocations, out maxLocations);

                    if (maxValues[0] > Threashold)
                    {
                        Rectangle match = new Rectangle(maxLocations[0], SubImage.Size);
                        imgSrc.Draw(match, new Bgr(Color.Blue), -1);
                        rectangles.Add(match);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }          

    }

    public void ShowImage()
    {
        Random rNo = new Random();
        string outFilename = "matched Templates" + rNo.Next();            
        CvInvoke.Imshow(outFilename, ResultImage);
    }     

}

if you find this helpful please vote is as useful. thanks

Harry007
  • 47
  • 1
  • 7