1.概述
利用计算机视觉技术对图像进行处理,通常会用到图像的匹配,图像匹配是指图像之间的比较,得到不同图像之间的相似度,在机器识别的过程中把不同传感器或同一传感器在不同时间、不同成像条件下对同一景物获得的两幅或多幅图像在空间上对准,或根据已知模式到另一幅图中寻找对应的模式。匹配方法大体分为基于灰度和基于特征两类,其中基于特征的配准方法研究较多。
基于灰度的模板匹配方法原理简单且在光照良好的条件下可以得到比较满意的匹配结果,故在本方案中根据输入的模板图像从采集图像中搜寻可进一步匹配的目标可采用此方法。
基于特征的匹配方法要先提取各个图像中的特征再完成特征间的匹配,通过匹配的特征建立图像间的映射关系求出匹配图像。一般而言提取特征点相对容易,能对图像间的分辨率、旋转、平移、光照变化等保持不变,故在利用模板匹配得到抓取的图像后进一步进行特征匹配。
基于特征匹配的方法有很多种如:FAST、HARRIS、SIFT、SURF、SUSAN等。其中SIFT算法由D.G.Lowe于1999年提出,2004年完善总结。SIFT是一种鲁棒性好的尺度不变特征描述方法,但SIFT算法计算数据量大、时间复杂度高、算法耗时长。
其中OpenCV也提供了很多图像匹配的方法,这篇文章将首先介绍一下基于图像模板进行图像匹配
2.模板匹配原理
模板匹配是通过模板在采集到的原图像进行滑动寻找与模板图像相似的目标。模板匹配不是基于直方图的方式,而是基于图像的灰度匹配。其基本原理是逐像素的把一个以一定大小的实时图像窗口的灰度矩阵与参考图像的所有可能的串口灰度阵列,按照某种相似度量方法进行搜索比较的匹配方法,从理论上说就是采用图像相关技术。
图1模板选取
图2模板匹配
如图1所示,为了利用模板匹配从源图像中得到匹配区域,从源图像选取该区域作为进行匹配的模板。模板从源图像左上角开始每次以一个像素点为单位进行移动,每到达一个位置,就会计算模板矩阵和源图像当前位置矩阵匹配的“好”“坏”程度即两个矩阵的相似程度,如图2所示。
模板滑动与源图像匹配过程中,将模板和当前模板覆盖区域的矩阵的计算结果存储在矩阵(R)中。R中每一个位置(x, y)都包含了匹配矩阵的计算结果。在OpenCV中提供了6种匹配度量方法。
(1).平方差匹配法CV_TM_SQDIFF
(2)归一化平方差匹配法CV_TM_SQDIFF_NORMED
(3)相关匹配法CV_TM_CCORR
(4)归一化相关匹配法CV_TM_CCORR_NORMED
(5)系数匹配法CV_TM_CCOEFF
其中
(6)化相关系数匹配法CV_TMCCOEFF_NORMED
通常来讲,随着从简单测量方法(平方差)到更复杂的测量方法(相关系数法),我们可以获得越来越准确的匹配。然而这同时也会以越来越大的计算量为代价。对于选取何种方法,针对不同的匹配情况进行对此分析比较,选取更适合自己应用场景同时兼顾速度和精度的最佳方案。
注意
值得注意的是对于方法SQDIFF和SQDIFF_NORMED两种方法来讲,越小的值就有着更高的匹配结果,而其余的方法则是数值越大匹配效果越好
3.OpenCV中提供的API
OpenCV中提供了matchTemplate()并配合minMaxLoc()函数实现图像的模板匹配过程。通过matchTemplate()函数根据输入模板搜寻输入图像中与模板相似的地方,获得匹配结果图像。通过minMaxLoc()函数来找到最大值和最小值.
matchTemplate
void cv::matchTemplate ( InputArray image,
InputArray templ,
OutputArray result,
int method,
InputArray mask = noArray()
)
参数解释:
InputArray Image: 待搜索的图像,且图像必须为8-bit或32-bit的浮点型图像
InputArray templ: 用于进行模板匹配的模板图像,类型和原图像一致,但是尺寸不能大于原图像
OutputArray result: 模板搜索结果输出图像,必须为单通道32-bit位浮点型图像,如果图像尺寸是WxH而template尺寸是wxh,则此参数result一定是(W-w+1)x(H-h+1)
int method: 模板匹配计算类型,在匹配原理中已经介绍过这六种方法了,这里不再赘述
InputArray mask=noArray(): 图像匹配时用的掩膜板,必须和模板图像有相同的数据类型和尺寸
minMaxLoc()
void cv::minMaxLoc ( InputArray src,
double * minVal,
double * maxVal = 0,
Point * minLoc = 0,
Point * maxLoc = 0,
InputArray mask = noArray()
)
参数解释:
InputArray src:输入的单通道数组
*double minVal:**double类型指针,返回最小值,如果没有定义返回NULL
double* maxVal:同上,返回最大值
Point* minLoc=0:Point类型的指针,在二维图像中返回最小值的位置坐标,如果没有定义返回NULL
*Point maxLoc=0: **Point同上,返回最大位置坐标
InputArray mask=noArray():可选掩膜板
4.示例代码
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
//定义全局变量
Mat srcImage, templateImage, dstImage;
const int trackbar_method_maxValue = 5;
int trackbar_method;
//定义回调函数
void method(int, void*);
int main()
{
srcImage=imread("src.jpg");
templateImage=imread("template.jpg");
//判断文件是否加载成功
if(srcImage.empty() || templateImage.empty())
{
cout << "图像加载失败!" << endl;
return -1;
}
else
cout << "图像加载成功..." << endl << endl;
namedWindow("原图像", WINDOW_AUTOSIZE);
namedWindow("模板图像", WINDOW_AUTOSIZE);
imshow("原图像", srcImage);
imshow("模板图像", templateImage);
//定义轨迹条参数
trackbar_method=1;
char mathodName[50];
namedWindow("匹配图像", WINDOW_AUTOSIZE);
sprintf(mathodName, "匹配方式%d\n 0:SQDIFF\n 1:SQDIFF_NORMED\n 2:TM_CCORR\n 3:TM_CCORR_NORMEND\n 4:TM_COEFF\n 5:TM_COEFF_NORMED", trackbar_method_maxValue);
createTrackbar(mathodName, "匹配图像",&trackbar_method, trackbar_method_maxValue,method);
method(trackbar_method, 0);
waitKey(0);
return 0;
}
void method(int, void*)
{
Mat display;
srcImage.copyTo(display);
//创建输出矩阵
int dstImage_rows=srcImage.rows-templateImage.rows + 1;
int dstImage_cols=srcImage.cols-templateImage.cols + 1;
dstImage.create(dstImage_rows, dstImage_cols, srcImage.type());
matchTemplate(srcImage, templateImage, dstImage,trackbar_method); //模板匹配
normalize(dstImage, dstImage, 0, 1, NORM_MINMAX); //归一化处理
//通过minMaxLoc定位最佳匹配位置
double minValue, maxValue;
Point minLocation, maxLocation;
Point matchLocation;
minMaxLoc(dstImage, &minValue, &maxValue, &minLocation, &maxLocation, Mat());
//对于方法SQDIFF和SQDIFF_NORMED两种方法来讲,越小的值就有着更高的匹配结果
//而其余的方法则是数值越大匹配效果越好
if(trackbar_method==CV_TM_SQDIFF||trackbar_method==CV_TM_SQDIFF_NORMED)
{
matchLocation=minLocation;
}
else
{
matchLocation=maxLocation;
}
rectangle(display, matchLocation, Point(matchLocation.x+templateImage.cols, matchLocation.y+templateImage.rows),Scalar(0,0,255));
imshow("匹配图像", display);
}
程序运行结果如下