概述
模板匹配是一种最原始、最基本的模式识别方法,研究某一特定对象物的图案位于图像的什么地方,进而识别对象物,这就是一个匹配问题。它是图像处理中最基本、最常用的匹配方法。模板匹配具有自身的局限性,主要表现在它只能进行平行移动,若原图像中的匹配目标发生旋转或大小变化,该算法无效。
相关法
以8位灰度图像为例,模板T(m,n)叠放在被搜索图S(W,H)上平移,模板覆盖被搜索图的那块区域叫子图
,i,j为子图左下角在被搜索图S上的坐标,搜索范围是:1≤i≤W-n,1≤j≤H-m。可以用下式衡量T和
的相似性:
将其归一化,得模板匹配的相关系数:
当模板和子图一样时,相关系数R(i,j)=1,在被搜索图S中完成全部搜索后,找出R的最大值
,其对应的子图
即为匹配目标。显然,用这种公式做图像匹配运算量最大、速度最慢。 [1]
误差法
误差法即衡量T和
的误差,其公式为:
其中E(i,j)为最小值处即为匹配目标。为提高计算速度,取一误差阈值
,当E(i,j)>
时便停止该点的计算,继续计算下一点。
模板越大,匹配速度越慢;模板越小,匹配速度越快。 [1]
二次匹配误差算法
二次匹配误差算法中匹配分两次进行。
第一次匹配是粗略匹配。取模板的隔行隔列数据,即四分之一的模板数据,在被搜索图上进行隔行隔列扫描匹配,即在原图的四分之一范围内匹配。由于数据量大幅度减少,匹配速度显著提高。
误差阈值
:
,其中,
为各点平均的最大误差,一般取40~50即可;m,n分别为模板的长和宽。
第二次匹配是精确匹配。在第1次误差最小点
的领域内,即在对角点为
,
的矩形内进行搜索匹配,得到最后结果。实验结果表明,二次匹配误差法的速度比其他算法快10倍左右。 [1]
编辑
高速模板匹配法
与边缘检测中使用的模板不同,模板匹配中使用的模板相当于(8×8~32×32)。从大幅面图像寻找与模板最一致的对象,计算量大,要哦花费相当多的时间。为使模板匹配高速化,Barnea等人提出了序贯相似性检——SSDA法(sequential similiarity detection algorithm)。 [2]
SSDA法计算图像f(x,y)在像素(u,v)的非相似度m(u,v)为:
以m(u,v)作为匹配尺度。式中(u,v)表示的不是模板与图像重合部分的中心坐标,而是重合部分左上角像素坐标。模板的大小为:n×m。
如果在图像(u,v)处有和模板一致的图案,则m(u,v)值很小,相反则较大。特别是在模板和图像重叠部分完全不一致的场合下,如果在模板内的各像素与图像重合部分对应像素的差的绝对值依次增加下去,其和就会急剧地增大。因此,在做加法的过程中,如果差的绝对值部分和超过了某一阈值时,就认为这位置上不存在和模板一致的图案,从而转移到下一个位置上计算m(u,v)。由于计算m(u,v)只是加减运算,而且这一计算在大多数位置中途便停止了,因此能大幅度地缩短计算时间,提高匹配速度。 [2]
还有一种把在图像上的模板移动分为粗检索和细检索两个阶段进行的匹配方法。首先进行粗检索,它不是让模板每次移动一个像素,而是每隔若干个像素把模板和图像重叠,并计算匹配的尺度,从而求出对象物大致存在的范围。然后,在这个大致范围内,让模板每隔一个像素移动一次,根据求出的匹配尺度确定对象所在的位置。这样,整体上计算模板匹配的次数减少,计算时间短,匹配速度就提高了。但是使用这种方法存在漏掉图像中最适当位置的危险性。 [2]
高精度定位的模板匹配
一般的图像中有较强的自相关性,因此,进行模板匹配计算的相似度就在以对象物存在的地方为中心形成平缓的峰。这样,即使模板匹配时从图像对象物的真实位置稍微离开一点,也表现出相当高的相似度。上面介绍的粗精检索高速化恰好利用了这一点。但为了求得对象物在图像中的精确位置,总希望相似度分布尽可能尖锐一些。
为了达到这一目的,人们提出了基于图案轮廓的特征匹配方法。图案轮廓的匹配与一般的匹配法比较,相似度表现出更尖锐的分布,从而利于精确定位。 [2]
一般来说,在检测对象的大小和方向未知的情况下进行模板匹配,必须具备各式各样大小和方向的模板,用各种模板进行匹配,从而求出最一致的对象及其位置。 [2]
另外,在对象的形状复杂时,最好不把整个对象作为一个模板,而是把对象分割成几个图案,把各个分图案作为模板进行匹配,然后研究分图案之间的位置关系,从而求图像中对象的位置。这样即使对象物的形状稍微变化,也能很好地确定位置。 [2]
编辑
在应用模板匹配法进行图像匹配时,应注意以下几点:
(1)归一化互相关注进行匹配,模板应在(M-P+1)×(N-Q+1)个参考位置上进行相关计算,计算量非常大,必要时可以采用序贯相似检验算法、幅度排序相关算法、FFT相关算法、分层搜索序贯判决等方法对其进行改进,以提高运算速度。
(2)模板匹配具有自身的局限性,主要表现在它只能进行平行移动,若原图像中的匹配目标发生旋转或大小变化,该算法无效。
(3)若原图像中要匹配的目标只有部分可见,该算法也无法完成匹配。 [3]
参考资料
定义:
模板匹配是在一副图像中寻找到与给定目标模板图像的技术。
在匹配过程中有两个主要因素:原始图像I,模板图像T。
为了在原始图像I上检测到与模板图像T最匹配的区域,需要在原始图像I上滑动模板窗口,每次需要将模板图像T在原始图像I上滑动一个像素大小(从左至右,从上至下),每次移动后计算出其相似度来表征匹配结果的好与坏。
将每个位置的匹配结果存储在R矩阵中,该矩阵的每一个点的亮度表示与模板图像T的匹配程度。
通过minMaxLoc函数找到R矩阵中的最大值、最小值。
匹配方法:
常见的相似度匹配方法:
a)method=CV_TM_SQDIFF,平方差匹配法
b)method=CV_TM_SQDIFF_NORMED,归一化平方差匹配法
c)method=CV_TM_CCORR,相关匹配法
d)method=CV_TM_CCORR_NORMED,归一化相关匹配法
e)method=CV_TM_CCOEFF,系数匹配法
f)method=CV_TM_CCOEFF_NOR,归一化相关系数匹配法
相似性匹配结果矩阵R的大小:
模板图像在原始图像上移动一个像素,并将计算出来的相似度数据存储在R矩阵中,那么在整个原始图像上每行横向移动只需移动raw_img.cols-mask_img_cols+1次;每列纵向移动只需移动raw_img.cols-mask_img.cols+1次;
OpenCV模板匹配函数:matchTemplate()
格式:void matchTemplate(inputArray image,inputArray templ,outputArray result,int method)
功能:用于匹配出和模板重叠的图像区域
参数:
inputArray image,待搜索的图像,且需为8位或32位浮点型图像;
inputArray templ,搜索模板,需和源图像有一样的数据类型,且尺寸不能大于源图像。
outputArray result,比较结果的映射图像,其必须为单通道、32位浮点型图像,如果图像尺寸是W*H,而templ的尺寸是w*h,则此参数result一定是(W-w+1)x(H-h+1) ;
int method,指定的匹配方法;
常见的匹配方法:
(1)平方差匹配法 method=TM_SQDIFF
(2)归一化平方差匹配法 method=TM_SQDIFF_NORMED
(3)相关匹配法 method=TM_CCORR
(4)归一化相关匹配法 method=TM_CCORR_NORMED
(5)系数匹配法 method=TM_CCOEFF
(6)归一化相关系数匹配法 method=TM_CCOEFF_NORMED
【注】:模板匹配的方法适用于模板很小,而原图像很大的情况下,不然得到的图像会很小,而且只是原图像左上的一部分,如果要检测的物体在右下角,可能在目标图中框不完全要检测的物体。
OpenCV函数:minMaxLoc()
功能:找到矩阵中全局最大值和最小值;
OpenCV模板匹配例程:
#include
using namespace std;
using namespace cv;
void match(Mat _raw_img,Mat _mask_img);
int main( int argc, char** argv )
{
Mat raw_img;
Mat mask_img;
raw_img = imread("raw.jpg" );
if( raw_img.empty() )
{
cerr << "Cannot load file " << "raw.jpg" << endl;
return -1;
}
mask_img = imread("mask.jpg"); //读取模板图像"mask.jpg"
if( mask_img.empty())
{
cerr << "Cannot load file " <<"mask.jpg" << endl;
return -1;
}
match( raw_img, mask_img);
imshow( "result", raw_img );
imshow( "template", mask_img ); //显示结果
/* wait until user press a key to exit */
waitKey( 0 );
return 0;
}
void match(Mat _raw_img,Mat _mask_img)
{
Mat res_img;
Mat mask;
Point min_loc_p, max_loc_p;
double min_val, max_val;
int res_cols = _raw_img.cols - _mask_img.cols + 1;
int res_rows = _raw_img.rows - _mask_img.rows + 1;
res_img = cvCreateImage( cvSize(res_cols,res_rows), IPL_DEPTH_32F, 3 );
matchTemplate( _raw_img, _mask_img, res_img,CV_TM_SQDIFF_NORMED );
//归一化平方差匹配法
minMaxLoc( res_img, &min_val, &max_val, &min_loc_p, &max_loc_p,mask);
rectangle( _raw_img, cvPoint( min_loc_p.x, min_loc_p.y ), cvPoint( min_loc_p.x + _mask_img.cols, min_loc_p.y + _mask_img.rows ), cvScalar( 255, 255, 255, 0 ), 1, 0, 0 );
}
1 #include "opencv2/opencv.hpp"
2 #include
3 using namespace std;
4 using namespace cv;
5
6 void main()
7 {
8 Mat temp=imread("E://mu.jpg");
9 Mat src=imread("E://lena.jpg");
10 Mat dst=src.clone();
11 imshow("temp",temp);
12
13 int width=src.cols-temp.cols+1;//result宽度
14 int height=src.rows-temp.rows+1;//result高度
15
16 Mat result(height,width,CV_32FC1);//创建结果映射图像
17 //matchTemplate(srcImg, templateImg, resultImg, CV_TM_SQDIFF); //平方差匹配法(最好匹配0)
18 //matchTemplate(srcImg, templateImg, resultImg, CV_TM_SQDIFF_NORMED); //归一化平方差匹配法(最好匹配0)
19 //matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCORR); //相关匹配法(最坏匹配0)
20 //matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCORR_NORMED); //归一化相关匹配法(最坏匹配0)
21 //matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCOEFF); //系数匹配法(最好匹配1)
22 matchTemplate(src,temp,result,CV_TM_CCOEFF_NORMED);//化相关系数匹配,最佳值1
23 imshow("result",result);
24 normalize(result,result,0,1,NORM_MINMAX,-1);//归一化到0-1范围
25
26 double minValue,maxValue;
27 Point minLoc,maxLoc;
28 minMaxLoc(result,&minValue,&maxValue,&minLoc,&maxLoc);
29 cout<<"minValue="<
注意:result的长宽正好是原图-模板图的长宽,result图中白亮程度表示匹配程度
1 #include "opencv2/opencv.hpp"
2 #include
3 using namespace std;
4 using namespace cv;
5
6 void main()
7 {
8 Mat frame,resultImg;
9 Mat templateImg = imread("E://green.jpg");
10 VideoCapture cap("E://1.mp4");
11 if(!cap.isOpened())
12 return;
13 int resultImg_cols,resultImg_rows;
14
15 while(1)
16 {
17 cap>>frame;
18 if(frame.empty()) break;
19 Mat showImg = frame.clone();
20 resultImg_cols = frame.cols - templateImg.cols + 1;
21 resultImg_rows = frame.rows - templateImg.rows + 1;
22 resultImg.create(resultImg_cols, resultImg_rows, CV_32FC1);
23 matchTemplate(frame, templateImg, resultImg, CV_TM_CCOEFF_NORMED); //化相关系数匹配法(最好匹配1)
24 normalize(resultImg, resultImg, 0, 1, NORM_MINMAX);
25
26 double minValue, maxValue;
27 Point minLoc, maxLoc;
28 Point matchLoc;
29
30 minMaxLoc(resultImg, &minValue, &maxValue, &minLoc, &maxLoc);
31 cout<<"max_value= "<=0.7)
34 rectangle(showImg, maxLoc, Point(maxLoc.x + templateImg.cols, maxLoc.y + templateImg.rows), Scalar(0, 255, 0), 2);
35 imshow("frame", frame);
36 imshow("result", showImg);
37 if(27 == waitKey(10))
38 break;
39 }
40 destroyAllWindows();
41
42 waitKey(0);
43 }
1 #include "opencv2/opencv.hpp"
2 #include
3 using namespace std;
4 using namespace cv;
5
6 void main()
7 {
8 Mat srcImg = imread("E://src.png");
9 Mat templateImg = imread("E://temp.png");
10 Mat resultImg;
11 Mat showImg = srcImg.clone();
12
13 int resultImg_cols = srcImg.cols - templateImg.cols + 1;
14 int resultImg_rows = srcImg.rows - templateImg.rows + 1;
15
16 resultImg.create(resultImg_cols, resultImg_rows, CV_32FC1);
17 matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCOEFF_NORMED); //化相关系数匹配法(最好匹配1)
18 normalize(resultImg, resultImg, 0, 1, NORM_MINMAX);
19 Mat midImg = resultImg.clone();
20
21 //多目标模板匹配---方法一
22 /*double matchValue;
23 int count0=0;
24 int tempW=0, tempH=0;
25 char matchRate[10];
26
27 for(int i=0; i(i, j);
32 sprintf(matchRate, "%0.2f", matchValue);
33 if(matchValue>=0.85 && (abs(j - tempW)>5) && (abs(i - tempH)>5) )
34 {
35 count0++;
36 putText(showImg, matchRate, Point(j-5, i-5), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 1);
37 rectangle(showImg, Point(j, i), Point(j + templateImg.cols, i + templateImg.rows), Scalar(0, 255, 0), 2);
38 tempW = j;
39 tempH = i;
40 }
41 }
42 }
43 cout<<"count="< resultImg.cols - 1 || endY > resultImg.rows - 1)
65 {
66 endX = resultImg.cols - 1;
67 endY = resultImg.rows- 1;
68 }
69 Mat temp = Mat::zeros(endX - startX, endY - startY, CV_32FC1);
70 //Mat ROI = resultImg(Rect(Point(startX, startY), temp.cols, temp.rows));
71 temp.copyTo(resultImg(Rect(startX, startY, temp.cols, temp.rows)));
72 minMaxLoc(resultImg, &minValue, &maxValue, &minLoc, &maxLoc);
73 if(maxValue<0.8) break;
74
75 cout<<"max_value= "<