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?

Chris
fonte

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]
dbliss
fonte
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)
Cleb
fonte
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.

JoshAdel
fonte
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()

0 -_- 0
fonte
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)
Saullo GP Castro
fonte
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.

Multihunter
fonte
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]]]]
romano
fonte
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).
richar8086
fonte