【问题标题】:How to detect if a 2D array is inside another 2D array?如何检测二维数组是否在另一个二维数组中?
【发布时间】:2013-03-29 20:35:54
【问题描述】:

所以在堆栈溢出成员的帮助下,我有以下代码:

data = "needle's (which is a png image) base64 code goes here"
decoded = data.decode('base64')
f = cStringIO.StringIO(decoded)
image = Image.open(f)
needle = image.load()

while True:
    screenshot = ImageGrab.grab()
    haystack = screenshot.load()
    if detectImage(haystack, needle):
        break
    else:
        time.sleep(5)

我编写了以下代码来检查针是否在大海捞针:

def detectImage(haystack, needle):
    counter = 0
    for hayrow in haystack:
        for haypix in hayrow:
            for needlerow in needle:
                for needlepix in needlerow:
                    if haypix == needlepix:
                        counter += 1

    if counter == 980: #the needle has 980 pixels
        return True
    else:
        return False

问题是我在第 3 行收到此错误:'PixelAccess' object is not iterable

有人建议我将 needle 和 haystack 复制到 numpy/scipy 数组中会更容易。然后我可以使用一个函数来检查 2D 数组 needle 是否在 2D 数组 haystack 内。

我需要帮助:

1) 将这些数组转换为 numpy 数组。

2) 检查二维数组针是否在二维数组干草堆内的函数。我的功能不起作用。

这些是图片:
针:

干草堆:

【问题讨论】:

  • 也许这一行:for x1 in haystack[0]: 应该说是for x1 in y1:。和for x2 in needle[0]: 应该是for x2 in y2:?否则,您将忽略 y 变量(但也许这是故意的)。
  • 哦,对了。你是对的。
  • 记住for ___ in 2dobject 会给你行。更好的命名约定可能是for hayrow in haystack ... for haypix in hayrow
  • haypix 中的 pix 代表什么?
  • 好的,我已经改了。谢谢你。

标签: python python-2.7 numpy scipy detection


【解决方案1】:

要将图像转换为 numpy 数组,您应该可以简单地执行以下操作:

import numpy as np
from PIL import Image

needle = Image.open('needle.png')
haystack = Image.open('haystack.jpg')

needle = np.asarray(needle)
haystack = np.asarray(haystack)

为了让您开始寻找针,请注意,这将为您提供与角匹配的所有位置的列表:

haystack = np.array([[1,2,3],[3,2,1],[2,1,3]])
needle = np.array([[2,1],[1,3]])

np.where(haystack == needle[0,0])
#(array([0, 1, 2]),   row-values
# array([1, 1, 0]))   col-values

然后,你可以查看所有的角匹配,看看那里的 subhaystack 是否匹配:

h,w = needle.shape
rows, cols = np.where(haystack == needle[0,0])
for row, col in zip(rows, cols):
    if np.all(haystack[row:row+h, col:col+w] == needle):
        print "found it at row = %i, col = %i"%(row,col)
        break
else:
    print "no needle in haystack"

下面是一个更强大的版本,可以找到最佳匹配,如果匹配好于某个百分比,则认为找到的针头。如果找到则返回角坐标,如果没有则返回None

def find_needle(needle, haystack, tolerance=.80):
    """ input:  PIL.Image objects
        output: coordinat of found needle, else None """

    # convert to grayscale ("L"uminosity) for simplicity.
    needle = np.asarray(needle.convert('L'))   
    haystack = np.asarray(haystack.convert('L'))

    h,w = needle.shape
    H,W = haystack.shape
    L = haystack.max()

    best = (None, None, 1)
    rows, cols = np.where((haystack - needle[0,0])/L < tolerance)
    for row, col in zip(rows, cols):
        if row+h > H or col+w > W: continue # out of range
        diff = np.mean(haystack[row:row+h, col:col+w] - needle)/L
        if diff < best[-1]:
            best = (diff, row, col)

    return best if best[-1] < tolerance else None

【讨论】:

  • 针:s24.postimg.org/f8fmxkqg1/needle.png 干草堆:s1.postimg.org/th80b1a26/haystack.jpg 是的,针完全在干草堆里。
  • 这仍然没有在你的大海捞针图像中找到针图像,但我相信这是因为一个是 jpg 并且可能 jpg 的损失会扭曲它。
  • 哇哦。我刚要放弃。谢谢!将测试并提供结果。
  • @askewchan 尝试了卷积方式,不起作用:干草堆图像的白色区域(所以[255, 255, 255])给出了卷积的最大值。
  • @Jaime:总的来说,卷积方法效果很好,至少在我的经验中是这样,但你需要标准化。顺便说一句,卷积是 OpenCV 的 matchTemplate 工作的方式之一。所有这些都只是简单的方程,在 numpy 中只有几行:docs.opencv.org/modules/imgproc/doc/object_detection.html
【解决方案2】:

我终于设法使互相关搜索工作的一个 numpy-only 实现...互相关是使用 cross-correlation theorem 和 FFT 计算的。

from __future__ import division
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

def cross_corr(a, b):
    a_rows, a_cols = a.shape[:2]
    b_rows, b_cols = b.shape[:2]
    rows, cols = max(a_rows, b_rows), max(a_cols, b_cols)
    a_f = np.fft.fft2(a, s=(rows, cols), axes=(0, 1))
    b_f = np.fft.fft2(b, s=(rows, cols), axes=(0, 1))
    corr_ab = np.fft.fft2(a_f.conj()*b_f, axes=(0,1))
    return np.rint(corr_ab / rows / cols)

def find_needle(haystack, needle, n=10):
    # convert to float and subtract 128 for better matching
    haystack = haystack.astype(np.float) - 128
    needle = needle.astype(np.float) - 128
    target = np.sum(np.sum(needle*needle, axis=0), axis=0)
    corr_hn = cross_corr(haystack, needle)
    delta = np.sum(np.abs(corr_hn - target), axis=-1)
    return np.unravel_index(np.argsort(delta, axis=None)[:n],
                            dims=haystack.shape[:2])

haystack = np.array(Image.open('haystack.jpg'))
needle = np.array(Image.open('needle.png'))[..., :3]
plt.imshow(haystack, interpolation='nearest')
dy, dx = needle.shape[:2]
candidates = find_needle(haystack, needle, 1)
for y, x in zip(*candidates):
    plt.plot([x, x+dx, x+dx, x, x], [y, y, y+dy,y+dy, y], 'g-', lw=2)
plt.show()

所以最高分是真针:

>>> print candidates
(array([553], dtype=int64), array([821], dtype=int64))

【讨论】:

  • +1:感谢您发布此消息!很高兴在 SO 上发布 numpy 版本的子图像匹配。我很想知道为什么它现在可以工作,尤其是关于白色像素与匹配像素等方面。
  • @tom10 很多都是正确应用我的互相关定理,并在使用np.sum(needle**2) 计算目标值时处理 int 溢出。但要让它真正起作用,最大的技巧是不对图像进行交叉关联,而是对图像进行交叉关联 - 128。仍然有大量图像会给出相同的交叉关联结果,但显然不是那么多。如果没有这个技巧,真正的针就像是第 100 位最佳匹配......坦率地说,我不认为如上所述的算法非常强大。
  • @tom10 如果您在 2*image - 1 中搜索,它确实可以完美处理二进制图像。
【解决方案3】:

可以在opencv中使用matchTemplate来检测位置:

import cv2
import numpy as np
import pylab as pl

needle = cv2.imread("needle.png")
haystack = cv2.imread("haystack.jpg")

diff = cv2.matchTemplate(haystack, needle, cv2.TM_CCORR_NORMED)
x, y = np.unravel_index(np.argmax(diff), diff.shape)

pl.figure(figsize=(12, 8))
im = pl.imshow(haystack[:,:, ::-1])
ax = pl.gca()
ax.add_artist(pl.Rectangle((y, x), needle.shape[1], needle.shape[0],  transform=ax.transData, alpha=0.6))

这是输出:

【讨论】:

  • 目前正在下载 OpenCV。同时,我怎么知道它是否找到了匹配项?通过查看差异值,对吗?可能有一些门槛,对吗?
  • 完美匹配的 TM_CCOEFF_NORMED 值为 1。您可以使用一些阈值,例如 0.9。
  • 收到此错误:NameError: name 'Rectangle' is not defined :(
  • 把它改成pl.Rectangle
  • cv2.TM_CCORR_NORMED 出于某种原因始终为 3。即使我改变图像。
猜你喜欢
  • 1970-01-01
  • 2021-06-04
  • 1970-01-01
  • 2021-12-31
  • 2012-04-10
  • 1970-01-01
  • 2022-12-07
  • 2018-04-22
  • 2014-09-03
相关资源
最近更新 更多