opencv基础57-模板匹配cv2.matchTemplate()->(目标检测、图像识别、特征提取)
OpenCV 提供了模板匹配(Template Matching)的功能,它允许你在图像中寻找特定模板(小图像)在目标图像中的匹配位置。模板匹配在计算机视觉中用于目标检测、图像识别、特征提取等领域。
以下是 OpenCV 中使用模板匹配的基本步骤:
-
加载图像: 首先,加载目标图像和要匹配的模板图像。
-
选择匹配方法: 选择适当的匹配方法,例如 cv2.TM_CCOEFF、cv2.TM_CCOEFF_NORMED、cv2.TM_CCORR、cv2.TM_CCORR_NORMED、cv2.TM_SQDIFF 或 cv2.TM_SQDIFF_NORMED。每种方法对应不同的匹配计算方式。
-
应用模板匹配: 使用 cv2.matchTemplate() 函数来进行模板匹配,该函数返回匹配结果的矩阵。
-
找到最佳匹配位置: 在匹配结果矩阵中,通过分析像素值找到最佳匹配位置,即目标图像中的匹配位置。
模板匹配是指在当前图像 A 内寻找与图像 B 最相似的部分,一般将图像 A 称为输入图像,将图像 B 称为模板图像。模板匹配的操作方法是将模板图像 B 在图像 A 上滑动,遍历所有像素以完成匹配。
例如,在图 15-1 中,希望在图中的大图像“lena”内寻找左上角的“眼睛”图像。此时,大图像“lena”是输入图像,“眼睛”图像是模板图像。查找的方式是,将模板图像在输入图像内从左上角开始滑动,逐个像素遍历整幅输入图像,以查找与其最匹配的部分。
模板匹配函数说明
在 OpenCV 内,模板匹配是使用函数 cv2.matchTemplate()实现的。该函数的语法格式为:
result = cv2.matchTemplate(image, templ, method[, mask ] )
其中:
- image 为原始图像,必须是 8 位或者 32 位的浮点型图像。
- templ 为模板图像。它的尺寸必须小于或等于原始图像,并且与原始图像具有同样的类
型。 - method 为匹配方法。该参数通过 TemplateMatchModes 实现,有 6 种可能的值,如表 15-1 所示。
- mask 为模板图像掩模。它必须和模板图像 templ 具有相同的类型和大小。通常情况下该值使用默认值即可。当前,该参数仅支持 TM_SQDIFF 和 TM_CCORR_NORMED 两个值。
函数 cv2.matchTemplate()的返回值 result 是由每个位置的比较结果组合所构成的一个结果集,类型是单通道 32 位浮点型。如果输入图像(原始图像)尺寸是 WH,模板的尺寸是 wh,
则返回值的大小为(W-w+1)*(H-h+1)。
在进行模板匹配时,模板在原始图像内遍历。在水平方向上:
I 表示输入图像,T 表示模板,R 表示输出的结果图像,x 和 y 表示位置信息。
- 遍历的起始坐标是原始图像左数第 1 个像素值(序号从 1 开始)。
- 最后一次比较是当模板图像位于原始图像的最右侧时,此时其左上角像素点所在的位置是 W-w+1。
因此,返回值 result 在水平方向上的大小是 W-w+1(水平方向上的比较次数)。
在垂直方向上:
- 遍历的起始坐标从原始图像顶端的第 1 个像素开始。
- 最后一次比较是当模板图像位于原始图像的最下端时,此时其左上角像素点所在位置是H-h+1。
所以,返回值 result 在垂直方向上的大小是 H-h+1(垂直方向上的比较次数)。
如果原始图像尺寸是 WH,模板的尺寸是 wh,则返回值的大小为(W-w+1)(H-h+1)。也就是说,模板图像要在输入图像内比较(W-w+1)(H-h+1)次。
太抽象了再看看下面的说明看看能不能好理解一点
例如,在图 15-2 中,左上方的 2×2 小方块是模板图像,右下方的 10×10 图像是输入图像
(原始图像)。在进行模板匹配时:
- 首先将模板图像置于输入图像的左上角。
- 模板图像在向右移动时,最远只能位于输入图像的最右侧边界处,此时模板图像左上角的像素对应着输入图像的第 9 列(输入图像宽度-模板图像宽度+1 = 10-2+1 = 9)。
- 模板图像在向下移动时,最远只能位于输入图像最下端的边界处。此时模板图像左上角的像素对应着输入图像的第 9 行(输入图像高度-模板图像高度+1 = 10-2+1 = 9)。
根据上述分析可知,比较结果 result 的大小满足(W-w+1)*(H-h+1),在上例中就是(10-2+1)×(10-2+1),即 9×9。也就是说,模板图像要在输入图像内总计比较 9×9 = 81 次,这些比较结果将构成一个 9×9 大小的二维数组。
这样感觉有点懂了。
这里需要注意
的是,函数 cv2.matchTemplate()通过参数 method 来决定使用不同的查找方法。
对于不同的查找方法,返回值 result 具有不同的含义。
例如:
- method 的值为 cv2.TM_SQDIFF 和 cv2.TM_SQDIFF_NORMED 时,result 值为 0 表示匹
配度最好,值越大,表示匹配度越差。 - method 的值为 cv2.TM_CCORR、cv2.TM_CCORR_NORMED、cv2.TM_CCOEFF 和
cv2.TM_CCOEFF_NORMED 时,result 的值越小表示匹配度越差,值越大表示匹配度越好。
从上述分析可以看出,查找方法不同,结果的判定方式也不同。在查找最佳匹配时,首先要确定使用的是何种 method,然后再确定到底是查找最大值,还是查找最小值。
代码示例:使用函数 cv2.matchTemplate()进行模板匹配。
要求参数 method 的值设置为 cv2.TM_SQDIFF,显示函数的返回结果及匹配结果。
先到网上找个图,然后从图中再截取一部分另存为一个图。
代码如下:
import cv2from matplotlib import pyplot as pltimg1 = cv2.imread('toukui.png')
#将图片转换为灰度图
img = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)template1 = cv2.imread('toukui2.png')template = cv2.cvtColor(template1,cv2.COLOR_BGR2GRAY)th, tw = template.shape[::]
rv = cv2.matchTemplate(img,template,cv2.TM_SQDIFF)minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(rv)
topLeft = minLoc
bottomRight = (topLeft[0] + tw, topLeft[1] + th)
cv2.rectangle(img,topLeft, bottomRight, 255, 2)
plt.subplot(121),plt.imshow(rv,cmap = 'gray')
plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img,cmap = 'gray')
plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
plt.show()
运行效果:
简单的目标检测效果就来了。
多模板匹配
也就是说目标检测时 出现了多个目标,在前面的例子中,我们在输入图像 中搜索其戴头盔,该子图在整个输入图像内仅出现了一次。但是,有些情况下,如果摄像头下面现在站了3个戴头盔的人,这时就需要找出多个匹配结果。而函数 cv2.minMaxLoc()仅仅能够找出最值,无法给出所有匹配区域的位置信息。所以,要想匹配多个结果,使用函数 cv2.minMaxLoc()是无法实现的,需要利用阈值进行处理。
下面分步骤介绍如何获取多模板匹配的结果。
1. 获取匹配位置的集合
函数 where()能够获取模板匹配位置的集合。对于不同的输入,其返回的值是不同的。
- 当输入(参数)是一维数组时,返回值是一维索引,只有一组索引数组。
- 当输入是二维数组时,返回的是匹配值的位置索引,因此会有两组索引数组表示返回值的位置。
以下代码查找在一维数组 a 中,数值大于 5 的元素的索引(即该元素所在的位置,数组的索引从 0 开始):
import numpy as np
a=np.array([3,6,8,1,2,88])
b=np.where(a>5)
print(b)
该段代码返回的结果为:
(array([1, 2, 5], dtype=int64),)
说明索引值为 1、2、5 的数组元素,它们的值是大于 5 的
。
上面介绍的是输入值为一维数组时的情况。
当输入值是二维数组时,函数 where()会返回满足条件的值在二维数组中的索引。
例如,以下代码查找在二维数组 am 中,值大于 5 的元素的索引:
import numpy as np
am=np.array([[3,6,8,77,66],[1,2,88,3,98],[11,2,67,5,2]])
b=np.where(am>5)
print(b)
该段代码返回的结果为:
(array([0, 0, 0, 0, 1, 1, 2, 2], dtype=int64),
array([1, 2, 3, 4, 2, 4, 0, 2], dtype=int64))
上述结果说明,存在二维数组 am,它的值为:
[[ 3 6 8 77 66]
[ 1 2 88 3 98]
[11 2 67 5 2]]
其中,位置[0, 1]、[0, 2]、[0, 3]、[0, 4]、[1, 2]、[1, 4]、[2, 0]、[2, 2]
上的元素值大于 5。
综上所述,函数 np.where()可以找出在函数 cv2.matchTemplate()
的返回值中,哪些位置上的值是大于阈值 threshold 的。
在具体实现时,可以采用的语句为:
loc = np.where( res >= threshold)
式中:
- res 是函数 cv2.matchTemplate()进行模板匹配后的返回值。
- threshold 是预设的阈值
- loc 是满足“res >= threshold”的像素点的索引集合。例如,在上面的二维数组 am 中,返回的大于 5 的元素索引集合为(array([0, 0, 0, 0, 1, 1, 2, 2], dtype=int64), array([1, 2, 3, 4,2, 4, 0, 2], dtype=int64))。返回值 loc 中的两个元素,分别表示匹配值的行索引和列索引。
2. 循环坐标值
这个没啥看头,知道什么叫循环就行
比如要处理多个值,通常需要用到循环。
例如,有一个列表,其中的值为 71、23、16,希望将这些值逐个输出,可以这样写代码:
value = [71,23,16]
for i in value:
print('value 内的值:', i)
运行上述代码,得到的输出结果为:
value 内的值: 71
value 内的值: 23
value 内的值: 16
因此,在获取匹配值的索引集合后,可以采用如下语句遍历所有匹配的位置,对这些位置做标记:
for i in 匹配位置集合:标记匹配位置。
3. 在循环中使用函数zip()
函数 zip()用可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
例如,以下代码使用函数 zip()将 t 内对应的元素打包成一个个元组,并打印了由这些元组组成的列表:
x = [1,2,3]
y = [4,5,6]
z = [7,8,9]
t = (x,y,z)
print(t)
for i in zip(*t):print(i)
上述代码中,语句 print(t)将 t 内的元素输出,结果为:
([1, 2, 3], [4, 5, 6], [7, 8, 9])
循环语句 for i in zip(*t)将 t 内的元素打包成元组后输出,结果为:
(1, 4, 7)
(2, 5, 8)
(3, 6, 9)
因此,如果希望循环遍历由 np.where()返回的模板匹配索引集合,可以采用的语句为:
for i in zip(*模板匹配索引集合):标记处理
例如,对于前面提到的数组 am,使用函数 zip()循环,就可以得到其中大于 5 的元素索引
的集合:
import numpy as np
am=np.array([[3,6,8,77,66],[1,2,88,3,98],[11,2,67,5,2]])
print(am)
b=np.where(am>5)
for i in zip(*b):print(i)
上述代码的输出结果为:
[[ 3 6 8 77 66]
[ 1 2 88 3 98]
[11 2 67 5 2]]
(0, 1)
(0, 2)
(0, 3)
(0, 4)
(1, 2)
(1, 4)
(2, 0)
(2, 2)
4. 调整坐标
函数 numpy.where()可以获取满足条件的模板匹配位置集合,然后可以使用函数cv2.rectangle()在上述匹配位置绘制矩形来标注匹配位置
。
使用函数 numpy.where()在函数 cv2.matchTemplate()的输出值中查找指定值,得到的形式为“(行号,列号)”的位置索引。
但是,函数 cv2.rectangle()中用于指定顶点的参数所使用的是形
式为“(列号,行号)”的位置索引。所以,在使用函数 cv2.rectangle()绘制矩形前,要先将函数numpy.where()得到的位置索引做“行列互换”。可以使用如下语句实现 loc 内行列位置的互换:loc[::-1]
如下语句将 loc 内的两个元素交换位置:
import numpy as np
loc = ([1,2,3,4],[11,12,13,14])
print(loc)
print(loc[::-1])
其中,语句 print(loc)所对应的输出为:
([1, 2, 3, 4], [11, 12, 13, 14])
语句 print(loc[::-1])所对应的输出为:
([11, 12, 13, 14], [1, 2, 3, 4])
5. 标记匹配图像的位置
最后一步就用 函数 cv2.rectangle()可以标记匹配图像的具体位置,分别指定要标记的原始图像、对角顶点、颜色、矩形边线宽度即可。
关于矩形的对角顶点:
- 其中的一个对角顶点 A 可以通过 for 循环语句从确定的满足条件的“匹配位置集合”内获取。
- 另外一个对角顶点,可以通过顶点 A 的位置与模板的宽(w)和高(h)进行运算得到。
因此,标记各个匹配位置的语句为:
for i in 匹配位置集合:cv2.rectangle(输入图像,i, (i[0] + w, i[1] + h), 255, 2)
代码示例:使用模板匹配方式,标记在输入图像内与模板图像匹配的多个子图像。
代码如下:
import cv2
import numpy as np
img = cv2.imread('lena4.bmp',0)
template = cv2.imread('lena4Temp.bmp',0)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.9
loc = np.where( res >= threshold)
for pt in zip(*loc[::-1]):cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), 255, 1)cv2.imshow("template",template)
cv2.imshow("result1",img)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
可以看到输入图像内多个与模板图像匹配的子图被标记出来
相关文章:

opencv基础57-模板匹配cv2.matchTemplate()->(目标检测、图像识别、特征提取)
OpenCV 提供了模板匹配(Template Matching)的功能,它允许你在图像中寻找特定模板(小图像)在目标图像中的匹配位置。模板匹配在计算机视觉中用于目标检测、图像识别、特征提取等领域。 以下是 OpenCV 中使用模板匹配的基…...
搜索插入位置
题目描述: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target …...
Centos Linux快速复制文件并查看进度的方法(保留文件原始时间戳等属性)
用cp命令保证快速复制,screen保证不丢失你对cp命令执行情况的掌控。 需要用到screen软件包,防止复制过程中终端被关闭。 centos linux直到7默认都没有screen,需要安装一下 普通账户: sudo yum -y install screen root账户&am…...

牛奶产业链的工业“链主品牌”利乐是如何诞生的?
瑞典的利乐公司,一个在乳品产业链中占据重要地位的“链主品牌”,通过提供创新的包装材料和解决方案,在全球范围内占据了显著的市场份额。利乐从不生产一滴奶,却赚取了中国乳业 75%的利润,一年创收超过 800 亿人民币。在…...

【历史上的今天】8 月 11 日:苹果电脑之父诞生;阿里巴巴收购雅虎中国;OpenAI 击败电竞世界冠军
整理 | 王启隆 透过「历史上的今天」,从过去看未来,从现在亦可以改变未来。 今天是 2023 年 8 月 11 日,在 1999 年的今天,欧亚一些地区观赏到了一次壮丽的日全食景象。这次日全食是本世纪最后一次日全食,将持续两分钟…...
Flutter 报错 Could not create task ‘xxx‘.this and base files have different roots
遇到此问题也是先去百度了,有的说改了Gradle版本、gradle-wrapper.properties版本和ext.kotlin_version版本之后解决的,我没尝试,我用蹩脚的英语大致读了一下就不是这样说的,况且我用有道翻译了也不是这个意思啊,我不知…...

单调递增的数字——力扣738
文章目录 题目描述解法题目描述 解法 #include<iostream> #include<string>using namespace std;int monotoneIncreasingDigits...

STL文件格式详解【3D】
STL(StereoLithography:立体光刻)文件是 3 维表面几何形状的三角形表示。 表面被逻辑地细分或分解为一系列小三角形(面)。 每个面由垂直方向和代表三角形顶点(角)的三个点来描述。 切片算法使用…...

【分布式技术专题】RocketMQ延迟消息实现原理和源码分析
痛点背景 业务场景 假设有这么一个需求,用户下单后如果30分钟未支付,则该订单需要被关闭。你会怎么做? 之前方案 最简单的做法,可以服务端启动个定时器,隔个几秒扫描数据库中待支付的订单,如果(当前时间-订…...

Qt中ffmpeg API存储和显示摄像头视频
Qt中ffmpeg API存储和显示摄像头视频的功能需要之前写的视频ffmpegAPI的视频播放的流程。 代码源码位置:https://download.csdn.net/download/qq_43812868/88157743?spm1001.2014.3001.5503 一、存储和显示摄像头的视频的流程 这是读取打开视频文件的流程&#x…...
jfinal tomcat部署
首先明确一下 JFinal 项目是标准的 java web 项目,其部署方式与普通 java web 项目没有任何差别。Java Web 项目在 Tomcat 下部署有一些不必要的坑需要避免,所以撰写此文方便大家绕过一些坑,以下部署以 linux 为例,windows 与此类…...
Linux - MongoDB 数据库自动退出服务问题/闪退
问题:MongoDB 自动退出服务问题 原因: 由于 Mongodb 服务,使用过多系统内存,导致系统强制停止 Mongodb 服务。 解决方法: 在 mongodb.conf 配置文件内,添加新配置内容: wiredTigerCacheSi…...
B2B2C多语言电商系统平台搭建,多用户商城系统搭建(app、小程序、微商城)
搭建B2B2C多语言电商系统平台以及多用户商城系统(包括app、小程序、微商城)的步骤如下: 步骤一:需求分析和规划 1. 确定项目的目标和范围。明确平台所需的功能、支持的语言、用户权限等要求。 2. 分析竞争对手,并确定…...

【MySQL】创建高级联结
目录 一、使用表别名 二、使用不同类型的联结 1.自联结 2.自然联结 3.外部联结 3.使用带聚集函数的联结 4.使用联结和联结条件 一、使用表别名 别名除了用于列名和计算字段外,SQL还允许给表名起别名。 起别名有两个好处: 一个是缩短SQL语句&am…...

chatGPT应用于房地产行业
作为 2023 年的房地产专业人士,您无疑认识到技术对行业的重大影响。近年来,一项技术进步席卷了世界——人工智能。人工智能彻底改变了房地产业务的各个方面,从简化管理任务到增强客户互动。 在本文中,我们将探讨几种巧妙的人工智…...
java之jmh初识及使用
最近有场景需要数据支撑json的toJsonString方法和java原生的toString方法的运行速度,因此选用了JMH测试工具。 以下代码大致意思是:初始化一个list集合,放入100个对象,然后遍历这个集合,调用fastjson的toJsonString方…...

利用状态监测和机器学习提高冷却塔性能的具体方法
在现代工业生产中,冷却塔扮演着至关重要的角色,它们的性能直接影响着工艺流程的稳定性和效率。为了确保冷却塔的正常运行和减少系统故障,状态监测和机器学习成为了关键技术。 图.冷却塔(PreMaint) 在前文《基于人工智…...
LeetCode_02_1289. 下降路径最小和 II
1289. 下降路径最小和 II 给你一个 n x n 整数矩阵 grid ,请你返回 非零偏移下降路径 数字和的最小值。 非零偏移下降路径 定义为:从 grid 数组中的每一行选择一个数字,且按顺序选出来的数字中,相邻数字不在原数组的同一列。 示…...
consul servicecheck 查看健康信息
在浏览器中地址栏输入如下信息:http://localhost:8500/v1/agent/checks 返回信息如下: { "service:springboot-security-oauth2-zuul-sso-server-1881": { "Node": "8DBQ2F05HUXZ2QO", "Check…...

什么是信息孤岛?如何打破信息孤岛?
一文让你看懂:什么是信息孤岛?信息孤岛形成的原因?以及如何打破信息孤岛? 本文重点结合了企业信息系统的需求,给出了整合企业现有信息系统的方法,能有效解决企业信息孤岛的问题,并帮助企业快速…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...