1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
|
.. _template_matching:
Template Matching
*****************
Goal
====
In this tutorial you will learn how to:
.. container:: enumeratevisibleitemswithsquare
* Use the OpenCV function :match_template:`matchTemplate <>` to search for matches between an image patch and an input image
* Use the OpenCV function :min_max_loc:`minMaxLoc <>` to find the maximum and minimum values (as well as their positions) in a given array.
Theory
======
What is template matching?
--------------------------
.. container:: enumeratevisibleitemswithsquare
Template matching is a technique for finding areas of an image that match (are similar) to a template image (patch).
How does it work?
------------------
.. container:: enumeratevisibleitemswithsquare
* We need two primary components:
a. **Source image (I):** The image in which we expect to find a match to the template image
b. **Template image (T):** The patch image which will be compared to the template image
our goal is to detect the highest matching area:
.. image:: images/Template_Matching_Template_Theory_Summary.jpg
:align: center
* To identify the matching area, we have to *compare* the template image against the source image by sliding it:
.. image:: images/Template_Matching_Template_Theory_Sliding.jpg
:align: center
* 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 :math:`(x,y)` in **R** contains the match metric:
.. image:: images/Template_Matching_Template_Theory_Result.jpg
:align: center
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 use the function :min_max_loc:`minMaxLoc <>` to locate the highest value (or lower, depending of the type of matching method) in the *R* matrix.
Which are the matching methods available in OpenCV?
----------------------------------------------------
Good question. OpenCV implements Template matching in the function :match_template:`matchTemplate <>`. The available methods are 6:
a. **method=CV\_TM\_SQDIFF**
.. math::
R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2
b. **method=CV\_TM\_SQDIFF\_NORMED**
.. math::
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}}
c. **method=CV\_TM\_CCORR**
.. math::
R(x,y)= \sum _{x',y'} (T(x',y') \cdot I(x+x',y+y'))
d. **method=CV\_TM\_CCORR\_NORMED**
.. math::
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}}
e. **method=CV\_TM\_CCOEFF**
.. math::
R(x,y)= \sum _{x',y'} (T'(x',y') \cdot I(x+x',y+y'))
where
.. math::
\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}
f. **method=CV\_TM\_CCOEFF\_NORMED**
.. math::
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
====
.. container:: enumeratevisibleitemswithsquare
* **What does this program do?**
.. container:: enumeratevisibleitemswithsquare
* Loads an input image and a image patch (*template*)
* Perform a template matching procedure by using the OpenCV function :match_template:`matchTemplate <>` with any of the 6 matching methods described before. The user can choose the method by entering its selection in the Trackbar.
* 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
* **Downloadable code**:
Click `here <https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching/MatchTemplate_Demo.cpp>`_
* **Code at glance:**
.. code-block:: cpp
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
/// Global Variables
Mat img; Mat templ; Mat result;
char* image_window = "Source Image";
char* result_window = "Result window";
int match_method;
int max_Trackbar = 5;
/// Function Headers
void MatchingMethod( int, void* );
/** @function main */
int main( int argc, char** argv )
{
/// Load image and template
img = imread( argv[1], 1 );
templ = imread( argv[2], 1 );
/// Create windows
namedWindow( image_window, CV_WINDOW_AUTOSIZE );
namedWindow( result_window, CV_WINDOW_AUTOSIZE );
/// Create Trackbar
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 0;
}
/**
* @function MatchingMethod
* @brief Trackbar callback
*/
void MatchingMethod( int, void* )
{
/// Source image to display
Mat img_display;
img.copyTo( img_display );
/// Create the result matrix
int result_cols = img.cols - templ.cols + 1;
int result_rows = img.rows - templ.rows + 1;
result.create( result_cols, result_rows, CV_32FC1 );
/// Do the Matching and Normalize
matchTemplate( img, templ, result, match_method );
normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
/// Localizing the best match with minMaxLoc
double minVal; double maxVal; Point minLoc; Point maxLoc;
Point matchLoc;
minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
/// For SQDIFF and SQDIFF_NORMED, the best matches are lower values. For all the other methods, the higher the better
if( match_method == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED )
{ matchLoc = minLoc; }
else
{ matchLoc = maxLoc; }
/// Show me what you got
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;
}
Explanation
===========
#. Declare some global variables, such as the image, template and result matrices, as well as the match method and the window names:
.. code-block:: cpp
Mat img; Mat templ; Mat result;
char* image_window = "Source Image";
char* result_window = "Result window";
int match_method;
int max_Trackbar = 5;
#. Load the source image and template:
.. code-block:: cpp
img = imread( argv[1], 1 );
templ = imread( argv[2], 1 );
#. Create the windows to show the results:
.. code-block:: cpp
namedWindow( image_window, CV_WINDOW_AUTOSIZE );
namedWindow( result_window, CV_WINDOW_AUTOSIZE );
#. Create the Trackbar to enter the kind of matching method to be used. When a change is detected the callback function **MatchingMethod** is called.
.. code-block:: cpp
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 );
#. Wait until user exits the program.
.. code-block:: cpp
waitKey(0);
return 0;
#. Let's check out the callback function. First, it makes a copy of the source image:
.. code-block:: cpp
Mat img_display;
img.copyTo( img_display );
#. Next, it creates the result matrix that will store the matching results for each template location. Observe in detail the size of the result matrix (which matches all possible locations for it)
.. code-block:: cpp
int result_cols = img.cols - templ.cols + 1;
int result_rows = img.rows - templ.rows + 1;
result.create( result_cols, result_rows, CV_32FC1 );
#. Perform the template matching operation:
.. code-block:: cpp
matchTemplate( img, templ, result, match_method );
the arguments are naturally the input image **I**, the template **T**, the result **R** and the match_method (given by the Trackbar)
#. We normalize the results:
.. code-block:: cpp
normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
#. We localize the minimum and maximum values in the result matrix **R** by using :min_max_loc:`minMaxLoc <>`.
.. code-block:: cpp
double minVal; double maxVal; Point minLoc; Point maxLoc;
Point matchLoc;
minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
the function calls as arguments:
.. container:: enumeratevisibleitemswithsquare
+ **result:** The source array
+ **&minVal** and **&maxVal:** Variables to save the minimum and maximum values in **result**
+ **&minLoc** and **&maxLoc:** The Point locations of the minimum and maximum values in the array.
+ **Mat():** Optional mask
#. For the first two methods ( CV\_SQDIFF and CV\_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:
.. code-block:: cpp
if( match_method == CV_TM_SQDIFF || 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:
.. code-block:: cpp
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 );
Results
=======
#. Testing our program with an input image such as:
.. image:: images/Template_Matching_Original_Image.jpg
:align: center
and a template image:
.. image:: images/Template_Matching_Template_Image.jpg
:align: center
#. 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.
============ ============ ============
|Result_0| |Result_2| |Result_4|
============ ============ ============
|Result_1| |Result_3| |Result_5|
============ ============ ============
.. |Result_0| image:: images/Template_Matching_Correl_Result_0.jpg
:align: middle
.. |Result_1| image:: images/Template_Matching_Correl_Result_1.jpg
:align: middle
.. |Result_2| image:: images/Template_Matching_Correl_Result_2.jpg
:align: middle
.. |Result_3| image:: images/Template_Matching_Correl_Result_3.jpg
:align: middle
.. |Result_4| image:: images/Template_Matching_Correl_Result_4.jpg
:align: middle
.. |Result_5| image:: images/Template_Matching_Correl_Result_5.jpg
:align: middle
#. 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.
.. image:: images/Template_Matching_Image_Result.jpg
:align: center
|