在Numpy图像中查找子图像

时间:2011-10-06 03:39:32

标签: python image numpy python-imaging-library

我有两个从PIL图像转换的Numpy数组(3维uint8)。

我想查找第一个图像是否包含第二个图像,如果是,请找出匹配所在的第一个图像内左上角像素的坐标。

有没有办法在Numpy中以足够快的方式完成,而不是使用(4!非常慢)纯Python循环?

2D示例:

a = numpy.array([
    [0, 1,  2,  3],
    [4, 5,  6,  7],
    [8, 9, 10, 11]
])
b = numpy.array([
    [2, 3],
    [6, 7]
])

如何做这样的事情?

position = a.find(b)

position将为(0, 2)

5 个答案:

答案 0 :(得分:31)

我正在使用OpenCVmatchTemplate 函数执行此操作。有一个很好的python绑定到OpenCV内部使用numpy,所以图像只是numpy数组。例如,假设您有一个100x100像素的BGR文件 testimage.bmp 。我们在位置(30,30)处获取10x10子图像并在原始位置找到它。

import cv2
import numpy as np

image = cv2.imread("testimage.bmp")
template = image[30:40,30:40,:]

result = cv2.matchTemplate(image,template,cv2.TM_CCOEFF_NORMED)
print np.unravel_index(result.argmax(),result.shape)

输出:

(30, 30)

您可以选择多种算法来将模板与原始模板匹配,cv2.TM_CCOEFF_NORMED只是其中之一。有关详细信息,请参阅文档,有些算法表示匹配为最小值,其他算法表示结果数组中的最大值。警告:OpenCV默认使用BGR通道顺序,所以要小心,例如:当您将加载了cv2.imread的图像与从PIL转换为numpy的图像进行比较时。您始终可以使用cv2.cvtColor来转换格式。

要查找超出给定阈值confidence所有匹配项,我会使用其中的某些内容从我的结果数组中提取匹配的坐标:

match_indices = np.arange(result.size)[(result>confidence).flatten()]
np.unravel_index(match_indices,result.shape)

这给出了一个长度为2的数组元组,每个数组都是一个匹配的坐标。

答案 1 :(得分:8)

这可以使用scipy correlate2d然后使用argmax来查找互相关中的峰值。

Here's更完整地解释数学和思想,以及一些例子。

如果你想保持纯粹的Numpy甚至不使用scipy,或者如果图像很大,你可能最好使用基于FFT的方法来进行交叉相关。

编辑:问题特别要求纯Numpy解决方案。但是如果你可以使用OpenCV或其他图像处理工具,使用其中一种显然更容易。下面的PiQuer给出了一个这样的例子,如果你可以使用它,我建议你这样做。

答案 2 :(得分:2)

我刚刚为N维数组编写了一个标准化互相关的独立实现。您可以从 here 获取。

使用scipy.ndimage.correlate直接计算互相关,或使用scipy.fftpack.fftn / ifftn在频域中计算互相关,具体取决于给定输入大小的最快者。

答案 3 :(得分:2)

您实际上可以使用regex将此问题简化为简单的字符串搜索,例如以下实现 - 接受两个PIL.Image个对象,并在needle内找到haystack的坐标}。这比使用逐像素搜索快约127倍。

def subimg_location(haystack, needle):
    haystack = haystack.convert('RGB')
    needle   = needle.convert('RGB')

    haystack_str = haystack.tostring()
    needle_str   = needle.tostring()

    gap_size = (haystack.size[0] - needle.size[0]) * 3
    gap_regex = '.{' + str(gap_size) + '}'

    # Split b into needle.size[0] chunks
    chunk_size = needle.size[0] * 3
    split = [needle_str[i:i+chunk_size] for i in range(0, len(needle_str), chunk_size)]

    # Build regex
    regex = re.escape(split[0])
    for i in xrange(1, len(split)):
        regex += gap_regex + re.escape(split[i])

    p = re.compile(regex)
    m = p.search(haystack_str)

    if not m:
        return None

    x, _ = m.span()

    left = x % (haystack.size[0] * 3) / 3
    top  = x / haystack.size[0] / 3

    return (left, top)

答案 4 :(得分:0)

import cv2
import numpy as np

img = cv2.imread("brows.PNG")              #main image
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

template = cv2.imread("websearch.PNG", cv2.IMREAD_GRAYSCALE)      #subimage
w,h = template.shape[::-1]

result = cv2.matchTemplate(gray_img,template, cv2.TM_CCOEFF_NORMED)
loc = np.where(result >= 0.9)

for pt in zip(*loc[::-1]):
    cv2.rectangle(img, pt,(pt[0] + w,pt[1] +h), (0,255,0),3)

cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()