【opencv 450 Image Processing】Template Matching模板匹配

Goal

在本教程中,您将学习如何:

使用 OpenCV 函数 matchTemplate() 搜索图像补丁输入图像之间的匹配

使用 OpenCV 函数 minMaxLoc() 查找给定数组中的最大值和最小值(以及它们的位置)。

Theory

What is template matching?

模板匹配是一种用于查找与模板图像(补丁)匹配(相似)的图像区域的技术。

虽然补丁patch必须是矩形,但可能并非所有矩形都是相关的。 在这种情况下,可以使用掩码来隔离应用于查找匹配的补丁patch部分。

How does it work?

我们需要两个主要组件:

源图像Source image (I)(I):我们期望在其中找到与模板图像匹配的图像

模板图像 Template image (T):将与源图像进行比较的补丁patch 图像

我们的目标是检测最高匹配区域

 

为了识别匹配区域,我们必须通过滑动将模板图像与源图像进行比较

 

通过滑动,我们的意思是一次移动一个像素(从左到右,从上到下)。 在每个位置,都会计算一个度量,因此它表示该位置匹配的“好”或“坏”(或补丁与源图像的特定区域的相似程度)。

对于在I 上的每个位置T,您将度量存储在result matrix R 中。R 中的每个位置 (x,y) 都包含匹配度量match metric:

 

上图是使用度量 TM_CCORR_NORMED 滑动补丁的结果 R。 最亮的位置表示最高匹配。 如您所见,红色圆圈标记的位置可能是具有最高值的位置,因此该位置(由该点形成的一个角和宽度和高度等于补丁图像的矩形the rectangle formed by that point as a corner and width and height equal to the patch image)被认为是匹配的。

在实践中,我们使用函数 minMaxLoc() 在 R 矩阵中定位最大值(或更低,取决于匹配方法的类型)

How does the mask work?

  1. 如果匹配需要掩膜,则需要三个组件:

源图像Source image(I):我们期望在其中找到与模板图像匹配的图像

模板图像 Template image (T):将与源图像进行比较的补丁图像

掩膜图像Mask image (M):mask,对模板进行masks的灰度图

  1. 目前只有两种匹配方法接受掩码TM_SQDIFFTM_CCORR_NORMED(有关 opencv 中所有可用匹配方法的说明,请参见下文)。
  2. 掩膜必须与模板具有相同的尺寸
  3. 掩膜应具有 CV_8U 或 CV_32F 深度以及与模板图像相同的通道数。 在 CV_8U 情况下,掩码值被视为二进制,即零和非零。 在 CV_32F 的情况下,值应该在 [0..1] 范围内,模板像素将乘以相应的掩码像素值。 由于样本中的输入图像具有 CV_8UC3 类型,因此mask也被读取为彩色图像。

 

Which are the matching methods available in OpenCV?

OpenCV 中可用的匹配方法有哪些?

好问题。 OpenCV 在函数 matchTemplate() 中实现模板匹配。 可用的方法有 6 种:

method=TM_SQDIFF

 

method=TM_SQDIFF_NORMED

 

method=TM_CCORR

 

method=TM_CCORR_NORMED

 

method=TM_CCOEFF

 

where

 

method=TM_CCOEFF_NORMED

 

Code

这个程序有做了什么?

加载输入图像、图像补丁(模板)和可选的掩码

使用 OpenCV 函数 matchTemplate() 和前面描述的 6 种匹配方法中的任何一种来执行模板匹配过程。 用户可以通过在 Trackbar 中输入其选择来选择方法。 如果提供了掩码,它将仅用于支持掩码的方法

规范化匹配过程的输出Normalize the output of the matching procedure

定位匹配概率较高的位置Localize the location with higher matching probability

在与最高匹配对应的区域周围绘制一个矩形

可下载代码:点击这里

opencv/MatchTemplate_Demo.cpp at 4.x · opencv/opencv · GitHub

代码一目了然

/**
 * @file MatchTemplate_Demo.cpp
 * @brief 模板匹配Sample code to use the function MatchTemplate
 * @author OpenCV team
 */

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>

using namespace std;
using namespace cv;

//! [declare]
/// Global Variables
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;//6中匹配方法
//! [declare]

/// 函数头 Function Headers
void MatchingMethod( int, void* );

/**
 * @function main
 */
int main( int argc, char** argv )
{
  if (argc < 3)
  {
    cout << "Not enough parameters" << endl;
    cout << "Usage:\n" << argv[0] << " <image_name> <template_name> [<mask_name>]" << endl;
    return -1;
  }

  //! [load_image]
  /// 加载图像和模板Load image and template
  img = imread( argv[1], IMREAD_COLOR );
  templ = imread( argv[2], IMREAD_COLOR );

  if(argc > 3) {
    use_mask = true;
    mask = imread( argv[3], IMREAD_COLOR );//读取掩膜
  }

  if(img.empty() || templ.empty() || (use_mask && mask.empty()))
  {
    cout << "无法读取其中一张图片Can't read one of the images" << endl;
    return EXIT_FAILURE;
  }
  //! [load_image]

  //! [create_windows]
  /// 创建窗口
  namedWindow( image_window, WINDOW_AUTOSIZE );
  namedWindow( result_window, WINDOW_AUTOSIZE );
  //! [create_windows]

  //! [create_trackbar]
  /// 创建滑动条
  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 );
  //! [create_trackbar]
	//模板匹配
  MatchingMethod( 0, 0 );

  //! [wait_key]
  waitKey(0);
  return EXIT_SUCCESS;
  //! [wait_key]
}

/**
 * @function MatchingMethod
 * @brief 滑动条回调 Trackbar callback
 */
void MatchingMethod( int, void* )
{
  //! [copy_source]
  /// 要显示的源图像Source image to display
  Mat img_display;
  img.copyTo( img_display );
  //! [copy_source]

  //! [create_result_matrix]
  /// 创建结果矩阵  Create the result matrix
  int result_cols =  img.cols - templ.cols + 1;
  int result_rows = img.rows - templ.rows + 1;

  result.create( result_rows, result_cols, CV_32FC1 );
  //! [create_result_matrix]

  //! [match_template]
  /// 进行匹配和规范化 Do the Matching and Normalize
  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); }//无掩膜模板匹配
  //! [match_template]

  //! [normalize] 归一化
  normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
  //! [normalize]

  //! [best_match]
  /// 使用 minMaxLoc 定位最佳匹配 Localizing the best match with minMaxLoc
  double minVal; double maxVal; Point minLoc; Point maxLoc;
  Point matchLoc;//最佳的匹配点
//在数组中找到全局最小和最大值 
  minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
  //! [best_match]

  //! [match_loc]
  /// 对于 SQDIFF 和 SQDIFF_NORMED ,最佳匹配是较低的值。 对于所有其他方法,越高越好 For SQDIFF and SQDIFF_NORMED, the best matches are lower values. For all the other methods, the higher the better
  if( match_method  == TM_SQDIFF || match_method == TM_SQDIFF_NORMED )
    { matchLoc = minLoc; }//对于 SQDIFF 和 SQDIFF_NORMED ,最佳匹配是较低的值
  else
    { matchLoc = maxLoc; }//最佳匹配点:左上角
  //! [match_loc]

  //! [imshow]
  /// 告诉我你得到了什么
  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 );
  //! [imshow]

  return;
}

Explanation

声明一些全局变量,例如图像、模板和结果矩阵,以及匹配方法和窗口名称:

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;

加载源图像、模板和可选的(如果匹配方法支持)掩码:

img = imread( argv[1], IMREAD_COLOR );
templ = imread( argv[2], IMREAD_COLOR );
if(argc > 3) {
use_mask = true;
mask = imread( argv[3], IMREAD_COLOR );
}
if(img.empty() || templ.empty() || (use_mask && mask.empty()))
{
cout << "Can't read one of the images" << endl;
return EXIT_FAILURE;
}

创建 Trackbar 以输入要使用的匹配方法的种类。 当检测到更改时,将调用回调函数。

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 );

让我们看看回调函数。 首先,它会复制源图像:

Mat img_display;
img.copyTo( img_display );

执行模板匹配操作。 参数自然是输入图像 I、模板 T、结果 R 和 match_method(由 Trackbar 给出),以及可选的掩码图像 M。

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() );

我们使用 minMaxLoc() 定位结果矩阵 R 中的最小值和最大值。

double minVal; double maxVal; Point minLoc; Point maxLoc;
Point matchLoc;
minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );

对于前两种方法( TM_SQDIFF 和 MT_SQDIFF_NORMED ),最佳匹配是最低值。 对于所有其他人,较高的值代表更好的匹配。 因此,我们将对应的值保存在 matchLoc 变量中:

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 );

Results

使用输入图像测试我们的程序,例如:

 

和模板图像:

 

生成以下结果矩阵(第一行是标准方法 SQDIFF、CCORR 和 CCOEFF,第二行是标准化版本中的相同方法)。 在第一列中,最暗的匹配度越高,对于其他两列,位置越亮,匹配度越高。

 

Result_0

 

Result_1

 

Result_2

 

Result_3

 

Result_4

 

Result_5

正确的匹配如下所示(右侧人脸周围的黑色矩形)。 请注意,CCORR 和 CCDEFF 给出了错误的最佳匹配,但它们的标准化版本正确,这可能是因为我们只考虑“最高匹配”而不考虑其他可能的高匹配。

 

opencv —— minMaxLoc 寻找图像全局最大最小值 - 老干妈就泡面 - 博客园 (cnblogs.com)

opencv —— minMaxLoc 寻找图像全局最大最小值

寻找最值:minMaxLoc 函数

minMaxLoc 函数的作用是在数组中找到全局最小和最大值

void minMaxLoc(InputArray src, double* minVal, double* maxVal = 0, Point* minLoc = 0, Point* maxLoc = 0, InputArray mask = noArray());

src,输入的数组,若是图像,需为单通道图像。

minVal,返回最小值的指针。若无需返回,此值设为 NULL。

maxVal,返回最大值的指针。若无需返回,此值设为 NULL。

minLoc,返回最小值位置的指针(二维情况下)。若无需返回,此值设为 NULL。

maxLoc,返回最大值位置的指针(二维情况下)。若无需返回,此值设为 NULL。

mask,可选的掩膜操作,非零掩码元素用于标记待统计元素,需要与输入图像集有相同尺

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值