Jak mogę dodać nowe wymiary do tablicy Numpy?

85

Zaczynam od numpy tablicy obrazu.

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

Kształt jest taki, jakiego można się spodziewać po obrazie 640x480 RGB.

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

Jednak ten obraz, który mam, to klatka wideo, która ma 100 klatek. Idealnie chciałbym mieć pojedynczą tablicę zawierającą wszystkie dane z tego filmu wideo, która img.shapezwraca (480, 640, 3, 100).

Jaki jest najlepszy sposób na dodanie następnej klatki - czyli następnego zestawu danych obrazu, kolejnej tablicy 480 x 640 x 3 - do mojej początkowej tablicy?

Chris
źródło

Odpowiedzi:

98

Pytasz, jak dodać wymiar do tablicy NumPy, aby ten wymiar mógł następnie zostać powiększony, aby pomieścić nowe dane. Wymiar można dodać w następujący sposób:

image = image[..., np.newaxis]
dbliss
źródło
10
Obecnie numpy.newaxisjest zdefiniowany jako None(w pliku numeric.py), więc równoważnie można użyć parametru `image = image [..., None].
Ray
58
Nie używaj None. Użyj, np.newaxisponieważ jawne jest lepsze niż niejawne.
Neil G,
7
Jak to możliwe? Nonenic nie oznacza. To jest wyraźne. Tak jest None. Podane jasno. None jest rzeczą w Pythonie. Nie ma wątpliwości. Noneto ostatni szczegół, głębiej nie można. Z drugiej strony numpy.newaxissugeruje None. To jest zasadniczo None. Tak jest None. Ale jest w Nonedomyśle. Nie jest to Nonejednak bezpośrednio wyrażone jako None. Wyraźnie określone jasno i szczegółowo, nie pozostawiając miejsca na zamieszanie lub wątpliwości. Ukryta sugestia, ale nie wyrażona bezpośrednio. Muszę dodać, że z punktu widzenia API jest bezpieczniejszy w użyciu numpy.newaxis.
Pedro Rodrigues
2
Zgadnij tutaj, bycie wyraźnym odnosi się raczej do „intencji kodera” niż do jasności składniowej / semantycznej.
Gabrer
Odpowiedź JoshAdela powinna być w tym przypadku wybrana jako właściwa i wymaga większej liczby głosów. Jego uwaga jest znacząca, ponieważ PO stara się dodawać do wyższego wymiaru nparray w miarę jego trwania. ndarray nie może zostać zwiększony po utworzeniu, należy wykonać kopię. Ta odpowiedź stworzy tylko kształt (480, 640, 3, 1) i za każdym razem, gdy dodasz nową ramkę, będziesz tworzyć kolejną kopię. Niedobrze.
Dan Boschen
59

Alternatywnie do

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

w odpowiedzi @dbliss możesz również użyć numpy.expand_dimslike

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

Na przykład (wzięty z linku powyżej):

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

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

Następnie

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

plony

array([[1, 2]])

i

y.shape

daje

(1, 2)
Cleb
źródło
jak dodać wartości w nowym wymiarze? jeśli y[1,0]to zrobię , daje indeks poza granicami błędu. y[0,1]jest dostępny
weima
@weima: Nie do końca jestem pewien, czego szukasz. Jaki jest Twój pożądany wynik?
Cleb
24

Możesz po prostu utworzyć tablicę o odpowiednim rozmiarze z góry i wypełnić ją:

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

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

jeśli ramki były pojedynczymi plikami jpg, które zostały nazwane w określony sposób (w przykładzie ramka_0.jpg, ramka_1.jpg itd.).

Uwaga (nframes, 480,640,3), zamiast tego możesz rozważyć użycie tablicy shape.

JoshAdel
źródło
1
Myślę, że to jest droga. jeśli używasz konkatenacji, będziesz musiał przenosić tablicę w pamięci za każdym razem, gdy do niej dodasz. dla 100 klatek to nie powinno w ogóle mieć znaczenia, ale jeśli chcesz przejść do większych filmów. Swoją drogą, użyłbym liczby klatek jako pierwszego wymiaru, więc miej tablicę (100.480.640,3), dzięki której możesz uzyskać dostęp do poszczególnych ramek (na co zwykle chcesz patrzeć, prawda?) Łatwiej (F [1 ] zamiast F [:,:,:, 1]). Oczywiście pod względem wydajności nie powinno to mieć żadnego znaczenia.
Magellan88
Zgadzam się z JoshAdelem i Magellanem88, pozostałe odpowiedzi są bardzo nieefektywne pod względem pamięci i czasu przetwarzania - nie można zwiększyć rozmiaru tablic po utworzeniu, więc kopia będzie zawsze tworzona, jeśli myślisz, że ją dołączasz.
Dan Boschen
10

Pythonic

X = X[:, :, None]

co jest równoważne

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

Ale ponieważ wyraźnie pytasz o układanie obrazów w stosy, polecam ułożenie listobrazów np.stack([X1, X2, X3]), które być może zebrałeś w pętli.

Jeśli nie podoba ci się kolejność wymiarów, możesz zmienić kolejność np.transpose()

0 -_- 0
źródło
6

Możesz np.concatenate()określić, które elementy axisdołączyć, używając np.newaxis:

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

Jeśli czytasz z wielu plików:

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

W numpy nie ma struktury, która pozwala później dołączyć więcej danych.

Zamiast tego numpy umieszcza wszystkie twoje dane w ciągłej porcji liczb (w zasadzie; tablica C), a każda zmiana rozmiaru wymaga przydzielenia nowej porcji pamięci do jej przechowywania. Szybkość Numpy wynika z możliwości przechowywania wszystkich danych w tablicy numpy w tej samej części pamięci; np. operacje matematyczne można zrównoleglać w celu zwiększenia szybkości i uzyskuje się mniej błędów pamięci podręcznej .

Będziesz więc mieć dwa rodzaje rozwiązań:

  1. Wstępnie przydziel pamięć dla tablicy numpy i wypełnij wartości, jak w odpowiedzi JoshAdela lub
  2. Przechowuj dane na normalnej liście Pythona, dopóki nie będą potrzebne do połączenia ich wszystkich (patrz poniżej)

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

Zwróć uwagę, że nie ma potrzeby wcześniejszego rozszerzania wymiarów poszczególnych tablic obrazów ani nie musisz wiedzieć, ile obrazów spodziewasz się z wyprzedzeniem.

Multihunter
źródło
2

Rozważ Podejście 1 z metodą zmiany kształtu i Podejście 2 z metodą np. Newaxis, które dają ten sam wynik:

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

W rezultacie otrzymujemy:

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]]]]
rzymski
źródło
1

Poszedłem w ten sposób:

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
źródło