3
\$\begingroup\$

I asked this question a few days ago. The question is about producing moving/rolling windows of an array in Python as in the following example:

from skimage.util import view_as_windows
view_as_windows(np.arange(10*10).reshape(10,10), (4,4),4)

Out[14]:
array([[[[ 0,  1,  2,  3],
         [10, 11, 12, 13],
         [20, 21, 22, 23],
         [30, 31, 32, 33]],

        [[ 4,  5,  6,  7],
         [14, 15, 16, 17],
         [24, 25, 26, 27],
         [34, 35, 36, 37]]],


       [[[40, 41, 42, 43],
         [50, 51, 52, 53],
         [60, 61, 62, 63],
         [70, 71, 72, 73]],

        [[44, 45, 46, 47],
         [54, 55, 56, 57],
         [64, 65, 66, 67],
         [74, 75, 76, 77]]]]). 

However, this removes elements of the original arrays that don't fit into another window. It was suggested that I should fill the arrays with zeros in order to get a full window, so I wrote the following code for doing so and I thought it would be nice to have a review in case I missed corner cases:

import numpy as np

def extend_image(image, steps=16):
    # Required number of rows
    required_rows = np.mod(image.shape[0], steps)
    # Required number of columns
    required_cols = np.mod(image.shape[1], steps) 
    # Concatenate original array with rows filled with zeros 
    partial = np.concatenate((image, np.zeros((required_rows,image.shape[1])))) 
    # Concatenate partial array with columns filled with zeros
    extended = np.concatenate((partial, np.zeros((partial.shape[0], required_cols))), axis=1) 
    return extended

This should work as follows:

view_as_windows(extend_image(np.arange(10*10).reshape(10,10), steps=4), (4,4),4)

array([[[[  0.,   1.,   2.,   3.],
         [ 10.,  11.,  12.,  13.],
         [ 20.,  21.,  22.,  23.],
         [ 30.,  31.,  32.,  33.]],

        [[  4.,   5.,   6.,   7.],
         [ 14.,  15.,  16.,  17.],
         [ 24.,  25.,  26.,  27.],
         [ 34.,  35.,  36.,  37.]],

        [[  8.,   9.,   0.,   0.],
         [ 18.,  19.,   0.,   0.],
         [ 28.,  29.,   0.,   0.],
         [ 38.,  39.,   0.,   0.]]],


       [[[ 40.,  41.,  42.,  43.],
         [ 50.,  51.,  52.,  53.],
         [ 60.,  61.,  62.,  63.],
         [ 70.,  71.,  72.,  73.]],

        [[ 44.,  45.,  46.,  47.],
         [ 54.,  55.,  56.,  57.],
         [ 64.,  65.,  66.,  67.],
         [ 74.,  75.,  76.,  77.]],

        [[ 48.,  49.,   0.,   0.],
         [ 58.,  59.,   0.,   0.],
         [ 68.,  69.,   0.,   0.],
         [ 78.,  79.,   0.,   0.]]],


       [[[ 80.,  81.,  82.,  83.],
         [ 90.,  91.,  92.,  93.],
         [  0.,   0.,   0.,   0.],
         [  0.,   0.,   0.,   0.]],

        [[ 84.,  85.,  86.,  87.],
         [ 94.,  95.,  96.,  97.],
         [  0.,   0.,   0.,   0.],
         [  0.,   0.,   0.,   0.]],

        [[ 88.,  89.,   0.,   0.],
         [ 98.,  99.,   0.,   0.],
         [  0.,   0.,   0.,   0.],
         [  0.,   0.,   0.,   0.]]]])
\$\endgroup\$
  • \$\begingroup\$ Is this a typo? np.mod(img.shape[1], steps) I don't see "img" anywhere else, only "image" \$\endgroup\$ – janos Jan 20 '15 at 21:32
  • \$\begingroup\$ Yes, that is a typo. I will correct it in a moment. \$\endgroup\$ – Robert Smith Jan 20 '15 at 22:52
3
\$\begingroup\$

numpy has a pad function. It has many options, but this extends your array from a (10,10) to (12,12), which can be partitioned a 3x3 set of (4,4) windows.

np.pad(A,((0,2),(0,2)),mode='constant',constant_values=0)

array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,  0],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19,  0,  0],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29,  0,  0],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39,  0,  0],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49,  0,  0],
       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59,  0,  0],
       [60, 61, 62, 63, 64, 65, 66, 67, 68, 69,  0,  0],
       [70, 71, 72, 73, 74, 75, 76, 77, 78, 79,  0,  0],
       [80, 81, 82, 83, 84, 85, 86, 87, 88, 89,  0,  0],
       [90, 91, 92, 93, 94, 95, 96, 97, 98, 99,  0,  0],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0]])

Your 'extend' returns a (20,20) array. extend_image(A,steps=12).astype(int)[:12,:12] produces the same (12,12). It is faster than np.pad (by about 30%).

numpy/lib/arraypad.py contains pad and the functions it uses, it you want to learn from it. Looks like it uses np.concatenate.

In this case of just padding with 0s, here's an even faster (but not as general) a solution:

def foo(A,step=2):
    shape=(A.shape[0]+step,A.shape[1]+step)
    A1=np.zeros(shape, A.dtype)
    A1[:A.shape[0], :A.shape[1]] = A
    return A1
\$\endgroup\$
  • \$\begingroup\$ Very nice to know about np.pad. Thanks! \$\endgroup\$ – Robert Smith Jan 22 '15 at 6:31
  • \$\begingroup\$ I added a faster, though less general solution. \$\endgroup\$ – hpaulj Jan 22 '15 at 6:36
  • \$\begingroup\$ You can still pass a constant_values argument to your function foo and instead of using zeros, use A1=np.ones(shape, A.dtype)*constant_values, right? \$\endgroup\$ – Robert Smith Jan 22 '15 at 6:45
  • \$\begingroup\$ @hpaulj - gladly upvoted your answer. I have a moving window as well and the value depends on prev and next i.e val = curr + prev + next. So I have 3 moving windows but this leaves out the first and last value. So I can now pad this by 1 and calculate the boundary values. \$\endgroup\$ – gansub Aug 12 '18 at 11:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.