Numpy summation with sliding window is really slow

后端 未结 1 908
甜味超标
甜味超标 2021-01-28 20:15

Code:

shape = np.array([6, 6])
grid = np.array([x.ravel() for x in np.meshgrid(*[np.arange(x) for i, x in enumerate(shape)], indexing=\'ij\')]).T
slices = [tuple         


        
1条回答
  •  广开言路
    2021-01-28 20:42

    Let's simplify the problem at bit - reduce the dimensions, and drop the final size 3 dimension:

    In [265]: shape = np.array([4,4])
    In [266]: grid = np.array([x.ravel() for x in np.meshgrid(*[np.arange(x) for i
         ...: , x in enumerate(shape)], indexing='ij')]).T
         ...: grid = [tuple(slice(box[i], box[i] + 3) for i in range(len(box))) fo
         ...: r box in grid]
         ...: 
         ...: 
    In [267]: len(grid)
    Out[267]: 16
    In [268]: score = np.arange(36).reshape(6,6)
    In [269]: X = np.array([score[x] for x in grid]).reshape(4,4,3,3)
    In [270]: X
    Out[270]: 
    array([[[[ 0,  1,  2],
             [ 6,  7,  8],
             [12, 13, 14]],
    
            [[ 1,  2,  3],
             [ 7,  8,  9],
             [13, 14, 15]],
    
            [[ 2,  3,  4],
             [ 8,  9, 10],
             [14, 15, 16]],
    
            ....
            [[21, 22, 23],
             [27, 28, 29],
             [33, 34, 35]]]])
    

    This is a moving window - one (3,3) array, shift over 1,..., shift down 1, etc

    With as_strided is is possible to construct a view of the array, that consists of all these windows, but without actually copying values. Having worked with as_strided before I was able construct the equivalent strides as:

    In [271]: score.shape
    Out[271]: (6, 6)
    In [272]: score.strides
    Out[272]: (48, 8)
    In [273]: ast = np.lib.stride_tricks.as_strided
    In [274]: x=ast(score, shape=(4,4,3,3), strides=(48,8,48,8))
    In [275]: np.allclose(X,x)
    Out[275]: True
    

    This could be extended to your (28,28,3) dimensions, and turned into the summation.

    Generating such moving windows has been covered in previous SO questions. And it's also implemented in one of the image processing packages.


    Adaptation for a 3 channel image,

    In [45]: arr.shape
    Out[45]: (6, 6, 3)
    In [46]: arr.strides
    Out[46]: (144, 24, 8)
    In [47]: arr[:3,:3,0]
    Out[47]: 
    array([[ 0.,  1.,  2.],
           [ 6.,  7.,  8.],
           [12., 13., 14.]])
    
    In [48]: x = ast(arr, shape=(4,4,3,3,3), strides=(144,24,144,24,8))
    In [49]: x[0,0,:,:,0]
    Out[49]: 
    array([[ 0.,  1.,  2.],
           [ 6.,  7.,  8.],
           [12., 13., 14.]])
    

    Since we are moving the window by one element at a time, the strides for x are easily derived form the source strides.

    For 4x4 windows, just change the shape

    x = ast(arr, shape=(3,3,4,4,3), strides=(144,24,144,24,8))
    

    In Efficiently Using Multiple Numpy Slices for Random Image Cropping

    @Divikar suggests using skimage

    With the default step=1, the result is compatible:

    In [55]: from skimage.util.shape import view_as_windows
    In [63]: y = view_as_windows(arr,(4,4,3))
    In [64]: y.shape
    Out[64]: (3, 3, 1, 4, 4, 3)
    In [69]: np.allclose(x,y[:,:,0])
    Out[69]: True
    

    0 讨论(0)
提交回复
热议问题