如果您正在寻找exact match 的大小和图像值,我会提出一个快速且完美的答案。
这个想法是在更大的H x W 图像中计算对所需h x w 模板 的蛮力搜索。蛮力方法包括查看图像上所有可能的h x w 窗口,并检查模板内的逐像素对应关系。然而,这在计算上非常昂贵,但可以加速。
im = np.atleast_3d(im)
H, W, D = im.shape[:3]
h, w = tpl.shape[:2]
通过使用智能integral images,可以非常快速地计算h x w 窗口内从每个像素开始的总和。一个积分图像是一个求和的面积表(cumulative summed array),可以用numpy快速计算为:
sat = im.cumsum(1).cumsum(0)
而且它具有非常好的特性,例如仅用 4 次算术运算即可计算窗口内所有值的总和:
因此,通过计算模板的总和并将其与积分图像上的h x w窗口总和进行匹配,很容易找到“可能窗口”的列表,其中内部值的总和与模板中值的总和(快速近似)。
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 个)。最后一步是检查模板是否完全匹配:
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)
注意这里y和x坐标对应的是图片中的A点,也就是模板的前一行和前一列。
综合起来:
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 中运行303x384 彩色图像和50x50 模板。
一个实际的例子:
>>> 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)
为了说明结果:
左边是原始图像,右边是模板。这里是完全匹配:
>>> fig, ax = plt.subplots()
>>> imshow(im)
>>> rect = Rectangle((x, y), tpl.shape[1], tpl.shape[0], edgecolor='r', facecolor='none')
>>> ax.add_patch(rect)
最后,只是一个用于测试的possible_matches 示例:
图像中两个窗口的总和是相同的,但函数的最后一步过滤了与模板不完全匹配的窗口。