Benchmarked some methods I've found:
(1) Sub-index assignment
(2) Stride trick
(3) Padding
(4) Concatenation
def dilation1(X, d):
Xd_shape = np.multiply(X.shape, d)
Xd = np.zeros(Xd_shape, dtype=X.dtype)
Xd[0:Xd_shape[0]:d[0], 0:Xd_shape[1]:d[1]] = X
return Xd
def dilation2(X, d):
Xd_shape = np.multiply(X.shape, d)
Xd = np.zeros(Xd_shape, dtype=X.dtype)
Xdn = np.lib.stride_tricks.as_strided(Xd, X.shape, np.multiply(Xd.strides, d))
Xdn[:] = X
return Xd
def dilation3(X, d):
Xd = X.reshape((X.shape[0],1,X.shape[1],1))
Xd = np.pad(Xd, ((0,0),(0,d[0]-1),(0,0),(0,d[1]-1)))
return Xd.reshape(np.multiply(X.shape,d))
def dilation4(X, d):
Xd = X.reshape((X.shape[0],1,X.shape[1],1))
mcol = np.zeros(Xd.shape[:3]+(d[1]-1,))
Xd = np.concatenate((Xd, mcol), 3)
mrow = np.zeros((Xd.shape[0],d[0]-1)+Xd.shape[2:])
Xd = np.concatenate((Xd, mrow), 1)
return Xd.reshape(np.multiply(X.shape,d))
#Example
b = np.arange(3*3).reshape((3,3))
b
# array([[0, 1, 2],
# [3, 4, 5],
# [6, 7, 8]])
dilation1(b, (3,2))
# array([[0, 0, 1, 0, 2, 0],
# [0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0],
# [3, 0, 4, 0, 5, 0],
# [0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0],
# [6, 0, 7, 0, 8, 0],
# [0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0]])
#Benchmark
a = np.random.randn(1024,1024)
%timeit -r 20 dilation1(a, (5,5))
#19.7 ms ± 890 µs per loop (mean ± std. dev. of 20 runs, 100 loops each)
%timeit -r 20 dilation2(a, (5,5))
#18.8 ms ± 526 µs per loop (mean ± std. dev. of 20 runs, 100 loops each)
%timeit -r 20 dilation3(a, (5,5))
#101 ms ± 2.32 ms per loop (mean ± std. dev. of 20 runs, 10 loops each)
%timeit -r 20 dilation4(a, (5,5))
#101 ms ± 1.59 ms per loop (mean ± std. dev. of 20 runs, 10 loops each)
So simply use the sub-index assignment.