模板匹配和卷积运算大致相同,模板图类似于卷积核,从原图的左上角开始进行滑动窗口的操作,最后得到一个特征图,这个特征图里的数值就是每次计算得到的相似度,通用匹配方式,相似值是(0-1)之间。
(最简单的一个例子,用两张相同的图片传入模板匹配函数中,只会进行一次相似计算,最后得到的特征图数值为([1,])
OpenCV中的模板匹配函数为matchTemplate,参数如下,其中的result就是得到的特征图,里面记录了每次匹配计算的相似度。
void cv::matchTemplate(InputArray image,
InputArray templ,
OutputArray result,
int method, // 匹配方法:TM_SQDIFF、TM_CCORR、TM_CCOEFF等
InputArray mask = noArray()
)
得到相似度特征图后,再找到其中最大的值(或自定义一个阈值)的坐标,根据模板图的宽高,就能找到在原图的所在位置。
下面是最大值的例子:
int main()
{
cv::Mat image = cv::imread("C:/Users/Opencv/temp/yuan.png");
cv::Mat matchImg = cv::imread("C:/Users/Opencv/temp/match.png");
if (image.empty() || matchImg.empty()) {
cout << "打开图片失败" << endl;
return -1;
}
cv::Mat result;
cv::matchTemplate(image, matchImg, result, cv::TM_CCOEFF_NORMED);
double maxVal, minVal;
cv::Point minLoc, maxLoc;
//寻找匹配结果中的最大值和最小值以及坐标位置
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);
// 绘制最佳匹配区域
cv::rectangle(image, cv::Rect(maxLoc.x, maxLoc.y, matchImg.cols, matchImg.rows), cv::Scalar(0, 0, 255), 2);
cv::imshow("原图", image);
cv::imshow("模板图", matchImg);
cv::imshow("结果图", result);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
result图的数值为灰度值,最白的那个点就是匹配度最高的那个创建。再来看一下result里的数值
使用最大值的操作,只能匹配到一个位置。而在实际应用中,往往遇到的场景是一副图片中存在多个相似目标需要被找到,可以要设定阈值来进行判定。
代码如下(示例):
int main()
{
cv::Mat img0 = cv::imread("C:/Users/jutze/ljw_C++/Opencv/temp/on.png");
cv::Mat img1 = img0.clone();
//cv::Mat img1;
//cv::flip(img, img1, 0);
cv::Mat img2 = img0.clone();
cv::Mat img3 = img0.clone();
cv::Mat out1, out2, image;
cv::hconcat(img0, img1, out1);
cv::hconcat(img2, img3, out2);
cv::vconcat(out1, out2, image);
cv::Mat matchImg = cv::imread("C:/Users/jutze/ljw_C++/Opencv/temp/onmatch.png");
if (image.empty() || matchImg.empty()) {
cout << "打开图片失败" << endl;
return -1;
}
cv::Mat result;
cv::matchTemplate(image, matchImg, result, cv::TM_CCOEFF_NORMED);
float threshold = 0.85;
cv::Mat Loc;
cv::findNonZero(result >= threshold, Loc); // 寻找大于阈值的点
for (int i = 0; i < Loc.total(); i++)
{
cv::Point pt = Loc.at<cv::Point>(i);
cv::rectangle(image, pt, cv::Point(pt.x + matchImg.cols, pt.y + matchImg.rows), cv::Scalar(0, 0, 255), 1);
}
cv::imshow("原图", image);
cv::imshow("模板图", matchImg);
cv::imshow("结果图", result);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
上面的图是作了一个拼接处理,但一般实际应用的图中,目标通常会存在一些旋转角度,尺寸大小不一致等问题,这些都会影响匹配结果,如下图所示。目前我自己所用到解决方式:一种是对原图进行旋转、缩放等相关处理。另一种是对模板图进行旋转、缩放等处理。都是通过多次匹配来进行,无疑会增加很大时间消耗,可能会有更好的办法吧,以后遇到再说。