模板匹配就是在整个图像区域发现与给定图像最相似的小块区域,所以模板匹配首先需要一个模板图像,另外需要一个待检测图像:
- 在待检测图像上,从左到右,从上到下,计算模板图像与重叠子图像的匹配度(相似度),匹配度(相似度)越大,两者相同的可能性越大。
- 对于每一个位置将计算的相似结果保存在矩阵 R 中。如果输入图像的大小为 WxH 且模板图像的大小为 wxh,则输出矩阵 R 的大小为 (W-w+1)x(H-h+1) 。
- 获得 R 后,从 R 中找出匹配度最高的位置,那么该位置对应的区域就是最匹配的,区域为以该点为顶点,长宽和模板图像一样大小的矩阵。)
OpenCV函数
matchTemplate
将模板与重叠的图像区域进行比较。
使用指定的方法将大小为 w×h 的重叠矩阵与 templ 进行比较,并将比较结果存储在 result 中。
函数完成比较后,可以使用 minMaxLoc 函数找到最佳匹配项,作为全局最小值(使用TM_SQDIFF时)或最大值(使用TM_CCORR或TM_CCOEFF时)。 在彩色图像的情况下,分子的模板求和和分母中的每个和在所有通道上进行,每个通道使用单独的平均值。 即,该函数可以获取彩色模板和彩色图像。 结果仍然是单通道图像,更易于分析。
enum cv::TemplateMatchModes {
cv::TM_SQDIFF = 0,
cv::TM_SQDIFF_NORMED = 1,
cv::TM_CCORR = 2,
cv::TM_CCORR_NORMED = 3,
cv::TM_CCOEFF = 4,
cv::TM_CCOEFF_NORMED = 5
}
void cv::matchTemplate( InputArray image,
InputArray templ,
OutputArray result,
int method,
InputArray mask = noArray()
)
//Python:
result = cv.matchTemplate(image, templ, method[, result[, mask]])
参数解释
参数 | 解释 |
image | 源图像,要搜索的图像,8位或32位浮点。 |
templ | 搜索的模板,不大于源图像并且具有相同的数据类型。 |
result | 比较结果图,单通道32位浮点。 如果 image 为 W×H 并且 templ 为 w×h ,则结果为 (W-w + 1)×(H-h + 1)。 |
method | 比较的方法 |
mask | 搜索模板的掩码。 必须与 templ 具有相同的数据类型和大小。默认情况下未设置。 当前,仅支持TM_SQDIFF 和 TM_CCORR_NORMED 方法。 |
比较方法:
- TM_SQDIFF:平方差匹配法,该方法采用平方差进行匹配,最好的匹配值为0;匹配越差,匹配值越大
- TM_SQDIFF_NORMED:归一化平方差匹配法
- TM_CCORR:相关匹配法,该方法采用乘法操作,数值越大表示匹配程度越好
- TM_CCORR_NORMED:归一化相关匹配法
- TM_CCOEFF:相关系数匹配法,1表示完美的匹配,-1表示最差的匹配
- TM_CCOEFF_NORMED:归一化相关系数匹配法
在 OpenCV 的实现中采用了傅里叶变换,速度会快很多。OpenCV 源码路径:modules/imgproc/src/templmatch.cpp
。
简单说一下 OpenCV 源码的方法:
对于 TM_CCORR 方法,
对于 TM_SQDIFF:
对于 TM_CCOEFF:
其中 为源图像中模板图像对应区域的和,
而对于以上三个公式中的
对于
对于 TM_SQDIFF_NORMED,TM_CCORR_NORMED 的分母部分都可以通过图像的(平方)积分来实现。
对于 TM_CCOEFF_NORMED 的分母:
其中 为模板图像的方差,
通过以上的分析,使用不同的方法计算,可以加快模板匹配的速度。
C++示例
string srcWindow = "src";
string temWindow = "tem";
string matchWindow = "match";
string resultWindow = "result";
int matchMethod = 5;
int main()
{
string outDir = "./";
Mat src = imread("src.jpg");
Mat tem = imread("tem.jpg");
Mat result;
Mat matchResult;
// 模板匹配
matchTemplate(src, tem, result, matchMethod);
// 获取最大值,最小值,以及对应的点
Point minLoc, maxLoc, temLoc;
double minVal, maxVal;
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);
if(matchMethod == 0 || matchMethod == 1)
{
temLoc = minLoc;
}
else
{
temLoc = maxLoc;
}
// 获取匹配到的图像
matchResult = src(Range(temLoc.y, temLoc.y+tem.rows), Range(temLoc.x, temLoc.x+tem.cols));
// 在原图画矩形
rectangle(src, Rect(temLoc.x, temLoc.y, tem.cols, tem.rows), Scalar(0, 0, 255), 2);
imshow(resultWindow, result);
imshow(srcWindow, src);
imshow(matchWindow, matchResult);
imshow(temWindow, tem);
imwrite(outDir+"src.jpg", src);
imwrite(outDir+"result.jpg", matchResult);
waitKey(0);
return 0;
}
效果