模板匹配的概念:
在源图片中寻找与模板图片最匹配部分的技术。
模板匹配如何实现:
需要2幅图像:
- 原图像 (I): 在这幅图像里,我们希望找到一块和模板匹配的区域
- 模板 (T): 将和原图像比照的图像块
目标是检测最匹配的区域:
![示例程序040--模板匹配 ../../../../../_images/Template_Matching_Template_Theory_Summary.jpg](https://i-blog.csdnimg.cn/blog_migrate/1a0eece70a594c2278f470d308b05016.jpeg)
为了确定匹配区域, 要滑动模板图像和原图像进行 比较 :
![示例程序040--模板匹配 ../../../../../_images/Template_Matching_Template_Theory_Sliding.jpg](https://i-blog.csdnimg.cn/blog_migrate/1fc672160d3a5c4261ae4feae6b3f5ec.jpeg)
滑动的意思是图像块一次移动一个像素 (从左往右,从上往下). 在每一个位置, 都进行一次度量计算来表明它是 “好” 或 “坏” 地与那个位置匹配 (或者说块图像和原图像的特定区域有多么相似).
对于 T 覆盖在 I 上的每个位置,你把度量值 保存 到 结果图像矩阵 (R) 中. 在 R 中的每个位置
都包含匹配度量值:
![示例程序040--模板匹配 ../../../../../_images/Template_Matching_Template_Theory_Result.jpg](https://i-blog.csdnimg.cn/blog_migrate/3fbb32cbce255ab599f334609f12ad04.jpeg)
上图就是 TM_CCORR_NORMED 方法处理后的结果图像 R . 最白的位置代表最高的匹配. 正如您所见, 红色椭圆框住的位置很可能是结果图像矩阵中的最大数值, 所以这个区域 (以这个点为顶点,长宽和模板图像一样大小的矩阵) 被认为是匹配的.
我们使用函数 minMaxLoc 来定位在矩阵 R 中的最大值点 (或者最小值, 根据函数输入的匹配参数) .
用到的函数:
matchTemplate 提供了6中模板匹配算法
1 CV_TM_SQDIFF 平方差匹配法:该方法采用平方差来进行匹配;最好的匹配值为0;匹配越差,匹配值越大。
2 CV_TM_SQDIFF_NORMED 标准平方差匹配
3 CV_TM_CCORR 相关匹配法:该方法采用乘法操作;数值越大表明匹配程度越好。
4 CV_TM_CCORR_NORMED 归一化相关匹配法
5 CV_TM_CCOEFF 相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。
6 CV_TM_CCOEFF_NORMED 归一化相关系数匹配法
通常,随着从简单的测量(平方差)到更复杂的测量(相关系数),我们可获得越来越准确的匹配(同时也意味着越来越大的计算代价). 最好的办法是对所有这些设置多做一些测试实验,以便为自己的应用选择同时兼顾速度和精度的最佳方案.
后三种的匹配效果比较完美,计算时间也越多。
代码及注释:
// 048 模板匹配 2.3版.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
/// 全局变量
Mat img; Mat templ; Mat result;
char* image_window = "Source Image";
char* result_window = "Result window";
int match_method;
int max_Trackbar = 5; //6种不同的匹配算法
/// 函数声明
void MatchingMethod( int, void* );
int main( int argc, char** argv )
{
img=imread("tempMatch_src.bmp");
templ=imread("tempmatch_temp.bmp");
namedWindow( image_window, CV_WINDOW_AUTOSIZE );
namedWindow( result_window, CV_WINDOW_AUTOSIZE );
/// 创建滑动条
char* trackbar_label = "Method:";
createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
MatchingMethod( 0, 0 );
waitKey(0);
return 0;
}
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_cols, result_rows, CV_32FC1 );
/// 进行匹配,匹配结果存入result矩阵
matchTemplate( img, templ, result, match_method );
//对结果进行归一化
normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
/// 通过函数 minMaxLoc ,找到最大、最小值及其位置。
double minVal; double maxVal; Point minLoc; Point maxLoc;
Point matchLoc;
minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
/// 对于方法 SQDIFF 和 SQDIFF_NORMED, 越小的数值代表更高的匹配结果. 而对于其他方法, 数值越大匹配越好
if( match_method == CV_TM_SQDIFF || match_method == CV_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;
}
运行结果:
![示例程序040--模板匹配 示例程序040--模板匹配](https://i-blog.csdnimg.cn/blog_migrate/8b60e604eb4dcecca12f712e0b0ec678.jpeg)
![示例程序040--模板匹配 示例程序040--模板匹配](https://i-blog.csdnimg.cn/blog_migrate/3ceef8bbcb47045ce9ced92c04eb5154.jpeg)
可以看到效果并不好,可能是图像前期没有做处理,比如二值等