模板匹配不是基于直方图的,而是通过在输入图像上滑动图像块,对实际图像块和输入图像进行匹配的一种匹配方法。
实现模板匹配:matchTemplate()函数,用于匹配出和模板重叠的图像区域;
函数原型C++
void matchTemplate( InputArray image, InputArray temp1, OutputArray result, int method )
【1】InputArray类型的image,待搜索的图像,且需为8位或32位浮点型图像;
【2】InputArray类型的temp1,搜索模板,需和源图片有一样的数据类型,且尺寸不能大于源图像;
【3】OutputArray类型的result,比较结果的映射图像,其必须为单通道、32位浮点型图像,如果图像尺寸是W x H而temp1尺寸是wxh,则此参数result一定是(W-w + 1)x(H-h+1);
【4】int类型的method,指定的匹配方法,提供一下6种方法:
平方差匹配法 method = TM_SQDIFF,使用平方差来进行匹配,最好匹配为0;而若匹配越差,匹配值则越大。(说明最黑的地方是匹配最好的地方)
归一化平方差匹配法:method = TM_SQDIFF_NORMED,最好匹配为0。
相关匹配法:method = TM_CCORR,这类方法才用模板和图像间的乘法操作,所以较大的数表示匹配程序较高,0标识最坏的匹配效果。
归一化相关匹配法 method = TM_CCORR_NORMED;最坏匹配为0。
系数匹配法method = TM_CCOEFF;最好匹配为1;
化相关系数匹配法method = TM_CCOEFF_NORMED;最好匹配为1
int MatchMethod=0;
int MaxTrackbarNum = 5;
struct CallbackParams {
Mat srcImage;
Mat templateImage;
};
//Mat srcImage, templateImage;
void templateMatching(int MatchMethod, void* userData)
{
CallbackParams* params = static_cast<CallbackParams*>(userData);
Mat result;
int result_cols = params->srcImage.cols - params->templateImage.cols + 1;
int result_rows = params->srcImage.rows - params->templateImage.rows + 1;
if (result_cols < 0 || result_rows < 0)
{
cout << "Please input correct image!";
return;
}
result.create(result_cols, result_rows, CV_32FC1);
// enum { TM_SQDIFF=0, TM_SQDIFF_NORMED=1, TM_CCORR=2, TM_CCORR_NORMED=3, TM_CCOEFF=4, TM_CCOEFF_NORMED=5 };
matchTemplate(params->srcImage, params->templateImage, result, MatchMethod); //最好匹配为1,值越小匹配越差
double minVal = -1;
double maxVal;
Point minLoc;
Point maxLoc;
Point matchLoc;
double matchVal;
normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
Mat mask = params->srcImage.clone(); //绘制最匹配的区域
double threshold = 0.98; // 设置阈值
vector<Point> matches;
while (true)
{
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);
Scalar x = Scalar(1);
if (MatchMethod == TM_SQDIFF || MatchMethod == CV_TM_SQDIFF_NORMED)
{
matchLoc = minLoc;
matchVal = 1-minVal;
}
else
{
matchLoc = maxLoc;
matchVal = maxVal;
x= Scalar(0);
}
if (matchVal >= threshold)
{
cout << "add one" << endl;
matches.push_back(matchLoc);
rectangle(result, matchLoc, Point(matchLoc.x + params->templateImage.cols, matchLoc.y + params->templateImage.rows), x, -1);
rectangle(mask, matchLoc, Point(matchLoc.x + params->templateImage.cols, matchLoc.y + params->templateImage.rows), Scalar(0, 255, 0), 2, 8, 0);
}
else
{
break;
}
}
//rectangle(mask, matchLoc, Point(matchLoc.x + params->templateImage.cols, matchLoc.y + params->templateImage.rows), Scalar(0, 255, 0), 2, 8, 0);
//rectangle(result, matchLoc, Point(matchLoc.x + params->templateImage.cols, matchLoc.y + params->templateImage.rows), Scalar(0, 255, 0), 2, 8, 0);
imshow("原始图", mask);
//imshow("result", result);
}
int main()
{
Mat img = imread(R"(C:\Users\rs\Desktop\img.png)");
Mat tempalteimg = imread(R"(C:\Users\rs\Desktop\tmpimg.png)");
CallbackParams params = { img, tempalteimg };
namedWindow("原始图", 1);
createTrackbar("方法", "原始图", &MatchMethod, MaxTrackbarNum, templateMatching, ¶ms);
templateMatching(0, ¶ms);
waitKey(0);
//Point_set_fitting();
}
以上代码实现匹配方法的选择以滚动条方式呈现,
createTrackbar()函数的函数原型为:
CV_EXPORTS int createTrackbar(const String& trackbarname, const String& winname,int* value, int count,TrackbarCallback onChange = 0, void* userdata = 0);
trackbarname:这个参数用来给这个滚动条取一个名字;
winname:这个参数用来指定你要吧这个滚动条用到那个窗口上;
value:这个参数用来设置滑块初始值位置,同时记录滑块以后的位置;
count:这个参数用来指定滚动条可以滚动的最大值;
onChange:这个参数可以理解为一个函数类型的变量(当然这样说感觉有点怪),用来接收回调函数函数名的,默认值为0;
userdata:这个变量这个参数是用户传给回调函数的数据,用来处理轨迹条事件,默认值为0。
并设置threshold输出多个匹配对象;
输出如下: