Como posso adicionar novas dimensões a uma matriz Numpy?


92

Estou começando com uma matriz numpy de uma imagem.

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

O formato é o que você espera de uma imagem RGB de 640x480.

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

No entanto, esta imagem que tenho é um quadro de um vídeo, que tem 100 quadros de comprimento. Idealmente, gostaria de ter uma única matriz que contenha todos os dados deste vídeo de forma que img.shaperetorne (480, 640, 3, 100).

Qual é a melhor maneira de adicionar o próximo quadro - isto é, o próximo conjunto de dados de imagem, outro array 480 x 640 x 3 - ao meu array inicial?

Respostas:


104

Você está perguntando como adicionar uma dimensão a uma matriz NumPy, de modo que essa dimensão possa ser aumentada para acomodar novos dados. Uma dimensão pode ser adicionada da seguinte forma:

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

10
Atualmente, numpy.newaxisestá definido para estar None(no arquivo numeric.py), então de forma equivalente você pode usar `image = image [..., None].
Ray

60
Não use None. Use np.newaxisporque explícito é melhor do que implícito.
Neil G,

7
Como pode ser? Nonenão implica nada. É explícito. É None. Declarado claramente. None é uma coisa em python. Não há duvidas. Noneé o último detalhe, você não pode ir mais fundo. Por outro lado, numpy.newaxisimplica None. É, essencialmente None,. É None. Mas está Noneimplicitamente. É Noneembora não expressa diretamente como None. Explicito declarado de forma clara e detalhada, não deixando espaço para confusão ou dúvida. Sugestão implícita, embora não expressa diretamente. Devo acrescentar que, do ponto de vista da API, é mais seguro de usar numpy.newaxis.
Pedro Rodrigues

3
Adivinhe aqui, ser explícito se refere à "intenção do codificador" e não à clareza sintática / semântica.
Gabrer

A resposta de JoshAdel deve ser selecionada como a resposta certa neste caso e precisa de mais votos. Seu ponto é significativo no sentido de que o OP está procurando aumentar o nparray de dimensão superior à medida que avança. O tamanho do ndarray não pode ser aumentado depois de criado, uma cópia deve ser feita. Esta resposta fará apenas a forma (480, 640, 3, 1) e toda vez que você adicionar um novo quadro, estará fazendo outra cópia. Não é bom.
Dan Boschen

61

Alternativamente para

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

na resposta de @dbliss , você também pode usar numpy.expand_dimscomo

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

Por exemplo (retirado do link acima):

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

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

Então

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

rendimentos

array([[1, 2]])

e

y.shape

(1, 2)

como adicionar valores na nova dimensão? se eu fizer y[1,0]isso, o índice está fora dos limites do erro. y[0,1]está acessível
weima

@weima: Não tenho certeza do que você está procurando. Qual é a sua saída desejada?
Cleb

25

Você pode simplesmente criar uma matriz do tamanho correto antecipadamente e preenchê-la:

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

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

se os quadros forem arquivos jpg individuais nomeados de alguma forma particular (no exemplo, frame_0.jpg, frame_1.jpg, etc).

Apenas uma nota, você pode considerar o uso de uma (nframes, 480,640,3)matriz em forma.


1
Acho que esse é o caminho a percorrer. se você usar a concatenação, precisará mover o array na memória sempre que adicionar a ele. para 100 frames, isso não importa, mas se você quiser ir para vídeos maiores. BTW, eu teria usado o número de quadros como a primeira dimensão para ter um array (100.480.640,3) de forma que você possa acessar quadros individuais (o que geralmente quer você vai querer olhar, certo?) Mais fácil (F [1 ] em vez de F [:,:,:, 1]). É claro que, em termos de desempenho, isso não deveria ter nenhuma importância.
Magellan88

Eu concordo com JoshAdel e Magellan88, as outras respostas são muito ineficientes em termos de memória e tempo de processamento - ndarrays não podem ser aumentados em tamanho depois de criados, então uma cópia sempre será feita se você achar que está acrescentando a ela.
Dan Boschen

12

Pitônico

X = X[:, :, None]

que é equivalente a

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

Mas como você está perguntando explicitamente sobre o empilhamento de imagens, recomendo empilhar as listimagens np.stack([X1, X2, X3])que você possa ter coletado em um loop.

Se você não gosta da ordem das dimensões, você pode reorganizar com np.transpose()


7

Você pode usar np.concatenate()especificando qual axisanexar, usando np.newaxis:

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

Se você estiver lendo muitos arquivos:

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

2

Não existe uma estrutura em numpy que permita anexar mais dados posteriormente.

Em vez disso, numpy coloca todos os seus dados em um bloco contíguo de números (basicamente; uma matriz C), e qualquer redimensionamento requer a alocação de um novo bloco de memória para mantê-lo. A velocidade do Numpy vem de ser capaz de manter todos os dados em um array numpy no mesmo pedaço de memória; por exemplo, as operações matemáticas podem ser paralelizadas para velocidade e você obtém menos perdas de cache .

Portanto, você terá dois tipos de soluções:

  1. Pré-aloque a memória para a matriz numpy e preencha os valores, como na resposta de JoshAdel, ou
  2. Mantenha seus dados em uma lista Python normal até que seja realmente necessário colocá-los todos juntos (veja abaixo)

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

Observe que não há necessidade de expandir as dimensões das matrizes de imagens individuais primeiro, nem você precisa saber quantas imagens você espera com antecedência.


2

Considere a abordagem 1 com o método de reformulação e a abordagem 2 com o método np.newaxis que produzem o mesmo 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)

Temos 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]]]]

1

Eu segui esta abordagem:

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).
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.