Come posso aggiungere nuove dimensioni a un array Numpy?


93

Sto iniziando con una serie numpy di un'immagine.

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

La forma è quella che ci si potrebbe aspettare da un'immagine RGB 640x480.

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

Tuttavia, questa immagine che ho è un fotogramma di un video, che è lungo 100 fotogrammi. Idealmente, vorrei avere un unico array che contenga tutti i dati di questo video in modo tale che img.shaperitorni (480, 640, 3, 100).

Qual è il modo migliore per aggiungere il fotogramma successivo, ovvero il set successivo di dati immagine, un altro array 480 x 640 x 3, al mio array iniziale?

Risposte:


105

Stai chiedendo come aggiungere una dimensione a un array NumPy, in modo che quella dimensione possa essere ingrandita per accogliere nuovi dati. È possibile aggiungere una dimensione come segue:

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

10
Attualmente, numpy.newaxisè definito come None(nel file numeric.py), quindi in modo equivalente potresti usare `image = image [..., None].
Ray

60
Non usare None. Usa np.newaxisperché esplicito è meglio di implicito.
Neil G

7
Come può essere? Nonenon implica nulla. È esplicito. Lo è None. Affermato chiaramente. None è una cosa in Python. Non c'è dubbio. Noneè l'ultimo dettaglio, non puoi andare più in profondità. D'altra parte, numpy.newaxisimplica None. Si tratta, in sostanza, None. Lo è None. Ma è Noneimplicitamente. E ' Noneanche se non direttamente espressa None. Esplicito dichiarato in modo chiaro e dettagliato, senza lasciare spazio a confusione o dubbio. Suggerito implicito ma non espresso direttamente. Devo aggiungere che, dal punto di vista delle API, è più sicuro da usare numpy.newaxis.
Pedro Rodrigues,

3
Indovina qui, essere espliciti si riferisce all '"intento del codificatore" piuttosto che alla chiarezza sintattica / semantica.
Gabrer

La risposta di JoshAdel dovrebbe essere selezionata come la risposta giusta in questo caso e necessita di più voti. Il suo punto è significativo in quanto l'OP sta cercando di aumentare il nparray di dimensioni superiori mentre procede. Una volta creati, gli ndarray non possono essere aumentati di dimensioni, è necessario crearne una copia. Questa risposta creerà solo la forma (480, 640, 3, 1) e ogni volta che aggiungi una nuova cornice ne farai un'altra copia. Non bene.
Dan Boschen

61

In alternativa a

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

nella risposta di @dbliss , puoi anche usare mi numpy.expand_dimspiace

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

Ad esempio (tratto dal link sopra):

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

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

Poi

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

rendimenti

array([[1, 2]])

e

y.shape

(1, 2)

come aggiungere valori nella nuova dimensione? se lo faccio y[1,0]dà un errore di indice fuori limite. y[0,1]è accessibile
weima

@weima: Non sono completamente sicuro di cosa stai cercando. Qual è il tuo output desiderato?
Cleb

25

Potresti semplicemente creare un array della dimensione corretta in primo piano e riempirlo:

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

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

se i frame fossero singoli file jpg a cui è stato assegnato un nome in un modo particolare (nell'esempio frame_0.jpg, frame_1.jpg, ecc.).

Solo una nota, potresti prendere in considerazione l'utilizzo di un (nframes, 480,640,3)array sagomato, invece.


1
Penso che questa sia la strada da percorrere. se usi la concatenazione dovrai spostare l'array in memoria ogni volta che lo aggiungi. per 100 fotogrammi che non dovrebbe importare affatto, ma se vuoi andare a video più grandi. A proposito, avrei usato il numero di fotogrammi come prima dimensione in modo da avere un array (100,480,640,3) in questo modo puoi accedere ai singoli fotogrammi (ciò che di solito vuoi guardare, giusto?) Più facile (F [1 ] invece di F [:,:,:, 1]). Ovviamente dal punto di vista delle prestazioni non dovrebbe importare affatto.
Magellan88

Sono d'accordo con JoshAdel e Magellan88, le altre risposte sono molto inefficienti dal punto di vista della memoria e del tempo di elaborazione: i ndarrays non possono essere aumentati di dimensioni una volta creati, quindi verrà sempre eseguita una copia se pensi di aggiungerli.
Dan Boschen

12

Divinatorio

X = X[:, :, None]

che è equivalente a

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

Ma poiché stai chiedendo esplicitamente di impilare le immagini, ti consiglio di impilare le listimmagini np.stack([X1, X2, X3])che potresti aver raccolto in un ciclo.

Se non ti piace l'ordine delle dimensioni puoi riorganizzarlo np.transpose()


7

Puoi usare np.concatenate()specificando quale axisaggiungere, usando np.newaxis:

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

Se stai leggendo da molti file:

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

2

Non esiste una struttura in numpy che ti consenta di aggiungere più dati in un secondo momento.

Invece, numpy mette tutti i tuoi dati in un blocco contiguo di numeri (fondamentalmente; un array C) e qualsiasi ridimensionamento richiede l'allocazione di un nuovo blocco di memoria per conservarlo. La velocità di Numpy deriva dalla capacità di mantenere tutti i dati in un array numpy nello stesso blocco di memoria; ad esempio, le operazioni matematiche possono essere parallelizzate per la velocità e si ottengono meno errori nella cache .

Quindi avrai due tipi di soluzioni:

  1. Pre-alloca la memoria per l'array numpy e inserisci i valori, come nella risposta di JoshAdel, o
  2. Mantieni i tuoi dati in un normale elenco Python fino a quando non è effettivamente necessario metterli tutti insieme (vedi sotto)

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

Si noti che non è necessario espandere prima le dimensioni dei singoli array di immagini, né è necessario sapere quante immagini ci si aspetta in anticipo.


2

Considera l'approccio 1 con il metodo reshape e l'approccio 2 con il metodo np.newaxis che producono lo stesso risultato:

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

Abbiamo come risultato:

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

Ho seguito questo approccio:

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).
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.