📜  在Python中使用 OpenCV 进行模板匹配

📅  最后修改于: 2022-05-13 01:55:22.985000             🧑  作者: Mango

在Python中使用 OpenCV 进行模板匹配

模板匹配是一种用于查找图像中与补丁(模板)相似的区域的技术。
补丁是具有某些特征的小图像。模板匹配的目标是在图像中找到补丁/模板。
要找到它,用户必须提供两个输入图像:源图像 (S) - 要在其中找到模板的图像和模板图像 (T) - 要在源图像中找到的图像。

  • 它基本上是一种在较大图像中搜索和查找模板图像位置的方法。
  • 这里的想法是找到与我们提供的模板匹配的图像的相同区域,并给出一个阈值
    • 阈值取决于我们想要在源图像中检测模板的精度。
    • 例如,如果我们正在应用人脸识别并且想要检测人的眼睛,我们可以提供眼睛的随机图像作为模板并搜索源(人脸)。
    • 在这种情况下,由于“眼睛”在人与人之间表现出很大的变化,即使我们将阈值设置为 50%(0.5),也会检测到眼睛。
    • 如果要搜索几乎相同的模板,则应将阈值设置为高。(t>=0.8)

模板匹配如何工作?

  • 模板图像只是在输入图像上滑动(如在 2D 卷积中)
  • 比较模板图像下输入图像的模板和补丁。
  • 将获得的结果与阈值进行比较。
  • 如果结果大于阈值,则该部分将被标记为检测到。
  • 在函数cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED) 中,第一个参数是主图像,第二个参数是要匹配的模板,第三个参数是用于匹配的方法。

Python
# Python program to illustrate
# template matching
import cv2
import numpy as np
 
# Read the main image
img_rgb = cv2.imread('mainimage.jpg').
 
# Convert it to grayscale
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
 
# Read the template
template = cv2.imread('template',0)
 
# Store width and height of template in w and h
w, h = template.shape[::-1]
 
# Perform match operations.
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
 
# Specify a threshold
threshold = 0.8
 
# Store the coordinates of matched area in a numpy array
loc = np.where( res >= threshold)
 
# Draw a rectangle around the matched region.
for pt in zip(*loc[::-1]):
    cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,255,255), 2)
 
# Show the final image with the matched area.
cv2.imshow('Detected',img_rgb)


Python
# Python program to illustrate
# multiscaling in template matching
import cv2
import numpy as np
  
# Read the main image
img_rgb = cv2.imread('mainimage.jpg')
  
# Convert to grayscale
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
  
# Read the template
template = cv2.imread('template',0)
  
# Store width and height of template in w and h
w, h = template.shape[::-1]
found = None
 
for scale in np.linspace(0.2, 1.0, 20)[::-1]:
 
    # resize the image according to the scale, and keep track
    # of the ratio of the resizing
    resized = imutils.resize(img_gray, width = int(img_gray.shape[1] * scale))
    r = img_gray.shape[1] / float(resized.shape[1])
  
    # if the resized image is smaller than the template, then break
    # from the loop
    # detect edges in the resized, grayscale image and apply template
    # matching to find the template in the image edged
    # = cv2.Canny(resized, 50, 200) result = cv2.matchTemplate(edged, template,
    # cv2.TM_CCOEFF) (_, maxVal, _, maxLoc) = cv2.minMaxLoc(result)
    # if we have found a new maximum correlation value, then update
    # the found variable if found is None or maxVal > found[0]:
    if resized.shape[0] < h or resized.shape[1] < w:
            break
    found = (maxVal, maxLoc, r)
  
# unpack the found variable and compute the (x, y) coordinates
# of the bounding box based on the resized ratio
(_, 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))
 
# draw a bounding box around the detected result and display the image
cv2.rectangle(image, (startX, startY), (endX, endY), (0, 0, 255), 2)
cv2.imshow("Image", image)
cv2.waitKey(0)


模板匹配的局限性:

  1. 图案出现必须保持参考图案图像(模板)的方向
  2. 因此,它不适用于模板的旋转或缩放版本,因为对象 wrt 模板的形状/大小/剪切等的变化会产生错误的匹配。
  3. 该方法在计算中到大图像的模式相关图像时效率低下,因为该过程非常耗时。

为了避免模板和原始图像大小不同导致的问题,我们可以使用multiscaling 。在这种情况下,仅仅因为您的模板尺寸与您要匹配的图像中区域的尺寸不匹配,并不意味着您不能应用模板匹配。

模板匹配中的多尺度机制

Multi scaling的过程如下:

  1. 在多个尺度上循环输入图像(即使输入图像逐渐变小)。
  2. 使用 cv2.matchTemplate 应用模板匹配并跟踪具有最大相关系数的匹配(以及具有最大相关系数的区域的 x、y 坐标)。
  3. 在遍历所有尺度后,取相关系数最大的区域并将其用作您的“匹配”区域。

Python

# Python program to illustrate
# multiscaling in template matching
import cv2
import numpy as np
  
# Read the main image
img_rgb = cv2.imread('mainimage.jpg')
  
# Convert to grayscale
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
  
# Read the template
template = cv2.imread('template',0)
  
# Store width and height of template in w and h
w, h = template.shape[::-1]
found = None
 
for scale in np.linspace(0.2, 1.0, 20)[::-1]:
 
    # resize the image according to the scale, and keep track
    # of the ratio of the resizing
    resized = imutils.resize(img_gray, width = int(img_gray.shape[1] * scale))
    r = img_gray.shape[1] / float(resized.shape[1])
  
    # if the resized image is smaller than the template, then break
    # from the loop
    # detect edges in the resized, grayscale image and apply template
    # matching to find the template in the image edged
    # = cv2.Canny(resized, 50, 200) result = cv2.matchTemplate(edged, template,
    # cv2.TM_CCOEFF) (_, maxVal, _, maxLoc) = cv2.minMaxLoc(result)
    # if we have found a new maximum correlation value, then update
    # the found variable if found is None or maxVal > found[0]:
    if resized.shape[0] < h or resized.shape[1] < w:
            break
    found = (maxVal, maxLoc, r)
  
# unpack the found variable and compute the (x, y) coordinates
# of the bounding box based on the resized ratio
(_, 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))
 
# draw a bounding box around the detected result and display the image
cv2.rectangle(image, (startX, startY), (endX, endY), (0, 0, 255), 2)
cv2.imshow("Image", image)
cv2.waitKey(0)

上面代码的逐步解释:

  • 在将模板的宽度和高度存储在 w 和 r 中之后,我们初始化一个找到的变量来跟踪具有最佳匹配的图像的区域和比例。从那里我们开始使用 np.linspace函数在图像的多个尺度上循环。此函数接受三个参数,即起始值、结束值和介于两者之间的相等块切片的数量。在此示例中,我们将从图像原始大小的 100% 开始,然后在 20 个相同大小的百分比块中逐步缩小到原始大小的 20%。
  • 然后,我们根据当前比例调整图像大小并计算旧宽度与新宽度的比率——正如您稍后将看到的,我们跟踪这个比率很重要。我们进行检查以确保输入图像大于我们的模板匹配。如果模板更大,那么我们的 cv2.matchTemplate 调用会抛出一个错误,所以如果是这种情况,我们就从循环中中断。
  • 此时我们可以将模板匹配应用于我们调整大小的图像:
    • cv2.minMaxLoc函数获取我们的相关结果并返回一个 4 元组,其中包括最小相关值、最大相关值、最小值的 (x, y) 坐标和 (x, y) 坐标最大值,分别。我们只对最大值和 (x, y) 坐标感兴趣,因此我们保留最大值并丢弃最小值。
  • 之后,我们检查在每次缩放迭代中匹配的图像区域。从那里,我们更新找到的变量,以跟踪迄今为止找到的最大相关值、最大值的 (x, y) 坐标,以及原始图像宽度与当前调整大小的图像宽度的比率.
  • 在遍历图像的所有比例之后,我们解包找到的变量,然后计算边界框的开始和结束 (x, y) 坐标。特别注意将边界框的坐标乘以比率,以确保坐标与输入图像的原始尺寸相匹配。
  • 最后,我们绘制边界框并将其显示到屏幕上。