Search code examples
pythonnumpymultidimensional-arrayreshapescikit-image

Reverse skimage view_as_blocks() with numpy.reshape()


I want to divide a 4x4 image with 2 channes into multiple non-overlapping squares.

After that, I want to rebuilt the image.

from skimage.util import view_as_blocks

# create testM array 
array([[[[0.53258505, 0.31525832, 0.21378392, 0.5019507 ],
         [0.31612498, 0.24320562, 0.93560226, 0.08232264],
         [0.89784454, 0.12741783, 0.88049819, 0.29542855],
         [0.11336386, 0.71023215, 0.45679456, 0.2318959 ]],

        [[0.61038755, 0.74389586, 0.85199794, 0.46680889],
         [0.01701045, 0.93953861, 0.03183684, 0.00740579],
         [0.58878569, 0.71348253, 0.33221104, 0.12276253],
         [0.04026615, 0.53837528, 0.06759152, 0.27477069]]]])

# use view_as_blocks() to get "grid" image
testB = view_as_blocks(testM, block_shape=(1,2,2,2)).reshape(-1,*(1,2,2,2))

Now I have multiple blocks of this array of the size 2x2:

array([[[[[0.53258505, 0.31525832],
          [0.31612498, 0.24320562]],

         ...

         [[0.33221104, 0.12276253],
          [0.06759152, 0.27477069]]]]])


However, I am not able to reshape it back to its prior shape:

testB.reshape(1,2,4,4)

Leads to this. Every "block" is just appended one value after the other but not treated as a block.

array([[[[0.53258505, 0.31525832, 0.31612498, 0.24320562],
         [0.61038755, 0.74389586, 0.01701045, 0.93953861],
         [0.21378392, 0.5019507 , 0.93560226, 0.08232264],
         [0.85199794, 0.46680889, 0.03183684, 0.00740579]],

        [[0.89784454, 0.12741783, 0.11336386, 0.71023215],
         [0.58878569, 0.71348253, 0.04026615, 0.53837528],
         [0.88049819, 0.29542855, 0.45679456, 0.2318959 ],
         [0.33221104, 0.12276253, 0.06759152, 0.27477069]]]])

I have tried multiple .swapaxes() prior to using reshape() but just can't get it to work.


Solution

  • What's happening is that your .reshape((-1, 1, 2, 2, 2)), that is, your linearising of the blocks, is causing a copy:

    import numpy as np
    from skimage.util import view_as_blocks
    
    arr = np.arange(24).astype(np.uint8).reshape((4, 6))
    blocked = view_as_blocks(arr, (2, 3))
    blocked_reshaped = blocked.reshape((-1, 2, 3))
    print(arr.shape)
    print(arr.strides)
    print(blocked.shape)
    print(blocked.strides)
    print(blocked_reshaped.shape)
    print(blocked_reshaped.strides)
    print(np.may_share_memory(blocked, blocked_reshaped))
    

    Result:

    (4, 6)
    (6, 1)
    (2, 2, 2, 3)
    (12, 3, 6, 1)
    (4, 2, 3)
    (6, 3, 1)
    False
    

    The strides are a clue that the elements of the array are no longer in the same linear order in the underlying memory, so reshaping causes the weird transposition that you've observed:

    block_reshaped_orig = blocked_reshaped.reshape((4, 6))
    print(arr)
    print(block_reshaped_orig)
    

    Result:

    [[ 0  1  2  3  4  5]
     [ 6  7  8  9 10 11]
     [12 13 14 15 16 17]
     [18 19 20 21 22 23]]
    [[ 0  1  2  6  7  8]
     [ 3  4  5  9 10 11]
     [12 13 14 18 19 20]
     [15 16 17 21 22 23]]
    

    I see two options:

    • if you can avoid the reshaping and copying, then your reshape call at the end will work just fine.
    • if you need that reshape for some of the other processing you're doing, then you can, somewhat ironically, use another view_as_blocks call and reshape to get back the original order:
    print(
        view_as_blocks(blocked_reshaped_orig, (2, 3)).reshape((4, -1))
    )
    

    Result:

    [[ 0  1  2  3  4  5]
     [ 6  7  8  9 10 11]
     [12 13 14 15 16 17]
     [18 19 20 21 22 23]]
    

    I hope this helps!