关于opencv:确定图像是否存在于较大图像中,如果存在,则使用Python查找

Determine if an image exists within a larger image, and if so, find it, using Python

我需要一个正在处理的Python程序,以便能够拍摄小图像,确定它是否存在于大图像中,如果存在,请报告其位置。如果没有,请报告。 (在我的情况下,大图像将是屏幕截图,小图像将是在HTML5画布中的屏幕上可能出现或可能不在屏幕上的图像。)在线查看时,我发现有关OpenCV中模板匹配的信息,具有出色的Python绑定。我根据在网上找到的非常相似的代码尝试了以下操作,同时也使用了numpy:

1
2
3
4
5
6
import cv2
import numpy as np
image = cv2.imread("screenshot.png")
template = cv2.imread("button.png")
result = cv2.matchTemplate(image,template,cv2.TM_CCOEFF_NORMED)
StartButtonLocation = np.unravel_index(result.argmax(),result.shape)

这没有做我需要做的,因为它总是在较大的图像中返回一个点;无论匹配多么糟糕,匹配最接近的点。我想要一种能够在大图像中找到与小图像精确匹配的像素,并且如果不存在,则会引发异常,或者返回False或类似的东西。而且,它必须相当快。有谁对如何执行此操作有个好主意吗?


如果您正在寻找exact match的大小和图像值,我将提出一个快速且完美的答案。

该想法是在较大的h x w图像中计算所需h x w模板的蛮力搜索。暴力破解方法将包括查看图像上所有可能的h x w窗口,并检查模板内的逐像素对应关系。但是,这在计算上非常昂贵,但是可以加速。

1
2
3
im = np.atleast_3d(im)
H, W, D = im.shape[:3]
h, w = tpl.shape[:2]

通过使用智能积分图像,可以真正快速地计算从每个像素开始的h x w窗口内的总和。积分图像是一个求和面积表(累积求和数组),可以使用numpy真正快速地计算出:

1
sat = im.cumsum(1).cumsum(0)

,它具有非常好的属性,例如仅用4次算术运算就可以计算一个窗口中所有值的总和:

From

1
2
iA, iB, iC, iD = sat[:-h, :-w], sat[:-h, w:], sat[h:, :-w], sat[h:, w:]
lookup = iD - iB - iC + iA

上面是图像中所有可能的h x w矩形的图像中所示操作的numpy矢量化(因此,非常快)。

这将减少可能的窗口数量(在我的一项测试中减少到2个)。最后一步是检查与模板的完全匹配:

1
2
3
4
posible_match = np.where(np.logical_and.reduce([lookup[..., i] == tplsum[i] for i in range(D)]))
for y, x in zip(*posible_match):
    if np.all(im[y+1:y+h+1, x+1:x+w+1] == tpl):
        return (y+1, x+1)

请注意,此处的yx坐标对应于图像中的A点,该点是模板的前一行和前一列。

放在一起:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def find_image(im, tpl):
    im = np.atleast_3d(im)
    tpl = np.atleast_3d(tpl)
    H, W, D = im.shape[:3]
    h, w = tpl.shape[:2]

    # Integral image and template sum per channel
    sat = im.cumsum(1).cumsum(0)
    tplsum = np.array([tpl[:, :, i].sum() for i in range(D)])

    # Calculate lookup table for all the possible windows
    iA, iB, iC, iD = sat[:-h, :-w], sat[:-h, w:], sat[h:, :-w], sat[h:, w:]
    lookup = iD - iB - iC + iA
    # Possible matches
    possible_match = np.where(np.logical_and.reduce([lookup[..., i] == tplsum[i] for i in range(D)]))

    # Find exact match
    for y, x in zip(*possible_match):
        if np.all(im[y+1:y+h+1, x+1:x+w+1] == tpl):
            return (y+1, x+1)

    raise Exception("Image not found")

它适用于灰度图像和彩色图像,并在7ms中运行,用于带有50x50模板的303x384彩色图像。

一个实际例子:

1
2
3
4
5
6
7
>>> from skimage import data
>>> im = gray2rgb(data.coins())
>>> tpl = im[170:220, 75:130].copy()

>>> y, x = find_image(im, tpl)
>>> y, x
(170, 75)

并说明结果:

enter

1
2
3
4
>>> fig, ax = plt.subplots()
>>> imshow(im)
>>> rect = Rectangle((x, y), tpl.shape[1], tpl.shape[0], edgecolor='r', facecolor='none')
>>> ax.add_patch(rect)

enter


由于您对OpenCV感到满意,所以我建议您从已经做的事情开始,并获得最佳匹配。找到最匹配的位置后,您可以检查它实际上是否是一个很好的匹配。

检查匹配是否良好就像提取匹配图像并将其与模板进行比较一样容易。要提取图像,您可能需要使用cv2.minMaxLoc(result)并处理输出。提取方法似乎取决于用于比较图像的方法,并在此通过示例完成。

提取图像后,您应该可以使用numpy.allclose或其他方法进行比较。