Como converter uma imagem PIL em uma matriz numpy?

257

Tudo bem, estou brincando com a conversão de um objeto de imagem PIL para frente e para trás em uma matriz numpy para que eu possa fazer transformações mais rápidas de pixel por pixel do que o PixelAccessobjeto do PIL permitiria. Eu descobri como colocar as informações de pixel em uma matriz numpy 3D útil por meio de:

pic = Image.open("foo.jpg")
pix = numpy.array(pic.getdata()).reshape(pic.size[0], pic.size[1], 3)

Mas não consigo descobrir como carregá-lo de volta no objeto PIL depois de fazer todas as minhas transformações impressionantes. Estou ciente do putdata()método, mas não consigo fazê-lo se comportar.

Akdom
fonte
6
Observe que pic.size[0]e pic.size[1]deve ser trocado (ou seja reshape(pic.size[1], pic.size[0], 3)), uma vez que sizeé width x heightou x * y, enquanto a ordem da matriz é rows x columns.
foges

Respostas:

286

Você não está dizendo exatamente como putdata()não está se comportando. Estou assumindo que você está fazendo

>>> pic.putdata(a)
Traceback (most recent call last):
  File "...blablabla.../PIL/Image.py", line 1185, in putdata
    self.im.putdata(data, scale, offset)
SystemError: new style getargs format but argument is not a tuple

Isso ocorre porque putdataespera uma sequência de tuplas e você está fornecendo uma matriz numpy. este

>>> data = list(tuple(pixel) for pixel in pix)
>>> pic.putdata(data)

vai funcionar, mas é muito lento.

A partir do PIL 1.1.6, a maneira "adequada" de converter imagens e matrizes numpy é simplesmente

>>> pix = numpy.array(pic)

embora a matriz resultante esteja em um formato diferente do seu (matriz 3-d ou linhas / colunas / rgb nesse caso).

Depois de fazer as alterações na matriz, você poderá fazer uma pic.putdata(pix)ou criar uma nova imagem Image.fromarray(pix).

dF.
fonte
2
Primeiro, não deve ser pic.putdata (data)? E numpy.asarray (pic) produz uma matriz somente leitura, então você precisa chamar numpy.array (pic) e não respondeu à pergunta ... no link que forneceu, parece ser pic = Image.fromarray ( pix). Corrija sua resposta e eu a aceito.
23468 akdom
2
Obrigado por isso ... Image.fromarraynão está listado na documentação do PIL (!), Então eu nunca o encontraria se não fosse por isso.
Nathan Reed
13
Essa página é listada numpy.asarray(pic)como a maneira "adequada" de converter, não numpy.array(pic). De acordo com esta resposta, array você fará uma cópia asarraye não (mas o asarrayresultado será somente leitura).
Arthur Tacca
1
Um aviso aqui (por meu próprio erro): você precisa considerar também a escala e os intervalos dos dados. Em muitos casos, você renderiza imagens com 0 a 255 bytes, mas pode esperar que eles sejam convertidos em, por exemplo, 0,0-1,0 na matriz numpy. Algumas conversões de unidades de uint8 fazer isso, mas, neste caso, não .. de modo a verificar-lo :)
BjornW
A segunda resposta é melhor.
Nathan
193

Abra Icomo uma matriz:

>>> I = numpy.asarray(PIL.Image.open('test.jpg'))

Faça algumas coisas para Iconvertê-lo novamente em uma imagem:

>>> im = PIL.Image.fromarray(numpy.uint8(I))

Filtre imagens numpy com FFT, Python

Se você quiser fazê-lo explicitamente por algum motivo, existem funções pil2array () e array2pil () usando getdata () nesta página em correlação.zip.

endólito
fonte
2
@ArditS .: Você import Imageprimeiro? Você tem o PIL instalado?
Endolith
5
A uint8conversão é necessária?
Neil Traft
4
numpy.asarray(Image.open(filename))parece funcionar para imagens .jpg, mas não para .png. O resultado é exibido como array(<PngImagePlugin.PngImageFile image mode=LA size=500x500 at 0x3468198>, dtype=object). Parece não haver métodos obviamente nomeados do PngImagePlugin.PngImageFileobjeto para resolver isso. Acho que devo fazer isso como uma nova pergunta, mas é muito relevante para este tópico. Alguém entende o que está acontecendo de errado aqui?
Jez
3
@Rebs: aqui está a razão pela qual isso é muito mais rápido: getdata()retorna uma sequência como objeto ( pillow.readthedocs.io/en/3.4.x/reference/… ), mas uma imagem de travesseiro implementa o __array_interface__que numpypode ser usado para acessar os bytes brutos de uma imagem sem precisar passar por um iterador (consulte github.com/python-pillow/Pillow/blob/… e docs.scipy.org/doc/numpy/reference/arrays.interface.html ). Você pode até usarnumpy.array(PIL.Image.open('test.jpg'))
tdp2110
3
@jez Verifique se o objeto Image está fechado antes de convertê-lo em numpy. O mesmo aconteceu comigo e descobri que fechei o objeto de imagem em algum lugar.
Shaohua Li
65

Estou usando o Pillow 4.1.1 (o sucessor do PIL) no Python 3.5. A conversão entre travesseiro e numpy é simples.

from PIL import Image
import numpy as np
im = Image.open('1.jpg')
im2arr = np.array(im) # im2arr.shape: height x width x channel
arr2im = Image.fromarray(im2arr)

Uma coisa que precisa ser notada é que o estilo Pillow imé o principal da coluna, enquanto o estilo numpy im2arré o principal da linha. No entanto, a função Image.fromarrayjá leva isso em consideração. Ou seja, arr2im.size == im.sizee arr2im.mode == im.modeno exemplo acima.

Devemos cuidar do formato de dados HxWxC ao processar as matrizes numpy transformadas, por exemplo, faça a transformação im2arr = np.rollaxis(im2arr, 2, 0)ou im2arr = np.transpose(im2arr, (2, 0, 1))no formato CxHxW.

Daniel
fonte
2
Este é o exemplo mais limpo, incluindo instruções de importação (obrigado por esse detalhe). Vamos votar nesta resposta para aumentar a visibilidade.
David Parks
Descobri que quando converti uma imagem desenhada PIL em uma matriz numpy, ao usar matplotlib imshow na matriz, ela foi mostrada de cabeça para baixo, exigindo uma np.flipudcorreção. Embora minha imagem PIL tenha sido criada do zero usando ImageDraw.Draw. Eu acho que é preciso ter cuidado de onde vem a origem de suas coordenadas.
CMCDragonkai
Saúde!! Eu tenho procurado por esta resposta por meio dia. Resolve meu problema de restaurar o eixo original após a imagem da plotagem para o original.
Tinkerbell
16

Você precisa converter sua imagem em uma matriz numpy da seguinte maneira:

import numpy
import PIL

img = PIL.Image.open("foo.jpg").convert("L")
imgarr = numpy.array(img) 
Billal Begueradj
fonte
Essa forma de conversão mantém a imagem, mas resulta em perda de cores. Enfim, para evitar a perda de cor?
Moondra 15/01
7
@moondra Se entendi sua pergunta, você pode substituir .convert("L") por.convert("RGB")
Billal Begueradj
3

O exemplo que eu usei hoje:

import PIL
import numpy
from PIL import Image

def resize_image(numpy_array_image, new_height):
    # convert nympy array image to PIL.Image
    image = Image.fromarray(numpy.uint8(numpy_array_image))
    old_width = float(image.size[0])
    old_height = float(image.size[1])
    ratio = float( new_height / old_height)
    new_width = int(old_width * ratio)
    image = image.resize((new_width, new_height), PIL.Image.ANTIALIAS)
    # convert PIL.Image into nympy array back again
    return array(image)
Uki D. Lucas
fonte
0

Se sua imagem estiver armazenada no formato Blob (ou seja, em um banco de dados), você poderá usar a mesma técnica explicada por Billal Begueradj para converter sua imagem de Blobs em uma matriz de bytes.

No meu caso, eu precisava das minhas imagens armazenadas em uma coluna de blob em uma tabela db:

def select_all_X_values(conn):
    cur = conn.cursor()
    cur.execute("SELECT ImageData from PiecesTable")    
    rows = cur.fetchall()    
    return rows

Criei uma função auxiliar para alterar meu conjunto de dados em np.array:

X_dataset = select_all_X_values(conn)
imagesList = convertToByteIO(np.array(X_dataset))

def convertToByteIO(imagesArray):
    """
    # Converts an array of images into an array of Bytes
    """
    imagesList = []

    for i in range(len(imagesArray)):  
        img = Image.open(BytesIO(imagesArray[i])).convert("RGB")
        imagesList.insert(i, np.array(img))

    return imagesList

Depois disso, pude usar os byteArrays na minha rede neural.

plt.imshow(imagesList[0])
Charles Vogt
fonte
0

Converter Numpy to PILimagem ePIL to Numpy

import numpy as np
from PIL import Image

def pilToNumpy(img):
    return np.array(img)

def NumpyToPil(img):
    return Image.fromarray(img)
Kamran Gasimov
fonte
-1
def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

Você pode transformar a imagem em numpy analisando a imagem na função numpy () depois de extrair os recursos (desnormalização)

Thiyagu
fonte