苦学Opencv的第九天:模板匹配

Python OpenCV入门到精通学习日记:模板匹配

前言

模板匹配是一种最原始、最基本的识别方法,可以在原始图像中寻找特定图像的位置。模板匹配经常应用于简单的图像查找场景中,例如,在集体合照中找到某个人的位置。

模板匹配
模板匹配方法
单模板匹配
多模板匹配

模板匹配

  • Python OpenCV入门到精通学习日记:模板匹配
    • 前言
    • 1 模板匹配方法
    • 2 单模板匹配
      • 2.1 单目标匹配
      • 2.2 多目标匹配
    • 3 多模板匹配
    • 4 小结

1 模板匹配方法

模板是被查找目标的图像,查找模板在原始图像中的哪个位置的过程就叫模板匹配。OpenCV提供的matchTemplate()方法就是模板匹配方法

result = cv2.matchTemplate(image, templ, method, mask)参数说明:image:原始图像。templ:模板图像,尺寸必须小于或等于原始图像。method:匹配的方法,可用参数值如表所示。mask :可选参数 。掩 模 ,只有cv2.TM_SQDIFF 和cv2.TM_CCORR_NORMED支持此参数,建议采用默认值。result:计算得出的匹配结果。如果原始图像的宽、高分别为W、H,模板图像的宽、高分别为w、h,result就是一个W-w+1列、H-h+1行的32位浮点型数组。数组中每一个浮点数都是原始图像中对应像素位置的匹配结果,其含义需要根据method参数来解读。
参数值含 义
CV2.TM_SQDIFF0差值平方和匹配,也叫平方差匹配。可以理解为差异程度,匹配程度越高,计算结果越小。完全匹配的结果为0
CV2.TM_SQDIFF_NORMED1标准差值平方和匹配,也叫标准平方差匹配,规则同上
CV2.TM_CCORR2相关匹配。可以理解为相似程度,匹配程度越高,计算结果越大
CV2.TM_CCORR_NORMED3标准相关匹配,规则同上
CV2.TM_CCOEFF4相关系数匹配,也属于相似程度,计算结果为-1~1的浮点数,1表示完全匹配,0表示毫无关系,-1表示2张图片亮度刚好相反
CV2.TM_CCOEFF_NORMED5标准相关系数匹配,规则同上

在模板匹配的计算过程中,模板会在原始图像中移动。模板与重叠区域内的像素逐个对比,最后将对比的结果保存在模板左上角像素点索引位置对应的数组位置中。

以下是计算出来的结果格式,所有的方法计算出来的数组格式都是相同的,只有数值不同。

在这里插入图片描述
模板将原始图像中每一块区域都覆盖一遍,但结果数组的行、列数并不等于原始图像的像素的行、列数。这是因为我们的参照点是模版的左上角,假设模板的宽为w,高为h,原始图像的宽为W,高为H,模板移动到原始图像的边缘之后就不会继续移动了,所以该区域的边长为“原始图像边长-模板边长+1”,最后加1是因为移动区域内的上下、左右的2个边都被模板覆盖到了,如果不加1会丢失数据

2 单模板匹配

匹配过程中只用到一个模板场景单模板匹配。原始图像中可能只有一个和模板相似的图像,也可能有多个。如果只获取匹配程度最高的那一个结果,这种操作叫作单目标匹配。如果需要同时获取所有匹配程度较高的结果,这种操作叫作多目标匹配

2.1 单目标匹配

单目标匹配只获取一个结果即可,就是匹配程度最高的结果。
matchTemplate()方法的计算结果是一个二维数组,OpenCV提供了一个minMaxLoc()方法专门用来解析这个二维数组中的最大值、最小值以及这2个值对应的坐标,minMaxLoc()方法的语法如下:

minValue, maxValue, minLoc, maxLoc = cv2.minMaxLoc(src,mask)参数说明:src:matchTemplate()方法计算得出的数组。mask:可选参数,掩模,建议使用默认值。
返回值说明:minValue:数组中的最小值。maxValue:数组中的最大值。minLoc:最小值的坐标,格式为(x, y)。maxLoc:最大值的坐标,格式为(x, y)。

平方差匹配的计算结果越小,匹配程度越高minMaxLoc()方法返回的minValue值就是模板匹配的最优结果,minLoc就是最优结果区域左上角的点坐标,区域大小与模板大小一致

在这里我们举个例子,首先我们需要图片和模板,首先我们截取一张我们的电脑壁纸:

请添加图片描述

然后我们找一个图标作为我们的实验品模板:

请添加图片描述

接下来开始编写代码:

# 导入cv2模块,它是OpenCV库的Python接口
import cv2# 读取名为"img_2.png"的图像文件,将其存储在变量img中
img = cv2.imread("img_2.png")# 读取名为"img_3.png"的图像文件,将其存储在变量templ中,用作模板
templ = cv2.imread("img_3.png")# 获取模板图像的高度、宽度和通道数,分别存储在height、width和c中
height, width, c = templ.shape# 使用matchTemplate函数在img图像中寻找templ模板的位置
# cv2.TM_SQDIFF_NORMED是匹配方法,表示使用平方差归一化方法
results = cv2.matchTemplate(img, templ, cv2.TM_SQDIFF_NORMED)# 打印匹配结果矩阵
print(results)# 使用minMaxLoc函数找到匹配结果中的最小值和最大值,以及它们的坐标位置
# minValue和maxValue分别存储最小和最大值
# minLoc和maxLoc分别存储最小值和最大值的坐标
minValue, maxValue, minLoc, maxLoc = cv2.minMaxLoc(results)# 根据最小值坐标计算矩形的起始点,即模板匹配的起始位置
resultPoint1 = minLoc# 根据模板的宽度和高度计算矩形的结束点,即模板匹配的结束位置
resultPoint2 = (resultPoint1[0] + width, resultPoint1[1] + height)# 在原图中用红色矩形标记出模板匹配的位置,线宽为2
cv2.rectangle(img, resultPoint1, resultPoint2, (0, 0, 255), 2)# 显示标记后的图像,窗口标题为"img"
cv2.imshow("img", img)# 等待用户按键,任何键都可以
cv2.waitKey()# 销毁所有由OpenCV创建的窗口
cv2.destroyAllWindows()

运行结果如下:
在这里插入图片描述

当我们有一个模板后,如何在两个很相似的图中准确匹配模板呢?接下来,使用模板匹配的相应方法模拟这个游戏。

我们在这里举个例子:
首先是模板:
请添加图片描述
然后是两个极其相似的情景:
请添加图片描述
请添加图片描述
注意看221路标的路障有个黄色的线。
现在开始识别:

import cv2
# 初始化一个空列表,用于存储图像
img = []
# 使用append方法将读取的图像添加到img列表中
img.append(cv2.imread("image_221.png"))
img.append(cv2.imread("image_222.png"))
templ = cv2.imread("templ.png")
# 用于存储匹配最佳图像的索引
index = -1
min = 1
for i in range(0,len(img)):results = cv2.matchTemplate(img[i],templ,cv2.TM_SQDIFF_NORMED)# 注意看这里他只匹配了第一行的数据,这样有时是不够准确的,但如果能够确认正确,这样也更加高效if min > any(results[0]):index = i
cv2.imshow("result",img[i])
cv2.waitKey()
cv2.destroyAllWindows()

在这段代码中,index 变量初始化为 -1 的原因主要有两个

  1. 默认值:在编程中,使用 -1 作为索引的初始值是一种常见的做法,特别是在处理列表或数组时。-1 表示没有有效的索引,因为索引通常是从 0 开始的。这样做的目的是为了在循环开始之前,确保 index 值不指向列表中的任何有效元素。
  2. 错误检查:如果循环中没有找到任何匹配项,index 将保持其初始值 -1。这样,在循环结束后,你可以通过检查 index 的值来判断是否有有效的匹配项被找到。如果 index 仍然是 -1,这意味着没有找到匹配度更高的图像;如果 index 变成了一个非负整数,这意味着找到了匹配度最高的图像。
    在这段代码中,index 用于跟踪在图像列表 img 中,模板图像 templ 匹配度最高的图像的索引。如果在遍历所有图像后没有找到匹配度更高的图像,index 将保持 -1,表示没有找到有效的匹配项。如果在循环中找到匹配度更高的图像,index 将被更新为当前图像的索引,这样在循环结束后,就可以通过 index 来访问匹配度最高的图像,并将其显示出来。

运行结果如下:
在这里插入图片描述

现在大家的图像文件中往往会有很多照片,有时会有很多的相同或者相似的图片,但我们如果想清除相同的文件我们在电脑上就需要一个一个打开用人眼来判断。这样很浪费时间和精力,而Opencv正好可以解决这个问题。

我们假设现在一个文件夹内有很多照片且文件格式不同:

在这里插入图片描述

import cv2  # 导入OpenCV库,用于图像处理
import os   # 导入操作系统接口库,用于文件路径操作
import sys  # 导入系统相关的参数和函数库# 设置图片所在的文件夹路径
pic_path = "C:\\Users\\lyh20\\PycharmProjects\\this is a bird program\\Pictures"
# 设置图片缩放到的尺寸,宽100像素,高100像素
width, height = 100, 100# 获取指定文件夹中所有的文件名列表
pic_file = os.listdir(pic_path)# 初始化一个列表来存储检测到的相似图片的索引
same_pic = []
# 初始化一个列表来存储读取的图片数据
imgs = []
# 初始化一个集合来存储已经检测过的图片索引,避免重复检测
have_same = set()
# 获取图片文件的数量
number = len(pic_file)# 如果文件夹中没有图片,则打印错误信息并退出程序
if number == 0:print("error:没有图像")sys.exit(0)# 读取图片并将其缩放到指定尺寸,然后添加到imgs列表中
for file_name in pic_file:pic_name = os.path.join(pic_path, file_name)  # 拼接完整的文件路径img = cv2.imread(pic_name)  # 读取图片img = cv2.resize(img, (width, height))  # 缩放图片imgs.append(img)  # 将图片添加到列表中# 对imgs列表中的图片进行两两比较,查找相似的图片
for i in range(0, number - 1):if i in have_same:  # 如果索引已经在集合中,则跳过continuetempl = imgs[i]  # 当前图片作为模板same = [i]  # 初始化一个列表存储当前找到的相似图片索引for j in range(i + 1, number):  # 从下一个图片开始比较if j in have_same:  # 如果索引已经在集合中,则跳过continuepic = imgs[j]  # 要比较的图片# 使用cv2.matchTemplate函数进行模板匹配,cv2.TM_CCOEFF_NORMED是匹配方法results = cv2.matchTemplate(pic, templ, cv2.TM_CCOEFF_NORMED)# 如果匹配结果大于0.9,认为图片相似if results > 0.9:same.append(j)  # 添加相似图片的索引have_same.add(i)  # 添加索引到集合中,避免重复比较have_same.add(j)# 如果找到多于一个相似图片,则添加到same_pic列表中if len(same) > 1:same_pic.append(same)# 打印出相似图片的文件名
for same_list in same_pic:text = "相同照片:"for same in same_list:text += str(pic_file[same]) + ","  # 添加文件名到字符串print(text)  # 打印结果

代码中给出了详细的注释,如果还有不明白的评论区提问哦。

运行结果如下:
相同照片:10.png,4.jpg,
相同照片:2.jpg,5.jpg,9.png,

具体的结果大家可以自己去试试看。

2.2 多目标匹配

多目标匹配需要将原始图像中所有与模板相似的图像都找出来,使用相关匹配或相关系数匹配可以很好地实现这个功能。如果计算结果大于某值(例如0.999),则认为匹配区域的图案和模板是相同的。

示例:使用cv2.TM_CCOEFF_NORMED方法进行模板匹配,使用for循环遍历matchTemplate()方法返回的结果,找到所有大于0.99的计算结果,在这些结果的对应区域位置绘制红色矩形边框。编写代码时要注意:数组的列数在图像坐标系中为横坐标,数组的行数在图像坐标系中为纵坐标。

这是图像和模板:
请添加图片描述
请添加图片描述

import cv2
img = cv2.imread("img_5.png")
templ = cv2.imread("img_6.png")
height,width,c = templ.shape
results = cv2.matchTemplate(img,templ,cv2.TM_CCOEFF_NORMED)
for y in range(len(results)):for x in range(len(results[y])):if results[y][x] > 0.99:cv2.rectangle(img,(x,y),(x+width,y+height),(0,0,255),2)cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()

运行结果如下:
在这里插入图片描述

3 多模板匹配

匹配过程中同时查找多个模板的操作叫多模板匹配。多模板匹配实际上就是进行了n次“单模板多目标匹配”操作,n的数量为模板总数。

每一个模板都要做一次“单模板多目标匹配”,最后把所有模板的匹配结果汇总到一起。“单模板多目标匹配”的过程可以封装成一个方法,方法参数为模板和原始图像,方法内部将计算结果再加工一
下,直接返回所有红框左上角和右下角两点横纵坐标的列表。在方法之外,将所有模板计算得出的坐标汇总到一个列表中,按照这些汇总的坐标一次性将所有红框都绘制出来。

import cv2  # 导入OpenCV库def myMatchTemplate(img, templ):  # 定义一个函数,用于模板匹配height, width, c = templ.shape  # 获取模板图像的高、宽和通道数loc = list()  # 初始化一个列表,用于存储红框的坐标results = cv2.matchTemplate(img, templ, cv2.TM_CCOEFF_NORMED)  # 按照标准相关系数匹配for i in range(len(results)):  # 遍历结果数组的行for j in range(len(results[0])):  # 遍历结果数组的列if results[i][j] > 0.99:  # 如果相关系数大于0.99则认为匹配成功loc.append((i, j, j + width, i + height))  # 在列表中添加匹配成功的红框对角线两点坐标return loc  # 返回红框坐标列表img = cv2.imread("img_5.png")  # 读取原始图像
templs = list()  # 初始化模板列表
templs.append(cv2.imread("img_6.png"))  # 添加模板1
templs.append(cv2.imread("img_7.png"))  # 添加模板2
templs.append(cv2.imread("img_8.png"))  # 添加模板3loc = list()  # 初始化一个列表,用于存储所有模板匹配成功位置的红框坐标
for temp in templs:  # 遍历所有模板loc += myMatchTemplate(img, temp)  # 记录该模板匹配得出的坐标for i in loc:  # 遍历所有红框的坐标cv2.rectangle(img, (i[0], i[1]), (i[2], i[3]), (0, 0, 255), 2)  # 在图片中绘制红框cv2.imshow("img", img)  # 显示匹配的结果
cv2.waitKey(0)  # 按下任何键盘按键后继续
cv2.destroyAllWindows()  # 释放所有窗体

我实在找不到合适的材料,所以大家可以自己去试试哈。

4 小结

我个人觉得opencv的模板匹配不怎么好用,至少我跟着书上的材料学习,是可以准确判定的,但是自己找的一些复杂的材料,判定准确性很低,但是也要好好学,我个人觉得不好用,可能是我才疏识浅,欢迎评论区留言。

明天要开始学习滤波器。

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

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

相关文章

PYTHON学习笔记(八、字符串及的使用)

目录 1、字符串 1.1、字符串的常用操作 1.2、格式化字符串 1.2.1、占位符格式化字符串 1.2.2、f-string格式化字符串 1.2.3、str.format( )格式化字符串 1.3、数据的验证 1.4、正则表达式 1.5.1元字符 1.5.2限定符 1.5.3其他字符 1.5.4re模块 1、字符串 1.1、字符…

PySimpleGUI的安装、使用介绍

PySimpleGUI的安装等介绍 如果直接使用pip命令是无法下载免费版的,通过设置的python Interpreter也不可以下载到5.0.0之前的版本了。 现在已经无法通过pycharm直接获取到PySimpleGUI的免费(无需登录)版,不过听说可以登入官网然后进…

【JS逆向课件:第十八课:JS操作2】

4.12、BOM对象(了解) BOM:Broswer object model,即浏览器提供我们开发者在javascript用于操作浏览器的对象。 4.12.1、window对象 窗口方法 // BOM Browser object model 浏览器对象模型// js中最大的一个对象.整个浏览器窗口出现的所有东西都是win…

C++笔记---缺省参数和函数重载

1. 缺省参数 1.1 定义 缺省参数是声明或定义函数时为函数的参数指定一个缺省值(默认值)。在调用该函数时,如果没有指定实参 则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。 void Func(…

docker 安装单机版redis

把这三个放上去 修改成自己的 按照自己需求来 照图片做 vim redis.conf vim startRedis.sh mv startRedis.sh deployRedis.sh sh deployRedis.sh docker run --privilegedtrue \ --name dev.redis --restartalways \ --network dev-net \ -v ./config/redis.conf:/etc/r…

《昇思25天学习打卡营第三十三天|7月26号》

昇思25天学习打卡营 在昇思25天学习打卡营的第33天7月26号,我深入学习了Python编程。通过课程的系统学习和实践编程项目,我逐渐掌握了Python语言的基本语法和核心概念。 特别是在函数定义和数据结构的应用上,我学习到了一些新的东西。以为平…

鸿蒙华为登录(以及导航页面跳转)

//登录华为登录界面以及跳转 //切记一定要写路径,不写路径,容易报错,还有一定要记得导一下包(Arouter) //接下来是鸿蒙界面导航跳转 //进行跳转 TabContent组件不支持设置通用宽度属性,其宽度默认撑满Tab…

RedHat9 | Ansible 处理任务失败

环境版本说明 RedHat9 [Red Hat Enterprise Linux release 9.0]Ansible [core 2.13.3]Python [3.9.10]jinja [3.1.2] 1. 忽略任务失败 Ansible评估各任务的返回代码,从而确定任务是成功还是失败通常而言,当任务失败时,Ansible将立即在该主…

C++从入门到起飞之——友元内部类匿名对象 全方位剖析!

🌈个人主页:秋风起,再归来~🔥系列专栏:C从入门到起飞 🔖克心守己,律己则安 目录 1、友元 2、内部类 3. 匿名对象 4、完结散花 1、友元 • 友元提供了⼀种突破类访问限定符封装的…

自定义prometheus监控获取nginx_upstream指标

1、前言 上篇文章介绍了nginx通过nginx_upstream_check_module模块实现后端健康检查,这篇介绍一下如何自定义prometheus监控获取nginx的upstream指标来实时监控nginx。 2、nginx_upstream_status状态 支持以下三种方式查看nginx_upstream的状态 /status?formatht…

数据结构-C语言-排序(4)

代码位置: test-c-2024: 对C语言习题代码的练习 (gitee.com) 一、前言: 1.1-排序定义: 排序就是将一组杂乱无章的数据按照一定的规律(升序或降序)组织起来。(注:我们这里的排序采用的都为升序) 1.2-排…

Artix7系列FPGA实现SDI视频编解码+图像缩放+多路视频拼接,基于GTP高速接口,提供4套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本博已有的FPGA图像缩放方案本博已有的已有的FPGA视频拼接叠加融合方案本方案的无缩放应用本方案在Xilinx--Kintex系列FPGA上的应用本方案在Xilinx--Zynq系列FPGA上的应用 3、详细设计方案设计原理框图S…

JavaScript(17)——事件监听

什么是事件? 事件是在编程时系统内发生的动作或发生的事情,比如用户在网页上单击一个按钮 什么是事件监听? 就是让程序检测是否有事件产生,一旦有事件触发,就立刻调用一个函数做出响应,也称为绑定事件或…

【Javascript】前端面试基础2【每日学习并更新10】

模块化开发是怎样做的? 立即执行函数,不暴露私有成员 异步加载JS的方式有哪些 那些操作会造成内存泄漏 是什么:内存泄漏指任何对象在您不再拥有或需要它之后仍然存在造成内存泄漏: setTimeout的第一个参数使用字符串而非函数的…

认识漏洞-GitLab 远程命令执行漏洞、致远OA-ajax.do未授权任意文件上传漏洞

为方便您的阅读,可点击下方蓝色字体,进行跳转↓↓↓ 01 [GitLab 远程命令执行漏洞复现(CVE-2021-22205)](https://mp.weixin.qq.com/s/4QT-vxKpBn4ppNM9ipt-nQ)02 [致远OA-ajax.do未授权任意文件上传Getshell](https://mp.weixin.qq.com/s/TH2A5J5TXU36Y…

使用Claude 3.5 Sonnet和Stable Diffusion XL:如何通过Amazon Bedrock不断优化图像生成直到满足需求

在Amazon Bedrock的AI模型中,Anthropic Claude 3系列现在新增了图像识别功能。特别是最新的Anthropic Claude 3.5 Sonnet,图像识别能力得到了显著提升。我进行了一些简单的试验和比较,深入探索了这些Claude模型在OCR(光学字符识别…

HarmonyOS和OpenHarmony区别联系

前言 相信我们在刚开始接触鸿蒙开发的时候经常看到HarmonyOS和OpenHarmony频繁的出现在文章和文档之中,那么这两个名词分别是什么意思,他们之间又有什么联系呢?本文将通过现有的文章和网站内容并与Google的AOSP和Android做对比,带…

【Django】开源前端库bootstrap,常用

文章目录 下载bootstrap源文件到本地项目引入bootstrap文件 官网:https://www.bootcss.com/V4版本入口:https://v4.bootcss.com/V5版本入口:https://v5.bootcss.com/ 这里使用成熟的V4版本,中文文档地址:https://v4.b…

SSM学习9:SpringBoot简介、创建项目、配置文件、多环节配置

简介 SpringBoot式用来简化Spring应用的初始搭建以及开发过程的一个框架 项目搭建 File -> New -> Project 选中pom.xml文件,设置为maven项目 项目启动成功 可以访问BasicController中的路径 配置文件 在resources目录下 application.properties 默…

CSP-J1 2022 入门级第一轮错题

CSP-J1 2022 入门级第一轮 假设字母表{a,b,c,d,e}在字符串出现的频率分别为10%,15%,30%,16%,29%。若使用哈夫曼编码方式对字母进行不定长的二进制编码,字母d的编码长度为(B)位。 A. 1 B. 2 C. 2 或 3 D. 3 一棵有n个结点的完全二叉树用数组进行存储与表示&#xf…