OpenCV中的模板匹配 matchTemplate
一直被诟病,原因是不能多尺度识别且不能旋转识别,后者比较复杂,这里只讨论前者。网上多数示例都是从一张图上截取一个目标,然后进行匹配识别,效果很好但明显没有实用性。其实通过设置“尺度池” Scaling Pool可以很好的解决这个问题,用尺度不一的模板进行匹配不就可以了吗?再以多线程加速,各尺度独立匹配,最后取最佳结果,这就是多尺度的思路。
另外,基于特征(SIFT/SURF等)的目标匹配则不需要考虑尺度和角度问题,本文参考 holybin大神的博客 进行了代码移植,识别结果在后面一并展示。
本文环境:Win10 + OpenCV4.1 + Contrib4.1 + VS2019
直接上代码:(匹配的结果评价指标有很多,这里选的是TM_CCOEFF_NORMED
)
void TemplateMatching(cv::Mat& src, cv::Mat& tmpl) {
vector<double> scales = { 0.5, 0.75, 1.25, 1.5 };
vector<std::future<Result>> results(scales.size());
for (int i = 0; i < scales.size(); ++i) {
results[i] = std::async(launch::async, [this, &src, &tmpl, scales, i]() -> Result
{
Mat t;
Size2d size(tmpl.cols * scales[i], tmpl.rows * scales[i]);
resize(tmpl, t, size);
Mat dst(src.size(), src.type());
matchTemplate(src, t, dst, TM_CCOEFF_NORMED);
cv::Point minPoint, maxPoint, matchLoc;
double minVal(-1), maxVal(-1), score(-1);
cv::minMaxLoc(dst, &minVal, &maxVal, &minPoint, &maxPoint);
double weight = scales[i] < 1. ? scales[i] : 1. / scales[i];
score = maxVal * weight;
matchLoc = maxPoint;
return Result{ score , Rect(matchLoc.x, matchLoc.y,t.cols,t.rows) };
});
}
Mat processImg = src.clone();
Result maxRes;
cv::RNG rng(time(0));
for (int i = 0; i < scales.size(); ++i) {
results[i].wait();
Result res = results[i].get();
Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
cv::putText(processImg, to_string(res.score), Point(res.bbox.x, res.bbox.y), 0, 1, color, 2);
cv::rectangle(processImg, res.bbox, color, 2, 8);
if (res.score > maxRes.score) {
maxRes = res;
}
}
cv::imshow("Template Matching Process", processImg);
Mat img = src.clone();
cv::putText(img, to_string(maxRes.score), Point(maxRes.bbox.x, maxRes.bbox.y), 0, 1, 255, 2);
cv::rectangle(img, maxRes.bbox, Scalar(0, 255, 0), 2, 8);
cv::imshow("Template Matching", img);
cv::waitKey(1);
}
整个工程代码已经上传,包括两种识别方法:多线程加速的多尺度模板匹配 和 SURF特征匹配