Chris
Chris

Reputation: 10041

How can I add new dimensions to a Numpy array?

I'm starting off with a numpy array of an image.

In[1]:img = cv2.imread('test.jpg')

The shape is what you might expect for a 640x480 RGB image.

In[2]:img.shape
Out[2]: (480, 640, 3)

However, this image that I have is a frame of a video, which is 100 frames long. Ideally, I would like to have a single array that contains all the data from this video such that img.shape returns (480, 640, 3, 100).

What is the best way to add the next frame -- that is, the next set of image data, another 480 x 640 x 3 array -- to my initial array?

Upvotes: 181

Views: 407296

Answers (13)

hridyeshK
hridyeshK

Reputation: 141

a = np.expand_dims(a, axis=-1) 

or

a = a[:, np.newaxis] 

or

a = a.reshape(a.shape + (1,))

Upvotes: 14

Saullo G. P. Castro
Saullo G. P. Castro

Reputation: 58865

You can use np.concatenate() use the axis parameter to specify the dimension that should be concatenated. If the arrays being concatenated do not have this dimension, you can use np.newaxis to indicate where the new dimension should be added:

import numpy as np
movie = np.concatenate((img1[:,np.newaxis], img2[:,np.newaxis]), axis=3)

If you are reading from many files:

import glob
movie = np.concatenate([cv2.imread(p)[:,np.newaxis] for p in glob.glob('*.jpg')], axis=3)

Upvotes: 9

abcd
abcd

Reputation: 10751

A dimension can be added to a numpy array as follows:

image = image[..., np.newaxis]

Upvotes: 207

teghfo
teghfo

Reputation: 27

there is three-way for adding new dimensions to ndarray .

first: using "np.newaxis" (something like @dbliss answer)

  • np.newaxis is just given an alias to None for making it easier to
    understand. If you replace np.newaxis with None, it works the same
    way. but it's better to use np.newaxis for being more explicit.
import numpy as np

my_arr = np.array([2, 3])
new_arr = my_arr[..., np.newaxis]

print("old shape", my_arr.shape)
print("new shape", new_arr.shape)

>>> old shape (2,)
>>> new shape (2, 1)

second: using "np.expand_dims()"

  • Specify the original ndarray in the first argument and the position to add the dimension in the second argument axis.
my_arr = np.array([2, 3])
new_arr = np.expand_dims(my_arr, -1)

print("old shape", my_arr.shape)
print("new shape", new_arr.shape)

>>> old shape (2,)
>>> new shape (2, 1)

third: using "reshape()"

my_arr = np.array([2, 3])
new_arr = my_arr.reshape(*my_arr.shape, 1)

print("old shape", my_arr.shape)
print("new shape", new_arr.shape)

>>> old shape (2,)
>>> new shape (2, 1)

Upvotes: 0

Don Feto
Don Feto

Reputation: 1486

This will help you add axis anywhere you want

    import numpy as np
    signal = np.array([[0.3394572666491664, 0.3089068053925853, 0.3516359279582483], [0.33932706934615525, 0.3094755563319447, 0.3511973743219001], [0.3394407172182317, 0.30889042266755573, 0.35166886011421256], [0.3394407172182317, 0.30889042266755573, 0.35166886011421256]])
    
    print(signal.shape)
#(4,3)
    print(signal[...,np.newaxis].shape)  or  signal[...:none]
#(4, 3, 1) 
    print(signal[:, np.newaxis, :].shape)  or signal[:,none, :]

#(4, 1, 3)

Upvotes: 0

user48956
user48956

Reputation: 15788

You can use stack with the axis parameter:

img.shape  # h,w,3
imgs = np.stack([img1,img2,img3,img4], axis=-1)   # -1 = new axis is last
imgs.shape #  h,w,3,nimages

For example: to convert grayscale to color:

>>> d = np.zeros((5,4), dtype=int)  # 5x4
>>> d[2,3] = 1

>>> d3.shape
Out[30]: (5, 4, 3)

>>> d3 = np.stack([d,d,d], axis=-2)  # 5x4x3   -1=as last axis
>>> d3[2,3]
Out[32]: array([1, 1, 1])

Upvotes: 2

Scott
Scott

Reputation: 5820

This worked for me:

image = image[..., None]

Upvotes: 1

0-_-0
0-_-0

Reputation: 1473

Pythonic

X = X[:, :, None]

which is equivalent to

X = X[:, :, numpy.newaxis] and X = numpy.expand_dims(X, axis=-1)

But as you are explicitly asking about stacking images, I would recommend going for stacking the list of images np.stack([X1, X2, X3]) that you may have collected in a loop.

If you do not like the order of the dimensions you can rearrange with np.transpose()

Upvotes: 27

Roman
Roman

Reputation: 21757

Consider Approach 1 with reshape method and Approach 2 with np.newaxis method that produce the same outcome:

#Lets suppose, we have:
x = [1,2,3,4,5,6,7,8,9]
print('I. x',x)

xNpArr = np.array(x)
print('II. xNpArr',xNpArr)
print('III. xNpArr', xNpArr.shape)

xNpArr_3x3 = xNpArr.reshape((3,3))
print('IV. xNpArr_3x3.shape', xNpArr_3x3.shape)
print('V. xNpArr_3x3', xNpArr_3x3)

#Approach 1 with reshape method
xNpArrRs_1x3x3x1 = xNpArr_3x3.reshape((1,3,3,1))
print('VI. xNpArrRs_1x3x3x1.shape', xNpArrRs_1x3x3x1.shape)
print('VII. xNpArrRs_1x3x3x1', xNpArrRs_1x3x3x1)

#Approach 2 with np.newaxis method
xNpArrNa_1x3x3x1 = xNpArr_3x3[np.newaxis, ..., np.newaxis]
print('VIII. xNpArrNa_1x3x3x1.shape', xNpArrNa_1x3x3x1.shape)
print('IX. xNpArrNa_1x3x3x1', xNpArrNa_1x3x3x1)

We have as outcome:

I. x [1, 2, 3, 4, 5, 6, 7, 8, 9]

II. xNpArr [1 2 3 4 5 6 7 8 9]

III. xNpArr (9,)

IV. xNpArr_3x3.shape (3, 3)

V. xNpArr_3x3 [[1 2 3]
 [4 5 6]
 [7 8 9]]

VI. xNpArrRs_1x3x3x1.shape (1, 3, 3, 1)

VII. xNpArrRs_1x3x3x1 [[[[1]
   [2]
   [3]]

  [[4]
   [5]
   [6]]

  [[7]
   [8]
   [9]]]]

VIII. xNpArrNa_1x3x3x1.shape (1, 3, 3, 1)

IX. xNpArrNa_1x3x3x1 [[[[1]
   [2]
   [3]]

  [[4]
   [5]
   [6]]

  [[7]
   [8]
   [9]]]]

Upvotes: 3

Cleb
Cleb

Reputation: 25997

Alternatively to

image = image[..., np.newaxis]

in @dbliss' answer, you can also use numpy.expand_dims like

image = np.expand_dims(image, <your desired dimension>)

For example (taken from the link above):

x = np.array([1, 2])

print(x.shape)  # prints (2,)

Then

y = np.expand_dims(x, axis=0)

yields

array([[1, 2]])

and

y.shape

gives

(1, 2)

Upvotes: 109

Multihunter
Multihunter

Reputation: 5918

There is no structure in numpy that allows you to append more data later.

Instead, numpy puts all of your data into a contiguous chunk of numbers (basically; a C array), and any resize requires allocating a new chunk of memory to hold it. Numpy's speed comes from being able to keep all the data in a numpy array in the same chunk of memory; e.g. mathematical operations can be parallelized for speed and you get less cache misses.

So you will have two kinds of solutions:

  1. Pre-allocate the memory for the numpy array and fill in the values, like in JoshAdel's answer, or
  2. Keep your data in a normal python list until it's actually needed to put them all together (see below)

images = []
for i in range(100):
    new_image = # pull image from somewhere
    images.append(new_image)
images = np.stack(images, axis=3)

Note that there is no need to expand the dimensions of the individual image arrays first, nor do you need to know how many images you expect ahead of time.

Upvotes: 2

richar8086
richar8086

Reputation: 176

I followed this approach:

import numpy as np
import cv2

ls = []

for image in image_paths:
    ls.append(cv2.imread('test.jpg'))

img_np = np.array(ls) # shape (100, 480, 640, 3)
img_np = np.rollaxis(img_np, 0, 4) # shape (480, 640, 3, 100).

Upvotes: 1

JoshAdel
JoshAdel

Reputation: 68682

You could just create an array of the correct size up-front and fill it:

frames = np.empty((480, 640, 3, 100))

for k in xrange(nframes):
    frames[:,:,:,k] = cv2.imread('frame_{}.jpg'.format(k))

if the frames were individual jpg file that were named in some particular way (in the example, frame_0.jpg, frame_1.jpg, etc).

Just a note, you might consider using a (nframes, 480,640,3) shaped array, instead.

Upvotes: 33

Related Questions