一、引言

模板匹配的作用在图像识别领域作用可大了。那什么是模板匹配?
模板匹配,就是在一幅图像中寻找另一幅模板图像最匹配(也就是最相似)的部分的技术。

说的有点抽象,下面给个例子说明就很明白了。

opencv模板匹配算法改进 opencv 模板匹配_模版

在上面这幅全明星照中,我们想找出姚明头像的位置,并把它标记出来,可以做到吗?

opencv模板匹配算法改进 opencv 模板匹配_模版_02


可以,这就是模板匹配的要做的事情。

其实模板匹配实现的思想也是很简单很暴力的,就是拿着模板图片(姚明头像)在原图(全明星照)中从左上至右下依次滑动,直到遇到某个区域的和模版非常相似,那么我们就认为该区域与模板匹配了。

二:用于模版匹配的函数

void matchTemplate( InputArray image,InputArray templ, OutputArray result, int method, InputArray mask = noArray() );

@param image:搜索对象图像 It must be 8-bit or 32-bit floating-point.

@param templ:模版,和image具有相同的数据类型,并且小于image

@param result:比较结果,必须是单通单32位浮点数

@param method:匹配方法,总共有六种,下面先贴上源码说明

@param mask:和templ具有相同的数据类型和大小,默认不设置

//! type of the template matching operation
enum TemplateMatchModes {
    TM_SQDIFF        = 0, //!< \f[R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2\f]
    TM_SQDIFF_NORMED = 1, //!< \f[R(x,y)= \frac{\sum_{x',y'} (T(x',y')-I(x+x',y+y'))^2}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f]
    TM_CCORR         = 2, //!< \f[R(x,y)= \sum _{x',y'} (T(x',y')  \cdot I(x+x',y+y'))\f]
    TM_CCORR_NORMED  = 3, //!< \f[R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f]
    TM_CCOEFF        = 4, //!< \f[R(x,y)= \sum _{x',y'} (T'(x',y')  \cdot I'(x+x',y+y'))\f]
                          //!< where
                          //!< \f[\begin{array}{l} T'(x',y')=T(x',y') - 1/(w  \cdot h)  \cdot \sum _{x'',y''} T(x'',y'') \\ I'(x+x',y+y')=I(x+x',y+y') - 1/(w  \cdot h)  \cdot \sum _{x'',y''} I(x+x'',y+y'') \end{array}\f]
    TM_CCOEFF_NORMED = 5  //!< \f[R(x,y)= \frac{ \sum_{x',y'} (T'(x',y') \cdot I'(x+x',y+y')) }{ \sqrt{\sum_{x',y'}T'(x',y')^2 \cdot \sum_{x',y'} I'(x+x',y+y')^2} }\f]
};



CV_TM_SQDIFF 平方差匹配法:该方法采用平方差来进行匹配;最好的匹配值为0;匹配越差,匹配值越大。


CV_TM_CCORR 相关匹配法:该方法采用乘法操作;数值越大表明匹配程度越好。


CV_TM_CCOEFF 相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。

(尾部带有NORMED的是归一化的方法,目的是为了减小光线 的影响)

CV_TM_SQDIFF_NORMED 归一化平方差匹配法
CV_TM_CCORR_NORMED 归一化相关匹配法
CV_TM_CCOEFF_NORMED 归一化相关系数匹配法

三:模板匹配的工作方式:
大致过程是这样的:通过在输入图像image上滑动图像块,对实际的图像块和模板图像templ进行匹配。 
假设我们有一张100x100的输入图像image,有一张10x10的模板图像templ,查找的过程是这样的: 
(1)从输入图像image的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像; 
(2)用临时图像和模板图像templ进行对比,对比结果记为c,存储在结果图像result的(0,0)处,即result在(0,0)处的像素值; 

(3)向右滑动切割图像块,重复(1)~(2)的步骤,并记录到结果图像result中;直到输入图像image的右下角。 

可以看出,如果原图的尺寸为W*H,模版的尺寸为w*h,那么result的尺寸为(W-w+1)*(H-h+1)

可见,直方图反向投影对比的是直方图,而模板匹配对比的是图像的像素值;模板匹配比直方图反向投影速度要快一些,但是有人认为直方图反向投影的鲁棒性会更好。

既然 result保存了匹配结果,那么我们如何通过result来获得最佳匹配区域呢?这就来了OPENCV中定位极值的函数:

void minMaxLoc(InputArray src, CV_OUT double* minVal,
                            CV_OUT double* maxVal = 0, CV_OUT Point* minLoc = 0,
                            CV_OUT Point* maxLoc = 0, InputArray mask = noArray());

@param src input single-channel array.输入的原图像,单通道


@param minVal pointer to the returned minimum value; NULL is used if not required. 返回最小值的指针,不需要设置为NULL即可


@param maxVal pointer to the returned maximum value; NULL is used if not required.返回最大值的指针,不需要设置为NULL即可


@param minLoc pointer to the returned minimum location (in 2D case); NULL is used if not required.返回最小值的位置的指针,不需要设置为NULL


@param maxLoc pointer to the returned maximum location (in 2D case); NULL is used if not required.返回最大值的位置的指针,不需要设置为NULL


@param mask optional mask used to select a sub-array.


简要说明: 
(1)minMaxLoc寻找矩阵src中最小值minVal及其位置minLoc;最大值maxVal及其位置maxLoc。 
(2)以上参数,若不需要求解,可置为NULL即可。 
(3)参数mask不晓得用法,可以忽略不管。

MinMaxLoc(result,&min_val,&max_val,&min_loc,&max_loc); 从result中提取最大值以及最大值的位置(即在result中该最大值max_val的坐标位置max_loc,即模板滑行时左上角的 坐标,类似于图中的坐标(x,y)。)

 由此得到:rect=cvRect(max_loc.x,max_loc.y,tmp->width,tmp->height);rect表示的矩形区域即是最佳的匹配区域。


四、范例(部分代码)

imshow("模版", dst);

	/******************确保模板大小小于roi********************/
	if (find_roi.rows < dst.rows || find_roi.cols < dst.cols)
	{
		printf("模版大于ROI");
		return false;
	}
		
	Mat right_mat;
	matchTemplate(find_roi, dst, right_mat, 0);
	minMaxLoc(right_mat, &minVal, &maxVal, &minLoc, &maxLoc);
	Point point = Point(minLoc.x + dst.cols / 2, minLoc.y + dst.rows / 2);
	rectangle(find_roi, Rect(minLoc.x, minLoc.y, dst.cols, dst.rows), Scalar(0, 0, 255), 1);
	circle(find_roi, point, 2, Scalar(255, 0, 0), 2);
imshow("find_roi", frame);



opencv模板匹配算法改进 opencv 模板匹配_模版_03



参考链接:



https://www.w3cschool.cn/opencv/opencv-pswj2dbc.html