模板匹配:
利用单独的模式图像,遍历可能包含该模式的更大图像,应用统计学算法,获得大图像上每个区域与模式图像相似程度的实数度量值.
在自动化设备中,机器视觉对象往往外形简单,光源稳定不变,几何失真也几乎可以忽略,于是,模板匹配就成为简易又非常实用的图像识别算法.
OpenCV提供的模板匹配API:
void matchTemplate(
InputArray image, //待检测图像
InputArray templ, //模板图像
OutputArray result, //匹配结果
int method //匹配算法选择
);
将templ图像的左上角第一个像素与image重合,遍历image图像,并比较.
比较后的匹配程度,会以实数的形式记录在result图像中,记录位置与image对应,由于模板本身有体积,所以result图像会比image图像小一些.
method参数表示可选的匹配算法,可取以下枚举值:
CV_TM_SQDIFF 平方差匹配法 匹配越好,值越小,最佳为0.
CV_TM_CCORR 相关匹配法 匹配越好,值越大.
CV_TM_CCOEFF 相关系数匹配法 匹配越好,值越大,0为随机
CV_TM_SQDIFF_NORMED 归一化平方差匹配法
CV_TM_CCORR_NORMED 归一化相关匹配法
CV_TM_CCOEFF_NORMED 归一化相关系数匹配法
经测试,其中,CV_TM_CCORR算法有严重偏离问题,简易不要使用.
其中平方差算法计算最简单,匹配准确度也最低,而相关系数匹配法计算最复杂,匹配准确度也最高.
在应用中,常使用归一化匹配方法,更容易设置阈值.
以下示例程序使用OpenCV自带的滑动条功能,调节阈值.
使用了归一化相关系数匹配法,设置阈值后,如果匹配成功,则用红色矩形圈出,如果匹配失败,则用蓝色矩形圈出.读者可以自选图像进行测试.
开发环境:win10+Visual Stdio 2015+OpenCV3.1.0
程序类型:win32控制台程序.
/* 功能:模板匹配选择匹配阈值的小测试. */ #include<opencv2/opencv.hpp> #include<iostream> using namespace std; using namespace cv; Mat frame; //待检测图像 Mat templ; //模板图像 //当前阈值 int threshold_v = 900; //将[0,1]部分,映射到[0,1000]. int threshold_max = 1000; //窗口名称 const char* WindowTitle1 = "src"; const char* WindowTitle2 = "templ"; //滑动条回调函数 void TestCallback(int, void*); int main(int argc,char* argv[]) { //加载图片,创建窗口 frame = imread("F:/project/picture/pic3/2.bmp"); templ = imread("F:/project/picture/pic3/w8.bmp"); namedWindow(WindowTitle1, CV_WINDOW_NORMAL); namedWindow(WindowTitle2, CV_WINDOW_NORMAL); //创建滑动条,确定选择阈值 createTrackbar("Threshold Value:", WindowTitle1, &threshold_v, threshold_max, TestCallback); TestCallback(0, 0); waitKey(0); } void TestCallback(int, void*) { //模板本身也是有一定体积的,所以result的宽高只能是这样 int width = frame.cols - templ.cols + 1; int height = frame.rows - templ.rows + 1; //将各像素的匹配值也输出为图像 Mat result(width, height, CV_32FC1); matchTemplate(frame, templ, result, CV_TM_CCOEFF_NORMED); cv::Point minLoc; cv::Point maxLoc; double minValue, maxValue; minMaxLoc(result, &minValue, &maxValue, &minLoc, &maxLoc, Mat()); cout << minValue << endl; cout << maxValue << endl; cout << minLoc << endl; cout << maxLoc << endl; double thresholdD = (double)threshold_v / 1000; if (maxValue>thresholdD) { rectangle(frame, Rect(maxLoc.x, maxLoc.y, templ.cols, templ.rows), Scalar(0, 0, 255), 2, 8); } else { rectangle(frame, Rect(maxLoc.x, maxLoc.y, templ.cols, templ.rows), Scalar(255, 0, 0), 2, 8); } imshow(WindowTitle1, frame); imshow(WindowTitle2, templ); }