Wie kann ich einem Numpy-Array neue Dimensionen hinzufügen?

85

Ich beginne mit einer Reihe von Bildern.

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

Die Form entspricht den Erwartungen eines 640 x 480 RGB-Bildes.

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

Dieses Bild, das ich habe, ist jedoch ein Bild eines Videos, das 100 Bilder lang ist. Im Idealfall hätte ich gerne ein einziges Array, das alle Daten aus diesem Video enthält, sodass sie img.shapezurückgegeben werden (480, 640, 3, 100).

Was ist der beste Weg, um das nächste Bild - dh den nächsten Satz von Bilddaten, ein weiteres 480 x 640 x 3-Array - zu meinem ursprünglichen Array hinzuzufügen?

Chris
quelle

Antworten:

98

Sie fragen, wie Sie einem NumPy-Array eine Dimension hinzufügen können, damit diese Dimension erweitert werden kann, um neue Daten aufzunehmen. Eine Dimension kann wie folgt hinzugefügt werden:

image = image[..., np.newaxis]
dbliss
quelle
10
Derzeit numpy.newaxisist definiert als None(in Datei numeric.py), so dass Sie äquivalent `image = image [..., None] verwenden können.
Ray
58
Nicht benutzen None. Verwenden Sie, np.newaxisweil explizit besser als implizit ist.
Neil G
7
Wie kann das sein? Nonebedeutet nichts. Es ist explizit. Es ist None. Klar ausgedrückt. None ist eine Sache in Python. Es gibt keinen Zweifel. Noneist das letzte Detail, man kann nicht tiefer gehen. Auf der anderen Seite numpy.newaxisimpliziert None. Es ist im Wesentlichen None. Es ist None. Ist aber Noneimplizit. Es wird Nonejedoch nicht direkt als ausgedrückt None. Explizit klar und detailliert ausgedrückt, ohne Raum für Verwirrung oder Zweifel. Implizit vorgeschlagen, aber nicht direkt ausgedrückt. Ich muss hinzufügen, dass es aus API-Sicht sicherer ist, es zu verwenden numpy.newaxis.
Pedro Rodrigues
2
Vermutlich bezieht sich das explizite Sein eher auf die "Codiererabsicht" als auf die syntaktische / semantische Klarheit.
Gabrer
Die Antwort von JoshAdel sollte in diesem Fall als die richtige Antwort ausgewählt werden und benötigt mehr Stimmen. Sein Standpunkt ist insofern von Bedeutung, als das OP im Laufe der Zeit versucht, das höherdimensionierte Nparray zu erweitern. ndarrays können nach dem Erstellen nicht vergrößert werden, es muss eine Kopie erstellt werden. Diese Antwort erstellt nur die Form (480, 640, 3, 1) und jedes Mal, wenn Sie einen neuen Rahmen hinzufügen, erstellen Sie eine weitere Kopie. Nicht gut.
Dan Boschen
59

Alternativ zu

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

In der Antwort von @dbliss können Sie auch numpy.expand_dimslike verwenden

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

Zum Beispiel (aus dem obigen Link):

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

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

Dann

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

ergibt

array([[1, 2]])

und

y.shape

gibt

(1, 2)
Cleb
quelle
Wie füge ich Werte in die neue Dimension ein? Wenn ich es tue y[1,0], gibt es Index außerhalb der Grenzen Fehler. y[0,1]ist zugänglich
Weima
@weima: Ich bin mir nicht ganz sicher, wonach du suchst. Was ist Ihre gewünschte Ausgabe?
Cleb
24

Sie können einfach ein Array mit der richtigen Größe im Voraus erstellen und es füllen:

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

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

Wenn es sich bei den Frames um einzelne JPG-Dateien handelt, die auf eine bestimmte Weise benannt wurden (im Beispiel frame_0.jpg, frame_1.jpg usw.).

Nur eine Anmerkung, Sie könnten stattdessen ein (nframes, 480,640,3)geformtes Array verwenden.

JoshAdel
quelle
1
Ich denke, das ist der richtige Weg. Wenn Sie die Verkettung verwenden, müssen Sie das Array jedes Mal im Speicher verschieben, wenn Sie es hinzufügen. Für 100 Frames sollte das überhaupt keine Rolle spielen, aber wenn Sie zu größeren Videos gehen möchten. Übrigens hätte ich die Anzahl der Frames als erste Dimension verwendet, also haben Sie ein (100.480.640,3) Array, mit dem Sie einfacher auf einzelne Frames zugreifen können (was möchten Sie normalerweise sehen, oder?) (F [1 ] anstelle von F [:,:,:, 1]). Natürlich sollte es in Bezug auf die Leistung überhaupt keine Rolle spielen.
Magellan88
Ich stimme JoshAdel und Magellan88 zu. Die anderen Antworten sind in Bezug auf Speicher und Verarbeitungszeit sehr ineffizient. Ndarrays können nach ihrer Erstellung nicht vergrößert werden. Daher wird immer eine Kopie erstellt, wenn Sie glauben, dass Sie sie anhängen.
Dan Boschen
10

Pythonic

X = X[:, :, None]

das ist äquivalent zu

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

Da Sie jedoch explizit nach dem Stapeln von Bildern fragen, würde ich empfehlen, die listBilder zu stapeln, die np.stack([X1, X2, X3])Sie möglicherweise in einer Schleife gesammelt haben.

Wenn Ihnen die Reihenfolge der Abmessungen nicht gefällt, können Sie sie neu anordnen np.transpose()

0 -_- 0
quelle
6

Sie können np.concatenate()angeben, welche axisangehängt werden sollen, indem Sie Folgendes verwenden np.newaxis:

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

Wenn Sie aus vielen Dateien lesen:

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

In numpy gibt es keine Struktur, mit der Sie später weitere Daten anhängen können.

Stattdessen fügt numpy alle Ihre Daten in einen zusammenhängenden Teil von Zahlen ein (im Grunde genommen ein C-Array), und für jede Größenänderung muss ein neuer Teil des Speichers zugewiesen werden, um sie zu speichern. Die Geschwindigkeit von Numpy beruht darauf, dass alle Daten in einem numpy-Array im selben Speicherbereich gespeichert werden können. Beispielsweise können mathematische Operationen aus Geschwindigkeitsgründen parallelisiert werden und Sie erhalten weniger Cache-Fehler .

Sie haben also zwei Arten von Lösungen:

  1. Ordnen Sie den Speicher für das Numpy-Array vorab zu und geben Sie die Werte ein, wie in JoshAdels Antwort oder
  2. Bewahren Sie Ihre Daten in einer normalen Python-Liste auf, bis sie tatsächlich alle zusammengefügt werden müssen (siehe unten).

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

Beachten Sie, dass Sie weder die Abmessungen der einzelnen Bildarrays zuerst erweitern noch wissen müssen, wie viele Bilder Sie im Voraus erwarten.

Multihunter
quelle
2

Betrachten Sie Ansatz 1 mit der Umformmethode und Ansatz 2 mit der Methode np.newaxis, die das gleiche Ergebnis erzielen:

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

Wir haben als Ergebnis:

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]]]]
römisch
quelle
1

Ich folgte diesem Ansatz:

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