苦学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://xiahunao.cn/news/3268371.html

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

相关文章

一个网站的js与cookie,页面缓存清理方案

浏览器在使用过程中会产生大量的缓存,Edge浏览器如何清理缓存?下面是Edge浏览器清理缓存的操作步骤。 页面缓存清理方案 打开开发者模式点击应用程序 按顺序点击即可 js缓存问题分析 修改完 js文件中的代码后,页面刷新好几次并没有重新加载…

软件测试---Linux

Linux命令使用:为了将来工作中与服务器设备进行交互而准备的技能(远程连接/命令的使用)数据库的使用:MySQL,除了查询动作需要重点掌握以外,其他操作了解即可什么是虚拟机 通过虚拟化技术,在电脑…

Leetcode—263. 丑数【简单】

2024每日刷题&#xff08;147&#xff09; Leetcode—263. 丑数 实现代码 class Solution { public:bool isUgly(int n) {if(n < 0) {return false;}for(const int prime: {2, 3, 5}) {while(n % prime 0) {n / prime;}}return n 1;} };运行结果 之后我会持续更新&#…

区块链在艺术市场中的创新:数字艺术品的溯源与版权保护

随着数字技术的迅猛发展&#xff0c;数字艺术品正逐渐成为艺术市场的重要组成部分。然而&#xff0c;数字艺术品的复制和版权问题日益突出&#xff0c;传统的版权管理方式面临挑战。区块链技术作为一种去中心化的分布式账本技术&#xff0c;为解决这些问题提供了新的可能性。本…

AJAX-XMLHttpRequest 详解

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 前言 XMLHttpRequest 概述 主要用途 工作流程 示例代码 GET 请求示例 POST 请求示例 注意事项 工作…

Hive3:Centos7环境部署Hive服务

一、安装说明 1、Hadoop集群情况 3台机器&#xff1a;4G2C、2G2C、2G2C 安装教程&#xff1a;Centos7环境安装Hadoop集群 2、安装MySQL&#xff0c;用于存储Hive的元数据 在102机器上安装MySQL 安装MySQL使用服务器的root账号 3、最后安装Hive 安装hive过程使用服务器的atgu…

如何录制电脑内部声音?全方位介绍电脑录音软件:8款在线录音!(2024重新整理)

如何录制电脑内部声音&#xff1f;不管是娱乐圈还是现实生活&#xff0c;【录音】这个功能的重要性不言而喻。而电脑录音已在影视配音、音视频剪辑、会议记录、在线教育等多个领域发光发热&#xff01; 本文将为您推荐8款电脑录音软件&#xff0c;并详细介绍电脑录音的多种方式…

Redis-jenkins

1. 什么是jenkins Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件项目可以进行持续集成。 2. 为什么使用jenkins 使用 Jenkins之前使用 Jenkins之…

深度解析Linux-C——函数和内存管理

目录 函数指针&#xff1a; 指针函数&#xff1a; 参数为指针的函数&#xff1a; 参数为数组的函数&#xff1a; C语言内存管理 stdlib.h头文件常用函数介绍 1、局部变量 2、全局变量 3、 堆空间变量 4、静态变量 5、常量 函数指针&#xff1a; 指向函数的指针&#…

鸿蒙APP架构及开发入门

1.鸿蒙系统 1.1 什么是鸿蒙 鸿蒙是一款面向万物互联时代的、全新的分布式操作系统。 在传统的单设备系统能力基础上&#xff0c;鸿蒙提出了基于同一套系统能力、适配多种终端形态的分布式理念&#xff0c;能够支持手机、平板、智能穿戴、智慧屏、车机、PC、智能音箱、耳机、…

《程序猿入职必会(4) · Vue 完成 CURD 案例 》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

lua 游戏架构 之 游戏 AI (九)ai_mgr Ai管理

定义ai_mgr的类&#xff0c;用于管理游戏中实体的AI组件。 先定义 AI行为枚举和优先级&#xff1a; lua 游戏架构 之 游戏 AI &#xff08;八&#xff09;ai_tbl 行为和优先级-CSDN博客https://blog.csdn.net/heyuchang666/article/details/140712839?spm1001.2014.3001.55…

天工三维实景建软件 GodWork 3D 7.24 软件下载License使用

1、天工三维实景建软件GodWork 3D 7.24,城市级实景三维数据生产面临大数据量空三稳定解算的难题。受限于目前主流软件的解算能力与效率&#xff0c;生产单位常采用分块处理方法&#xff0c;接边处精度需要有足够的控制点来保证&#xff0c;这增加了外业布设控制点与内业的工作量…

巴斯勒相机(Basler) ACE2 dart 系列说明和软件

巴斯勒相机(Basler) ACE2 dart 系列说明和软件

开始尝试从0写一个项目--后端(三)

器材管理 和员工管理基本一致&#xff0c;就不赘述&#xff0c;展示代码为主 新增器材 表设计&#xff1a; 字段名 数据类型 说明 备注 id bigint 主键 自增 name varchar(32) 器材名字 img varchar(255) 图片 number BIGINT 器材数量 comment VARC…

Redis底层数据结构的实现

文章目录 1、Redis数据结构1.1 动态字符串1.2 intset1.3 Dict1.4 ZipList1.5 ZipList的连锁更新问题1.6 QuickList1.7 SkipList1.8 RedisObject 2、五种数据类型2.1 String2.2 List2.3 Set2.4 ZSET2.5 Hash 1、Redis数据结构 1.1 动态字符串 Redis中保存的Key是字符串&#xf…

php收银系统源码-收银员操作权限

收银系统是很多门店&#xff0c;尤其是连锁门店营业的必备工具&#xff0c;收银员每天需要通过收银系统记录商品的售卖数量&#xff0c;以及收款&#xff0c;会员开卡&#xff0c;核销订单等工作。但很多门店都不希望给收银员太高的权限&#xff0c;自然就离不开收银员的权限管…

windows server服务器/linux服务器离线安装pandas

windows server服务器/linux服务器离线安装pandas pypi官网下载whl文件速度较慢&#xff0c;推荐使用国内的镜像源来下载&#xff0c;镜像源地址为 清华大学 &#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple/ 阿里云&#xff1a;http://mirrors.aliyun.com/pypi/simple…

Python | Leetcode Python题解之第278题第一个错误的版本

题目&#xff1a; 题解&#xff1a; # The isBadVersion API is already defined for you. # def isBadVersion(version: int) -> bool:class Solution:def firstBadVersion(self, n: int) -> int:left, right 1, nwhile left < right:mid left (right - left) //…

Dockerfile指令详解和Docker操作命令

1.容器的特点&#xff1a;1&#xff09;自包含&#xff08;包括应用程序及其运行环境&#xff09;&#xff1b;2&#xff09;可移植&#xff1b;3&#xff09;相互隔离&#xff1b;4&#xff09;轻量级。 2.docker成为容器的事实标准在于&#xff1a;1&#xff09;在运行环境上…