¿Cómo puedo agregar nuevas dimensiones a una matriz Numpy?

85

Estoy comenzando con una gran variedad de imágenes.

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

La forma es la que cabría esperar de una imagen RGB de 640x480.

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

Sin embargo, esta imagen que tengo es un fotograma de un video, que tiene 100 fotogramas de largo. Idealmente, me gustaría tener una sola matriz que contenga todos los datos de este video de manera que img.shaperegrese (480, 640, 3, 100).

¿Cuál es la mejor manera de agregar el siguiente fotograma, es decir, el siguiente conjunto de datos de imagen, otra matriz de 480 x 640 x 3, a mi matriz inicial?

Chris
fuente

Respuestas:

98

Está preguntando cómo agregar una dimensión a una matriz NumPy, de modo que esa dimensión pueda crecer para adaptarse a nuevos datos. Se puede agregar una dimensión de la siguiente manera:

image = image[..., np.newaxis]
dbliss
fuente
10
Actualmente, numpy.newaxisse define como None(en archivo numeric.py), por lo que, de manera equivalente, podría usar `image = image [..., None].
Ray
58
No lo use None. Úselo np.newaxisporque explícito es mejor que implícito.
Neil G
7
¿Como puede ser? Noneno implica nada. Es explícito. Lo es None. Dicho claramente. None es una cosa en Python. No hay duda. Nonees el último detalle, no se puede profundizar. Por otro lado, numpy.newaxisimplica None. Es, en esencia, None. Lo es None. Pero es Noneimplícitamente. Se Noneaunque no expresa directamente como None. Expresado explícitamente de forma clara y detallada, sin dejar lugar a confusión o duda. Implícito sugerido pero no expresado directamente. Debo agregar que, desde la perspectiva de la API, es más seguro de usar numpy.newaxis.
Pedro Rodrigues
2
Supongo que aquí, ser explícito se refiere a la "intención del codificador" más que a la claridad sintáctica / semántica.
Gabrer
La respuesta de JoshAdel debe seleccionarse como la respuesta correcta en este caso y necesita más votos. Su punto es significativo en el sentido de que el OP está buscando agregar al nparray de mayor dimensión a medida que avanza. ndarray no se puede aumentar de tamaño una vez creado, se debe hacer una copia. Esta respuesta solo hará la forma (480, 640, 3, 1) y cada vez que agregue un nuevo marco, estará haciendo otra copia. No está bien.
Dan Boschen
59

Alternativamente a

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

en la respuesta de @dbliss , también puedes usar numpy.expand_dimslike

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

Por ejemplo (tomado del enlace anterior):

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

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

Entonces

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

rendimientos

array([[1, 2]])

y

y.shape

da

(1, 2)
Cleb
fuente
¿Cómo agregar valores en la nueva dimensión? si lo hago y[1,0], da un error de índice fuera de los límites. y[0,1]es accesible
Weima
@weima: No estoy completamente seguro de lo que buscas. ¿Cuál es su salida deseada?
Cleb
24

Puede crear una matriz del tamaño correcto por adelantado y llenarla:

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

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

si los marcos eran archivos jpg individuales que se nombraron de alguna manera en particular (en el ejemplo, frame_0.jpg, frame_1.jpg, etc.).

Solo una nota, en su lugar, podría considerar usar una (nframes, 480,640,3)matriz con forma.

JoshAdel
fuente
1
Creo que este es el camino a seguir. si usa la concatenación, deberá mover la matriz en la memoria cada vez que la agregue. por 100 fotogramas que no deberían importar en absoluto, pero si quieres ir a videos más grandes. Por cierto, habría usado el número de fotogramas como la primera dimensión, así que tenga una matriz (100,480,640,3) de esa manera puede acceder a fotogramas individuales (lo que normalmente querrá ver, ¿verdad?) Más fácil (F [1 ] en lugar de F [:,:,:, 1]). Por supuesto, el rendimiento no debería importar en absoluto.
Magellan88
Estoy de acuerdo con JoshAdel y Magellan88, las otras respuestas son muy ineficientes en cuanto a memoria y tiempo de procesamiento: los ndarrays no se pueden aumentar de tamaño una vez creados, por lo que siempre se hará una copia si cree que lo está agregando.
Dan Boschen
10

Pitónico

X = X[:, :, None]

que es equivalente a

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

Pero como está preguntando explícitamente acerca de apilar imágenes, le recomendaría apilar las listimágenesnp.stack([X1, X2, X3]) que puede haber recopilado en un bucle.

Si no le gusta el orden de las dimensiones, puede reorganizarlo con np.transpose()

0 -_- 0
fuente
6

Puede usar np.concatenate()especificando cuál axisagregar, usando np.newaxis:

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

Si está leyendo de muchos archivos:

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

No hay una estructura en numpy que le permita agregar más datos más adelante.

En su lugar, numpy coloca todos sus datos en una parte contigua de números (básicamente, una matriz de C), y cualquier cambio de tamaño requiere asignar una nueva parte de memoria para contenerla. La velocidad de Numpy proviene de poder mantener todos los datos en una matriz numerosa en la misma porción de memoria; por ejemplo, las operaciones matemáticas se pueden paralelizar para aumentar la velocidad y se obtienen menos errores de caché .

Entonces tendrás dos tipos de soluciones:

  1. Asigne previamente la memoria para la matriz numpy y complete los valores, como en la respuesta de JoshAdel, o
  2. Mantenga sus datos en una lista de Python normal hasta que sea realmente necesario para ponerlos todos juntos (ver más abajo)

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

Tenga en cuenta que no es necesario expandir primero las dimensiones de las matrices de imágenes individuales, ni tampoco es necesario saber cuántas imágenes espera de antemano.

Cazador múltiple
fuente
2

Considere el enfoque 1 con el método de remodelación y el enfoque 2 con el método np.newaxis que producen el mismo resultado:

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

Tenemos como resultado:

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]]]]
romano
fuente
1

Seguí este enfoque:

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
fuente