3

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.

gaussit
  • 85
  • 1
  • 9
  • Try a mix of reshape, transpose (or swap) and reshape, e.g. `arr1.reshape(2,2,2,2,2).transpose(0,1,3,2,4).reshape(2,4,4) ` – hpaulj Jan 23 '20 at 20:59
  • What are you doing between the two `reshape`s? Avoiding those may be a simple as using [`np.ndindex`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndindex.html#numpy.ndindex) as a for-loop iterator instead of looping over that first dimension. – Daniel F Jan 24 '20 at 08:24
  • @hpaulj: I tried several variants but just can't find the right one after a lot of trying – gaussit Jan 27 '20 at 11:47
  • @DanielF: I am using a `skimage` to partition the images into non-overlaying blocks. – gaussit Jan 27 '20 at 11:49

2 Answers2

2

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!

Juan
  • 5,433
  • 21
  • 23
  • Thank you for your input! I can indeed reproduce it with a 2D array. With a 3d array however (I need 3d arrays unfortunately), I can't find the right setting when reapplying `view_as_blocks()` the array is always mixed up... – gaussit Jan 27 '20 at 11:51
2
In [30]: testM=np.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]]]]) 
    ...:                                                                                         
In [31]: testM.shape                                                                             
Out[31]: (1, 2, 4, 4)
In [32]: from skimage.util import view_as_blocks                                                 
In [33]: testB = view_as_blocks(testM, block_shape=(1,2,2,2))                                    
In [34]: testB.shape                                                                             
Out[34]: (1, 1, 2, 2, 1, 2, 2, 2)

Is this really the shape you want? Anyways, applying the reshape, combines the initial 4 dimensions:

In [36]: testB.reshape(-1,*(1,2,2,2)).shape                                                      
Out[36]: (4, 1, 2, 2, 2)

When I suggested

arr1.reshape(2,2,2,2,2).transpose(0,1,3,2,4).reshape(2,4,4)

I was thinking that a source with shape (2,4,4) was divided into (2,2) windows. That is, each (4,4) subarray became a (2,2,2,2) array, a (2,2) array of (2,2) blocks. With many size 2 and 4 dimensions its hard to keep track of which is which.

Here's the transformation that I was aiming at:

In [62]: testM1 = testM[0,0,:,:]                                                                 
In [63]: testM1                                                                                  
Out[63]: 
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 ]])
In [64]: testB1 = view_as_blocks(testM1, block_shape=(2,2))                                      
In [65]: testB1.shape                                                                            
Out[65]: (2, 2, 2, 2)
In [66]: testB1.transpose(0,2,1,3).reshape(4,4)                                                  
Out[66]: 
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 ]])

Take one (4,4) block and divide into (2,2,2,2) windows, and then back.

I suspect the same sort of transformation applies to your more complicated dimensions, but I don't have the time (or interest) to work out the details.

hpaulj
  • 221,503
  • 14
  • 230
  • 353