Python中的OpenCV MatchTemplate函数有多个匹配项

7

我正在尝试在一张大图里找到一张小图,并使用了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)

这个方法可以很好地获取左上角的坐标,但是它只能得到一个点。如果在大图片中有多个匹配项,我该如何获取所有的匹配项?


请查看以下链接:https://dev59.com/ULroa4cB1Zd3GeqPuvYX#61780200 和 https://stackoverflow.com/questions/67368951/opencv-matchtemplate-and-np-where-keep-only-unique-values/67374288#67374288。 - fmw42
3个回答

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函数来代替。


谢谢!我有两个问题
  1. 为什么使用了CV_TM_SQDIFF方法?
  2. 请解释一下这句话 string (min_x,max_y,minloc,maxloc) = cv2.minMaxLoc(result)
- user2046488
我使用了CV_TM_SQDIFF,因为我修改了自己的代码;minMax行是错误留下来的;但那是获取最佳匹配的快速过程,我将编辑答案。 - b_m
能否请您更详细地解释一下这段代码?为什么要用 result.shape[0]*result.shape[1] - Flash Thunder
这个答案存在问题,就像其他答案所述,它返回了太多不必要的匹配。 - gameon67

6

@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 。这两种技术在此答案中都有演示。


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)

解释:

我们进行非极大值抑制是因为在原始图像中每个匹配周围会有很多“好”的结果(例如将模板向右移动1个像素通常会得到一个好的结果,因此您会在每个对象实例周围获得一堆重叠的边界框)。非极大值抑制将过滤每个图像区域中的一个良好匹配。

请注意,这种非极大值抑制不直接使用分数,因此在使用matchTemplate时,上面提到的this answer可能更适合。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接