Как я могу добавить новые измерения в массив Numpy?


92

Я начинаю с массива изображений.

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

Форма - это то, что вы могли ожидать от изображения RGB 640x480.

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

Однако это изображение, которое у меня есть, представляет собой кадр видео длиной 100 кадров. В идеале я хотел бы иметь один массив, содержащий все данные из этого видео, которые img.shapeвозвращаются (480, 640, 3, 100).

Как лучше всего добавить следующий кадр - то есть следующий набор данных изображения, еще один массив 480 x 640 x 3 - в мой исходный массив?

Ответы:


104

Вы спрашиваете, как добавить измерение в массив NumPy, чтобы затем это измерение можно было увеличить для размещения новых данных. Размер можно добавить следующим образом:

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

10
В настоящее время numpy.newaxisопределяется как None(в файле numeric.py), поэтому вы можете использовать `image = image [..., None].
Рэй

60
Не используйте None. Используйте, np.newaxisпотому что явное лучше, чем неявное.
Neil G

7
Как это может быть? Noneничего не подразумевает. Это явно. Это так None. Сказано четко. None это вещь в питона. Нет сомнений. Noneэто последняя деталь, вы не можете углубиться. С другой стороны, numpy.newaxisподразумевает None. По сути, это None. Это так None. Но это Noneбезоговорочно. Он Noneхотя и не непосредственно выражается None. Явная заявил четко и в деталях, не оставляя места для путаницы или сомнений. Неявно предлагается, но прямо не выражается. Я должен добавить, что с точки зрения API его безопаснее использовать numpy.newaxis.
Педро Родригес,

3
Угадайте, что явное выражение относится скорее к «замыслу кодера», чем к синтаксической / семантической ясности.
Gabrer

Ответ ДжошАдела следует выбрать как правильный ответ в этом случае и требует большего количества голосов. Его точка зрения важна в том смысле, что OP пытается добавить к nparray более высокого размера по мере продвижения. После создания ndarray не может быть увеличен в размере, необходимо сделать копию. Этот ответ создаст только форму (480, 640, 3, 1), и каждый раз, когда вы добавляете новый кадр, вы будете делать новую копию. Не хорошо.
Дэн Бошен

61

Альтернативно

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

в ответе @dbliss вы также можете использовать numpy.expand_dimsкак

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

Например (взято по ссылке выше):

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

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

потом

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

дает

array([[1, 2]])

и

y.shape

дает

(1, 2)

как добавить значения в новое измерение? если я это сделаю, y[1,0]это дает ошибку индекса за пределами границ. y[0,1]доступно
weima

@weima: Не совсем уверен, что вам нужно. Каков ваш желаемый результат?
Cleb

25

Вы можете просто заранее создать массив нужного размера и заполнить его:

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

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

если кадры были отдельными jpg-файлами, которые были названы определенным образом (в примере, frame_0.jpg, frame_1.jpg и т. д.).

Просто примечание: (nframes, 480,640,3)вместо этого вы можете рассмотреть возможность использования фигурного массива.


1
Я думаю, это правильный путь. если вы используете конкатенацию, вам нужно будет перемещать массив в памяти каждый раз, когда вы добавляете к нему. для 100 кадров это вообще не имеет значения, но если вы хотите перейти к более крупным видео. Кстати, я бы использовал количество кадров в качестве первого измерения, поэтому имейте массив (100,480,640,3), чтобы вы могли получить доступ к отдельным кадрам (что обычно нужно, чтобы вы захотели посмотреть, не так ли?) ] вместо F [:,:,:, 1]). Конечно, с точки зрения производительности это не имеет никакого значения.
Magellan88

Я согласен с JoshAdel и Magellan88, другие ответы очень неэффективны с точки зрения памяти и времени обработки - ndarrays не может быть увеличен в размере после создания, поэтому копия всегда будет создаваться, если вы думаете, что добавляете к ней.
Дэн Бошен

12

Питонический

X = X[:, :, None]

что эквивалентно

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

Но поскольку вы явно спрашиваете о штабелировании изображений, я бы порекомендовал сложить listизображенияnp.stack([X1, X2, X3]) которые вы, возможно, собрали, в цикле.

Если вам не нравится порядок размеров, вы можете изменить его с помощью np.transpose()


7

Вы можете np.concatenate()указать, что axisнужно добавить, используя np.newaxis:

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

Если вы читаете из большого количества файлов:

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

2

В numpy нет структуры, которая позволила бы вам добавить больше данных позже.

Вместо этого numpy помещает все ваши данные в непрерывный фрагмент чисел (в основном, массив C), и любое изменение размера требует выделения нового фрагмента памяти для его хранения. Скорость Numpy зависит от возможности хранить все данные в массиве numpy в одном и том же фрагменте памяти; например, математические операции могут быть распараллелены для увеличения скорости, и вы получите меньше промахов в кэше .

Итак, у вас будет два вида решений:

  1. Предварительно выделите память для массива numpy и заполните значения, как в ответе JoshAdel, или
  2. Храните данные в обычном списке Python до тех пор, пока действительно не понадобится собрать их все вместе (см. Ниже)

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

Обратите внимание, что нет необходимости сначала расширять размеры отдельных массивов изображений, и вам не нужно знать, сколько изображений вы ожидаете заранее.


2

Рассмотрим подход 1 с методом изменения формы и подход 2 с методом np.newaxis, которые дают тот же результат:

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

В результате имеем:

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

1

Я последовал такому подходу:

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).
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.