模板匹配(TemplateMatching)就是在一幅图像中寻找和模板图像(template)最相似的区域,模板匹配不是基于直方图的,而是通过在输入图像上滑动图像块(模板)同时对比相似度,来对模板和输入图像进行匹配的一种方法。该方法原理简单计算速度快,能够应用于目标识别,目标跟踪等多个领域。
(1)首先需要一个模板图像 T(子图像)和一个待检测的图像(源图像 S)
(2)在待检测图像从左到右,从上到下计算模板图像与重叠子图像的匹配度,匹配度越高,两者相同的可能性越大。
优点: 原理简单计算速度快
缺点:模板图像尺寸和待检测图像尺寸问题。模板尺寸必须小于待检测图片尺寸
int main()
{
Mat temp = imread("E:\\img\\T2.jpg");
Mat src = imread("E:\\img\\S.jpg");
Mat dst = src.clone();
imshow("temp", temp);
//1.构建结果图像resultImg(注意大小和类型)
//如果原图(待搜索图像)尺寸为W x H, 而模版尺寸为 w x h, 则结果图像尺寸一定是(W-w+1)x(H-h+1)
//结果图像必须为单通道32位浮点型图像
int width = src.cols - temp.cols + 1;//result宽度
int height = src.rows - temp.rows + 1;//result高度
Mat result(height, width, CV_32FC1);//创建结果映射图像
//2.模版匹配
//这里我们使用的匹配算法是标准平方差匹配 method=TM_SQDIFF_NORMED,数值越小匹配度越好
//matchTemplate(src, temp, result, TM_SQDIFF); //平方差匹配法(最好匹配0)
matchTemplate(src, temp, result, TM_SQDIFF_NORMED); //归一化平方差匹配法(最好匹配0)
//matchTemplate(src, temp, result, TM_CCORR); //相关匹配法(最坏匹配0)
//matchTemplate(src, temp, result, TM_CCORR_NORMED); //归一化相关匹配法(最坏匹配0)
//matchTemplate(src, temp, result, TM_CCOEFF); //系数匹配法(最好匹配1)
//matchTemplate(src, temp, result, TM_CCOEFF_NORMED);//化相关系数匹配,最佳值1
imshow("result", result);
//3.正则化(归一化到0-1)
normalize(result, result, 0, 1, NORM_MINMAX, -1);//归一化到0-1范围
//4.找出result中的最大值及其位置
double minValue, maxValue;
Point minLoc, maxLoc;
// 定位极值的函数
minMaxLoc(result, &minValue, &maxValue, &minLoc, &maxLoc);
cout << "minValue=" << minValue << endl;
cout << "maxValue=" << maxValue << endl;
//5.根据result中的最大值位置在源图上画出矩形和中心点
rectangle(dst, maxLoc, Point(maxLoc.x + temp.cols, maxLoc.y + temp.rows), Scalar(0, 255, 0), 2, 8);
imshow("dst", dst);
waitKey(0);
return 0;
}
注意:result的长宽正好是(原图-模板图)的长宽,result图中白亮程度表示匹配程度
模板匹配函数cv::MatchTemplate一次计算模板与待测图像的相似度,并将结果存入映
图像result中,也就是result图像中的每一个点的值代表一次相似度比较结果;其中,模通过在待检测的图像上从左到右,从上到下滑动,每到达一个像素点,就会以这个像素点左上角顶点从原图像中截取一个与模板大小一样的图像进行像素比较的运算,模板在滑动过程中,将模板和当前截取的图像的比较计算结果储存在result矩阵中,result的大小为(W-w+1,H-h+1),在result中的每个位置(x,y)的值都表示以这个点为左上角顶点截取的图像模板像素计算后的计算结果;模板在待测图像上每次在横向或者纵向上每次移动一个像素点然后进行一次比较,所以横向比较W-w+1次,纵向比较H-h+1次,最终得到一个(W-w+1)x(H-h+1)的result矩阵。
ROI区域的获取
从result中提取最大值(相似度最高)以及最大值的位置(即在result中该最大值max_val的坐标位置max_loc,即模板滑行时左上角的坐标,类似于图中的坐标(x,y);由此得到rect=cvRect(max_loc.x,max_loc.y,tmp->width,tmp->height); 其中rect表示最佳的匹配的矩形区域。
// 1.读入图片
Mat srcImg = imread("E://img//S1.png");
Mat templateImg = imread("E://img//T3.jpg");
Mat resultImg;
Mat showImg = srcImg.clone();
// 设置result图片的大小
int resultImg_cols = srcImg.cols - templateImg.cols + 1;
int resultImg_rows = srcImg.rows - templateImg.rows + 1;
resultImg.create(resultImg_cols, resultImg_rows, CV_32FC1);
// 2.模板匹配
matchTemplate(srcImg, templateImg, resultImg, TM_CCOEFF_NORMED); //化相关系数匹配法(最好匹配1)
// 3.归一化
normalize(resultImg, resultImg, 0, 1, NORM_MINMAX);
Mat midImg = resultImg.clone();
//多目标模板匹配---方法一
double matchValue;
int count0=0;
int tempW=0, tempH=0;
char matchRate[10];
for(int i=0; i(i, j);
sprintf_s(matchRate, "%0.2f", matchValue);
if(matchValue>=0.85 && (abs(j - tempW)>5) && (abs(i - tempH)>5) )
{
count0++;
//将文字显示在图片上
putText(showImg, matchRate, Point(j-5, i-5), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 1);
rectangle(showImg, Point(j, i), Point(j + templateImg.cols, i + templateImg.rows), Scalar(0, 255, 0), 2);
tempW = j;
tempH = i;
}
}
}
cout<<"count="<
//多目标模板匹配---方法二
double minValue, maxValue;
Point minLoc, maxLoc;
Point matchLoc;
char matchRate[10];
for (int i = 0; i < 100; i++)
{
int startX = maxLoc.x - 4;
int startY = maxLoc.y - 4;
int endX = maxLoc.x + 4;
int endY = maxLoc.y + 4;
if (startX < 0 || startY < 0)
{
startX = 0;
startY = 0;
}
if (endX > resultImg.cols - 1 || endY > resultImg.rows - 1)
{
endX = resultImg.cols - 1;
endY = resultImg.rows - 1;
}
Mat temp = Mat::zeros(endX - startX, endY - startY, CV_32FC1);
//Mat ROI = resultImg(Rect(Point(startX, startY), temp.cols, temp.rows));
temp.copyTo(resultImg(Rect(startX, startY, temp.cols, temp.rows)));
minMaxLoc(resultImg, &minValue, &maxValue, &minLoc, &maxLoc);
if (maxValue < 0.89) break;
cout << "max_value= " << maxValue << endl;
sprintf_s(matchRate, "%0.2f", maxValue);
putText(showImg, matchRate, Point(maxLoc.x - 5, maxLoc.y - 5), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 1);
rectangle(showImg, maxLoc, Point(maxLoc.x + templateImg.cols, maxLoc.y + templateImg.rows), Scalar(0, 255, 0), 2);
}
imshow("midImg", midImg);
imshow("resultImg", resultImg);
imshow("dst", showImg);
模板匹配 --- matchTemplate()
CV_EXPORTS_W void matchTemplate(
InputArray image,
InputArray temp1,
OutputArray result,
int method);
image:待搜索图像(大图)
temp1:搜索模板,需和原图一样数据类型且尺寸大小不能大于源图像
reuslt:比较结果的映射图像,其必须为单通道的,32位浮点型图像,如果原图(待搜索图像)尺寸为W*H,二temp1的尺寸为w*h,则result的尺寸一定是(W-w+1)*(H-h+1)
method:指定的匹配方法,有如下六种:
(1)平方差匹配 method=TM_SQDIFF
这类方法利用平方差来进行匹配,最好匹配为0。匹配越差,匹配值越大。
(2)标准平方差匹配 method=TM_SQDIFF_NORMED
(3)相关匹配 method=TM_CCORR
这类方法采用模板和图像间的乘法操作,所以较大的数表示匹配程度较高,0标识最坏的匹配效果。
(4)标准相关匹配 method=TM_CCORR_NORMED
(5)相关匹配 method=TM_CCOEFF
这类方法将模版对其均值的相对值与图像对其均值的相关值进行匹配,1表示完美匹配,-1表示匹配很差,0表示没有任何相关性(随机序列)。
(6)标准相关匹配 method=TM_CCOEFF_NORMED
矩阵归一化 --- normalize() 函数的作用是进行矩阵归一化。
void normalize(
InputArray src,
OutputArray dst,
double alpha=1,
double beta=0,
int norm_type=NORM_L2,
int dtype=-1,
InputArray mask=noArray() )
src:输入源图像,Mat类型
dst:输出结果图像,需要和原图一样的尺寸和类型
alpha:归一化后的最小值,默认为1
beta:归一化后的最大值,默认为0
norm_type:归一化类型,可选:NORM_INF, NORM_L1, NORM_L2(默认)等
dtype:默认值为-1,此参数为负值时,输出矩阵和src有同样的类型
mask:可选的掩码操作
寻找最值 --- minMaxLoc() 函数的作用是在数组中找到全局最小值和最大值
CV_EXPORTS_W void minMaxLoc(
InputArray src,
CV_OUT double* minVal,
CV_OUT double* maxVal = 0,
CV_OUT Point* minLoc=0,
CV_OUT Point* maxLoc=0,
InputArray mask=noArray());
src:输入源图像,单通道图像
minVal:返回最小值的指针,若无需返回,则置为0
maxVal:返回最大值的指针,若无需返回,则置为0
minLoc:返回最小位置的指针,若无需返回,则置为0
maxLoc:返回最大位置的指针,若无需返回,则置为0
mask:可选的掩码操作
部分参考来自22、【opencv入门】模板匹配 - 阿牧路泽 - 博客园 (cnblogs.com)