opencv python MatchTemplate函数多重匹配

8 投票
3 回答
14180 浏览
提问于 2025-04-17 14:57

我正在尝试在一张大图中找到一张小图,并使用了 MatchTemplate() 这个方法。

img = cv2.imread("c:\picture.jpg")
template = cv2.imread("c:\template.jpg")

result = cv2.matchTemplate(img,template,cv2.TM_CCOEFF_NORMED)
y,x = np.unravel_index(result.argmax(), result.shape)

这个方法运行得很好,我总是能得到小图左上角的坐标,但这只是一个点。如果在大图中有多个匹配的地方,我该怎么才能找到所有的匹配呢?

3 个回答

2

你应该避免使用 cv2.minMaxLoc(result),因为这个方法只会找到一个最好的结果。而我们想要的是多个好结果,这些结果都要高于某个阈值。

使用非极大值抑制是一种在图像中找到多个匹配的方法。

  import cv2
  from imutils.object_detection import non_max_suppression # pip install imutils
  
  # Load the image and template
  image = cv2.imread(img_path, cv2.IMREAD_COLOR)
  template = cv2.imread(template_path, cv2.IMREAD_COLOR)
  
  # Perform template matching 
  result = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
  
  # Filter results to only good ones
  threshold = 0.90 # Larger values have less, but better matches.
  (yCoords, xCoords) = np.where(result >= threshold)

  # Perform non-maximum suppression.
  template_h, template_w = template.shape[:2]
  rects = []
  for (x, y) in zip(xCoords, yCoords):
    rects.append((x, y, x + template_w, y + template_h))
  pick = non_max_suppression(np.array(rects))

  # Optional: Visualize the results
  for (startX, startY, endX, endY) in pick:
    cv2.rectangle(image, (startX, startY), (endX, endY),(0, 255, 0), 2)
  cv2.imshow('Results', image)

解释:

我们使用非极大值抑制的原因是,在原始图像中,每个匹配周围会有很多“好”的结果(比如,把模板移动一个像素通常会得到一个不错的结果,这样就会在原始图像中每个物体的实例周围产生很多重叠的边框)。非极大值抑制会过滤掉,只保留每个图像区域中的一个好匹配。

需要注意的是,这个非极大值抑制并不是直接使用分数,所以上面提到的这个答案在使用 matchTemplate 时可能更合适。

7

@b_m的回答是可行的,但它会找到太多的匹配项。匹配的过程是将模板在图像上滑动,几乎在每一个像素上进行比较。(或者说几乎每个像素,扫描区域会因为模板的大小而减少)。这意味着在一个好的匹配附近,你会得到很多其他的匹配,它们只差一个像素。如果你把匹配结果做成一张图,你会看到有很多匹配。

import cv2
import numpy as np

image = cv2.imread('smiley.png', cv2.IMREAD_COLOR )
template = cv2.imread('template.png', cv2.IMREAD_COLOR)

h, w = template.shape[:2]

method = cv2.TM_CCOEFF_NORMED

threshold = 0.95

res = cv2.matchTemplate(image, template, method)
# min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

cv2.imwrite('output.png', 255*res)

输入图像:

在这里输入图像描述

使用眼睛作为模板:

在这里输入图像描述

看看输出结果。两个眼睛附近有很多白色像素。你会得到相当多的高分匹配:

在这里输入图像描述

找到同一图像中多个模板的另一种方法是通过覆盖找到的区域来修改图像,然后再进行匹配。但更好的方法是修改结果并重新运行 minMaxLoc。这两种技术在 这个回答 中都有演示。

8

下面是操作方法:

result = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)

#the get the best match fast use this:
(min_x, max_y, minloc, maxloc) = cv2.minMaxLoc(result)
(x,y) = minloc

#get all the matches:
result2 = np.reshape(result, result.shape[0]*result.shape[1])
sort = np.argsort(result2)
(y1, x1) = np.unravel_index(sort[0], result.shape) # best match
(y2, x2) = np.unravel_index(sort[1], result.shape) # second best match

不过,这种方法并不是最快的,因为它会把所有的匹配结果都排序,包括那些完全错误的结果。如果你对性能有要求,可以使用bottleneck库里的partsort函数,这样会更高效。

撰写回答