OpenCV如何模板匹配

 返回:OpenCV系列文章目录(持续更新中......)

上一篇:OpenCV如何实现背投
下一篇 :OpenCV在图像中寻找轮廓

目标

在本教程中,您将学习如何:

  • 使用 OpenCV 函数 matchTemplate()搜索图像贴片和输入图像之间的匹配项
  • 使用 OpenCV 函数 minMaxLoc()查找给定数组中的最大值和最小值(以及它们的位置)。

理论

什么是模板匹配?

模板匹配是一种用于查找图像中与模板图像(补丁)匹配(相似)的区域的技术。

虽然补丁必须是一个矩形,但可能不是所有的矩形都是相关的。在这种情况下,可以使用掩码来隔离补丁中应该用于查找匹配项的部分。

它是如何工作的?

  • 我们需要两个主要组件:

    1. 源图像(I):我们希望在其中找到与模板图像匹配的图像
    2. 模板图像(T):将与源图像进行比较的修补图像

    我们的目标是检测匹配度最高的区域:

  • 要识别匹配区域,我们必须通过滑动模板图像将模板图像与源图像进行比较

  • 滑动是指一次移动一个像素(从左到右,从上到下)。在每个位置,都会计算一个指标,以表示该位置的匹配程度(或补丁与源图像的特定区域的相似程度)。
  • 对于 T 相对于 I 的每个位置,将指标存储结果矩阵 R 中。R 中的每个位置 (x,y)都包含匹配指标:

上图是用公制TM_CCORR_NORMED滑动贴片的结果 R。最亮的位置表示匹配度最高。如您所见,红色圆圈标记的位置可能是值最高的位置,因此该位置(由该点形成的矩形作为角,宽度和高度等于补丁图像)被视为匹配。

  • 在实践中,我们使用函数 minMaxLoc()在 R 矩阵中找到最高值(或更低值,具体取决于匹配方法的类型)

模板匹配如何工作的?

  • 如果匹配需要遮罩,则需要三个组件:
    1. 源图像(I):我们希望在其中找到与模板图像匹配的图像
    2. 模板图像(T):将与源图像进行比较的修补图像
    3. 蒙版图像(M):蒙版,用于遮罩模板的灰度图像
  • 目前只有两种匹配方法接受掩码:TM_SQDIFF 和 TM_CCORR_NORMED(有关 opencv 中可用的所有匹配方法的说明,请参见下文)。
  • 蒙版的尺寸必须与模板相同
  • 蒙版应具有CV_8U或CV_32F深度,以及与模板图像相同的通道数。CV_8U情况下,掩码值被视为二进制值,即零和非零。CV_32F情况下,这些值应落在 [0..1] 范围内,模板像素将乘以相应的蒙版像素值。由于示例中的输入图像具有CV_8UC3类型,因此掩码也被读取为彩色图像。

OpenCV 中可用的匹配方法有哪些?

问得好。OpenCV 在函数 matchTemplate()中实现模板匹配。可用的方法有 6 种:

1、方法=TM_SQDIFF

 

2、方法=TM_SQDIFF_NORMED

3、方法=TM_CCORR

4、方法=TM_CCORR_NORMED

′)2

5、方法=TM_CCOEFF

哪里

6、方法=TM_CCOEFF_NORMED

C++代码:

  • 这个程序是做什么的?
    • 加载输入图像、图像补丁(模板)和可选的蒙版
    • 通过将 OpenCV 函数 matchTemplate() 与前面描述的 6 种匹配方法中的任何一种结合使用来执行模板匹配过程。用户可以通过在跟踪栏中输入其选择来选择方法。如果提供了掩码,则该掩码将仅用于支持掩码的方法
    • 规范化匹配过程的输出
    • 以更高的匹配概率定位位置
    • 在与最高匹配项对应的区域周围绘制一个矩形
  • 可下载代码: 点击这里
  • 代码一览:
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
 
using namespace std;
using namespace cv;
 
bool use_mask;
Mat img; Mat templ; Mat mask; Mat result;
const char* image_window = "Source Image";
const char* result_window = "Result window";
 
int match_method;
int max_Trackbar = 5;
 
void MatchingMethod( int, void* );
 
const char* keys =
"{ help h| | Print help message. }"
"{ @input1 | Template_Matching_Original_Image.jpg | image_name }"
"{ @input2 | Template_Matching_Template_Image.jpg | template_name }"
"{ @input3 | | mask_name }";
 
int main( int argc, char** argv )
{
 CommandLineParser parser( argc, argv, keys );
 samples::addSamplesDataSearchSubDirectory( "doc/tutorials/imgproc/histograms/template_matching/images" );
 
 img = imread( samples::findFile( parser.get<String>("@input1") ) );
 templ = imread( samples::findFile( parser.get<String>("@input2") ), IMREAD_COLOR );
 
 if(argc > 3) {
 use_mask = true;
 mask = imread(samples::findFile( parser.get<String>("@input3") ), IMREAD_COLOR );
 }
 
 if(img.empty() || templ.empty() || (use_mask && mask.empty()))
 {
 cout << "Can't read one of the images" << endl;
 return EXIT_FAILURE;
 }
 
 namedWindow( image_window, WINDOW_AUTOSIZE );
 namedWindow( result_window, WINDOW_AUTOSIZE );
 
 const char* trackbar_label = "Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED";
 createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
 
 MatchingMethod( 0, 0 );
 
 waitKey(0);
 return EXIT_SUCCESS;
}
 
void MatchingMethod( int, void* )
{
 Mat img_display;
 img.copyTo( img_display );
 
 int result_cols = img.cols - templ.cols + 1;
 int result_rows = img.rows - templ.rows + 1;
 
 result.create( result_rows, result_cols, CV_32FC1 );
 
 bool method_accepts_mask = (TM_SQDIFF == match_method || match_method == TM_CCORR_NORMED);
 if (use_mask && method_accepts_mask)
 { matchTemplate( img, templ, result, match_method, mask); }
 else
 { matchTemplate( img, templ, result, match_method); }
 
 normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
 
 double minVal; double maxVal; Point minLoc; Point maxLoc;
 Point matchLoc;
 
 minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
 
 if( match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED )
 { matchLoc = minLoc; }
 else
 { matchLoc = maxLoc; }
 
 rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
 rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
 
 imshow( image_window, img_display );
 imshow( result_window, result );
 
 return;
}

解释

声明一些全局变量,例如图像、模板和结果矩阵,以及匹配方法和窗口名称:

bool use_mask;
Mat img; Mat templ; Mat mask; Mat result;
const char* image_window = "Source Image";
const char* result_window = "Result window";
 
int match_method;
int max_Trackbar = 5;

加载源图像、模板,以及可选的掩码(如果匹配方法支持):

img = imread( samples::findFile( parser.get<String>("@input1") ) );
 templ = imread( samples::findFile( parser.get<String>("@input2") ), IMREAD_COLOR );
 
 if(argc > 3) {
 use_mask = true;
 mask = imread(samples::findFile( parser.get<String>("@input3") ), IMREAD_COLOR );
 }
 
 if(img.empty() || templ.empty() || (use_mask && mask.empty()))
 {
 cout << "Can't read one of the images" << endl;
 return EXIT_FAILURE;
 }

创建跟踪栏以输入要使用的匹配方法的种类。检测到更改时,将调用回调函数。

const char* trackbar_label = "Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED";
 createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );

让我们来看看回调函数。首先,它复制源图像:

 Mat img_display;
 img.copyTo( img_display );

执行模板匹配操作。参数自然是输入图像 I、模板 T、结果 R 和 match_method(由 Trackbar 给出),以及可选的蒙版图像 M

 bool method_accepts_mask = (TM_SQDIFF == match_method || match_method == TM_CCORR_NORMED);
 if (use_mask && method_accepts_mask)
 { matchTemplate( img, templ, result, match_method, mask); }
 else
 { matchTemplate( img, templ, result, match_method); }

我们对结果进行归一化:

 normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );

我们使用 minMaxLoc() 对结果矩阵 R 中的最小值和最大值进行本地化。

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

对于前两种方法(TM_SQDIFF 和 MT_SQDIFF_NORMED),最佳匹配是最低值。对于所有其他值,较高的值表示更好的匹配。因此,我们将相应的值保存在 matchLoc 变量中:

 if( match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED )
 { matchLoc = minLoc; }
 else
 { matchLoc = maxLoc; }

显示源图像和结果矩阵。在尽可能高的匹配区域周围绘制一个矩形:

 rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
 rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
 
 imshow( image_window, img_display );
 imshow( result_window, result );

结果

  1. 使用输入图像测试我们的程序,例如:

和模板图像:

生成以下结果矩阵(第一行是标准方法 SQDIFF、CCORR 和 CCOEFF,第二行是其规范化版本中的相同方法)。在第一列中,最暗的匹配度越好,对于其他两列,位置越亮,匹配度越高。

  1. 右边的匹配项如下所示(右边那个人的脸周围的黑色矩形)。请注意,CCORR 和 CCDEFF 给出了错误的最佳匹配,但是它们的规范化版本是正确的,这可能是因为我们只考虑“最高匹配”,而不是其他可能的高匹配。

参考文献:

1、《Template Matching》 -------Ana Huamán

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/581905.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

为什么我的Mac运行速度变慢 mac运行速度慢怎么办 如何使用CleanMyMac X修复它

近些年伴随着苹果生态的蓬勃发展&#xff0c;越来越多的用户开始尝试接触Mac电脑。然而很多人上手Mac后会发现&#xff0c;它的使用逻辑与Windows存在很多不同&#xff0c;而且随着使用时间的增加&#xff0c;一些奇奇怪怪的文件也会占据有限的磁盘空间&#xff0c;进而影响使用…

电脑已经有了一个Windows10,再多装一个Windows10组成双系统

前言 前段时间已经讲过一次双Windows系统的安装教程&#xff0c;但是小白重新去看了一下&#xff0c;发现写的内容太多&#xff0c;怕小伙伴看了之后一脸萌。 所以今天咱们就重新再来讲讲&#xff1a;在同一台机器上安装Windows10双系统的教程。 注意哦&#xff01;这里的Wi…

用来传输文件的协议-FTP

一.FTP协议--文件传输协议 1.了解FTP协议 &#xff08;1&#xff09;FTP服务是用来传输文件的协议 FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;是TCP/IP协议组中的协议之一&#xff0c;用于互联网上的控制文件的双向传输。是传输文件到Linu…

C++:string 类

在C中定义一个 std::string 字符串可以采用以下几种方式&#xff1a; 1.使用字符串字面量初始化&#xff1a; std::string str "Hello, world!"; 2.使用构造函数初始化&#xff1a; std::string szStringB("Hello wolven"); 3.使用重复字符初始化&am…

51单片机入门(一)

1. 51单片机的基础介绍 2. RAM和ROM的区别 总体而言&#xff0c;RAM和ROM在计算机系统中起着不同的角色&#xff0c;RAM用于临时存储运行时数据&#xff0c;而ROM用于存储永久性的固件和系统程序。 3. 为什么叫51单片机 因为51系列单片机都是使用Intel 8031指令系统的单片机…

【链表——数据结构】

文章目录 1.单链表1.定义2.基本操作2.1.不带头结点2.2后插2.3前插2.4删除2.5按位查找2.6按值查找2.7求单链表长度2.8 建表 2.双链表1.初始化2.插入(后插)3.删除(后删)4.遍历 3.循环链表1.循环单链表2.循环双链表3.代码问题 4.静态链表1.简述基本操作的实现1.初始化3.删除某个结…

前端---Bootstrap---的下载和使用

目录 Bootstrap的下载 网页链接: 下载步骤: Bootstrap的使用 引用步骤: Bootstrap常用: Bootstrap-栅格系统 Bootstrap-组件 Bootstrap 是由 Twiter 公司开发维护的前端 U框架&#xff0c;它提供了大量编写好的 CSS 样式&#xff0c;允许开发者结合一定 HTML结构及JavaS…

二维码门楼牌管理应用平台建设:档案管理的新篇章

文章目录 前言一、二维码门楼牌管理应用平台的构建背景二、九小场所档案管理的重要性三、二维码门楼牌管理应用平台在九小场所档案管理中的应用四、二维码门楼牌管理应用平台的优势与挑战五、结语 前言 随着信息技术的飞速发展&#xff0c;二维码门楼牌管理应用平台的建设已成…

《Fundamentals of Power Electronics》——三端电池的旋转、负载差分连接

以下是关于三端电池的旋转的相关知识点&#xff1a; Buck电路、Boost电路和Buck-Boost电路均包含一个与单刀单掷开关相连的电感。如下图所示。 将上图中的电感和开关网络视为一个标有a,b,c三端的基础电池。该电池在电源和负载之间有三种不同的连接方式。a-A b-B c-C连接方式组…

BERT一个蛋白质-季军-英特尔创新大师杯冷冻电镜蛋白质结构建模大赛-paipai

关联比赛: “创新大师杯”冷冻电镜蛋白质结构建模大赛 解决方案 团队介绍 paipai队、取自 PAIN AI&#xff0c;核心成员如我本人IvanaXu(IvanaXu GitHub)&#xff0c;从事于金融科技业&#xff0c;面向银行信用贷款的风控、运营场景。但我们团队先后打过很多比赛&#xf…

文件Tools工具 支持WORD/PDF/EXCEL/PDF等格式的转换软件

文件Tools工具 支持WORD/PDF/Excel/PDF等格式的转换软件 支持功能 Word转PDFWORD转EXCELWORD转EPUBPDF转WORDPDF转EXCELPDF转PPTPDF版本转换EXCEL转PDFEXCEL转WORDPDF转EXCELEPUB转WORDEPUB转PDFHTML转PDF&#xff08;需配置chromium&#xff09;点击查看配置教程简易二维码生…

TablePlus for Mac/Win:开启高效数据开发新纪元

在当今数字化时代&#xff0c;数据的重要性日益凸显。无论是企业还是个人&#xff0c;都需要一款强大而实用的本地原生数据开发软件来提升工作效率。而 TablePlus for Mac/Win 正是这样一款卓越的工具&#xff0c;它为用户带来了全新的体验&#xff0c;让数据开发变得更加轻松、…

Matlab实现CNN-BiLSTM模型,对一维时序信号进行分类

1、利用Matlab2021b训练CNN-BiLSTM模型&#xff0c;对采集的一维时序信号进行分类二分类或多分类 2、CNN-BiLSTM时序信号多分类执行结果截图 训练进度&#xff1a; 网络分析&#xff1a; 指标变化趋势&#xff1a; 代码下载方式&#xff08;代码含数据集与模型构建&#xff0…

go引入自建包名报错 package XXX is not in std和goland设置GO111MODULE提示冲突

首先在引入自建包的时候报错 查找网上的解决方法&#xff1a; 1、goland取消勾选Enable Go modules integration 2、set GO111MODULEoff 但是都没解决&#xff0c;而且更奇怪的是&#xff0c;我在cmd里面查看go env就显示set GO111MODULEoff 但是在goland里面的终端输入 go…

面试大厂,面试官问:为什么要使用盒模型?

1. 基础知识 什么是 CSS 盒模型 CSS 盒模型描述了页面中元素的布局和空间分配方式。每个元素都被看作是一个盒子&#xff0c;这个“盒子”由 4 个部分组成&#xff1a; 内容&#xff08;Content&#xff09;、内边距&#xff08;Padding&#xff09;、边框&#xff08;Borde…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-6.5, 汇编 led.s,第一次点亮LED灯

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

Llama 3 安装使用方法

Llama3简介&#xff1a; llama3是一种自回归语言模型&#xff0c;采用了transformer架构&#xff0c;目前开源了8b和70b参数的预训练和指令微调模型&#xff0c;400b正在训练中&#xff0c;性能非常强悍&#xff0c;并且在15万亿个标记的公开数据进行了预训练&#xff0c;比ll…

读天才与算法:人脑与AI的数学思维笔记13_Coq证明助手

1. 计算机 1.1. 对于计算机来说&#xff0c;它就很擅长处理这种重复而机械且计算量庞大的任务 1.1.1. 在速度与准确性等方面&#xff0c;计算机是远超过手工计算的 1.2. 计算机只能执行指令&#xff0c;并无自主创造力 1.2.1. 想…

JavaScript 的基本术语大全

文章目录 1、概述2、基本术语2.1、有效负载 (Payload)2.2、ReadableStream2.3、模块系统2.4、DOM (Document Object Model)2.5、事件 (Events)2.6、活动委托 (Event Delegation)2.7、内容安全策略 (CSP)2.8、渐进增强和优雅降级2.9、JSON (JavaScript Object Notation)2.10、AJ…

绝地求生:竞技比赛RP占比改动详解

大好&#xff0c;我闲游盒&#xff01; 在上周29.1版本更新后&#xff0c;官方也发布了关于竞技比赛&#xff1a;RP的改动公告&#xff0c;这里就为大家简单讲解一下具体改动的地方~ 官方希望能够通过优化让RP、段位和竞技比赛更能准确的反馈出大家自身的实力。 第一项改动是在…
最新文章