opencv 模板匹配_详细剖析模板匹配

点击上方“新机器视觉”,选择加"星标"或“置顶”

重磅干货,第一时间送达ca23cd8c45248059aad0474b08e539f3.png

模板匹配介绍

我们需要2幅图像:

原图像 (I): 在这幅图像里,我们希望找到一块和模板匹配的区域

模板 (T): 将和原图像比照的图像块

acf8518d0f6d22ee29fab509a646f028.png

  • 模板匹配就是在整个图像区域发现与给定子图像匹配的小块区域。

  • 所以模板匹配首先需要一个模板图像T(给定的子图像)

  • 另外需要一个待检测的图像-源图像S

  • 工作方法,在带检测图像上,从左到右,从上向下计算模板图像与重叠子图像的匹配度,匹配程度越大,两者相同的可能性越大。

模板匹配原理

我们的目标是检测最匹配模板的原图像的区域:

4dd24bfa8468be2e92afd0f43b9f052c.png

为了确定匹配模板区域, 我们不得不滑动模板图像和原图像进行比较 :

bb9e4251227e0f44fcd2cec8b1c50378.png

对于 模板(T) 覆盖在 原图像 (I) 上的每个位置,你把度量值保存 到 结果图像矩阵 ( R ) 中. 在 R 中的每个位置 (x,y) 都包含匹配度量值:

2ad4d83c3ff95f0211324c054dd44f0c.png

上图(右)就是 TM_CCORR_NORMED 方法处理后的结果图像 R . 最白的位置代表最高的匹配. 正如您所见, 黑色框住的位置很可能是结果图像矩阵中的最大数值, 所以这个区域 (以这个点为顶点,长宽和模板图像一样大小的矩阵) 被认为是匹配的.

实际上, 我们使用函数 minMaxLoc 来定位在矩阵 R 中的最大值点 (或者最小值, 根据函数输入的匹配参数) .

void minMaxLoc(InputArray src, double* minVal, double* maxVal=0, Point* minLoc=0, Point* maxLoc=0, InputArray mask=noArray())
  • src:输入图像。

  • minVal:在矩阵 src中存储的最小值,可输入NULL表示不需要。

  • maxVal :在矩阵 src中存储的最大值,可输入NULL表示不需要。

  • minLoc:在结果矩阵中最小值的坐标,可输入NULL表示不需要,Point类型。

  • maxLoc:在结果矩阵中最大值的坐标,可输入NULL表示不需要,Point类型。

  • mask:可选的掩模

模板匹配介绍 – 匹配算法介绍:

OpenCV中提供了六种常见的匹配算法如下:

  • 计算平方不同 :计算出来的值越小,越相关 TM_SQDIFF = 0

0d70e8bf471f9e443678ad43224f9b03.png

  • 计算相关性:计算出来的值越大,越相关 TM_CCORR = 2

4cb7cb3b7390c754123d1ccf49ffb26f.png

  • 计算相关系数:计算出来的值越大,越相关 TM_CCOEFF = 4

bfc4c39c3d72633ded50648f79e17ed2.png

  • 计算归一化平方不同 :计算出来的值越接近0,越相关 TM_SQDIFF_NORMED = 1

c47f842aa7c9f583a378c0bd3afa4ff6.png

  • 计算归一化相关性:计算出来的值越接近1,越相关 TM_CCORR_NORMED = 3

cea7347cfa75d64496b34ecd27a912a1.png

  • 计算归一化相关系数:计算出来的值越接近1,越相关 TM_CCOEFF_NORMED = 5

a2ac102749a6936f3f381f048ab32063.png

总结如下:

a79f64fb4c0d4c195c4e0f6995ef6164.png

相关API介绍cv::matchTemplate

matchTemplate(
InputArray image,// 源图像,必须是8-bit或者32-bit浮点数图像
InputArray templ,// 模板图像,类型与输入图像一致
OutputArray result,// 输出结果,必须是单通道32位浮点数,假设源图像WxH,模板图像wxh, 则结果必须为W-w+1, H-h+1的大小。
int method,//使用的匹配方法
InputArray mask=noArray()//(optional)
)

fc6f10304f958d4ad882e22e4e4eea4e.png

程序代码

#include 
#include 
using namespace std;
using namespace cv;

// 定义一些全局变量, 例如原图像(img), 模板图像(templ) 和结果图像(result) , 
// 还有匹配方法以及窗口名称:
Mat img, templ, result;
char* image_window = "Source Image";
char* result_window = "Result window";

int match_method = TM_SQDIFF;
int max_Trackbar = 5;

void MatchingMethod( int, void* );

int main( int argc, char** argv ){
 // 1. 载入原图像和模板块
 img = imread("E:/Experiment/OpenCV/Pictures/TargetSearch.jpg");
 templ = imread("E:/Experiment/OpenCV/Pictures/Target.jpg");
 imshow("模板图像",templ);

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

 // 2. 创建滑动条并输入将被使用的匹配方法. 一旦滑动条发生改变,回调函数 MatchingMethod 就会被调用.
 char* trackbar_label = "模板匹配方式";
 createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );

 MatchingMethod( 0, 0 );

 waitKey(0);
 return 0;
}

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_cols, result_rows, CV_32FC1 );

 // 执行模板匹配操作,并对结果进行归一化:
 matchTemplate( img, templ, result, match_method );
 normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );

 // 通过函数 minMaxLoc 定位最匹配的位置
 double minVal, maxVal; 
 Point minLoc, maxLoc;
 Point matchLoc;
 
 //通过使用函数 minMaxLoc ,我们确定结果矩阵 R 的最大值和最小值的位置.
 minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );//寻找result中最大值,最小值,及它们所在的位置

 // 对于方法 SQDIFF 和 SQDIFF_NORMED, 越小的数值代表更高的匹配结果. 而对于其他方法, 数值越大匹配越好
 if( match_method  == CV_TM_SQDIFF || match_method == CV_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;
}

运行结果

7cddc895688eeb7499d78c5ec283808a.png

9af0ae67da764cbdea1b19b3c4e655f3.png

本文主要借鉴”Madcola“和”Micheal超“两位大神的文章。两位大神的博客主页是:

https://www.cnblogs.com/skyfsm/(Madcola)

https://blog.csdn.net/qq_42887760(Micheal超)

转自:小白学视觉

404a15fd7510daafe1da6aeb7ffb7c6a.gif End 404a15fd7510daafe1da6aeb7ffb7c6a.gif

声明:部分内容来源于网络,仅供读者学术交流之目的。文章版权归原作者所有。如有不妥,请联系删除。

291710845136f0a1b036555ec8a88a48.png

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode笔记】143. 重排链表(Java、链表、栈、快慢指针)

文章目录题目描述思路 & 代码更新版:快慢指针 翻转链表题目描述 一看题目反序:用栈更新:O(1) 空间复杂度 思路 & 代码 先快慢指针,找到需要入栈的起点,然后逐个入栈然后逐个出栈,进行重排即可…

hdfs中与file数组类似的数组_如何在 JavaScript 中克隆数组

作者:Yazeed Bzadough译者:allenJavaScript 有很多方法可以做任何事情,现在我们研究数组。1.扩展运算符(浅拷贝)自从 ES6 发布以来,这一直是最受欢迎的方法。这是一个简短的语法,你会发现它在使用像 React 和 Redux 这…

【LeetCode笔记】54. 螺旋矩阵(Java、迭代、递归)

文章目录题目描述思路 & 代码递归迭代更新版 - 递归题目描述 递归和迭代都实现了,递归相对比较好理解,也比较好实现 思路 & 代码 递归 每次都把最外圈的元素跑完一行 or 一列的时候,跑一次结束边界:left > right …

ubuntu boot空间不足_安装 Ubuntu 双系统

在很久以前就想试一试Linux了,一直没有实践。一是觉得太麻烦,二是怕把电脑弄坏。后来实践了一次后发现,其实并没有想象中那么难,这是一个完全独立的系统,与隔壁的Windows半毛钱关系都没有。把引导搞定就不怕了。另外&a…

【LeetCode笔记】112 113. 路径总和 I II(Java、递归、DFS)

文章目录路径总和 I路径总和 II比较简单,就连着一起写了 路径总和 I 注意:一定得走到叶子才算 直接看代码吧,注释也就几行。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* T…

安装python环境与运行_linux下怎么安装python的运行环境

展开全部 1、2113linux下安装python3 a、 准备编译环境(环境如果不对的5261话,可能遇到各种问题,比如wget无法下4102载1653https链接的文件)1 2 yum groupinstall Development Tools yum install zlib-devel bzip2-devel openssl-devel ncurses-devel2 下…

【LeetCode笔记】437. 路径总和III(Java、双重递归、二叉树)

文章目录题目描述思路 & 代码题目描述 说实话确实比I & II要恶心一点这里直接用双递归秒了(今天才发现我是之前先写的III,再写的I & II)有前缀和的写法,但是懒得整了 思路 & 代码 和之前不同,这里…

python中break和continue的区别_python中break和continue的区别

原博文 2017-08-01 09:56 − break和continue 1.break 意思为结束循环 例&#xff1a; i 0 while i<10: i1 if i5: #当i5时&#xff0c;结束整个循环 ...030778 相关推荐 2019-12-20 11:37 − 1、break break的使用场景&#xff1a;循环语句和switch-case分支语句。 它的作…

【LeetCode笔记】128. 最长连续序列(Java、哈希表、动态规划)

文章目录题目描述思路 & 代码变式题&#xff1a;返回答案数组更新可恶。。居然碰到了周一面试没撕出最优复杂度的题 题目描述 难点在于时间复杂度O(n)&#xff0c;否则来个sort()还是很轻松的 思路 & 代码 一般来说&#xff0c;时间复杂度可以用空间复杂度来弥补&a…

【LeetCode笔记】148. 排序链表(Java、归并排序、快慢指针、双重递归)

文章目录题目描述思路 & 代码二刷题目描述 难点在于时空复杂度的要求 思路 & 代码 转化成&#xff1a;归并排序 合并两个有序链表 即可利用快慢指针来拆分成两条链表注意&#xff1a;链表的拆分 & 连接时间复杂度O(n * logn)&#xff0c;空间复杂度 O(1) /**…

java substring截取字符串_lt;12gt;深入了解字符串

深入了解字符串完整视频教程&#xff1a;Java轻松入门经典教程[柠檬学院]-学习视频教程-培训课程-腾讯课堂​ke.qq.com字符串主要用于编程&#xff0c;用于概念说明、函数解释、用法详述等&#xff0c;字符串在存储上类似于字符数组&#xff0c;所以它的每一个单元都是可以提取…

【LeetCode笔记】88. 合并两个有序数组(Java、双指针)

文章目录题目描述思路 & 代码更新题目描述 比较简单&#xff0c;直接看思路吧&#xff01; 思路 & 代码 从后往前进行填数即可&#xff08;正确性可以保证&#xff09;时间复杂度 O(n) class Solution {public void merge(int[] nums1, int m, int[] nums2, int n…

thymealf如何实现传单个变量给html_梦回2013,看尤大vue的第一行代码,如何用30行代码实现vue(超简洁,适合初学者)...

非非非标题党&#xff0c;干货预警&#xff01;&#xff01;&#xff01;介绍大家好&#xff0c;我是清池交友 app 开发日记&#xff0c;记录清池交友 app 开发中学习过程和踩坑日记&#xff0c;伪全栈[1]技术栈&#xff1a;前端 js&#xff0c;vue&#xff0c;uniapp&#xff…

【LeetCode笔记】142. 环形链表 II(Java、快慢指针)

文章目录题目描述思路 & 代码更新版三刷 - 再更新题目描述 相对于环形链表&#xff0c;这里要求找到环的起点难点在于 O(1)&#xff0c;否则可以直接哈希表冲 思路 & 代码 找出快慢指针的路程关系&#xff0c;得出结论&#xff08;详见代码注释&#xff09; /*** …

python找出在原图中的位置_Python 通过截图匹配原图中的位置(opencv)实例

安装依赖 1&#xff09;下载安装opencv-2.4.9&#xff0c;并将cv2.pyd拷贝到python安装目录的site-package下 2&#xff09;pip install numpy 3&#xff09;pip install aircv 准备一张原图和截图 原图截图代码 import cv2 import aircv as ac # print circle_center_pos def …

【LeetCode笔记】287. 寻找重复数(Java、快慢指针、原地、链表)

文章目录题目描述思路 & 代码更新题目描述 可以理解成数组版本的 环形链表 II更多详细思路可见以上超链接。 思路 & 代码 如何转化成逻辑上的链表&#xff1f;nums[i] 是 第 i 个结点的 next 指针构造成一个有 nums.length - 重复次数 个结点的链表如此&#xff0…

【LeetCode笔记】338. 比特位计数(Java、位运算、动态规划)

文章目录题目描述思路 & 代码无注释二刷题目描述 难点在于 O(n) 思路 & 代码 理解题意&#xff0c;分析出 O(n) 复杂度应该是要用到之前的值来得到当前值——动态规划核心结论&#xff1a;奇数比前一个数多一个1&#xff0c;偶数和偶数除二后的数的1数量一样边界 &…

【LeetCode笔记】560. 和为K的子数组(Java、前缀和、哈希表)

文章目录题目描述思路 & 代码暴力法 O(n2n^2n2)前缀和 哈希表 O(n)二刷题目描述 第一道前缀和题目&#xff5e; 思路 & 代码 暴力法 O(n2n^2n2) 固定一个值&#xff0c;从后往前找满足的条件即可 class Solution {public int subarraySum(int[] nums, int k) {i…

caffe不支持relu6_国产AI框架再进化!百度Paddle Lite发布:率先支持华为NPU在线编译,全新架构更多硬件支持...

乾明 边策 发自 凹非寺 量子位 报道 | 公众号 QbitAI国产AI框架飞桨刚刚带来新进化&#xff1a;Paddle Lite正式发布&#xff01;高扩展、高性能、轻量化&#xff0c;还是首个支持华为NPU在线编译的深度学习端侧推理框架&#xff0c;剑指加大力度攻占移动端侧场景。而且大环境如…

【LeetCode笔记】1143. 最长公共子序列(Java、动态规划、字符串)

文章目录题目描述思路 & 代码二刷三刷题目描述 算是很高频的面试题了&#xff0c;而且题号还挺后的 思路 & 代码 注意&#xff1a;dp[i]][j] 和 charAt(i) 有1的下标差&#xff08;dp初始化边界&#xff09;核心思想&#xff1a;当前值可构成新的最大子串就左上角值…