【opencv学习笔记】028之模板匹配——matchTemplate函数详解

发布时间 2024-01-12 23:10:19作者: zwghz

目录

 ​一、前言​

 ​二、模板匹配​

 ​1、模板匹配是个啥​

 ​2、常用匹配算法​

 ​1.平方差匹配-CV_TM_SQDIFF​

 ​2.标准平方差匹配-CV_TM_SQDIFF_NORMED​

 ​3.相关匹配-CV_TM_CCORR​

 ​4.标准相关匹配-CV_TM_CCORR_NORMED​

 ​5.相关匹配-CV_TM_CCOEFF​

 ​6.标准相关匹配-CV_TM_CCOEFF_NORMED​

 ​3、API​

 ​4、代码展示​

 ​5、执行结果​


一、前言

遭遇了点突发情况,所以今天更新的有点晚,也不知道能不能等到今天发出去了。

终于可以从模板匹配开始了,说明基础博客即将更新完毕,下一步,开始更新机器学习算法博客,感谢大家的鼎力支持。

如果想看其他有关于OpenCV学习方法介绍、学习教程、代码实战、常见报错及解决方案等相关内容,可以直接看我的OpenCV分类:

 

二、模板匹配

1、模板匹配是个啥

我们先来了解一下模板匹配是个啥。

我们先不说定义,先从理解开始,单纯的理解这个概念:

就是有一个模板,然后我有一个大图像要去和模板匹配。

这个解释很暴力有木有!

那接下来我们来介绍一下标准的解释和定义。

模板匹配是一种最原始、最基本的模式识别方法,研究某一特定对象物的图案位于图像的什么地方,进而识别对象物,这就是一个匹配问题。它是图像处理中最基本、最常用的匹配方法。

根据上面的解释呢,我们就能得到如下定义:

模板就是一副已知的小图像,而模板匹配就是在一副大图像中搜寻目标,已知该图中有要找的目标,且该目标同模板有相同的尺寸、方向和图像元素,通过一定的算法可以在图中找到目标,确定其坐标位置。

 

比如上面这个图中,左边大的是图像,右面小的是模板。

这里我们注意到一个描述:

相同的尺寸、方向和图像元素

这就说明,我们要找的模板是里图像里标标准准存在的,这里说的标标准准,就是说,一旦图像或者模板发生变化,比如旋转,修改某几个像素,图像翻转等操作之后,我们就无法进行匹配了,这也是这个算法的弊端。

所以这种匹配算法,相当于“人工智障式”匹配,就是在待检测图像上,从左到右,从上向下对模板图像与小东西的图像进行比对。

2、常用匹配算法

我们会经常使用到如下六个匹配算法:

1.平方差匹配 method=CV_TM_SQDIFF

2.标准平方差匹配 method=CV_TM_SQDIFF_NORMED

3.相关匹配 method=CV_TM_CCORR

4.标准相关匹配 method=CV_TM_CCORR_NORMED

5.相关匹配 method=CV_TM_CCOEFF

6.标准相关匹配 method=CV_TM_CCOEFF_NORMED

之后我们会讲到一个API,里面有一个参数method,它是指比较方法,主要方法在cv::TemplateMatchModes。就是如下几个:

enum TemplateMatchModes {
    TM_SQDIFF = 0, //!< \f[R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2\f]
    TM_SQDIFF_NORMED = 1, //!< \f[R(x,y)= \frac{\sum_{x',y'} (T(x',y')-I(x+x',y+y'))^2}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f]
    TM_CCORR = 2, //!< \f[R(x,y)= \sum _{x',y'} (T(x',y') \cdot I(x+x',y+y'))\f]
    TM_CCORR_NORMED = 3, //!< \f[R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}\f]
    TM_CCOEFF = 4, //!< \f[R(x,y)= \sum _{x',y'} (T'(x',y') \cdot I'(x+x',y+y'))\f]
    //!< where
    //!< \f[\begin{array}{l} T'(x',y')=T(x',y') - 1/(w \cdot h) \cdot \sum _{x'',y''} T(x'',y'') \\ I'(x+x',y+y')=I(x+x',y+y') - 1/(w \cdot h) \cdot \sum _{x'',y''} I(x+x'',y+y'') \end{array}\f]
    TM_CCOEFF_NORMED = 5 //!< \f[R(x,y)= \frac{ \sum_{x',y'} (T'(x',y') \cdot I'(x+x',y+y')) }{ \sqrt{\sum_{x',y'}T'(x',y')^2 \cdot \sum_{x',y'} I'(x+x',y+y')^2} }\f]
};

 

1.平方差匹配-CV_TM_SQDIFF

从名字来理解,平方差匹配就是通过计算每个像素点的差的平方的和,和数学中统计里面的平方差类似。但是因为我们要的只是一个值,所以我们最后不需要求平均。

其公式如下:

如果相近,则每个差都很小,最终的和也很小,如果完全一致,则差为0,所以最好的匹配就是这个值为零的时候。值越大,匹配越差。

2.标准平方差匹配-CV_TM_SQDIFF_NORMED

这个只是对上面的进行了标准化处理,经过处理后,上面的值就不会太大。

 

3.相关匹配-CV_TM_CCORR

这类方法采用模板和图像间的乘法操作,所以较大的数表示匹配程度较高,0标识最坏的匹配效果。

4.标准相关匹配-CV_TM_CCORR_NORMED

这个只是对上面的进行了标准化处理,经过处理后,上面的值就不会太大。

5.相关匹配-CV_TM_CCOEFF

这类方法将模版对其均值的相对值与图像对其均值的相关值进行匹配,1表示完美匹配,-1表示糟糕的匹配,0表示没有任何相关性(随机序列)。

6.标准相关匹配-CV_TM_CCOEFF_NORMED

这个只是对上面的进行了标准化处理,经过处理后,上面的值就不会太大。

匹配算法很多,但是具体用哪一个,我们要根据自己的需要进行选择, 也可能需要尝试去对比。

 

3、API

接下来我们讲一下API。

void matchTemplate(
InputArray image,
InputArray templ,
OutputArray result,
int method,
InputArray mask = noArray()
);
函数参数含义如下:

(1)InputArray类型的src ,运行搜索的图像。它必须是8位或32位浮点。

(2)InputArray类型的templ,已搜索模板。它不能大于源映像,并且具有相同的数据类型。。

(3)OutputArray类型的result,比较结果的映射。它必须是单通道32位浮点。

(4)int类型的method,指定比较方法的参数,请参见cv::TemplateMatchModes。

(5)InputArray类型的mask,搜索模板的掩码。它必须与temp具有相同的数据类型和大小。默认情况下不设置。

 

4、代码展示

 

#include<iostream>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

Mat src; Mat templ; Mat result;
char* src_window = "【输入图像】";
char* result_window = "【结果窗口】";

int match_method;
int max_Trackbar = 5;

// 函数声明
void MatchingMethod(int, void*);

int main()
{
    src = imread("E:/image/girl2.png");
    templ = imread("E:/image/girl2_1.png");
    if (!src.data || !templ.data)
    {
    cout << "could not load image !";
    return -1;
    }

    // 创建窗口
    namedWindow(src_window, CV_WINDOW_AUTOSIZE);
    namedWindow(result_window, CV_WINDOW_AUTOSIZE);

    imshow(src_window, src);

    // 创建滑动条
    char* trackbar_label = "Method";
    createTrackbar(trackbar_label, src_window, &match_method, max_Trackbar, MatchingMethod);

    MatchingMethod(0, 0);

    waitKey(0);
    return 0;
}

void MatchingMethod(int, void*)
{
    // 将被显示的原图像
    Mat src_display;
    src.copyTo(src_display);

    // 创建输出结果的矩阵
    int result_cols = src.cols - templ.cols + 1;
    int result_rows = src.rows - templ.rows + 1;

    result.create(result_cols, result_rows, CV_32FC1);

    // 进行匹配和标准化
    matchTemplate(src, templ, result, match_method);
    normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());

    // 通过函数 minMaxLoc 定位最匹配的位置
    double minVal; double maxVal; Point minLoc; Point maxLoc;
    Point matchLoc;

    minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

    // 对于方法 SQDIFF 和 SQDIFF_NORMED, 越小的数值代表更高的匹配结果. 而对于其他方法, 数值越大匹配越好
    if (match_method == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED)
    {
    matchLoc = minLoc;
    }
    else
    {
    matchLoc = maxLoc;
    }

    rectangle(src_display, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0,0,255), 2, 8, 0);
    rectangle(result, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 0, 255), 2, 8, 0);

    imshow(src_window, src_display);
    imshow(result_window, result);

    return;
}

 

5、执行结果

原图

 

模板

 

匹配结果

 

大家也可以自己尝试一下呀,一定要多做练习!