opencv-python 4.12. 模板匹配

发布时间 2023-04-06 16:06:02作者: 一枚码农

理论

模板匹配是一种在较大图像中搜索和查找模板图像位置的方法。为此,OpenCV附带了一个函数cv.matchTemplate()。它只是在输入图像上滑动模板图像(如在2D卷积中),并比较模板图像下的输入图像的模板和补丁。在OpenCV中实现了几种比较方法。它返回一个灰度图像,其中每个像素表示该像素的邻域与模板匹配的程度。
cv2.matchTemplate(image,templ,method[,result[,mask]])->result

  • image:源图像,待匹配图像,8bit整数型、32bit浮点型,可以是单通道或多通道;
  • templ:模板图像,类型同源图像,尺寸必须小于源图像;
  • method:匹配方法;
  • mask:掩码;
  • result:返回结果,32bit浮点型,源图像为W×H,模板图像为w×h,生成的图像对象为(W−w+1)×(H−h+1);

method可选值:

  • cv.TM_SQDIFF: 判断 minVal 越小,效果越好计算模板与目标图像的方差,由于是像素值差值的平方的和,所以值越小匹配程度越高;
  • cv.TM_CCOEFF_NORMED: 判断 maxVal 越接近1,效果越好 范化的cv::TM_CCOEFF,-1 ~ 1之间。
  • cv.TM_CCORR_NORMED: 判断 maxVal 越接近1,效果越好 范化的cv::TM_CCORR,0-1之间,我用的这个;
  • cv.TM_SQDIFF_NORMED: 判断 minVal 越接近0,效果越好 范化的cv.TM_SQDIFF,取值为0-1之间,完美匹配返回值为0;
  • cv.TM_CCORR: 判断 maxVal 越大,效果越好 使用dot product计算匹配度,越高匹配度就好;
  • cv.TM_CCOEFF: 判断 maxVal 越大,效果越好 采用模板与目标图像像素与各自图像的平均值计算dot product,正值越大匹配度越高,负值越大图像的区别越大,但如果图像没有明显的特征(即图像中的像素值与平均值接近)则返回值越接近0;

如果输入图像的大小(WxH)且模板图像的大小(wxh),则输出图像的大小为(W-w + 1,H-h + 1)。获得结果后,可以使用cv.minMaxLoc()函数查找最大/最小值的位置。将其作为矩形的左上角,并将(w,h)作为矩形的宽度和高度。那个矩形是你的模板区域。

注意:如果你使用cv.TM_SQDIFF作为比较方法,则最小值会给出最佳匹配。

OpenCV中的模板匹配

我们将尝试所有比较方法,以便我们可以看到它们的结果如何:

import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\9.jpg', 0)
template = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\9_1.jpg', 0)
w, h = template.shape[::-1]

methods = ['cv.TM_CCOEFF', 'cv.TM_CCOEFF_NORMED', 'cv.TM_CCORR',
           'cv.TM_CCORR_NORMED', 'cv.TM_SQDIFF', 'cv.TM_SQDIFF_NORMED']

for i, meth in enumerate(methods):
    img_name = f'img_{i}'
    img_name = img.copy()
    method = eval(meth)

    # Apply template Matching
    res = cv.matchTemplate(img_name, 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_name, top_left, bottom_right, 0, 2)

    plt.subplot(6, 2, i * 2 + 1), plt.imshow(res, cmap='gray')
    plt.title(f'Matching Result {i}'), plt.xticks([]), plt.yticks([])
    plt.subplot(6, 2, i * 2 + 2), plt.imshow(img_name, cmap='gray')
    plt.title(f'Detected Point {meth}'), plt.xticks([]), plt.yticks([])
    plt.suptitle(meth)

plt.show()

image
你可以看到使用cv.TM_CCORR的结果不如我们预期的那样好。

与多个对象匹配的模板

像上边方块是有很多个,但是只标出来一个的位置,这是因为cv.minMaxLoc()不会给出所有的位置。在这种情况下,我们将使用阈值化。

import cv2 as cv
import numpy as np

img = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\9.jpg')
template = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\9_1.jpg')

img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
template_gray = cv.cvtColor(template, cv.COLOR_BGR2GRAY)
w, h = template_gray.shape

res = cv.matchTemplate(img_gray, template_gray, cv.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold)

for pt in zip(*loc[::-1]):
    cv.rectangle(img, pt, (pt[0] + w, pt[1] + h), (25, 255, 0), 1)

cv.imshow('img', img)
cv.waitKey(0)

image