所谓的模板匹配,即在给定的图片中查找和模板最相似的区域,该算法的输入包括模板和测试图片,整个任务的思路就是按照滑窗的思路不断的移动模板图片,计算其与图像中对应区域的匹配度,最终将匹配度最高的区域选择为最终的结果。下图展示了一个样例,左边是对应的模板,右边是在测试图片中检测的结果。
Opencv中集成了一个模板匹配算法,用户调用cv2.matchtemplate函数就可以实现该功能。下面展示了一个该函数的使用样例,该样例的模板是一枚硬币,cv2.matchtemplate函数可以准确的在测试图片中检测到所有的硬币。
import cv2
import numpy as np
from matplotlib import pyplot as plt
img_rgb = cv2.imread('mario.png')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.png',0)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where( res >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
cv2.imwrite('res.png',img_rgb)
下图展示了另外一个案例,匹配的模板如左图所示,匹配的结果如右图所示,通过观察该图我们可以发现cv2.matchtemplate函数并没有输出准确的结果,它仅仅输出了一部分区域,主要的原因是因为模板的大小和测试图片中的目标的大小之间存在着较大的差异,而使用滑窗思路在测试图片中只能获得对应大小的一块区域,本文的重点就是针对这个问题提出一个多尺度的模板匹配算法。
# coding=utf-8
# 导入python包
import numpy as np
import argparse
import imutils
import glob
import cv2
# 构建并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-t", "--template", required=True, help="Path to template image")
ap.add_argument("-i", "--images", required=True, help="Path to images where template will be matched")
ap.add_argument("-v", "--visualize", help="Flag indicating whether or not to visualize each iteration")
args = vars(ap.parse_args())
# 读取模板图片
template = cv2.imread(args["template"])
# 转换为灰度图片
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
# 执行边缘检测
template = cv2.Canny(template, 50, 200)
(tH, tW) = template.shape[:2]
# 显示模板
cv2.imshow("Template", template)
# 遍历所有的图片寻找模板
for imagePath in glob.glob(args["images"] + "/*.jpg"):
# 读取测试图片并将其转化为灰度图片
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
found = None
# 循环遍历不同的尺度
for scale in np.linspace(0.2, 1.0, 20)[::-1]:
# 根据尺度大小对输入图片进行裁剪
resized = imutils.resize(gray, width = int(gray.shape[1] * scale))
r = gray.shape[1] / float(resized.shape[1])
# 如果裁剪之后的图片小于模板的大小直接退出
if resized.shape[0] < tH or resized.shape[1] < tW:
break
# 首先进行边缘检测,然后执行模板检测,接着获取最小外接矩形
edged = cv2.Canny(resized, 50, 200)
result = cv2.matchTemplate(edged, template, cv2.TM_CCOEFF)
(_, maxVal, _, maxLoc) = cv2.minMaxLoc(result)
# 结果可视化
if args.get("visualize", False):
# 绘制矩形框并显示结果
clone = np.dstack([edged, edged, edged])
cv2.rectangle(clone, (maxLoc[0], maxLoc[1]), (maxLoc[0] + tW, maxLoc[1] + tH), (0, 0, 255), 2)
cv2.imshow("Visualize", clone)
cv2.waitKey(0)
# 如果发现一个新的关联值则进行更新
if found is None or maxVal > found[0]:
found = (maxVal, maxLoc, r)
# 计算测试图片中模板所在的具体位置,即左上角和右下角的坐标值,并乘上对应的裁剪因子
(_, maxLoc, r) = found
(startX, startY) = (int(maxLoc[0] * r), int(maxLoc[1] * r))
(endX, endY) = (int((maxLoc[0] + tW) * r), int((maxLoc[1] + tH) * r))
# 绘制并显示结果
cv2.rectangle(image, (startX, startY), (endX, endY), (0, 0, 255), 2)
cv2.imshow("Image", image)
cv2.waitKey(0)
注:运行方法:python xxx.py -t 模板.jpg -i 测试图片.jpg(其中xxx,模板,测试图片需要根据你的情况进行适当的调整哈。)
上图展示了改进后的多尺度模板匹配算法的效果。我们可以发现改进后的算法可以准确的在测试图片中检测出和模板大小不相同的区域,和改进之前的效果相比,具有很大的性能提升。尺度问题是现实生活中经常会遇到的一个问题,因而解决这个问题具有重要的研究意义。下面对整个多尺度处理的过程进行展示和分析。
上图展示了改进后的多尺度模板匹配的过程,即通过在不同的尺度中进行模板的匹配,最终从多个尺度中选择匹配度最高的结果进行输出即可。
尽管改进后的多尺度模板匹配算法可以很好的解决尺度问题,但是该算法对物体旋转和非仿射性变换的匹配效果并不鲁棒,聪明的你肯定想到了一个解决方案,那就是使用关键点匹配法,比较经典的关键点检测算法包括SIFT和SURF等,主要的思路是首先通过关键点检测算法获取模板和测试图片中的关键点;然后使用关键点匹配算法处理即可,这些关键点可以很好的处理尺度变化、视角变换、旋转变化、光照变化等,具有很好的不变性。
[1] 参考链接1
[2] 参考链接2
[1] 该博客是本人原创博客,如果您对该博客感兴趣,想要转载该博客,请与我联系(qq邮箱:[email protected]),我会在第一时间回复大家,谢谢大家的关注.
[2] 由于个人能力有限,该博客可能存在很多的问题,希望大家能够提出改进意见。
[3] 如果您在阅读本博客时遇到不理解的地方,希望您可以联系我,我会及时的回复您,和您交流想法和意见,谢谢。
[4] 本文测试的图片可以通过该链接进行下载。网盘链接- 提取码:a2xc。
[5] 本人业余时间承接各种本科毕设设计和各种小项目,包括图像处理(数据挖掘、机器学习、深度学习等)、matlab仿真、python算法及仿真等,有需要的请加QQ:1575262785详聊!!!