matchTemplate
模板匹配和卷积运算大致相同,模板图类似于卷积核,从原图的左上角开始进行滑动窗口的操作,最后得到一个特征图,这个特征图里的数值就是每次计算得到的相似度,通用匹配方式,相似值是(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;
}
上面的图是作了一个拼接处理,但一般实际应用的图中,目标通常会存在一些旋转角度,尺寸大小不一致等问题,这些都会影响匹配结果,如下图所示。目前我自己所用到解决方式:一种是对原图进行旋转、缩放等相关处理。另一种是对模板图进行旋转、缩放等处理。都是通过多次匹配来进行,无疑会增加很大时间消耗,可能会有更好的办法吧,以后遇到再说。