模板匹配
本小节主要介绍如何使用模板匹配在图像中发现对象,用到以下函数: cv.matchTemplate(), cv.minMaxLoc()
- 用法如下:
- cv.matchTemplate( image, templ, method[, result[, mask]] ) -> result
- cv.minMaxLoc( src[, mask] ) -> minVal, maxVal, minLoc, maxLoc
另外,我们在匹配之前。一定要将模板图片的像素大小和待匹配的原图中的ROI区域一定要保持一致,因为matchTemplate的匹配过程是:拿着模板滑过原图,按照模板左上角为参照,比较整个模板区域和待匹配区域的匹配程度的。
- 模板匹配的六个参数
-
TM_SQDIFF:计算平方差值:,计算出来的值越小,则模板和原图像相关程度就越大
-
TM_CCORR:计算相关性,计算出来的值越大,则模板和原图像相关程度就越大
-
TM_CCOEFF:计算相关系数,计算出来的值越大,则模板和原图像相关程度就越大
-
TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,则模板和原图像相关程度就越大
-
TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,则模板和原图像相关程度就越大
-
TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,则模板和原图像相关程度就越大
-
实际运用时,最好使用归一化的方法
-
匹配过程:“按图索骥”,将原图像划分为一个个和模板大小一致的区域,然后从左到右、从上到下按区比对,看模板位于哪个区域,滑动值一般设置为一个像素大小
-
差异量化:对应像素点之间的灰度值差异
一、概念
模板匹配是一种在较大图像中搜索和查找模板图像位置的方法。 为此,OpenCV 附带了一个函数 cv.matchTemplate()。 它只是将模板图像滑到输入图像上(如在 2D 卷积中),并在模板图像下比较输入图像的模板和补丁。 OpenCV 中实现了几种比较方法。 它返回一个灰度图像,其中每个像素表示该像素的邻域与模板匹配的程度 (只是一个“程度”阵列,而非“图像”)。
如果输入图像的大小为 (WxH),模板图像的大小为 (wxh),则输出图像的大小为 (W-w+1, H-h+1)。 得到结果灰度阵列后,您可以使用 cv.minMaxLoc() 函数查找最大值/最小值在哪里。 把它作为矩形的左上角,把(w,h)作为矩形的宽和高。 该矩形即是您的模板区域。
- 注意:如果您使用 cv.TM_SQDIFF 作为比较方法,则最小值给出最佳匹配。
import cv2 as cv
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
def cv_show(name, img):
cv.imshow(name, img)
cv.waitKey(0)
cv.destroyAllWindows()
def compare(imgs):
# for i in range(len(imgs)):
# imgs[i][:,-3:-1,:] = [255,255,255]
res = np.hstack(imgs)
cv_show('Compare', res)
二、使用OpenCV进行模板匹配
我们将在照片中搜索xy的背部。 所以我创建了一个模板如下:
back = cv.imread('xy_back.png')
cv_show('Template',back)
# 模板最好使用灰度图
template = cv.cvtColor(back,cv.COLOR_BGR2GRAY)
# 获取模板的尺寸便于之后在原图中绘制出Bounding box
# 行数是高度,列数是宽度,别弄混
h,w = template.shape
img = cv.imread('xy.png')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 计算模板像素点和原图的“差异性”
res = cv.matchTemplate(gray, template, cv.TM_SQDIFF_NORMED)
# 得到结果阵列的特征点值和位置
minv, maxv, minLoc, maxLoc = cv.minMaxLoc(res)
# 获取最佳匹配点,也就是差异最小点
W,H = minLoc
result = cv.rectangle(img.copy(), (W,H),( W+w-1, H+h-1), (0, 0, 255), 2)
cv_show('Result', result)
# 显示所有类型的匹配结果
img = cv.imread('xy.png',0)
img2 = img.copy()
template = cv.imread('xy_back.png',0)
w, h = template.shape[::-1]
# All the 6 methods for comparison in a list
methods = ['cv.TM_CCOEFF', 'cv.TM_CCOEFF_NORMED', 'cv.TM_CCORR',
'cv.TM_CCORR_NORMED', 'cv.TM_SQDIFF', 'cv.TM_SQDIFF_NORMED']
for meth in methods:
img = img2.copy()
# 获取方法名称字符串
method = eval(meth)
# Apply template Matching
res = cv.matchTemplate(img,template,method)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
# If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum
if method in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv.rectangle(img,top_left, bottom_right, 255, 2)
plt.subplot(121),plt.imshow(res,cmap = 'gray')
plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img,cmap = 'gray')
plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
plt.suptitle(meth)
plt.show()
从左边图像可以看出,除了TM_SQDIFF or TM_SQDIFF_NORMED以外,其余的所有匹配方式匹配最好的点就接近纯白,而上面两二者接近纯黑,且匹配结果阵列的大小确为(W-w+1, H-h+1)。另外,基本上归一化模式都做得很好,非归一化中的 cv.TM_CCORR 模式匹配得最差!
三、多对象匹配
在上一节中,我们在图像中搜索了夏羽的背部,它在图像中只出现过一次。 假设您正在搜索一个多次出现的对象, cv.minMaxLoc() 不会为您提供所有位置。 在这种情况下,我们将使用阈值。 所以在这个例子中,我们将使用著名游戏马里奥的截图,我们将在其中找到砖块。
- 待匹配图像:
- 模板图像:
brick = cv.imread('brick.png')
mary = cv.imread('Mary.png')
template_b = cv.cvtColor(brick, cv.COLOR_BGR2GRAY)
template_m = cv.cvtColor(mary, cv.COLOR_BGR2GRAY)
img = cv.imread('SM.png')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
h_b, w_b = template_b.shape
h_m, w_m = template_m.shape
# 进行匹配,值越大匹配的越好
res_b = cv.matchTemplate(gray, template_b, cv.TM_CCORR_NORMED)
thresh = 0.9
# 使用np.where方法重新构造一个符合要求的结果阵列,
# (loc[0], loc[1])分别是符合要求的所有点集的y,x坐标
loc = np.where( res_b >= thresh)
# 这里逆序遍历的原因是(loc[0], loc[1])分别是符合要求的所有点集的y,x坐标,一语中的的
for pt in zip(*(loc[::])):
cv.rectangle(img, (pt[1], pt[0]), (pt[1]+w_b, pt[0]+h_b), (0,255, 0), 2)
cv_show('Res', img)
# cv.rectangle(img, (loc[0][-2],loc[1][-2]), (loc[0][-2]+w_b,loc[1][-2]+h_b), (0,255, 0), 2)
#cv_show('Test', img)
# 方法正确
# minv,maxv, minloc,maxloc = cv.minMaxLoc(res_b)
# W,H = maxloc
# res = cv.rectangle(img.copy(), (W,H), (W+w_b, H+h_b), (0, 0, 255), 2)
# cv_show('Result', res)
SM = cv.imread('SM.png')
compare([SM, img])