开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天
1 模板匹配的概念与原理
模板匹配是一项在一幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术。在OpenCV中,模板匹配由MatchTemplate()函数完成。需要注意,模板匹配不是基于直方图的,而是通过在输入图像上滑动图像块,对实际的图像块和输入图像进行匹配的一种匹配方法。
如下图所示,通过一个人脸“图像模板”,在整幅输入图像上移动这张“脸”,来寻找和这张脸相似的最优匹配。
2 实现模板匹配:matchTemplate()函数
matchTemplate()用于匹配出和模板重叠的图像区域。
C++:
void matchTemplate(InputArray image, InputArray templ,OutputArray result,int method)
- 第一个参数,InputArray 类型的image,待搜索的图像,且需为8位或32位浮点型图像。
- 第二个参数,InputArray类型的templ,搜索模板,需和源图片有一样的数据类型,且尺寸不能大于源图像。
- 第三个参数,OutputArray 类型的 result,比较结果的映射图像。其必须为单通道、32位浮点型图像.如果图像尺寸是WxH而templ 尺寸是wxh,则 此参数result一定是(W-w+1)x(H-h+1).
- 第四个参数,int类型的method,指定的匹配方法,OpenCV为我们提供了如下6种图像匹配方法可供使用。
1.平方差匹配法 method=TM_SQDIFF
这类方法利用平方差来进行匹配,最好匹配为0。而若匹配越差,匹配值则越大。
2.归一化平方差匹配法 method=TM_SQDIFF_NORMED
3.相关匹配法 method=TM_CCORR 这类方法采用模板和图像间的乘法操作,所以较大的数表示匹配程度较高,0标识最坏的匹配效果。
4.归一化相关匹配法 method=TM_CCORR_NORMED
5.系数匹配法 method=TM_CCOEFF
这类方法将模版对其均值的相对值与图像对其均值的相关值进行匹配,1表示完美匹配,—1表示糟糕的匹配,而0表示没有任何相关性(随机序列)。
上述的6个宏,在OpenCV2依然是可以加上“CV_”前缀,分别写作:CV_TM_SQDIFF、CV_TM_SQDIFF_NORMED、CV_TM_CCORR、CV TM_CCORR_NORMED、CV_TM_CCOEFF、CV_TM_CCOEFF NORMED。
通常,随着从简单的测量(平方差)到更复杂的测量(相关系数),我们可获 得越来越准确的匹配。然而,这同时也会以越来越大的计算量为代价。比较科学的办法是对所有这些方法多次测试实验,以便为自己的应用选择同时兼顾速度和精度的最佳方案。
3 综合示例:模板匹配
讲解完成基本概念和函数用法,下面依然是放出一个经过详细注释的示例程序源代码,演示了不同的模板匹配方法对人脸进行检测。
源码
//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//--------------------------------------------------------------------------------------
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
//-----------------------------------【宏定义部分】--------------------------------------------
// 描述:定义一些辅助宏
//--------------------------------------------------------------------------------------------
#define WINDOW_NAME1 "【原始图片】" //为窗口标题定义的宏
#define WINDOW_NAME2 "【匹配窗口】" //为窗口标题定义的宏
//-----------------------------------【全局变量声明部分】------------------------------------
// 描述:全局变量的声明
//-----------------------------------------------------------------------------------------
Mat g_srcImage; Mat g_templateImage; Mat g_resultImage;
int g_nMatchMethod;
int g_nMaxTrackbarNum = 5;
//-----------------------------------【全局函数声明部分】--------------------------------------
// 描述:全局函数的声明
//-----------------------------------------------------------------------------------------------
void on_Matching( int, void* );
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//---------------------------------------------------------------------------------------------
int main( )
{
//【0】改变console字体颜色
system("color 1F");
//【1】载入原图像和模板块
g_srcImage = imread( "1.jpg", 1 );
g_templateImage = imread( "2.jpg", 1 );
//【2】创建窗口
namedWindow( WINDOW_NAME1, CV_WINDOW_AUTOSIZE );
namedWindow( WINDOW_NAME2, CV_WINDOW_AUTOSIZE );
//【3】创建滑动条并进行一次初始化
createTrackbar( "方法", WINDOW_NAME1, &g_nMatchMethod, g_nMaxTrackbarNum, on_Matching );
on_Matching( 0, 0 );
waitKey(0);
return 0;
}
//-----------------------------------【on_Matching( )函数】--------------------------------
// 描述:回调函数
//-------------------------------------------------------------------------------------------
void on_Matching( int, void* )
{
//【1】给局部变量初始化
Mat srcImage;
g_srcImage.copyTo( srcImage );
//【2】初始化用于结果输出的矩阵
int resultImage_cols = g_srcImage.cols - g_templateImage.cols + 1;
int resultImage_rows = g_srcImage.rows - g_templateImage.rows + 1;
g_resultImage.create( resultImage_cols, resultImage_rows, CV_32FC1 );
//【3】进行匹配和标准化
matchTemplate( g_srcImage, g_templateImage, g_resultImage, g_nMatchMethod );
normalize( g_resultImage, g_resultImage, 0, 1, NORM_MINMAX, -1, Mat() );
//【4】通过函数 minMaxLoc 定位最匹配的位置
double minValue; double maxValue; Point minLocation; Point maxLocation;
Point matchLocation;
minMaxLoc( g_resultImage, &minValue, &maxValue, &minLocation, &maxLocation, Mat() );
//【5】对于方法 SQDIFF 和 SQDIFF_NORMED, 越小的数值有着更高的匹配结果. 而其余的方法, 数值越大匹配效果越好
if( g_nMatchMethod == CV_TM_SQDIFF || g_nMatchMethod == CV_TM_SQDIFF_NORMED )
{ matchLocation = minLocation; }
else
{ matchLocation = maxLocation; }
//【6】绘制出矩形,并显示最终结果
rectangle( srcImage, matchLocation, Point( matchLocation.x + g_templateImage.cols , matchLocation.y + g_templateImage.rows ), Scalar(0,0,255), 2, 8, 0 );
rectangle( g_resultImage, matchLocation, Point( matchLocation.x + g_templateImage.cols , matchLocation.y + g_templateImage.rows ), Scalar(0,0,255), 2, 8, 0 );
imshow( WINDOW_NAME1, srcImage );
imshow( WINDOW_NAME2, g_resultImage );
}
运行此程序,可以得到三个窗口。说明窗口、附有滑动条的原始图窗口以及根据滑动条数值变化的匹配效果图窗口。
首先,让我们通过说明窗口了解此程序的使用方法。如下图图所示。
附有滑动条的原始窗口
平方差匹配法(SQDIFF)
归一化平方差匹配法(SQDIFF NORMED)
相关匹配法(TM CCORR)
归一化相关匹配法(TM CCORR NORMED)
相关系数匹配法(TM COEFF)
归一化相关系数匹配法(TM COEFF NORMED)
通过上述实验可知,除了相关匹配法(TM CCORR)得到了错误的匹配结果之外,其他5种匹配方式都匹配较为准去。