模板匹配不是基于直方图的,而是通过在输入图像上滑动图像块,对实际图像块和输入图像进行匹配的一种匹配方法。

实现模板匹配: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输出多个匹配对象;

C++ 模板匹配matchTemplate_opencv

输出如下:

C++ 模板匹配matchTemplate_opencv_02