Як я можу додати нові розміри до масиву Numpy?


85

Я починаю з numpy-масиву зображення.

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

Форма - це те, що можна очікувати від зображення формату 640x480 RGB.

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

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

Який найкращий спосіб додати наступний кадр - тобто наступний набір даних зображення, ще один масив 480 x 640 x 3 - до мого початкового масиву?

Відповіді:


97

Ви запитуєте, як додати вимір до масиву NumPy, щоб цей вимір можна було вирощувати для розміщення нових даних. Розмір можна додати наступним чином:

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

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

58
Не використовуйте None. Використовуйте, np.newaxisоскільки явне краще, ніж неявне.
Neil G

7
Як це може бути? Noneнічого не означає. Це явно. Це так None. Сказано чітко. None це річ в пітона. Немає сумнівів. Noneце остання деталь, ви не можете заглибитися. З іншого боку, numpy.newaxisмає на увазі None. Це, по суті, None. Це так None. Але це Noneнеявно. Хоча це Noneпрямо не виражається як None. Явна заявив чітко та в деталях, не залишаючи місця для плутанини або сумнівів. Неявна пропозиція, хоча прямо не виражена. Потрібно додати, що з точки зору API безпечніше використовувати numpy.newaxis.
Педро Родрігес,

2
Здогадуйтесь тут, якщо явно йдеться про "намір кодера", а не про синтаксичну / семантичну чіткість.
Габрер,

Відповідь JoshAdel слід вибрати як правильну відповідь у цьому випадку і потребує більшої кількості голосів. Його сенс важливий тим, що OP намагається додати до вищого розміру nparray, коли він рухається. Після створення ndarray не можна збільшувати розмір, потрібно зробити копію. Ця відповідь дасть лише форму (480, 640, 3, 1), і кожного разу, коли ви додасте новий кадр, ви будете робити ще одну копію. Не добре.
Ден Бошен

59

Як варіант

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

у відповіді @dbliss ви також можете використовувати numpy.expand_dimslike

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

24

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

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 ] замість F [:,:,:, 1]). Звичайно, мудрі результати роботи не повинні мати ніякого значення.
Magellan88,

Я погоджуюсь з JoshAdel та Magellan88, інші відповіді - дуже неефективна пам'ять і час обробки - ndarrays не можна збільшувати в розмірі після створення, тому копія завжди буде зроблена, якщо ви думаєте, що додаєте до неї.
Ден Бошен

10

Пітонічний

X = X[:, :, None]

що еквівалентно

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

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

Якщо вам не подобається порядок розмірів, ви можете переставити np.transpose()


6

Ви можете 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. Зберігайте свої дані у звичайному списку пітонів, поки це насправді не буде потрібно, щоб скласти їх усі разом (див. Нижче)

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.