一、引言
模板匹配的作用在图像识别领域作用可大了。那什么是模板匹配?
模板匹配,就是在一幅图像中寻找另一幅模板图像最匹配(也就是最相似)的部分的技术。
说的有点抽象,下面给个例子说明就很明白了。
在上面这幅全明星照中,我们想找出姚明头像的位置,并把它标记出来,可以做到吗?
可以,这就是模板匹配的要做的事情。
其实模板匹配实现的思想也是很简单很暴力的,就是拿着模板图片(姚明头像)在原图(全明星照)中从左上至右下依次滑动,直到遇到某个区域的和模版非常相似,那么我们就认为该区域与模板匹配了。
二:用于模版匹配的函数
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);
参考链接:
https://www.w3cschool.cn/opencv/opencv-pswj2dbc.html