【OpenCV入门】第九部分——模板匹配

文章结构

  • 模板匹配方法
  • 单模板匹配
    • 单目标匹配
    • 多目标匹配
  • 多模板匹配

模板匹配方法

模板是被查找的图像。模板匹配是指查找模板在原始图像中的哪个位置的过程。

result = cv2.matchTemplate(image, templ, method, mask)
  • image: 原始图像
  • templ: 模板图像,尺寸必须小于或等于原始图像
  • method: 匹配的方法
  • mask:(可选)掩模,只有 cv2.TM_SQDIFF和 c2.TM_CCORR_NORMED 支持此参数,建议采用默认值
  • result: 计算得出的匹配结果。如果原始图像的宽、高分别为 W、H,模板图像的宽、高分别为 w、h,result 就是一个 W-w+1列、H-h+1行的32位浮点型数组。数组中每个浮点数都是原始图像中对应像素位置的匹配结果,其含义需要根据 method 参数来解读

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

在这里插入图片描述
使用 cv2.TM_SQDIFF(平方差匹配)方法计算出的数组格式如下所示(其他方法计出的数组格式相同,仅数值不同):

在这里插入图片描述
模板会将原始图像中每一块区域都覆盖一遍,但结果数组的行列数并不会等于原始图像的像素的行列数。假设模板的宽为 w、高为h,原始图像的宽为 W、高为H,

在这里插入图片描述
模板移动到原始图像的边缘之后就不会继续移动了,所以模板的移动区域如下图所示,该区域的边长为“原始图像边长 - 模板边长 +1”,最后加 1 是因为移动区域内的上下、左右的两个边都被模板覆盖到了,如果不加1会丢失数据。
在这里插入图片描述

单模板匹配

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

单目标匹配

单目标匹配只获取一个结果即可,就是匹配程度最高的结果 (如果使用平方差匹配,则为计算出的最小结果;如果使用相关匹配或相关系数匹配,则为计算出的最大结果)。本节以平方差匹配为例做介绍。

matchTemplate() 方法的计算结果是一个二维数组,minMaxLoc()方法专门用来解析这个二维数组中的最大值、最小值以及这两个值对应的坐标:

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

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

实例1: 为原始图片中匹配成功的区域绘制红框

import cv2img = cv2.imread("background.jpg") # 读取原始图像
templ = cv2.imread("template.png") # 读取模板图像
height, width, c = templ.shape # 获取模板图像的高度、宽度和通道数
results = cv2.matchTemplate(img, templ, cv2.TM_SQDIFF_NORMED) # 按照标准平方差方式匹配
# 获取匹配结果中的最小值、最大值、最小值坐标和最大值坐标
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)
cv2.imshow("img", img) # 显示匹配的结果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体

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

实例2: 从两幅图像中选择最佳的匹配结果

import cv2image = []  # 存储原始图像的列表
# 向image列表添加原始图像image_221.png
image.append(cv2.imread("image_221.png"))
# 向image列表添加原始图像image_222.png
image.append(cv2.imread("image_222.png"))
templ = cv2.imread("templ.png")  # 读取模板图像
index = -1  # 初始化车位编号列表的索引为-1
min = 1
for i in range(0, len(image)):  # 循环匹配image列表中的原始图像# 按照标准平方差方式匹配results = cv2.matchTemplate(image[i], templ, cv2.TM_SQDIFF_NORMED)# 获得最佳匹配结果的索引if min > any(results[0]):index = i
cv2.imshow("result", image[index])  # 显示最佳匹配结果
cv2.waitKey()  # 按下任何键盘按键后
cv2.destroyAllWindows()  # 释放所有窗体

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

实例3: 查找重复的图像
现在有十张图像,这些图像格式不同,而且分辨率也各不相同。想要找到重复的图像,可以使用matchTemplate()来判断两幅图像的相似度,如果相似度大于0.9,就认为这两幅图像是相同的。

import cv2
import os
import sysPIC_PATH = "C:\\Users\\Administrator\\Desktop\\test\\"  # 照片文件夹地址
width, height = 100, 100  # 缩放比例pic_file = os.listdir(PIC_PATH)  # 所有照片文件列表
same_pic_index = []  # 相同图像的索引列表
imgs = []  # 缩放后的图像对象列表
has_same = set()  # 相同图像的集合
count = len(pic_file)  # 照片数量if count == 0:  # 如果照片数量为零print("没有图像")sys.exit(0)  # 停止程序for file_name in pic_file:  # 遍历照片文件pic_name = PIC_PATH + file_name  # 拼接完整文件名img = cv2.imread(pic_name)  # 创建文件的图像img = cv2.resize(img, (width, height))  # 缩放成统一大小imgs.append(img)  # 按文件顺序保存图像对象for i in range(0, count - 1):  # 遍历所有图像文件,不遍历最后一个图像if i in has_same:  # 如果此图像已经找到相同的图像continue  # 跳过templ = imgs[i]  # 取出模板图像same = [i]  # 与templ内容相同的图像索引列表for j in range(0 + i + 1, count):  # 从templ的下一个位置开始遍历if j in has_same:  # 如果此图像已经找到相同的图像continue  # 跳过pic = imgs[j]  # 取出对照图像results = cv2.matchTemplate(pic, templ, cv2.TM_CCOEFF_NORMED)  # 比较两图像相速度if results > 0.9:  # 如果相似度大于90%,认为是同一张照片same.append(j)  # 记录对照图像的索引has_same.add(i)  # 模板图像已找到相同图像has_same.add(j)  # 对照图像已找到相同图像if len(same) > 1:  # 如果模板图像找到了至少一张与自己相同的图像same_pic_index.append(same)  # 记录相同图像的索引for same_list in same_pic_index:  # 遍历所有相同图像的索引text = "相同的照片:"for same in same_list:text += str(pic_file[same]) + ", "  # 拼接文件名print(text)

结果如下:

在这里插入图片描述

多目标匹配

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

实例4: 为原始图片中所有匹配成功的图案绘制红框
使用cv2.TM_CCOEFF_NORMED方式进行模板匹配,使用for循环遍历matchTemplate()方法返回的结果,找到所有大于0.99的计算结果,在这些结果的对应区域位置绘制红色矩形边框。

import cv2
img = cv2.imread("background2.jpg")  # 读取原始图像
templ = cv2.imread("template.png")  # 读取模板图像
width, height, 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:  # 如果相关系数大于0.99则认为匹配成功# 在最佳匹配结果位置绘制红色方框cv2.rectangle(img, (x, y), (x + width, y + height), (0, 0, 255), 2)
cv2.imshow("img", img)  # 显示匹配的结果
cv2.waitKey()  # 按下任何键盘按键后
cv2.destroyAllWindows()  # 释放所有窗体

结果如下:

在这里插入图片描述

实例5: 统计一条快轨线路的站台总数

import cv2image = cv2.imread("image.png")  # 读取原始图像
templ = cv2.imread("templ.png")  # 读取模板图像
height, width, c = templ.shape  # 获取模板图像的高度、宽度和通道数
results = cv2.matchTemplate(image, templ, cv2.TM_CCOEFF_NORMED)  # 按照标准相关系数匹配
station_Num = 0  # 初始化快轨的站台个数为0
for y in range(len(results)):  # 遍历结果数组的行for x in range(len(results[y])):  # 遍历结果数组的列if results[y][x] > 0.99:  # 如果相关系数大于0.99则认为匹配成功# 在最佳匹配结果位置绘制蓝色矩形边框cv2.rectangle(image, (x, y), (x + width, y + height), (255, 0, 0), 2)station_Num += 1  # 快轨的站台个数加1
cv2.putText(image, "the numbers of stations: " + str(station_Num), (0, 30),cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)  # 在原始图像绘制快轨站台的总数
cv2.imshow("result", image)  # 显示匹配的结果
cv2.waitKey()  # 按下任何键盘按键后
cv2.destroyAllWindows()  # 释放所有窗体

结果如下:

在这里插入图片描述

实例6: 优先选择直线距离最短的地铁站

import cv2
import numpy as np
import mathimage = cv2.imread("image.png")  # 读取原始图像
templ = cv2.imread("templ.png")  # 读取模板图像
height, width, c = templ.shape  # 获取模板图像的高度、宽度和通道数
results = cv2.matchTemplate(image, templ, cv2.TM_CCOEFF_NORMED)  # 按照标准相关系数匹配
point_X = []  # 用于存储最佳匹配结果左上角横坐标的列表
point_Y = []  # 用于存储最佳匹配结果左上角纵坐标的列表
for y in range(len(results)):  # 遍历结果数组的行for x in range(len(results[y])):  # 遍历结果数组的列if results[y][x] > 0.99:  # 如果相关系数大于0.99则认为匹配成功# 在最佳匹配结果位置绘制红色方框cv2.rectangle(image, (x, y), (x + width, y + height), (255, 0, 0), 2)point_X.extend([x])  # 把最佳匹配结果左上角的横坐标添加到列表中point_Y.extend([y])  # 把最佳匹配结果左上角的纵坐标添加到列表中
# 出发点的横、纵坐标
start_X = 62
start_Y = 150
# 计算出发点到人民广场地铁站的距离
place_Square = np.array([point_X[0], point_Y[0]])
place_Start = np.array([start_X, start_Y])
minus_SS = place_Start - place_Square
start_Square = math.hypot(minus_SS[0], minus_SS[1])
# 计算出发点到解放大路地铁站的距离
place_Highroad = np.array([point_X[1], point_Y[1]])
minus_HS = place_Highroad - place_Start
start_Highroad = math.hypot(minus_HS[0], minus_HS[1])
# 用绿色的线画出距离较短的路线
if start_Square < start_Highroad:cv2.line(image, (start_X, start_Y), (point_X[0], point_Y[0]), (0, 255, 0), 2)
else:cv2.line(image, (start_X, start_Y), (point_X[1], point_Y[1]), (0, 255, 0), 2)
cv2.imshow("result", image)  # 显示匹配的结果
cv2.waitKey()  # 按下任何键盘按键后
cv2.destroyAllWindows()  # 释放所有窗体

结果如下:

在这里插入图片描述

多模板匹配

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

实例7: 同时匹配3个不同的模板

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

import cv2def myMatchTemplate(img, templ):  # 自定义方法:获取模板匹配成功后所有红框位置的坐标width, height, c = templ.shape  # 获取模板图像的宽度、高度和通道数results = cv2.matchTemplate(img, templ, cv2.TM_CCOEFF_NORMED)  # 按照标准相关系数匹配loc = list()  # 红框的坐标列表for i in range(len(results)):  # 遍历结果数组的行for j in range(len(results[i])):  # 遍历结果数组的列if results[i][j] > 0.99:  # 如果相关系数大于0.99则认为匹配成功# 在列表中添加匹配成功的红框对角线两点坐标loc.append((j, i, j + width, i + height))return locimg = cv2.imread("background2.jpg")  # 读取原始图像
templs = list()  # 模板列表
templs.append(cv2.imread("template.png"))  # 添加模板1
templs.append(cv2.imread("template2.png"))  # 添加模板2
templs.append(cv2.imread("template3.png"))  # 添加模板3loc = list()  # 所有模板匹配成功位置的红框坐标列表
for t in templs:  # 遍历所有模板loc += myMatchTemplate(img, t)  # 记录该模板匹配得出的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()  # 按下任何键盘按键后
cv2.destroyAllWindows()  # 释放所有窗体

结果如下:

在这里插入图片描述

实例8: 控制台输出4辆车分别停在了哪个车位上
在这里插入图片描述

import cv2image = cv2.imread("image.png")  # 读取原始图像
templs = []  # 模板列表
templs.append(cv2.imread("car1.png"))  # 添加模板图像1
templs.append(cv2.imread("car2.png"))  # 添加模板图像2
templs.append(cv2.imread("car3.png"))  # 添加模板图像3
templs.append(cv2.imread("car4.png"))  # 添加模板图像3
for car in templs:  # 遍历所有模板图像# 按照标准相关系数匹配results = cv2.matchTemplate(image, car, cv2.TM_CCOEFF_NORMED)for i in range(len(results)):  # 遍历结果数组的行for j in range(len(results[i])):  # 遍历结果数组的列# print(results[i][j])if results[i][j] > 0.99:  # 如果相关系数大于0.99则认为匹配成功if 0 < j <= 140:print("车位编号:", 1)elif j <= 330:print("车位编号:", 2)elif j <= 500:print("车位编号:", 3)else:print("车位编号:", 4)break

1号车位水平像素的取值范围是0 ~ 140,2号车位水平像素的取值范围是 141 ~ 330,3号车位水平像素的取值范围是331 ~ 500,4号车位水平像素的取值范围是 501 ~ 650。

结果如下:

在这里插入图片描述

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

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

相关文章

Android Aidl跨进程通讯(三)--进阶使用

学更好的别人&#xff0c; 做更好的自己。 ——《微卡智享》 本文长度为2478字&#xff0c;预计阅读6分钟 前言 Android的AIDL使用和异常报错都已经介绍过了&#xff0c;今天这篇还是在原来的Demo基础上加入几个AIDL的进阶使用方法。 】 AIDL进阶使用 微卡智享 in,out,inout的使…

外包干了2个月,技术退步明显了...

先说一下自己的情况&#xff0c;大专生&#xff0c;19年通过校招进入湖南某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年8月份&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

windows安装MongoDB后进入命令交互界面失败解决方案

MongoDB下载链接&#xff1a;https://www.mongodb.com/download-center MongoDB安装教程&#xff1a;https://juejin.cn/post/6844903912000978952 不要安装最新的高版本MongoDB&#xff0c;因为配置方法可能会有差别&#xff0c;比如7.0一直失败的话就换5.0及以下版本试试&am…

【LeetCode-中等题】208. 实现 Trie (前缀树)

文章目录 题目方法一&#xff1a;利用数组构建26叉树方法二&#xff1a;利用哈希表构建26叉树 题目 方法一&#xff1a;利用数组构建26叉树 插入图示&#xff1a; 全搜索和前缀搜索&#xff1a; 注意&#xff1a;全局匹配匹配完直接返回插入时的标志位 而前缀匹配时&#xff…

软考(1)-面向对象的概念

目录 一. 软考基本信息 1. 软考时间&#xff1a; 2. 软考科目&#xff1a; 3.专业知识介绍 -- 综合知识考点分布 4. 专业介绍 -- 软件设计考点分布 二. 面向对象概念 1. 封装 考点一&#xff1a;对象 考点二&#xff1a;封装private 2. 继承 考点三&#xff1a;类 考…

React中父子组件参数传递讲解

文章目录 结合案例&#xff1a;github搜索案例1.父容器代码2.搜索Search子模块代码3.展示Lisi子模块代码 父子参数传递分析1.子(Search)传父(App)2.父(App)传子(List) 结合案例&#xff1a;github搜索案例 案例结果展示如下图 1.父容器代码 import React, { Component } fr…

【MySQL学习笔记】(十)使用索引的操作

简单使用索引 创建索引创建主键索引创建唯一键索引普通索引的创建 查询索引删除索引索引创建原则 数据库中的索引与书籍中的索引类似&#xff0c;在一本书中&#xff0c;利用索引可以快速地查找所需信息&#xff0c; 而无须阅读整本书。在数据库中&#xff0c;索引使数据库程序…

伯俊ERP与金蝶云星空对接集成表头表体组合查询连通分布式调出单新增(调拨出库对接分布式调出(KD调拨)6月)

伯俊ERP与金蝶云星空对接集成表头表体组合查询连通分布式调出单新增(调拨出库对接分布式调出&#xff08;KD调拨&#xff09;6月) 对接系统&#xff1a;伯俊ERP 伯俊科技&#xff0c;依托在企业信息化建设方面的领先技术与实践积累&#xff0c;致力于帮助企业实现全渠道一盘货。…

Git常用命令用法

参考视频&#xff1a;真的是全能保姆 git、github 保姆级教程入门&#xff0c;工作和协作必备技术&#xff0c;github提交pr - pull request_哔哩哔哩_bilibili 1.Git初始化 首先设置名称和邮箱。然后初始化一下&#xff0c;然后就创建了一个空的Git仓库。 PS D:\golang\oth…

【Linux】地址空间概念

目录 前言&#xff1a; 地址空间回顾 验证&#xff1a;一个变量是否会有两个值&#xff1f; 一. 什么是地址空间 虚拟地址与物理地址之间的关系 二. 地址空间是如何设计的 1. 回答一个变量两个值 2.扩展 继续深入理解 三. 为什么要有地址空间 原因&#xff1a; 1. 使…

【探索Linux】—— 强大的命令行工具 P.7(进程 · 进程的概念)

阅读导航 前言一、冯诺依曼体系结构二、操作系统&#xff08;OS&#xff09;1. 概念 三、进程1. 进程的概念2. PCB&#xff08;Process Control Block&#xff09;3. 查看进程 四、fork函数1. 函数简介2. 调用方式3. 返回值4. 使用示例 五、进程的几种状态1. 状态简介2. 进程状…

react中使用cytoscape

1. 安装 cytoscape npm install cytoscape 2. 使用 import React, { useEffect, useRef, useState } from "react"; import cytoscape from "cytoscape";const peopleList [{"data": {"id": "1","label": &qu…

三步搭建个人网站并发布上线【内网穿透】

三步搭建个人网站并发布上线【内网穿透】 文章目录 三步搭建个人网站并发布上线【内网穿透】前言一、在本地电脑上制作一个网站二、使用WordPress建立网站三、通过cpolar建立的数据隧道发布到公网上 前言 在这个个性飞扬的时代&#xff0c;每个人都希望拥有表现自我的平台&…

git快速使用

1、下载git 设置签名 2、基本概念 工作区&#xff1a;写代码的地方。 暂存区&#xff1a;.git的.index 工作区&#xff1a;.git 3、常用操作 本地codinggit init&#xff0c; 初始化一个本地仓库&#xff0c;项目根目录下会出现个.gitgit remote add origin gitgithub.com…

Git的基本使用笔记——狂神说

版本控制 版本迭代&#xff0c; 版本控制( Revision control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史&#xff0c;方便查看更改历史记录&#xff0c;备份以便恢复以前的版本的软件工程技术。 实现跨区域多人协同开发 追踪和记载一个或者多个文件的…

网络类型+二层封装技术

一、网络类型分类 1、点到点网络 在一个网段中&#xff0c;只能部署两个节点&#xff08;两个IP&#xff09;&#xff0c;如GRE环境就属于虚拟的点到点网络类型&#xff0c;通常是串线连接。 如下图当前只存在两个节点&#xff0c;但并不是点到点&#xff0c;因为它中间网段…

SAP message-06 027 供应商 还未为采购组织 创建

目录 错误信息 一、错误原因 二、修正 错误信息 When creating a RFQ in ME41 for a vendor that is not assigned to the Purchasing Organization, error "Vendor & has not been created for purch. organization" is not raised. Supplier AEQI1 has not be…

css之层叠上下文

之前调元素的显示优先级时&#xff0c;只会默默的调z-index以达到效果&#xff0c;但有时不生效&#xff0c;又不知道根因。刚好详细了解到层叠上下文&#xff0c;可以解释此类问题。 什么是层叠上下文&#xff1f; 在CSS2.1规范中&#xff0c;每个盒模型的位置是三维的&…

SpringMVC_SSM整合

一、回顾SpringMVC访问接口流程 1.容器加载分析 容器分析 手动注册WebApplicationContext public class ServletConfig extends AbstractDispatcherServletInitializer {Overrideprotected WebApplicationContext createServletApplicationContext() {//获取SpringMVC容器An…

Spark 6:Spark SQL DataFrame

SparkSQL 是Spark的一个模块, 用于处理海量结构化数据。 SparkSQL是用于处理大规模结构化数据的计算引擎 SparkSQL在企业中广泛使用&#xff0c;并性能极好 SparkSQL&#xff1a;使用简单、API统一、兼容HIVE、支持标准化JDBC和ODBC连接 SparkSQL 2014年正式发布&#xff0c;当…