Bạn có thể làm một số điều tuyệt vời với các as_strided
chức năng kết hợp với phát sóng của numpy. Dưới đây là hai phiên bản chức năng của bạn:
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
Đây là phiên ipython để so sánh chúng.
Mẫu mà tôi sẽ sử dụng cho bản demo:
In [72]: template
Out[72]:
array([[-1, 1, -1],
[ 1, 2, 1],
[-1, 1, -1]])
Một đầu vào nhỏ vì vậy chúng tôi có thể kiểm tra kết quả:
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.]])
Áp dụng hai chức năng để x
và kiểm tra xem chúng tôi nhận cùng một kết quả:
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.]])
Bây giờ tạo một đầu vào lớn hơn "hình ảnh":
In [76]: z = np.random.randn(500, 500)
và kiểm tra việc thực hiện:
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
Không quá tồi tàn. :)
Hai nhược điểm:
- Việc tính toán trong
sumsqdiff2
sẽ tạo ra một mảng tạm rằng, đối với một mẫu 3x3, sẽ là 9 lần so với kích thước của input_image
. (Nói chung nó sẽ là template.size
lần kích thước của input_image
.)
- Những "thủ đoạn sải chân" này sẽ không giúp bạn khi bạn Cythonize mã. Khi chuyển đổi sang Cython, bạn thường kết thúc việc đưa trở lại các vòng lặp mà bạn đã loại bỏ khi vector hóa với vón cục.
Bạn cũng có thể xem PyPy, sử dụng JIT – jh314