我有两个从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)
。
答案 0 :(得分:31)
我正在使用OpenCV的matchTemplate
函数执行此操作。有一个很好的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()