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

news2024/12/24 0:35:26

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:  # 如果索引已经在集合中,则跳过
        continue
    templ = imgs[i]  # 当前图片作为模板
    same = [i]  # 初始化一个列表存储当前找到的相似图片索引
    for j in range(i + 1, number):  # 从下一个图片开始比较
        if j in have_same:  # 如果索引已经在集合中,则跳过
            continue
        pic = 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"))  # 添加模板3

loc = 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.coloradmin.cn/o/1951645.html

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

相关文章

Linux中,MySQL索引、事物与存储引擎

MySQL索引介绍 索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址。在数据十分庞大的时候,索引可以大大加快查询的速度。这是因为使用索引后可以不用扫描全表来定位某行的数据,而是先通过索引表找到该行数据对应的物理地址然后访…

tinyxml2的入门教程

tinyxml2的入门教程 前言一、tinyxml2 创建xml 文件二、tinyxml2 添加数据三、tinyxml2 更改数据四、tinyxml2 删除数据五、tinyxml2 打印总结 前言 xml 是一种标记型文档,有两种基本解析方式:DOM(Document Object Model,文档对象模型)和SAX…

C++ 数字和数组解析

文章目录 1. 定义数字 2. 数学运算 3. 随机数 4. 数组 声明数组 初始化数组 5. 访问数组元素 6. 数组类型 7. 多维数组 二维数组 初始化二维数组 访问二维数组元素 8. 指向数组的指针 9. 传递数组给函数 10. 从函数返回数组 1. 定义数字 通常,当需要…

嵌入式人工智能(23-基于树莓派4B的温湿度传感器DHT11)

1、湿度传感器 目前市面上,仅测量湿度的传感器很少,普遍使用的都是温/湿度传感器,即以温/湿度一体式的探 头作为测温元件,将温度和湿度信号采集出来,再经过稳压滤波、运算放大、非线性校正、V转换、恒流及反向保护等电…

[Meachines] Lame smbd3.0-RCE

信息收集 IP AddressOpening Ports10.10.10.3TCP:21,22,139,445,3632 $ nmap -p- 10.10.10.3 --min-rate 1000 -sC -sV 21/tcp open ftp vsftpd 2.3.4 |_ftp-anon: Anonymous FTP login allowed (FTP code 230) | ftp-syst: | STAT: | FTP server status: | …

【Stable Diffusion】(基础篇五)—— 使用SD提升分辨率

使用SD提升分辨率 本系列博客笔记主要参考B站nenly同学的视频教程,传送门:B站第一套系统的AI绘画课!零基础学会Stable Diffusion,这绝对是你看过的最容易上手的AI绘画教程 | SD WebUI 保姆级攻略_哔哩哔哩_bilibili 在前期作画的…

【NoSQL数据库】Redis知识小册

一、缓存穿透 缓存穿透是先查Redis,发现缓存中没有数据,再查数据库。然而,如果查询的数据在数据库中也不存在,那么每次查询都会绕过缓存,直接落到数据库上。 解决方案一、缓存空数据 查询Redis缓存:首先查…

独立开发者系列(34)——node项目部署

本节梳理node项目的部署,搭建一个外部能正常访问的node网站。将开发好的项目搭建到系统里面。Node的部署比PHP版本要复杂一些。部署项目前要理解几个概念。Nodejs版本管理器概念。 NVM概念,我们平时开发是在本地电脑上开发,开发的时候&#x…

计算机毕业设计:基于SSM的宠物领养系统

私信获取完整代码 一、选题背景介绍 📖☕️🌊📝📚🎩🚀📣 🎩 宠物领养系统:帮助爱宠人士更好的去查看可以领养的宠物,帮助宣传相关保护宠物相关知识 &…

PHP家政系统自营+多商户独立端口系统源码小程序

家政行业的新篇章 引言:家政行业的数字化转型 近年来,随着科技的飞速发展和人们生活节奏的加快,家政服务行业也迎来了数字化转型的浪潮。为了提升服务效率、优化用户体验,越来越多的家政公司开始探索“家政系统自营多商户小程序…

Qt Designer的父子部件样式表设置学习

在Qt Designer中新建图像界面,放置两个QWidget,命名为widget_1和widget_2;两个QPushButton,pushButton_1和pushButton_2。 它们的父子从属关系如下: 设置widget_1的样式表: QWidget{ background-color: r…

lua 游戏架构 之 游戏 AI (八)ai_tbl 行为和优先级

定义一系列的AI行为类型和它们的优先级,以及一个映射表ai_tbl来关联每种AI行为类型与对应的脚本文件和优先级。以下是对代码的详细解释: lua 游戏架构 之 游戏 AI (一)ai_base-CSDN博客https://blog.csdn.net/heyuchang666/artic…

构建现代化农业产业服务平台的系统架构

随着全球农业产业的发展和技术的进步,农业生产管理面临着越来越复杂的挑战和机遇。建立一个现代化的农业产业服务平台系统架构,不仅能够提高农业生产效率和管理水平,还能促进农民收入增长和可持续发展。本文将探讨如何设计和实施这样一个系统…

git配置环境变量

一.找到git安装目录 打开此git安装目录下的bin文件,复制此文件路径 二.配置环境变量 2.1 右键点击此电脑的属性栏 2.2 点击高级系统配置 2.3 点击环境变量 2.4 按图中步骤进行配置 三.配置完成 win r 输入cmd打开终端 终端页面中输入 git --version 如图所示…

PySide(PyQt)的QPropertyAnimation(属性动画)

学不完,根本学不完:(,感觉逐渐陷入了学习深渊。。。 QPropertyAnimation 是 PySide(PyQt) 中一个用于在时间轴上平滑地改变对象属性的类。它常用于制作动画效果,比如移动、缩放或改变透明度等。 基本概念 QPropertyAnimation 是 Qt …

文件包含漏洞--pyload

文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 一.PHP伪协议利用 php://协议 php://filter :用于在读取作用和写入文件时进行过滤和转换操作。 作用1:利用base64编码过滤器读取源码 通常利用文件包含执行php://filte…

17.5【C语言】static的补充说明

static &#xff08;静态的) 作用&#xff1a;修饰局部变量&#xff0c;修饰全局变量&#xff0c;修饰函数 对比两段代码 #include <stdio.h> void test() {int a 5;a;printf("%d ", a); } int main() {int i 0;for(i0; i<5; i){test();}return 0; } …

ElasticSearch(三)—文档字段参数设置以及元字段

一、 字段参数设置 analyzer&#xff1a; 指定分词器。elasticsearch 是一款支持全文检索的分布式存储系统&#xff0c;对于 text类型的字段&#xff0c;首先会使用分词器进行分词&#xff0c;然后将分词后的词根一个一个存储在倒排索引中&#xff0c;后续查询主要是针对词根…

【H.264】H.264详解(二)—— H264视频码流解析示例源码

文章目录 一、前言二、示例源码【1】目录结构【2】Makefile源码【3】h264parser.c源码【4】编译运行【5】源码下载地址 声明&#xff1a;此篇示例源码非原创&#xff0c;原作者雷霄骅。雷霄骅&#xff0c;中国传媒大学通信与信息系统专业博士生&#xff0c;在此向雷霄骅雷神致敬…

SEO与数据中心代理IP的结合能带来哪些便利?

本文将探讨将SEO与数据中心代理IP结合所带来的好处&#xff0c;以及如何利用这种组合来提升网站在搜索引擎中的排名和可见性。 1. 数据中心代理IP的作用和优势 数据中心代理IP指的是由数据中心提供的IP地址&#xff0c;用于隐藏真实服务器的位置和身份。与其他类型的代理IP相…