在本教程中,您将学习如何:
使用 OpenCV 函数 matchTemplate() 搜索图像补丁和输入图像之间的匹配
使用 OpenCV 函数 minMaxLoc() 查找给定数组中的最大值和最小值(以及它们的位置)。
模板匹配是一种用于查找与模板图像(补丁)匹配(相似)的图像区域的技术。
虽然补丁patch必须是矩形,但可能并非所有矩形都是相关的。 在这种情况下,可以使用掩码来隔离应用于查找匹配的补丁patch部分。
我们需要两个主要组件:
源图像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 矩阵中定位最大值(或更低,取决于匹配方法的类型)
源图像Source image(I):我们期望在其中找到与模板图像匹配的图像
模板图像 Template image (T):将与源图像进行比较的补丁图像
掩膜图像Mask image (M):mask,对模板进行masks的灰度图
OpenCV 中可用的匹配方法有哪些?
好问题。 OpenCV 在函数 matchTemplate() 中实现模板匹配。 可用的方法有 6 种:
method=TM_SQDIFF
method=TM_SQDIFF_NORMED
method=TM_CCORR
method=TM_CCORR_NORMED
method=TM_CCOEFF
method=TM_CCOEFF_NORMED
这个程序有做了什么?
加载输入图像、图像补丁(模板)和可选的掩码
使用 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
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] << " []" << 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;
}
声明一些全局变量,例如图像、模板和结果矩阵,以及匹配方法和窗口名称:
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 );
使用输入图像测试我们的程序,例如:
和模板图像:
生成以下结果矩阵(第一行是标准方法 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,可选的掩膜操作,非零掩码元素用于标记待统计元素,需要与输入图像集有相同尺