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\$
2
  • \$\begingroup\$ Is this a typo? np.mod(img.shape[1], steps) I don't see "img" anywhere else, only "image" \$\endgroup\$
    – janos
    Jan 20, 2015 at 21:32
  • \$\begingroup\$ Yes, that is a typo. I will correct it in a moment. \$\endgroup\$
    – r_31415
    Jan 20, 2015 at 22:52

1 Answer 1

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\$
4
  • \$\begingroup\$ Very nice to know about np.pad. Thanks! \$\endgroup\$
    – r_31415
    Jan 22, 2015 at 6:31
  • \$\begingroup\$ I added a faster, though less general solution. \$\endgroup\$
    – hpaulj
    Jan 22, 2015 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\$
    – r_31415
    Jan 22, 2015 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, 2018 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.