模板匹配:为minMaxLoc创建掩码的有效方法?

时间:2018-05-31 00:11:38

标签: python-3.x numpy opencv

OpenCV中的模板匹配非常棒。并且您可以将掩码传递给cv2.minMaxLoc,以便您只在图像的一部分中搜索(排序)所需的模板。您还可以在matchTemplate操作中使用掩码,但这只会掩盖模板。

我想找一个模板,我想确保这个模板位于我图片的其他区域。

计算minMaxLoc的掩码似乎很重。也就是说,计算 准确的 掩码感觉很重。如果以简单的方式计算遮罩,则忽略模板的大小。

示例是有序的。我的输入图像如下所示。他们有点做作。我想找到糖果棒,但只有在钟面白圈内 完全 时才会找到。

CLOCK1 clock1

CLOCK2 clock2

模板 template

在clock1中,直板在圆形钟面内,它是一个“通过”。但是在clock2中,糖果棒只是部分在脸部内部,我希望它是一个“失败”。这是一个简单方法的代码示例。我用cv.HoughCircles找到钟面。

import numpy as np
import cv2

img = cv2.imread('clock1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

template = cv2.imread('template.png')
t_h, t_w = template.shape[0:2]  # template height and width

# find circle in gray image using Hough transform
circles = cv2.HoughCircles(gray, method = cv2.HOUGH_GRADIENT, dp = 1, 
                           minDist  = 150, param1 = 50, param2 = 70,
                           minRadius = 131, maxRadius = 200)
i = circles[0,0]
x0 = i[0]
y0 = i[1]
r  = i[2] 

# display circle on color image
cv2.circle(img,(x0, y0), r,(0,255,0),2)

# do the template match
result = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)

# finally, here is the part that gets tricky. we want to find highest
# rated match inside circle and we'd like to use minMaxLoc

# make mask by drawing circle on zero array
mask = np.zeros(result.shape, dtype = np.uint8)  # minMaxLoc will throw
                                                 # error w/o np.uint8
cv2.circle(mask, (x0, y0), r, color = 1, thickness = -1)

# call minMaxLoc
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result, mask = mask)

# draw found rectangle on img
if max_val > 0.4:  # use 0.4 as threshold for finding candy bar
    cv2.rectangle(img, max_loc, (max_loc[0]+t_w, max_loc[1]+t_h), (0,255,0), 4)

cv2.imwrite('output.jpg', img)

使用clock1输出 output from clock1

使用clock2输出 甚至找到了糖果吧 虽然它的一部分是在圈外 output from clock2

因此,为了正确制作一个掩码,我使用了一堆NumPy操作。我制作了四个单独的面具(一个用于模板边界框的每个角落),然后将它们组合在一起。我不知道OpenCV中的任何便利功能会为我做掩码。我有点紧张,所有阵列操作都很昂贵。有更好的方法吗?

h, w = result.shape[0:2]

# make arrays that hold x,y coords 
grid = np.indices((h, w))
x = grid[1]
y = grid[0]

top_left_mask  = np.hypot(x - x0, y - y0) - r < 0
top_right_mask = np.hypot(x + t_w - x0, y - y0) - r < 0
bot_left_mask  = np.hypot(x - x0, y + t_h - y0) - r < 0
bot_right_mask = np.hypot(x + t_w - x0, y + t_h - y0) - r < 0

mask = np.logical_and.reduce((top_left_mask, top_right_mask, 
                              bot_left_mask, bot_right_mask))
mask = mask.astype(np.uint8)
cv2.imwrite('mask.png', mask*255)

这就是“花式”面具的样子:
enter image description here

似乎是正确的。由于模板形状,它不能是圆形的。如果我用这个面具运行clock2.jpg我得到: output with fancy mask on clock2.jpg

有效。没有发现糖果棒。但我希望我能用更少的代码行完成......

修改: 我做了一些分析。我运行了100个循环的“简单”方式和“准确”方式和每秒计算帧数(fps):

  • 简单方法:12.7 fps
  • 准确方式:7.8 fps

因此使用NumPy制作面具需要付出一些代价。这些测试是在相对强大的工作站上完成的。它可能会在更适度的硬件上变得更加丑陋......

1 个答案:

答案 0 :(得分:2)

方法1:&#39;掩码&#39; cv2.matchTemplate

之前的图像 只是为了踢,我试图制作我传给cv2.matchTemplate的图像的自己的面具,看看我能达到什么样的表现。要清楚,这不是一个合适的掩模 - 我将所有像素设置为忽略一种颜色(黑色或白色)。这是为了解决只有TM_SQDIFF和TM_CORR_NORMED支持正确掩码的事实。

@Alexander Reynolds在评论中提出了一个非常好的观点,即如果模板图像(我们试图找到的东西)有很多黑色或大量白色,必须要小心。对于许多问题,我们将知道先验模板的外观,我们可以指定白色背景或黑色背景。

我使用的cv2.multiply似乎比numpy.multiply更快。 <?php include_once('common/header.php');?> <div class="content-wrapper"> <div class="container-fluid"> <!-- Breadcrumbs--> <ol class="breadcrumb"> <li class="breadcrumb-item"> <a href="#">Dashboard</a> </li> <li class="breadcrumb-item active">My Dashboard</li> </ol> </div> <!-- HERE WILL BE ALL PAGE DATA WILL BE SHOWN --> <?php echo $chart_content; ?> </div> <?php require_once('common/footer.php');?> 具有额外的优势,它会自动将结果剪辑到0到25​​5之间。

cv2.multiply

分析结果:

  • 黑色背景12.3 fps
  • 白色背景12.1 fps

在原始问题中,使用此方法相对于12.7 fps的性能影响非常小。然而,它的缺点是它仍然会找到仍然粘在边缘上的模板。根据问题的确切性质,这在许多应用中都是可以接受的。

方法2:使用cv2.boxFilter为minMaxLoc创建掩码

在这种技术中,我们从一个圆形掩码开始(如在OP中),但随后用cv2.boxFilter修改它。我们将import numpy as np import cv2 import time img = cv2.imread('clock1.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) template = cv2.imread('target.jpg') t_h, t_w = template.shape[0:2] # template height and width mask_background = 'WHITE' start_time = time.time() for i in range(100): # do 100 cycles for timing # find circle in gray image using Hough transform circles = cv2.HoughCircles(gray, method = cv2.HOUGH_GRADIENT, dp = 1, minDist = 150, param1 = 50, param2 = 70, minRadius = 131, maxRadius = 200) i = circles[0,0] x0 = i[0] y0 = i[1] r = i[2] # display circle on color image cv2.circle(img,(x0, y0), r,(0,255,0),2) if mask_background == 'BLACK': # black = 0, white = 255 on grayscale mask = np.zeros(img.shape, dtype = np.uint8) elif mask_background == 'WHITE': mask = 255*np.ones(img.shape, dtype = np.uint8) cv2.circle(mask, (x0, y0), r, color = (1,1,1), thickness = -1) img2 = cv2.multiply(img, mask) # element wise multiplication # values > 255 are truncated at 255 # do the template match result = cv2.matchTemplate(img2, template, cv2.TM_CCOEFF_NORMED) # call minMaxLoc min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) # draw found rectangle on img if max_val > 0.4: cv2.rectangle(img, max_loc, (max_loc[0]+t_w, max_loc[1]+t_h), (0,255,0), 4) fps = 100/(time.time()-start_time) print('fps ', fps) cv2.imwrite('output.jpg', img) 从默认的内核中心更改为左上角(0,0)

anchor

此代码提供与OP相同的掩码,但是为11.89 fps。与方法1 相比,此技术可以提供更高的准确性,但性能略高。。