| OpenCV 5.0.0alpha Open Source Computer Vision

Loading...
Searching...
No Matches
Template Matching

Prev Tutorial: Back Projection
Next Tutorial: Finding contours in your image

Original author | Ana Huamán
Compatibility | OpenCV >= 3.0

# Goal

In this tutorial you will learn how to:

- Use the OpenCV function matchTemplate() to search for matches between an image patch and an input image
- Use the OpenCV function minMaxLoc() to find the maximum and minimum values (as well as their positions) in a given array.

# Theory

## What is template matching?

Template matching is a technique for finding areas of an image that match (are similar) to a template image (patch).

While the patch must be a rectangle it may be that not all of the rectangle is relevant. In such a case, a mask can be used to isolate the portion of the patch that should be used to find the match.

## How does it work?

- We need two primary components:

  1. Source image (I): The image in which we expect to find a match to the template image
  2. Template image (T): The patch image which will be compared to the source image

  our goal is to detect the highest matching area:

- To identify the matching area, we have to compare the template image against the source image by sliding it:

- By sliding, we mean moving the patch one pixel at a time (left to right, up to down). At each location, a metric is calculated so it represents how "good" or "bad" the match at that location is (or how similar the patch is to that particular area of the source image).
- For each location of T over I, you store the metric in the result matrix R. Each location \((x,y)\) in R contains the match metric:

the image above is the result R of sliding the patch with a metric TM_CCORR_NORMED. The brightest locations indicate the highest matches. As you can see, the location marked by the red circle is probably the one with the highest value, so that location (the rectangle formed by that point as a corner and width and height equal to the patch image) is considered the match.

- In practice, we locate the highest value (or lower, depending of the type of matching method) in the R matrix, using the function minMaxLoc()

## How does the mask work?

- If masking is needed for the match, three components are required:
  1. Source image (I): The image in which we expect to find a match to the template image
  2. Template image (T): The patch image which will be compared to the source image
  3. Mask image (M): The mask, a grayscale image that masks the template
- Only two matching methods currently accept a mask: TM_SQDIFF and TM_CCORR_NORMED (see below for explanation of all the matching methods available in opencv).
- The mask must have the same dimensions as the template
- The mask should have a CV_8U or CV_32F depth and the same number of channels as the template image. In CV_8U case, the mask values are treated as binary, i.e. zero and non-zero. In CV_32F case, the values should fall into [0..1] range and the template pixels will be multiplied by the corresponding mask pixel values. Since the input images in the sample have the CV_8UC3 type, the mask is also read as color image.

## Which are the matching methods available in OpenCV?

Good question. OpenCV implements Template matching in the function matchTemplate(). The available methods are 6:

1. method=TM_SQDIFF

   \[R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2\]

2. method=TM_SQDIFF_NORMED

   \[R(x,y)= \frac{\sum_{x',y'} (T(x',y')-I(x+x',y+y'))^2}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\]

3. method=TM_CCORR

   \[R(x,y)= \sum _{x',y'} (T(x',y') \cdot I(x+x',y+y'))\]

4. method=TM_CCORR_NORMED

   \[R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\]

5. method=TM_CCOEFF

   \[R(x,y)= \sum _{x',y'} (T'(x',y') \cdot I'(x+x',y+y'))\]

   where

   \[\begin{array}{l} T'(x',y')=T(x',y') - 1/(w \cdot h) \cdot \sum _{x'',y''} T(x'',y'') \\ I'(x+x',y+y')=I(x+x',y+y') - 1/(w \cdot h) \cdot \sum _{x'',y''} I(x+x'',y+y'') \end{array}\]

6. method=TM_CCOEFF_NORMED

   \[R(x,y)= \frac{ \sum_{x',y'} (T'(x',y') \cdot I'(x+x',y+y')) }{ \sqrt{\sum_{x',y'}T'(x',y')^2 \cdot \sum_{x',y'} I'(x+x',y+y')^2} }\]

# Code

- What does this program do?
  - Loads an input image, an image patch (template), and optionally a mask
  - Perform a template matching procedure by using the OpenCV function matchTemplate() with any of the 6 matching methods described before. The user can choose the method by entering its selection in the Trackbar. If a mask is supplied, it will only be used for the methods that support masking
  - Normalize the output of the matching procedure
  - Localize the location with higher matching probability
  - Draw a rectangle around the area corresponding to the highest match

C++

- Downloadable code: Click here
- Code at glance:
  #include "opencv2/imgcodecs.hpp"
  #include "opencv2/highgui.hpp"
  #include "opencv2/imgproc.hpp"
  #include <iostream>
  using namespace std;
  using namespace cv;
  bool use_mask;
  Mat img; Mat templ; Mat mask; Mat result;
  const char* image_window = "Source Image";
  const char* result_window = "Result window";
  int match_method;
  int max_Trackbar = 5;
  void MatchingMethod( int, void* );
  const char* keys =
  "{ help h| | Print help message. }"
  "{ @input1 | Template_Matching_Original_Image.jpg | image_name }"
  "{ @input2 | Template_Matching_Template_Image.jpg | template_name }"
  "{ @input3 | | mask_name }";
  int main( int argc, char** argv )
  {
  CommandLineParser parser( argc, argv, keys );
  samples::addSamplesDataSearchSubDirectory( "doc/tutorials/imgproc/histograms/template_matching/images" );
  img = imread( samples::findFile( parser.get<String>("@input1") ) );
  templ = imread( samples::findFile( parser.get<String>("@input2") ), IMREAD_COLOR );
  if(argc > 3) {
  use_mask = true;
  mask = imread(samples::findFile( parser.get<String>("@input3") ), IMREAD_COLOR );
  }
  if(img.empty() || templ.empty() || (use_mask && mask.empty()))
  {
  cout << "Can't read one of the images" << endl;
  return EXIT_FAILURE;
  }
  namedWindow( image_window, WINDOW_AUTOSIZE );
  namedWindow( result_window, WINDOW_AUTOSIZE );
  const char* trackbar_label = "Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED";
  createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
  MatchingMethod( 0, 0 );
  waitKey(0);
  return EXIT_SUCCESS;
  }
  void MatchingMethod( int, void* )
  {
  Mat img_display;
  img.copyTo( img_display );
  int result_cols = img.cols - templ.cols + 1;
  int result_rows = img.rows - templ.rows + 1;
  result.create( result_rows, result_cols, CV_32FC1 );
  bool method_accepts_mask = (TM_SQDIFF == match_method || match_method == TM_CCORR_NORMED);
  if (use_mask && method_accepts_mask)
  { matchTemplate( img, templ, result, match_method, mask); }
  else
  { matchTemplate( img, templ, result, match_method); }
  normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
  double minVal; double maxVal; Point minLoc; Point maxLoc;
  Point matchLoc;
  minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
  if( match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED )
  { matchLoc = minLoc; }
  else
  { matchLoc = maxLoc; }
  rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
  rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
  imshow( image_window, img_display );
  imshow( result_window, result );
  return;
  }
  cv::CommandLineParser
  Designed for command line parsing.
  Definition utility.hpp:890
  cv::Mat
  n-dimensional dense array class
  Definition mat.hpp:951
  cv::Mat::copyTo
  void copyTo(OutputArray m) const
  Copies the matrix to another one.
  cv::Mat::create
  void create(int rows, int cols, int type)
  Allocates new array data if needed.
  cv::Mat::cols
  int cols
  Definition mat.hpp:2425
  cv::Mat::empty
  bool empty() const
  Returns true if the array has no elements.
  cv::Mat::rows
  int rows
  the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
  Definition mat.hpp:2425
  cv::Point_< int >
  cv::Point_::y
  _Tp y
  y coordinate of the point
  Definition types.hpp:202
  cv::Point_::x
  _Tp x
  x coordinate of the point
  Definition types.hpp:201
  cv::normalize
  void normalize(InputArray src, InputOutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray())
  Normalizes the norm or value range of an array.
  cv::minMaxLoc
  void minMaxLoc(InputArray src, double *minVal, double *maxVal=0, Point *minLoc=0, Point *maxLoc=0, InputArray mask=noArray())
  Finds the global minimum and maximum in an array.
  cv::String
  std::string String
  Definition cvstd.hpp:151
  CV_32FC1
  #define CV_32FC1
  Definition interface.h:129
  cv::imshow
  void imshow(const String &winname, InputArray mat)
  Displays an image in the specified window.
  cv::rectangle
  void rectangle(InputOutputArray img, Point pt1, Point pt2, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
  Draws a simple, thick, or filled up-right rectangle.
  cv::matchTemplate
  void matchTemplate(InputArray image, InputArray templ, OutputArray result, int method, InputArray mask=noArray())
  Compares a template against overlapped image regions.
  highgui.hpp
  main
  int main(int argc, char *argv[])
  Definition highgui_qt.cpp:3
  imgcodecs.hpp
  imgproc.hpp
  cv
  Definition core.hpp:107
  std
  STL namespace.

Java

- Downloadable code: Click here
- Code at glance:
  import java.awt.GridLayout;
  import java.awt.Image;
  import java.util.Hashtable;
  import javax.swing.ImageIcon;
  import javax.swing.JFrame;
  import javax.swing.JLabel;
  import javax.swing.JSlider;
  import javax.swing.event.ChangeEvent;
  import javax.swing.event.ChangeListener;
  import org.opencv.core.Core;
  import org.opencv.core.CvType;
  import org.opencv.core.Mat;
  import org.opencv.core.Point;
  import org.opencv.core.Scalar;
  import org.opencv.highgui.HighGui;
  import org.opencv.imgcodecs.Imgcodecs;
  import org.opencv.imgproc.Imgproc;
  class MatchTemplateDemoRun implements ChangeListener {
  Boolean use_mask = false;
  Mat img = new Mat(), templ = new Mat();
  Mat mask = new Mat();
  int match_method;
  JLabel imgDisplay = new JLabel(), resultDisplay = new JLabel();
  public void run(String[] args) {
  if (args.length < 2) {
  System.out.println("Not enough parameters");
  System.out.println("Program arguments:\n<image_name> <template_name> [<mask_name>]");
  System.exit(-1);
  }
  img = Imgcodecs.imread(args[0], Imgcodecs.IMREAD_COLOR);
  templ = Imgcodecs.imread(args[1], Imgcodecs.IMREAD_COLOR);
  if (args.length > 2) {
  use_mask = true;
  mask = Imgcodecs.imread(args[2], Imgcodecs.IMREAD_COLOR);
  }
  if (img.empty() || templ.empty() || (use_mask && mask.empty())) {
  System.out.println("Can't read one of the images");
  System.exit(-1);
  }
  matchingMethod();
  createJFrame();
  }
  private void matchingMethod() {
  Mat result = new Mat();
  Mat img_display = new Mat();
  img.copyTo(img_display);
  int result_cols = img.cols() - templ.cols() + 1;
  int result_rows = img.rows() - templ.rows() + 1;
  result.create(result_rows, result_cols, CvType.CV_32FC1);
  Boolean method_accepts_mask = (Imgproc.TM_SQDIFF == match_method || match_method == Imgproc.TM_CCORR_NORMED);
  if (use_mask && method_accepts_mask) {
  Imgproc.matchTemplate(img, templ, result, match_method, mask);
  } else {
  Imgproc.matchTemplate(img, templ, result, match_method);
  }
  Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
  Point matchLoc;
  Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
  if (match_method == Imgproc.TM_SQDIFF || match_method == Imgproc.TM_SQDIFF_NORMED) {
  matchLoc = mmr.minLoc;
  } else {
  matchLoc = mmr.maxLoc;
  }
  Imgproc.rectangle(img_display, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()),
  new Scalar(0, 0, 0), 2, 8, 0);
  Imgproc.rectangle(result, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()),
  new Scalar(0, 0, 0), 2, 8, 0);
  Image tmpImg = HighGui.toBufferedImage(img_display);
  ImageIcon icon = new ImageIcon(tmpImg);
  imgDisplay.setIcon(icon);
  result.convertTo(result, CvType.CV_8UC1, 255.0);
  tmpImg = HighGui.toBufferedImage(result);
  icon = new ImageIcon(tmpImg);
  resultDisplay.setIcon(icon);
  }
  @Override
  public void stateChanged(ChangeEvent e) {
  JSlider source = (JSlider) e.getSource();
  if (!source.getValueIsAdjusting()) {
  match_method = source.getValue();
  matchingMethod();
  }
  }
  private void createJFrame() {
  String title = "Source image; Control; Result image";
  JFrame frame = new JFrame(title);
  frame.setLayout(new GridLayout(2, 2));
  frame.add(imgDisplay);
  int min = 0, max = 5;
  JSlider slider = new JSlider(JSlider.VERTICAL, min, max, match_method);
  slider.setPaintTicks(true);
  slider.setPaintLabels(true);
  // Set the spacing for the minor tick mark
  slider.setMinorTickSpacing(1);
  // Customizing the labels
  Hashtable<Integer, JLabel> labelTable = new Hashtable<>();
  labelTable.put(new Integer(0), new JLabel("0 - SQDIFF"));
  labelTable.put(new Integer(1), new JLabel("1 - SQDIFF NORMED"));
  labelTable.put(new Integer(2), new JLabel("2 - TM CCORR"));
  labelTable.put(new Integer(3), new JLabel("3 - TM CCORR NORMED"));
  labelTable.put(new Integer(4), new JLabel("4 - TM COEFF"));
  labelTable.put(new Integer(5), new JLabel("5 - TM COEFF NORMED : (Method)"));
  slider.setLabelTable(labelTable);
  slider.addChangeListener(this);
  frame.add(slider);
  frame.add(resultDisplay);
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  frame.pack();
  frame.setVisible(true);
  }
  }
  public class MatchTemplateDemo {
  public static void main(String[] args) {
  // load the native OpenCV library
  System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  // run code
  new MatchTemplateDemoRun().run(args);
  }
  }
  cv::Point
  Point2i Point
  Definition types.hpp:209
  cv::Scalar
  Scalar_< double > Scalar
  Definition types.hpp:712

Python

- Downloadable code: Click here
- Code at glance:
  from __future__ import print_function
  import sys
  import cv2 as cv
  use_mask = False
  img = None
  templ = None
  mask = None
  image_window = "Source Image"
  result_window = "Result window"
  match_method = 0
  max_Trackbar = 5
  def main(argv):
  if (len(sys.argv) < 3):
  print('Not enough parameters')
  print('Usage:\nmatch_template_demo.py <image_name> <template_name> [<mask_name>]')
  return -1
  global img
  global templ
  img = cv.imread(sys.argv[1], cv.IMREAD_COLOR)
  templ = cv.imread(sys.argv[2], cv.IMREAD_COLOR)
  if (len(sys.argv) > 3):
  global use_mask
  use_mask = True
  global mask
  mask = cv.imread( sys.argv[3], cv.IMREAD_COLOR )
  if ((img is None) or (templ is None) or (use_mask and (mask is None))):
  print('Can\'t read one of the images')
  return -1
  cv.namedWindow( image_window, cv.WINDOW_AUTOSIZE )
  cv.namedWindow( result_window, cv.WINDOW_AUTOSIZE )
  trackbar_label = 'Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED'
  cv.createTrackbar( trackbar_label, image_window, match_method, max_Trackbar, MatchingMethod )
  MatchingMethod(match_method)
  cv.waitKey(0)
  return 0
  def MatchingMethod(param):
  global match_method
  match_method = param
  img_display = img.copy()
  method_accepts_mask = (cv.TM_SQDIFF == match_method or match_method == cv.TM_CCORR_NORMED)
  if (use_mask and method_accepts_mask):
  result = cv.matchTemplate(img, templ, match_method, None, mask)
  else:
  result = cv.matchTemplate(img, templ, match_method)
  cv.normalize( result, result, 0, 1, cv.NORM_MINMAX, -1 )
  _minVal, _maxVal, minLoc, maxLoc = cv.minMaxLoc(result, None)
  if (match_method == cv.TM_SQDIFF or match_method == cv.TM_SQDIFF_NORMED):
  matchLoc = minLoc
  else:
  matchLoc = maxLoc
  cv.rectangle(img_display, matchLoc, (matchLoc[0] + templ.shape[0], matchLoc[1] + templ.shape[1]), (0,0,0), 2, 8, 0 )
  cv.rectangle(result, matchLoc, (matchLoc[0] + templ.shape[0], matchLoc[1] + templ.shape[1]), (0,0,0), 2, 8, 0 )
  cv.imshow(image_window, img_display)
  cv.imshow(result_window, result)
  pass
  if __name__ == "__main__":
  main(sys.argv[1:])
  cv::waitKey
  int waitKey(int delay=0)
  Waits for a pressed key.
  cv::namedWindow
  void namedWindow(const String &winname, int flags=WINDOW_AUTOSIZE)
  Creates a window.
  cv::createTrackbar
  int createTrackbar(const String &trackbarname, const String &winname, int *value, int count, TrackbarCallback onChange=0, void *userdata=0)
  Creates a trackbar and attaches it to the specified window.
  cv::imread
  CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
  Loads an image from a file.

# Explanation

- Declare some global variables, such as the image, template and result matrices, as well as the match method and the window names:

  C++
  bool use_mask;
  Mat img; Mat templ; Mat mask; Mat result;
  const char* image_window = "Source Image";
  const char* result_window = "Result window";
  int match_method;
  int max_Trackbar = 5;
  Java
  Boolean use_mask = false;
  Mat img = new Mat(), templ = new Mat();
  Mat mask = new Mat();
  int match_method;
  JLabel imgDisplay = new JLabel(), resultDisplay = new JLabel();
  Python
  use_mask = False
  img = None
  templ = None
  mask = None
  image_window = "Source Image"
  result_window = "Result window"
  match_method = 0
  max_Trackbar = 5

- Load the source image, template, and optionally, if supported for the matching method, a mask:

  C++
  img = imread( samples::findFile( parser.get<String>("@input1") ) );
  templ = imread( samples::findFile( parser.get<String>("@input2") ), IMREAD_COLOR );
  if(argc > 3) {
  use_mask = true;
  mask = imread(samples::findFile( parser.get<String>("@input3") ), IMREAD_COLOR );
  }
  if(img.empty() || templ.empty() || (use_mask && mask.empty()))
  {
  cout << "Can't read one of the images" << endl;
  return EXIT_FAILURE;
  }
  Java
  img = Imgcodecs.imread(args[0], Imgcodecs.IMREAD_COLOR);
  templ = Imgcodecs.imread(args[1], Imgcodecs.IMREAD_COLOR);
  Python
  global img
  global templ
  img = cv.imread(sys.argv[1], cv.IMREAD_COLOR)
  templ = cv.imread(sys.argv[2], cv.IMREAD_COLOR)
  if (len(sys.argv) > 3):
  global use_mask
  use_mask = True
  global mask
  mask = cv.imread( sys.argv[3], cv.IMREAD_COLOR )
  if ((img is None) or (templ is None) or (use_mask and (mask is None))):
  print('Can\'t read one of the images')
  return -1

- Create the Trackbar to enter the kind of matching method to be used. When a change is detected the callback function is called.

  C++
  const char* trackbar_label = "Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED";
  createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
  Java
  int min = 0, max = 5;
  JSlider slider = new JSlider(JSlider.VERTICAL, min, max, match_method);
  Python
  trackbar_label = 'Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED'
  cv.createTrackbar( trackbar_label, image_window, match_method, max_Trackbar, MatchingMethod )

- Let's check out the callback function. First, it makes a copy of the source image:

  C++
  Mat img_display;
  img.copyTo( img_display );
  Java
  Mat img_display = new Mat();
  img.copyTo(img_display);
  Python
  img_display = img.copy()

- Perform the template matching operation. The arguments are naturally the input image I, the template T, the result R and the match_method (given by the Trackbar), and optionally the mask image M.

  C++
  bool method_accepts_mask = (TM_SQDIFF == match_method || match_method == TM_CCORR_NORMED);
  if (use_mask && method_accepts_mask)
  { matchTemplate( img, templ, result, match_method, mask); }
  else
  { matchTemplate( img, templ, result, match_method); }
  Java
  Boolean method_accepts_mask = (Imgproc.TM_SQDIFF == match_method || match_method == Imgproc.TM_CCORR_NORMED);
  if (use_mask && method_accepts_mask) {
  Imgproc.matchTemplate(img, templ, result, match_method, mask);
  } else {
  Imgproc.matchTemplate(img, templ, result, match_method);
  }
  Python
  method_accepts_mask = (cv.TM_SQDIFF == match_method or match_method == cv.TM_CCORR_NORMED)
  if (use_mask and method_accepts_mask):
  result = cv.matchTemplate(img, templ, match_method, None, mask)
  else:
  result = cv.matchTemplate(img, templ, match_method)

- We normalize the results:

  C++
  normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
  Java
  Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
  Python
  cv.normalize( result, result, 0, 1, cv.NORM_MINMAX, -1 )

- We localize the minimum and maximum values in the result matrix R by using minMaxLoc().

  C++
  double minVal; double maxVal; Point minLoc; Point maxLoc;
  Point matchLoc;
  minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
  Java
  Point matchLoc;
  Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
  Python
  _minVal, _maxVal, minLoc, maxLoc = cv.minMaxLoc(result, None)

- For the first two methods ( TM_SQDIFF and MT_SQDIFF_NORMED ) the best match are the lowest values. For all the others, higher values represent better matches. So, we save the corresponding value in the matchLoc variable:

  C++
  if( match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED )
  { matchLoc = minLoc; }
  else
  { matchLoc = maxLoc; }
  Java
  if (match_method == Imgproc.TM_SQDIFF || match_method == Imgproc.TM_SQDIFF_NORMED) {
  matchLoc = mmr.minLoc;
  } else {
  matchLoc = mmr.maxLoc;
  }
  Python
  if (match_method == cv.TM_SQDIFF or match_method == cv.TM_SQDIFF_NORMED):
  matchLoc = minLoc
  else:
  matchLoc = maxLoc

- Display the source image and the result matrix. Draw a rectangle around the highest possible matching area:

  C++
  rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
  rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
  imshow( image_window, img_display );
  imshow( result_window, result );
  Java
  Imgproc.rectangle(img_display, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()),
  new Scalar(0, 0, 0), 2, 8, 0);
  Imgproc.rectangle(result, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()),
  new Scalar(0, 0, 0), 2, 8, 0);
  Image tmpImg = HighGui.toBufferedImage(img_display);
  ImageIcon icon = new ImageIcon(tmpImg);
  imgDisplay.setIcon(icon);
  result.convertTo(result, CvType.CV_8UC1, 255.0);
  tmpImg = HighGui.toBufferedImage(result);
  icon = new ImageIcon(tmpImg);
  resultDisplay.setIcon(icon);
  Python
  cv.rectangle(img_display, matchLoc, (matchLoc[0] + templ.shape[0], matchLoc[1] + templ.shape[1]), (0,0,0), 2, 8, 0 )
  cv.rectangle(result, matchLoc, (matchLoc[0] + templ.shape[0], matchLoc[1] + templ.shape[1]), (0,0,0), 2, 8, 0 )
  cv.imshow(image_window, img_display)
  cv.imshow(result_window, result)

# Results

1. Testing our program with an input image such as:

and a template image:

1. Generate the following result matrices (first row are the standard methods SQDIFF, CCORR and CCOEFF, second row are the same methods in its normalized version). In the first column, the darkest is the better match, for the other two columns, the brighter a location, the higher the match.

1. The right match is shown below (black rectangle around the face of the guy at the right). Notice that CCORR and CCDEFF gave erroneous best matches, however their normalized version did it right, this may be due to the fact that we are only considering the "highest match" and not the other possible high matches.