1

Supose i have,

x = [[1 ,2],
     [3 ,4]]

and id like to have,

y = [[1 ,1 ,2 ,2],
     [1 ,1 ,2 ,2],
     [3 ,3 ,4 ,4],
     [3 ,3 ,4 ,4]]

I already did it using repeat but my question is, if theres a way in pure numpy which is faster and vectorized.

My second question is, how could you efficiently downsaple from y back to x?

thanks and have a nice day :)

user15770670
  • 105
  • 6

3 Answers3

2

Using np.kron() the kronecker product:

x = np.array([[1, 2], [3, 4]])
y = np.kron(x,np.ones((2,2))   # np.ones((n,n)) where n indicate the number of repetition. 
obchardon
  • 10,614
  • 1
  • 17
  • 33
1

You can try this one: Uses simple broadcasted multiplication followed by transpose and reshape operations.

x = np.array([[1, 2], [3, 4]])
m, n = x.shape
k = 2 # upsampling factor

tmp = x.reshape(-1, 1, 1) * np.ones((1, k, k))
y = tmp.reshape(m, n, k, k).transpose(0, 2, 1, 3).reshape(m*k, n*k)
print(y)
array([[1., 1., 2., 2.],
       [1., 1., 2., 2.],
       [3., 3., 4., 4.],
       [3., 3., 4., 4.]])

To get x back from y, simply reverse the reshape and transpose operations and do a maxpool kind of operation along the last two axes to get back the original m x n shaped array:

x = y.reshape(m, n, k, k).transpose(0, 2, 1, 3).max(axis = (2, 3))

This method works for any array of shape m x n and upsampling factor k.

swag2198
  • 2,546
  • 1
  • 7
  • 18
1

If you don't care about the output shape, you can do this operation just by changing the metadata

x = np.array([[1, 2], [3, 4]])
np.lib.stride_tricks.as_strided(x, (4,2,2), (8,0,0))

Output:

array([[[1, 1],
        [1, 1]],

       [[2, 2],
        [2, 2]],

       [[3, 3],
        [3, 3]],

       [[4, 4],
        [4, 4]]])

further, np.block will give the desired output shape (by copying)

x = np.lib.stride_tricks.as_strided(x, (4,2,2), (8,0,0))
np.block([[x[0], x[1]], 
          [x[2], x[3]]])

Output:

array([[1, 1, 2, 2],
       [1, 1, 2, 2],
       [3, 3, 4, 4],
       [3, 3, 4, 4]])

The np.block can also be simulated by using transpose and reshape:

x = np.lib.stride_tricks.as_strided(x, (1,2,2,2,2), (32,16,0,8,0))
x.reshape(4,4) # copy!

Output:

array([[1, 1, 2, 2],
       [1, 1, 2, 2],
       [3, 3, 4, 4],
       [3, 3, 4, 4]])

Although, this will copy memory just like np.block. You can verify this trying to set the shape directly by x.shape = (4,4):

AttributeError: Incompatible shape for in-place modification. Use `.reshape()` to make a copy with the desired shape.

In the same fashion the output can be down-sampled just by changing the shape and strides:

np.lib.stride_tricks.as_strided(x, (2,2), (8*8,8*2))

Output:

array([[1, 2],
       [3, 4]])

Notice that this is done without any copying. The reason i multiply with 8 is that it is the size in bytes of a 64 bit integer.

Generalizable solution

def upsample(x, k):
    return np.lib.stride_tricks.as_strided(x, (np.prod(x.shape), k, k), (x.dtype.itemsize, 0, 0))
Kevin
  • 3,096
  • 2
  • 8
  • 37
  • when i run the first snippet somehow my output is: array([[[ 1, 1], [ 1, 1]], [[ 3, 3], [ 3, 3]], [[ 1, 1], [ 1, 1]], [[462730709, 462730709], [462730709, 462730709]]]) – user15770670 May 12 '21 at 16:05
  • This version is not generalized for any input. But the principle is the same, I will edit my answer with a solution that will work for any array. – Kevin May 12 '21 at 16:07
  • thanks! but the output was produced using x = np.array([[1, 2], [3, 4]]) – user15770670 May 12 '21 at 16:09
  • make sure that the dtype for x is 64 bit integer. Try running ```x = x.astype(np.int64)``` first – Kevin May 12 '21 at 16:10
  • I edited my answer with a generalizable solution of the first code snippet. It should work with any dtype and positive up-sampling factor. This should give you enough information to make the other methods also work for any array. – Kevin May 12 '21 at 16:24