OpenCV模板匹配(cv2.matchTemplate)

9 篇文章 3 订阅
3 篇文章 1 订阅

摘要

在这里插入图片描述

在本教程中,您将学习如何使用OpenCVcv2.matchTemplate函数实现模板匹配。

除了轮廓滤波处理之外,模板匹配可以说是对象检测的最简单形式之一:

  • 模板匹配实现简单,只需要2-3行代码
  • 模板匹配计算效率高
  • 它不需要执行阈值化、边缘检测等操作来生成二值化图像(而轮廓检测处理需要)
  • 通过简单扩展,模板匹配可以检测输入图像中相同或者相似对象的多个实例(我们将在下篇博客介绍)

当然,模板匹配不是完美的。尽管它有很多优点,但是如果输入图像中存在变化的因素,包括旋转、缩放、视角变化等,模板匹配很容易就会失效。

如果你的输入图像中包含这些类型的变化因素,则你不应使用模板匹配,而应该使用专用的对象检测器,包括 HOG + 线性 SVM;Faster R-CNN;SSD;YOLO 等。

但是,在旋转、缩放和视角恒定的情况下,模板匹配可以完美地发挥作用。

要了解如何使用OpenCV执行模板匹配,请继续阅读。

正文

OpenCV模板匹配(cv2.matchTemplate)

首先,在本教程的第一部分中,我们将讨论什么是模板匹配以及OpenCV是如何通过cv2.matchTemplate函数实现模板匹配的。

之后,我们将配置我们的开发环境并查看我们的项目目录结构。

最后,我们将使用OpenCV来实现模板匹配,将其应用于一些示例图像,并讨论它在哪些情况下运行良好,哪些情况下模板匹配不可行,以及如何改善模板匹配的结果。

什么是模板匹配?

在这里插入图片描述

图1:模板匹配工作流程的一个例子,我们取我们想要检测的对象/模板(左),源图像(中),然后在源图像中找到模板(右)。
 

模板匹配可以看作是对象检测的一种非常基本的形式。使用模板匹配,我们可以使用包含要检测对象的“模板”来检测输入图像中的对象。

基本上来说,这意味着我们需要两个图像来应用模板匹配:

  1. 源图像:这是我们希望在其中找到与模板匹配的图像。
  2. 模板图像:我们在源图片中搜索的“对象图像块” 。

为了在源图像中找到模板图像,我们在源图像中从左到右和从上到下依次滑动模板:
在这里插入图片描述

图2:应用模板匹配就像在源图像上从左到右,从上到下滑动模板,在每一个位置都计算一个指标以表明这个位置处两个图像块之间匹配程度的高低。
 

在每个(x,y)位置,都会计算一个度量来表示匹配的“好”或“坏”。通常,我们使用归一化的相关系数来确定两个图像块之间像素强度有多“相似”:
在这里插入图片描述

图3:用于模板匹配的归一化相关系数的公式
 

有关相关系数的完整推导,包括OpenCV支持的所有其他模板匹配方法,请参考OpenCV文档

对于模板T在源图像·I上的每个位置,取两者重合部分的图像块,计算相似度度量结果,存储在我们的结果矩阵R中 。源图像中的每个(x,y)坐标在结果矩阵R中包含一个条目,除非模板越界。
在这里插入图片描述

图4:在源图像上叠加显示结果矩阵`R`。请注意,结果矩阵的最亮区域是如何出现在咖啡杯的左上角的,从而表明相关性在该处为最大值。
 

在这里,我们可以可视化叠加在原始图像上的结果矩阵R。请注意R与原始模板大小不相同。这是因为整个模板必须在源图像的内部滑动,得到等大的两个图像块,才能计算相关性。如果模板超出了源的边界,我们将不计算相似性度量。

结果矩阵R中最亮的位置表示最佳匹配位置,而暗区表示源图像和模板图像之间的相关性很小。

当模板图像中的水杯,与原图像中的水杯,两者完全重合的时候,模板图像左上角所在的原图像位置,存储的是模板与原图像相似度的最大值。

尽管模板匹配非常简单并且在应用上计算效率高,但是存在许多限制。如果目标存在任何的比例变化、旋转变化或视角变化,模板匹配都可能会失效。

在几乎所有情况下,您都需要确保要检测的模板与在源中检测的对象几乎完全相同。即使外观很小的偏差也会极大地影响模板匹配的结果,并使其在应用中无法使用。

OpenCV的 “cv2.matchTemplate” 函数

在这里插入图片描述

图5: OpenCV中用于模板匹配的 cv2.matchTemplat函数定义
 

我们可以使用OpenCVcv2.matchTemplate 函数进行模板匹配,该函数具有三个参数:

  1. 输入图像:包含我们要检测的对象的图像
  2. 模板图像:对象的图像
  3. 模板匹配方法

在这里,我们使用归一化的相关系数,这通常是您要使用的模板匹配方法,但是OpenCV也支持其他模板匹配方法

cv2.matchTemplate 的输出结果是具有特定尺寸的相似度结果矩阵:

  • 宽度: image.shape [ 1 ] - template.shape [ 1 ] + 1
  • 高度: image.shape [ 0 ] - template.shape [ 0 ] + 1

然后,我们可以在结果R中寻找具有最大的相关系数的位置,该系数所在的位置即输入图像中模板最可能存在区域(您将在本教程的后面部分中学习如何操作)。

另外值得注意的是,如果您只想检测模板图像上的特定区域,则可以为模板图像提供一个掩膜,如下所示:

result = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED, mask)

掩膜,即为模板图像上感兴趣的区域,用于忽略模板图像上无用的干扰的特征,即不属于检测目标的干扰特征。对于模板上你不希望被搜索的区域,掩膜值应该设置为0。对于模板图像上您要进行搜索的区域,掩膜值应该设置为255。掩膜与模板图像具有相同的维度,并且每个元素的类型也需要一致。

配置开发环境

要遵循本指南,您需要在系统上安装OpenCV库。

幸运的是,OpenCV可通过pip安装:

pip install opencv-contrib-python

项目结构

在深入探讨之前,让我们回顾一下我们的项目目录结构。

目录应如下所示:

│──images
│├──8_diamonds.png
│├──coke_bottle.png
│├──coke_bottle_rotated.png
│├──coke_logo.png
│└──diamonds_template.png
└── single_template_matching.py

1个目录,6个文件。single_template_matching.py进行模板匹配。

images里面是我们将进行模板匹配的五个图像,我们将在本教程的后面部分看到每个图像。

使用OpenCV实施模板匹配

#导入必要的软件包
import argparse
import cv2
#构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", type=str, required=True,
	help="path to input image where we'll apply template matching")
ap.add_argument("-t", "--template", type=str, required=True,
	help="path to template image")
args = vars(ap.parse_args())

在第2行和第3行,我们导入所需的Python包。我们只需要argparse用于命令行参数解析和cv2

我们继续分析命令行参数:

  • image:我们将应用模板匹配的输入图像的路径
  • template:我们要在输入图像中查找的模板图像

接下来,让我们准备图像和模板以进行模板匹配:

#从磁盘加载输入图像和模板图像,然后显示在我们的屏幕上
print("[INFO] loading images...")
image = cv2.imread(args["image"])
template = cv2.imread(args["template"])
cv2.imshow("Image", image)
cv2.imshow("Template", template)
#将图像和模板都转换为灰度
imageGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
templateGray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)

模板匹配通常应用于灰度图像,因此第22和23行将图像转换为灰度图像。

接下来,我们只需要调用cv2.matchTemplate函数:

#执行模板匹配
print("[INFO] performing template matching...")
result = cv2.matchTemplate(imageGray, templateGray,	cv2.TM_CCOEFF_NORMED)
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(result)

我们向该函数传递了三个必需的参数:

  1. 我们要在其中查找对象的输入图像
  2. 我们要在输入图像中检测的对象的模板图像
  3. 模板匹配方法

通常归一化相关系数cv2.TM_CCOEF_NORMED在大多数情况下都能很好地工作,但是您可以参考OpenCV文档以获取有关其他模板匹配方法的更多详细信息。

一旦我们调用了cv2.matchTemplate函数,我们收到一个具有以下空间尺寸的矩阵:

  • 宽度: image.shape [ 1 ] - template.shape [ 1 ] + 1
  • 高度: image.shape [ 0 ] - template.shape [ 0 ] + 1

如果这个矩阵的元素有较大的相似度值(更接近1),则模板中的物体在该位置可能性更高。相似地,如果矩阵具有较小的相似度值(更接近0),则该位置不太可能存在目标物体。我们使用cv2.minMaxLoc(第29行)来查找传入结果矩阵的最大元素所在位置。

一旦我们获得具有最大归一化相关系数的位置的(x,y)坐标(maxLoc),我们可以提取坐标并导出目标物体边界框坐标:

#确定起点和终点的(x,y)坐标边界框
(startX, startY) = maxLoc
endX = startX + template.shape[1]
endY = startY + template.shape[0]

最后一步是在图像上用矩形显示出边界框:

#在图像上绘制边框
v2.rectangle(image, (startX, startY), (endX, endY), (255, 0, 0), 3)
#显示输出图像
cv2.imshow("Output", image)
cv2.waitKey(0)

OpenCV模板匹配结果

现在,我们准备将模板匹配与OpenCV配合使用!

在本项目根目录打开终端并执行以下命令:

$ python single_template_matching.py --image images/coke_bottle.png \
	--template images/coke_logo.png
[INFO] loading images...
[INFO] performing template matching...

在此示例中,我们有一个包含可口可乐瓶的输入图像:
在这里插入图片描述

图6:我们的示例输入图像
 

我们的目标是检测图像中的可乐徽标:
在这里插入图片描述

图7:包含可乐徽标的模板图像。我们的目标是检测输入图像中的可乐徽标
 

通过应用OpenCVcv2.matchTemplate函数,我们可以正确地定位coke_logo.pngcoke_bottle.png 图片中的位置:
在这里插入图片描述

图8:通过OpenCV成功应用模板匹配
 

这种方法之所以有效,是因为其中的可口可乐徽标 coke_logo.pngcoke_bottle.png中的尺寸想同。类似地,徽标以相同的视角观采集,相对没有旋转,如果徽标的比例不同或视角不同,则该方法将失效。

例如,让我们尝试这个示例图像,但是这次我稍微旋转了可口可乐瓶子,并缩小了瓶子的尺寸:
在这里插入图片描述

图9: 左:包含相同可口可乐瓶的输入图像,但是此图像已旋转并略微缩小。中:我们要在左侧图像中检测的模板图像。右:应用模板匹配的输出。请注意,我们无法成功检测可口可乐徽标,但是我们得到了假阳性检测结果!由于比例和旋转角度不同,我们未能正确地检测到可口可乐徽标。
 

这里的关键是模板匹配对旋转,视角和比例的变化非常敏感。发生这种情况时,您可能需要应用更高级的对象检测技术。

在以下示例中,我们正在处理一副纸牌,并尝试检测八颗钻石纸牌上的“钻石”符号:

python single_template_matching.py --image images/8_diamonds.png \
	--template images/diamonds_template.png 
[INFO] loading images...
[INFO] performing template matching...

在这里插入图片描述

图10: 左:包含“钻石”模板的输入图像。中:我们要在左侧图像中检测到的“钻石”模板。右:使用OpenCV应用基本模板匹配的输出。标准模板匹配不适用于处理多个检测。

在左边,我们有diamonds_template.png图像。我们使用OpenCVcv2.matchTemplate查找所有菱形符号的(右)。

…但是发生了什么?

为什么没有检测到所有菱形符号?

答案是 cv2.matchTemplate函数本身无法检测多个对象实例!

不过,有一个解决方案——将在下个教程中介绍OpenCV匹配多模板。

关于模板匹配的假阳性检测的注意事项

您会注意到,在我们旋转的可口可乐徽标示例中,我们未能检测到可乐徽标;但是,我们的代码仍然“报告”已找到徽标:
在这里插入图片描述

图11:使用模板匹配未能检测到可口可乐徽标
 

请记住,cv2.matchTemplate函数确实不知道是否正确找到了对象——它只是在输入图像上滑动模板图像,计算归一化的相关分数,然后返回分数最大的位置。

要过滤掉假阳性检测结果,您应该抓住 最大值 并使用if语句以筛选出低于特定阈值的分数。

总结

在本教程中,您学习了如何使用OpenCVcv2.matchTemplate 函数。

模板匹配是对象检测的基本形式。它非常快速且高效,但是缺点是当对象的旋转,缩放或视角改变时,它会失败——发生这种情况时,您需要一种更高级的对象检测技术。

不过,假设您可以在捕获照片的环境中控制对象的比例或将对象的比例归一化。在这种情况下,您可以使用模板匹配,避免标记数据、训练对象检测器和调优其超参数等繁琐任务。

翻译自:OpenCV Template Matching ( cv2.matchTemplate ), by Adrian Rosebrock.
Reference :Adrian Rosebrock, “OpenCV Template Matching ( cv2.matchTemplate )”, PyImageSearch, https://www.pyimagesearch.com/2021/03/22/opencv-template-matching-cv2-matchtemplate/, accessed on 31 August 2021.

评论 2 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值