本篇博客记录学习OpenCV-Python模板匹配的相关知识。
模板匹配是用来在一副大图中搜寻查找模版图像位置的方法。 OpenCV 为我们提供了函数: cv2.matchTemplate()。如同 2D 卷积,它也是用模板图像在输入图像(大图)上滑动,并在每一个位置对模板图像和与其对应的输入图像的子区域进行比较。OpenCV 提供了几种不同的比较方法(见下文)。其返回的结果是一个灰度图像,每一个像素值表示了此区域与模板的匹配
程度。如果输入图像的大小是( W乘H),模板的大小是( w乘h),输出的结果的大小就是( W-w+1, H-h+1)。当你得到这幅图之后,就可以使用函数cv2.minMaxLoc() 来找到其中的最小值和最大值的位置了。第一个值为矩形左上角的点,( w, h)为模板矩形的宽和高。这个矩形就是找到的模板区域了。
OpenCV 提供的几种不同的比较方法:
接下来我们用一个示例进行验证,虽然有六种不同的比较方法,但是在实际应用中一般会使用归一化的比较方法,下面的示例我我们用TM_SQDIFF_NORMED比较方法。
模板图片:
实验代码:
import cv2 as cv
import numpy as np
# 模板匹配,就是在整个图像区域发现与给定子图像匹配的小块区域,
# 需要模板图像T和待检测图像-源图像S
# 工作方法:在待检测的图像上,从左到右,从上倒下计算模板图像与重叠子图像匹配度,
# 匹配度越大,两者相同的可能性越大。
def template_demo():
tpl = cv.imread("F:/Pycharm/opencv_exercises-master/images/rabbit.jpg") # 模板图片
print("模板大小:", tpl.shape)
target = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu22.jpg") # 原图
print("原图大小:", target.shape)
cv.imshow("template", tpl)
cv.imshow("target", target)
methods = [cv.TM_SQDIFF_NORMED, cv.TM_CCORR_NORMED, cv.TM_CCOEFF_NORMED] # 三种模板匹配方法
th, tw = tpl.shape[:2]
result = cv.matchTemplate(target, tpl, cv.TM_SQDIFF_NORMED) # 得到匹配结果
print("结果大小:", result.shape)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
print("min_val", min_val)
print("max_val", max_val)
print("min_loc", min_loc)
print("max_loc", max_loc)
tl = min_loc
br = (tl[0] + tw, tl[1] + th)
cv.rectangle(target, tl, br, (0, 0, 255), 2) # tl为左上角坐标,br为右下角坐标,从而画出矩形
cv.imshow("target", target)
if __name__ == '__main__':
template_demo()
cv.waitKey(0) # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
cv.destroyAllWindows() # 关闭所有窗口
可见其返回的结果是一个灰度图像,每一个像素值表示了此区域与模板的匹配程度。如果输入图像的大小是( 727,1011),模板的大小是( 317,256),输出的结果的大小就是( 727-317+1, 1011-256+1)-->(411,756) 。
接下来我们看一下其他两种归一化的比较方法效果图。
实验代码:
import cv2 as cv
import numpy as np
# 模板匹配,就是在整个图像区域发现与给定子图像匹配的小块区域,
# 需要模板图像T和待检测图像-源图像S
# 工作方法:在待检测的图像上,从左到右,从上倒下计算模板图像与重叠子图像匹配度,
# 匹配度越大,两者相同的可能性越大。
def template_demo():
tpl = cv.imread("F:/Pycharm/opencv_exercises-master/images/rabbit.jpg") # 模板图片
print("模板大小:", tpl.shape)
target = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu22.jpg") # 原图
print("原图大小:", target.shape)
cv.imshow("template", tpl)
cv.imshow("target", target)
methods = [cv.TM_SQDIFF_NORMED, cv.TM_CCORR_NORMED, cv.TM_CCOEFF_NORMED] # 三种模板匹配方法
th, tw = tpl.shape[:2]
for md in methods:
print(md)
result = cv.matchTemplate(target, tpl, md) # 得到匹配结果
print("结果大小:", result.shape)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
print("min_val", min_val)
print("max_val", max_val)
print("min_loc", min_loc)
print("max_loc", max_loc)
if md == cv.TM_SQDIFF_NORMED: # cv.TM_SQDIFF_NORMED最小时最相似,其他最大时最相似
tl = min_loc # 左上角点位
else:
tl = max_loc
br = (tl[0] + tw, tl[1] + th)
cv.rectangle(target, tl, br, (0, 0, 255), 2) # tl为左上角坐标,br为右下角坐标,从而画出矩形
cv.imshow("match-"+np.str(md), target)
if __name__ == '__main__':
template_demo()
cv.waitKey(0) # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
cv.destroyAllWindows() # 关闭所有窗口
效果图:
数据结果:
上面介绍的都是单个匹配,但是如果我们要是进行人脸检测,虽然实际应用是通过机器学习算法实现,但是用我们今天学习的知识也是可以实现的,可能用上面的方法就有点行不通了,因为一张图中会有多张人脸,这时候我们就需要匹配多个对象。下面用个简单的例子进行测试。
步骤也是一样的:
实验代码:
import cv2 as cv
import numpy as np
# 模板匹配,就是在整个图像区域发现与给定子图像匹配的小块区域,
# 需要模板图像T和待检测图像-源图像S
# 工作方法:在待检测的图像上,从左到右,从上倒下计算模板图像与重叠子图像匹配度,
# 匹配度越大,两者相同的可能性越大。
def more_template_demo():
tpl = cv.imread("F:/Pycharm/opencv_exercises-master/images/mario_coin.jpg") # 模板图片
target = cv.imread("F:/Pycharm/opencv_exercises-master/images/mario.jpg") # 原图
cv.imshow("template", tpl)
cv.imshow("target", target)
th, tw = tpl.shape[:2]
result = cv.matchTemplate(target, tpl, cv.TM_CCOEFF_NORMED) # 得到匹配结果
print("result", result)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
print("max_val", max_val)
threshold = 0.8
# 取模板匹配程度大于80%的坐标
local = np.where(result >= threshold)
for tl in zip(*local[::-1]): # *表示可选参数
botton_right = (tl[0] + tw, tl[1] + th)
cv.rectangle(target, tl, botton_right, (0, 0, 255), 2)
cv.imshow("target", target)
if __name__ == '__main__':
more_template_demo()
cv.waitKey(0) # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
cv.destroyAllWindows() # 关闭所有窗口
实验效果图:
整体工程代码:
import cv2 as cv
import numpy as np
# 模板匹配,就是在整个图像区域发现与给定子图像匹配的小块区域,
# 需要模板图像T和待检测图像-源图像S
# 工作方法:在待检测的图像上,从左到右,从上倒下计算模板图像与重叠子图像匹配度,
# 匹配度越大,两者相同的可能性越大。
def template_demo():
tpl = cv.imread("F:/Pycharm/opencv_exercises-master/images/rabbit.jpg") # 模板图片
print("模板大小:", tpl.shape)
target = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu22.jpg") # 原图
print("原图大小:", target.shape)
cv.imshow("template", tpl)
cv.imshow("target", target)
methods = [cv.TM_SQDIFF_NORMED, cv.TM_CCORR_NORMED, cv.TM_CCOEFF_NORMED] # 三种模板匹配方法
th, tw = tpl.shape[:2]
# result = cv.matchTemplate(target, tpl, cv.TM_SQDIFF_NORMED) # 得到匹配结果
# print("结果大小:", result.shape)
# min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
# print("min_val", min_val)
# print("max_val", max_val)
# print("min_loc", min_loc)
# print("max_loc", max_loc)
# tl = min_loc
# br = (tl[0] + tw, tl[1] + th)
# cv.rectangle(target, tl, br, (0, 0, 255), 2) # tl为左上角坐标,br为右下角坐标,从而画出矩形
# cv.imshow("target", target)
for md in methods:
print(md)
result = cv.matchTemplate(target, tpl, md) # 得到匹配结果
print("结果大小:", result.shape)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
print("min_val", min_val)
print("max_val", max_val)
print("min_loc", min_loc)
print("max_loc", max_loc)
if md == cv.TM_SQDIFF_NORMED: # cv.TM_SQDIFF_NORMED最小时最相似,其他最大时最相似
tl = min_loc # 左上角点位
else:
tl = max_loc
br = (tl[0] + tw, tl[1] + th)
cv.rectangle(target, tl, br, (0, 0, 255), 2) # tl为左上角坐标,br为右下角坐标,从而画出矩形
cv.imshow("match-" + np.str(md), target)
# cv.imshow("match-" + np.str(md), result)
def more_template_demo():
tpl = cv.imread("F:/Pycharm/opencv_exercises-master/images/mario_coin.jpg") # 模板图片
target = cv.imread("F:/Pycharm/opencv_exercises-master/images/mario.jpg") # 原图
cv.imshow("template", tpl)
cv.imshow("target", target)
th, tw = tpl.shape[:2]
result = cv.matchTemplate(target, tpl, cv.TM_CCOEFF_NORMED) # 得到匹配结果
print("result", result)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
print("max_val", max_val)
threshold = 0.8
# 取模板匹配程度大于80%的坐标
local = np.where(result >= threshold)
for tl in zip(*local[::-1]): # *表示可选参数
botton_right = (tl[0] + tw, tl[1] + th)
cv.rectangle(target, tl, botton_right, (0, 0, 255), 2)
cv.imshow("target", target)
if __name__ == '__main__':
# template_demo()
more_template_demo()
cv.waitKey(0) # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
cv.destroyAllWindows() # 关闭所有窗口