如何检测一张图片是否包含在另一张图片中?

3

我正在尝试检测一张图片是否与另一张图片完全匹配,并在此情况下将一个变量设置为True。但是,我所读的所有内容都没有找到太多结果,除了一个特定的线程给出了以下代码。

import cv2

method = cv2.TM_SQDIFF_NORMED

# Read the images from the file
small_image = cv2.imread('ran_away.png')
large_image = cv2.imread('pokemon_card.png')

result = cv2.matchTemplate(small_image, large_image, method)

# We want the minimum squared difference
mn,_,mnLoc,_ = cv2.minMaxLoc(result)

# Draw the rectangle:
# Extract the coordinates of our best match
MPx,MPy = mnLoc

# Step 2: Get the size of the template. This is the same size as the match.
trows,tcols = small_image.shape[:2]

# Step 3: Draw the rectangle on large_image
cv2.rectangle(large_image, (MPx,MPy),(MPx+tcols,MPy+trows),(0,0,255),2)

# Display the original image with the rectangle around the match.
cv2.imshow('output',large_image)

# The image is only displayed if we call this
cv2.waitKey(0)

然而这会打开一个输出,执行我不想要的操作。我只想检测图像是否在图像中,并且如果是,则将其打印到控制台。在我的特定情况下,我正在尝试检测此图像 Ran_away.png 是否在此图像中 Pokemon_card.png 如果是,则在控制台上打印出口袋妖怪已经逃跑了。
2个回答

4

你的代码展示了基本的模板匹配。请阅读有关此主题的一些教程以及cv2.matchTemplate的文档,特别是要理解不同的template match modes

我只能想到以下解决方案来处理你的任务:不要使用TM_SQDIFF_NORMED,而应使用TM_SQDIFF,这样你将在result中得到绝对值而不是相对值:

  • 对于TM_SQDIFF_NORMED,即使匹配不正确,最佳匹配也总是接近0.0的某个值。
  • 对于TM_SQDIFF,接近0.0的一些值表示实际上的正确匹配。

因此,现在,简单地编写一个方法,进行模板匹配,并检测result的最小值是否低于某个接近0.0的阈值,比如说10e-6。如果是,打印出任何你想要的内容,如果不是,请执行其他操作:

import cv2


def is_template_in_image(img, templ):

    # Template matching using TM_SQDIFF: Perfect match => minimum value around 0.0
    result = cv2.matchTemplate(img, templ, cv2.TM_SQDIFF)

    # Get value of best match, i.e. the minimum value
    min_val = cv2.minMaxLoc(result)[0]

    # Set up threshold for a "sufficient" match
    thr = 10e-6

    return min_val <= thr


# Read template
template = cv2.imread('ran_away.png')

# Collect image file names
images = ['pokemon_card.png', 'some_other_image.png']

for image in images:
    if is_template_in_image(cv2.imread(image), template):
        print('{}: {}'.format(image, 'Pokemon has ran away.'))
    else:
        print('{}: {}'.format(image, 'Nothing to see here.'))

输出:
pokemon_card.png: Pokemon has ran away.
some_other_image.png: Nothing to see here.

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.19041-SP0
Python:        3.9.1
PyCharm:       2021.1.1
OpenCV:        4.5.2
----------------------------------------

3
我找到了一个解决方案,使用相对较新的NumPy方法sliding_window_view

使用给定的窗口形状创建一个数组的滑动窗口视图。

也称为滚动或移动窗口,该窗口在数组的所有维度上滑动,并在所有窗口位置提取数组子集。

1.20.0版中新增。

注意:由于兼容性问题的考虑,我已经在新的虚拟环境中安装了最新的NumPy版本。

用于检查sliding_window_view如何工作的简单测试:

import numpy as np
from numpy.lib.stride_tricks import sliding_window_view

t = np.array([[ [0,0,0], [1,1,1]],
              [ [2,2,2], [3,3,3]]])

x = np.array([[ [0,0,0],  [1,1,1],  [2,2,2],  [3,3,3]],
              [[10,10,10], [11,11,11], [12,12,12], [13,13,13]],
              [[20,20,20], [21,21,21], [22,22,22], [23,23,23]]])

x[1:3, 1:3, :] = t  # Copy t to x - it looks like there is a problem along edges

v = sliding_window_view(x, (2,2,3))

print(v-t)

结果开始:

[[[[[[ 0  0  0]
     [ 0  0  0]]

这意味着从v的所有“窗口”中减去了t,与预期相符。


添加以下命令以测试np.all

print(np.where((v == t).all(axis=(3, 4, 5))))

输出结果为:

(array([1], dtype=int64), array([1], dtype=int64), array([0], dtype=int64))

all(axis=(3, 4, 5))表示沿着第3、4和5个维度的元素全都是True时,返回True
在上面的示例中,我们在索引[1, 1]处找到了一个匹配项。


以下是使用NumPy检测完全匹配的解决方案:

import cv2
import numpy as np
from numpy.lib.stride_tricks import sliding_window_view

# Read the images from the file
small_image = cv2.imread('ran_away.png')
#small_image = cv2.imread('icon.png');
large_image = cv2.imread('pokemon_card.png')

v = sliding_window_view(large_image, small_image.shape)

match_idx = np.where((v == small_image).all(axis=(3, 4, 5)))

if len(match_idx[0]) > 0:
    row = match_idx[0][0]
    col = match_idx[1][0]

    cv2.rectangle(large_image, (col, row), (col+small_image.shape[1], row+small_image.shape[1]), (0, 255, 0), 2)

    cv2.imshow('large_image', large_image)
    cv2.waitKey()
    cv2.destroyAllWindows()

结果:
在这里输入图片描述 (这是一张图片链接,无法直接翻译成文字)

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