as_stridednumpy のブロードキャスト機能と組み合わせることで、驚くべきことができます。関数の 2 つのバージョンを次に示します。
import numpy as np
from numpy.lib.stride_tricks import as_strided
def sumsqdiff(input_image, template, valid_mask=None):
    if valid_mask is None:
        valid_mask = np.ones_like(template)
    total_weight = valid_mask.sum()
    window_size = template.shape
    ssd = np.empty((input_image.shape[0] - window_size[0] + 1,
                    input_image.shape[1] - window_size[1] + 1))
    for i in xrange(ssd.shape[0]):  
        for j in xrange(ssd.shape[1]):  
            sample = input_image[i:i + window_size[0], j:j + window_size[1]]  
            dist = (template - sample) ** 2  
            ssd[i, j] = (dist * valid_mask).sum()
    return ssd
def sumsqdiff2(input_image, template, valid_mask=None):
    if valid_mask is None:
        valid_mask = np.ones_like(template)
    total_weight = valid_mask.sum()
    window_size = template.shape
    # Create a 4-D array y, such that y[i,j,:,:] is the 2-D window
    #     input_image[i:i+window_size[0], j:j+window_size[1]]
    y = as_strided(input_image,
                    shape=(input_image.shape[0] - window_size[0] + 1,
                           input_image.shape[1] - window_size[1] + 1,) +
                          window_size,
                    strides=input_image.strides * 2)
    # Compute the sum of squared differences using broadcasting.
    ssd = ((y - template) ** 2 * valid_mask).sum(axis=-1).sum(axis=-1)
    return ssd
それらを比較する ipython セッションを次に示します。
デモに使用するテンプレート:
In [72]: template
Out[72]: 
array([[-1,  1, -1],
       [ 1,  2,  1],
       [-1,  1, -1]])
結果を調べるための小さな入力:
In [73]: x
Out[73]: 
array([[  0.,   1.,   2.,   3.,   4.,   5.,   6.],
       [  7.,   8.,   9.,  10.,  11.,  12.,  13.],
       [ 14.,  15.,  16.,  17.,  18.,  19.,  20.],
       [ 21.,  22.,  23.,  24.,  25.,  26.,  27.],
       [ 28.,  29.,  30.,  31.,  32.,  33.,  34.]])
2 つの関数を に適用してx、同じ結果が得られることを確認します。
In [74]: sumsqdiff(x, template)
Out[74]: 
array([[  856.,  1005.,  1172.,  1357.,  1560.],
       [ 2277.,  2552.,  2845.,  3156.,  3485.],
       [ 4580.,  4981.,  5400.,  5837.,  6292.]])
In [75]: sumsqdiff2(x, template)
Out[75]: 
array([[  856.,  1005.,  1172.,  1357.,  1560.],
       [ 2277.,  2552.,  2845.,  3156.,  3485.],
       [ 4580.,  4981.,  5400.,  5837.,  6292.]])
次に、より大きな入力「画像」を作成します。
In [76]: z = np.random.randn(500, 500)
パフォーマンスを確認します。
In [77]: %timeit sumsqdiff(z, template)
1 loops, best of 3: 3.55 s per loop
In [78]: %timeit sumsqdiff2(z, template)
10 loops, best of 3: 33 ms per loop
汚すぎる格好はやめて。:)
2 つの欠点:
- の計算によりsumsqdiff2、3x3 テンプレートの場合、 の 9 倍のサイズになる一時的な配列が生成されinput_imageます。(一般的にはtemplate.sizeの倍の大きさになりinput_imageます。)
- これらの「ストライド トリック」は、コードを Cythonize する場合には役に立ちません。Cython に変換すると、numpy でベクトル化するときに削除したループを元に戻すことになることがよくあります。