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?

11 Answers

You're asking how to add a dimension to a NumPy array, so that that dimension can then be grown to accommodate new data. A dimension can be added as follows:

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

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) 
3

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.

2

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()

You can use np.concatenate() specifying which axis to append, using np.newaxis:

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) 

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]]]] 

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.

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). 

This worked for me:

image = image[..., None] 
1

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]) 

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) 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy