一、函数:
a.matchTemplate
函数:
(1)函数原型:
CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ,
OutputArray result, int method, InputArray mask = noArray() );
(2)函数作用:
-
matchTemplate
函数通过在图像上滑动模板,并使用指定的匹配方法比较图像与模板的重叠区域,将比较结果存储在result
中。
(3)参数:
image
:要搜索的图像。必须是8位无符号或32位浮点类型。templ
:被搜索的模板。大小必须不超过源图像,并且数据类型与源图像相同。result
:比较结果的映射。必须是单通道的32位浮点类型。如果image
的大小是W × H
,模板是w × h
,那么result
的大小是(W-w+1) × (H-h+1)
。method
:指定比较方法的参数,可参见TemplateMatchModes
。不同的方法使用不同的公式来计算比较结果。mask
:可选的掩码。它必须与templ
大小相同,具有相同数量的通道或仅一个通道(用于所有模板和图像通道)。如果数据类型是CV_8U
,则掩码被解释为二进制掩码,只有掩码非零的元素被使用,并且实际掩码值(权重等于1)不变。对于数据类型CV_32F
,掩码值被用作权重。
(4)模板匹配模式(TemplateMatchModes
):
-
描述了可用的比较方法的公式。
I
表示图像,T
表示模板,R
表示结果,M
表示可选掩码。求和是在模板和/或图像块上进行的。
(5)最佳匹配查找:
-
函数执行比较后,可以使用
minMaxLoc
函数找到全局最小值(当使用TM_SQDIFF
时)或最大值(当使用TM_CCORR
或TM_CCOEFF
时)。
(6)多通道图像处理:
-
对于彩色图像,模板在分子中的求和和分母中的每个求和都是跨所有通道进行的,并且每个通道使用单独的平均值。这意味着函数可以接受彩色模板和彩色图像,但结果仍然是单通道图像,这更容易分析。
(7)应用场景:
-
matchTemplate
函数常用于图像识别、目标检测和图像匹配任务中,通过比较模板与图像的各个部分来找到最佳匹配位置。
(8)注意点:
-
在使用
matchTemplate
函数时,需要确保输入图像和模板的大小和类型兼容,并且输出结果图像具有正确的数据类型和大小。
b.TemplateMatchModes枚举:
(1)TM_SQDIFF (0):
-
平方差方法。计算模板和图像区域之间的平方差总和。
-
如果没有掩码,公式为:
-
使用掩码时,公式为:
-
TM_SQDIFF_NORMED (1):归一化平方差方法。计算平方差的归一化版本。
-
如果没有掩码,公式为:
-
使用掩码时,公式为:
-
TM_CCORR (2):相关系数方法。计算模板和图像区域之间的相关系数总和。
-
如果没有掩码,公式为:
-
使用掩码时,公式为:
-
TM_CCORR_NORMED (3):归一化相关系数方法。计算相关系数的归一化版本。
-
如果没有掩码,公式为:
-
使用掩码时,公式为:
-
TM_CCOEFF (4):常数系数方法。计算模板和图像区域的常数系数相关性,其中
T'
和I'
是减去平均值后的模板和图像区域。 -
没有掩码时的公式为:
-
使用掩码时的公式为:
-
TM_CCOEFF_NORMED (5):归一化常数系数方法。计算常数系数相关性的归一化版本。
-
公式为:
二、示例代码:
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/objdetect.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>
using namespace cv;
using namespace std;
VideoCapture createInput(bool useCamera, std::string videoPath);
// 主函数入口点
int main() {
// 设置日志级别为不输出任何日志信息
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
// 定义是否使用摄像头或视频文件的标志,0 表示使用视频文件
bool useCamera = 0;
// 定义视频文件的路径
string videoPath = "C:\\Users\\86173\\Desktop\\TI\\passerby.mp4";
// 根据 useCamera 标志选择摄像头或视频文件创建视频捕获对象
VideoCapture cap = createInput(useCamera, videoPath);
// 检查视频是否成功打开,如果失败打印错误信息并退出程序
if (!cap.isOpened()) {
cout << "fail to open video...\n" << endl;
return -1;
}
// 定义Mat对象用于存储帧、模板、结果等
Mat frame, tempMat, resultMat, refMat, dispMat;
// 初始化计数器
int cnt = 0;
// 循环读取视频帧直到视频结束
while (1) {
// 从视频流中读取下一帧
cap >> frame;
// 如果帧为空,则退出循环
if (frame.empty()) break;
// 如果是第一帧,让用户选择ROI并复制到refMat
if (cnt == 0) {
Rect2d r;
// 调用 selectROI 函数让用户选择ROI
r = selectROI(frame, true);
// 根据选择的ROI提取模板
tempMat = frame(r);
// 复制模板到refMat
tempMat.copyTo(refMat);
// 销毁所有创建的窗口
destroyAllWindows();
}
// 设置模板匹配方法为TM_SQDIFF,即最小平方差
int match_method = TM_SQDIFF;
// 调用 matchTemplate 函数进行模板匹配
matchTemplate(frame, refMat, resultMat, match_method);
// 归一化匹配结果
normalize(resultMat, resultMat, 0, 1, NORM_MINMAX, -1, Mat());
// 声明变量用于minMaxLoc函数
double minVal, maxVal;
Point minLoc, maxLoc;
Point matchLoc;
// 寻找resultMat中的最小值和最大值位置
minMaxLoc(resultMat, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
// 根据匹配方法确定最佳匹配位置
if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED) {
matchLoc = minLoc; // 最小平方差方法使用最小值位置
}
else {
matchLoc = maxLoc; // 其他方法使用最大值位置
}
// 复制当前帧到dispMat并在匹配位置绘制矩形框
frame.copyTo(dispMat);
rectangle(dispMat, matchLoc, Point(matchLoc.x + refMat.cols,
matchLoc.y + refMat.rows), Scalar::all(0), 2, 8, 0);
// 显示模板和匹配结果
imshow("template", refMat);
imshow("模板匹配", dispMat);
// 如果用户按下 'q' 键,则退出循环
if (waitKey(30) == 'q') {
break;
}
// 增加计数器
cnt++;
}
// 程序结束,返回0
return 0;
}
//选择视频或者摄像头,1摄像头,0视频。
VideoCapture createInput(bool useCamera, string videoPath)
{
//选择输入
VideoCapture capVideo;
if (useCamera) {
capVideo.open(0);
}
else {
capVideo.open(videoPath);
}
return capVideo;
}