opencv-python 模板匹配
模板匹配:在给定的图像中查找和模板最相似的区域。模板匹配类似于卷积,模板在原图上从左上角原点(0,0)开始滑动,计算模板与滑动窗口的差别程度,计算方法有6种,每次计算的结果放在一个矩阵中,最后输出差别程度的矩阵。原始图像为A*B,模板大小是a*b的话,输出的矩阵大小为:(A-a+1)*(B-b+1)。
1 模板匹配
opencv中的模板匹配函数是:matchTemplate(img,template,method)
模板匹配计算方法(最好用归一化的参数):
TM_SQDIFF 计算平方不同,计算出的值越小,越相关
TM_CCORR 计算相关性,计算出来的值越大,越相关
TM_CCOEFF 计算相关系数,计算出来的值越大,越相关
TM_SQDIFF_NORMED 计算归一化平方不同,计算出来的值越接近0,越相关
TM_CCORR_NORMED 计算归一化相关性,计算出来的值越接近1,越相关
TM_CCOEFF_NORMED 计算归一化相关系数,计算出来的值越接近1,越相关
图像和对应的匹配图像如下:
import cv2 import numpy as np import matplotlib.pyplot as plt %matplotlib inline img = cv2.imread('./cat.jpg') template = cv2.imread('./cat_face.jpg') h,w = template.shape[:2] res = cv2.matchTemplate(img,template,cv2.TM_CCORR_NORMED) #模板匹配 print(res) min_val,max_val,min_loc,max_loc = cv2.minMaxLoc(res) #获取匹配结果的最值和对应位置,方便计算后续矩形所需的坐标点 print(max_val,max_loc) cv2.rectangle(img,max_loc,(max_loc[0]+w,max_loc[1]+h),color=[0,0,255]) #用矩形把匹配的结果显示出来 cv2.imshow('img',img) img2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.subplot(121),plt.imshow(res,cmap='gray') plt.subplot(122),plt.imshow(img2) plt.show() cv2.waitKey(0) cv2.destroyAllWindows()
把六种模板匹配计算方法都试一下:
import cv2 import numpy as np import matplotlib.pyplot as plt %matplotlib inline img = cv2.imread('./cat.jpg') template = cv2.imread('./cat_face.jpg') #匹配的模板图片 h,w = template.shape[:2] #获取模板的高宽 #定义模板匹配方法列表 methods = ['cv2.TM_SQDIFF','cv2.TM_CCORR','cv2.TM_CCOEFF','cv2.TM_SQDIFF_NORMED','cv2.TM_CCORR_NORMED','cv2.TM_CCOEFF_NORMED'] for meth in methods: #循环显示匹配的结果,把所有匹配方法都用plt显示出来 img_copy = img.copy() #把原始图片拷贝一下,保证后续的匹配方法不被影响 method = eval(meth) #取匹配方法的真值 res = cv2.matchTemplate(img_copy,template,method) #模板匹配 min_val,max_val,min_loc,max_loc = cv2.minMaxLoc(res) #获取匹配结果的最值和对应位置 if method in [cv2.TM_SQDIFF,cv2.TM_SQDIFF_NORMED]: #如果是这两种匹配方法,计算出的值越小,越相关,其余的匹配方法相反 start_point = min_loc end_point = (min_loc[0]+w,min_loc[1]+h) else: start_point = max_loc end_point = (max_loc[0]+w,max_loc[1]+h) cv2.rectangle(img_copy,start_point,end_point,color=[0,0,255]) #用矩形把匹配的结果显示出来 img2 = cv2.cvtColor(img_copy, cv2.COLOR_BGR2RGB) #用plt显示图片的时候要把通道转换成RGB格式 plt.subplot(121),plt.imshow(res,cmap='gray') #显示匹配结果的灰度图 plt.subplot(122),plt.imshow(img2) plt.suptitle(meth) #显示匹配方法标题 plt.show()
2 匹配多个对象
首先介绍一下np.where函数
np.where 函数是三元表达式 x if condition else y 的向量化版本,它有两种用法:
1.np.where(condition,x,y) 当where内有三个参数时,第一个参数表示条件,当条件成立时where方法返回x,当条件不成立时where返回y
2.np.where(condition) 当where内只有一个参数时,那个参数表示条件,当条件成立时,where返回的是每个符合condition条件元素的坐标,返回的是以元组的形式
图像和对应的匹配图像如下:
import cv2 import numpy as np #匹配多个对象 import matplotlib.pyplot as plt %matplotlib inline img = cv2.imread('./mario.jpg') template = cv2.imread('./mario_coin.jpg') #匹配的模板图片 h,w = template.shape[:2] #获取模板的高宽 res = cv2.matchTemplate(img,template,cv2.TM_CCORR_NORMED) #print(res) threshold = 0.8 #设定阈值 loc = np.where(res >= threshold) #返回res中值大于0.8的y轴,x轴的索引(分开的) #print(loc) # loc = np.argwhere(res >= threshold) #用argwhere可以直接获取坐标[y,x],但是坐标类型是list # print(loc) # for left_top in loc: # left_top = tuple(left_top[::-1]) #把坐标list转换位元组 # bottom_right = (left_top[0]+w,left_top[1]+h) # cv2.rectangle(img,left_top,bottom_right,[0,0,255],1) for ptr in zip(*loc[::-1]): # *表示可选参数 因为loc是先y坐标再x坐标,所以用loc[::-1]翻转一下,然后再用zip函数拼接一下 end_point = (ptr[0]+w,ptr[1]+h) cv2.rectangle(img,ptr,end_point,[0,0,255],1) cv2.imshow('img',img) cv2.waitKey(0) cv2.destroyAllWindows()
匹配结果如下: