HOWTO · Matplotlib
Converter uma NumPy Array em imagem PIL em Python
Este tutorial explica como podemos converter um array NumPy para uma imagem PIL usando o Image.fromarray() do pacote PIL.
Nesta página
Use Image.fromarray() do Pillow para criar uma imagem PIL a partir de um array NumPy. Um array bidimensional representa pixels em tons de cinza; arrays com formato (height, width, 3) ou (height, width, 4) representam pixels RGB ou RGBA. O dtype e os valores do array também importam, então confira-os antes da conversão. PIL é o nome de importação conhecido, enquanto Pillow é a biblioteca mantida atualmente.
Para uma imagem RGB, crie um array de três canais do tipo uint8 e passe-o para Image.fromarray(). A saída informa o modo da imagem, seu tamanho (width, height) e o pixel no canto superior esquerdo.
O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.
import numpy as np
from PIL import Image
pixels = np.array(
[[[255, 0, 0], [0, 255, 0]], [[0, 0, 255], [255, 255, 255]]],
dtype=np.uint8,
)
image = Image.fromarray(pixels)
print(image.mode, image.size, image.getpixel((0, 0)))
RGB (2, 2) (255, 0, 0)
A imagem resultante é um objeto Pillow mantido na memória. Você pode conferir seu modo e suas dimensões e, em seguida, exibi-la em um ambiente compatível com pré-visualizações de imagem ou salvá-la em um arquivo.
Converter um array NumPy em imagem PIL com Image.fromarray()
Image.fromarray(array) infere o modo da imagem com base no formato e no dtype do array. Para dados em tons de cinza, use um array bidimensional com formato (height, width). Para dados RGB, use (height, width, 3); um quarto canal opcional corresponde a RGBA. As duas primeiras dimensões são linhas e colunas, portanto um array com 40 linhas e 60 colunas cria uma imagem com 60 pixels de largura e 40 de altura.
Use np.uint8 para amostras de imagem comuns de 8 bits quando os valores já forem inteiros entre 0 e 255. Não faça a conversão de tipo sem verificar: converter dados de ponto flutuante trunca as partes fracionárias, e valores fora do intervalo representável podem sofrer wraparound. Para dados medidos em outra escala, decida como eles devem ser mapeados para a intensidade de exibição antes da conversão. Por exemplo, valores no intervalo de 0 a 1 podem ser multiplicados por 255 e arredondados; dados em outro intervalo precisam de uma normalização explícita, adequada ao seu significado.
Preparar o formato, o dtype e o intervalo de valores do array
Um array com formato (height, width) representa uma imagem em tons de cinza. Arrays RGB e RGBA acrescentam uma dimensão de canais no final, com três ou quatro valores por pixel. Um formato como (height, width, 1) não é o formato bidimensional usual para tons de cinza; remova esse último eixo unitário com array[..., 0] quando ele representar tons de cinza. Antes da conversão, confira array.shape, array.dtype e os valores mínimo e máximo. O array deve conter uma grade retangular regular; listas aninhadas irregulares não são dados de imagem.
Veja uma conversão autossuficiente para tons de cinza. Os valores já estão no intervalo de 8 bits, então não é necessário redimensioná-los. A imagem resultante usa o modo de tons de cinza L e tem tamanho (3, 2).
O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.
import numpy as np
from PIL import Image
gray = np.array([[0, 64, 128], [32, 160, 255]], dtype=np.uint8)
image = Image.fromarray(gray)
print(image.mode, image.size)
L (3, 2)
Se a origem for um array de ponto flutuante com valores de 0 a 1, faça a conversão de forma deliberada com np.round(array * 255).astype(np.uint8). Essa transformação só é adequada quando esses valores são intensidades normalizadas. Para medições como temperaturas ou pontuações arbitrárias, escolha limites inferior e superior significativos, limite ou normalize os valores a esse intervalo e documente o mapeamento; caso contrário, o contraste e os valores dos pixels podem induzir a erro.
Aplicar um mapa de cores do Matplotlib antes de criar uma imagem PIL
Use um mapa de cores do Matplotlib quando a origem for um campo escalar bidimensional e você quiser representar seus valores por meio de cores. Normalize os dados para o intervalo esperado pelo mapa de cores escolhido e, em seguida, converta o resultado em um array RGB ou RGBA de 8 bits. O mapa de cores plasma retorna canais normalizados de vermelho, verde, azul e alfa; remover o canal alfa com [..., :3] produz um array RGB. Essa etapa é separada de Image.fromarray(): o mapa de cores gera as cores, e o Pillow envolve esses pixels em uma imagem.
O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.
import numpy as np
import matplotlib
from PIL import Image
values = np.array([[0.0, 0.25], [0.75, 1.0]])
colors = matplotlib.colormaps["plasma"](values)[..., :3]
rgb = np.round(colors * 255).astype(np.uint8)
image = Image.fromarray(rgb)
print(image.mode, image.size)
RGB (2, 2)
Este exemplo pressupõe que values já esteja normalizado entre 0 e 1. Se o intervalo escalar real for diferente, normalize os dados com limites apropriados antes de aplicar o mapa de cores. Um mapa de cores é útil para visualização, mas transforma valores escalares em cores de exibição; portanto, não substitua por ele as medições originais quando elas ainda forem necessárias para análise. A imagem resultante é RGB e tem tamanho (2, 2).
Redimensionar e salvar a imagem PIL
Depois da conversão, redimensione a imagem com image.resize((width, height)); o Pillow recebe o novo tamanho na ordem largura e depois altura. Salvar é diferente de exibir: show() abre uma pré-visualização usando o ambiente local, enquanto save() grava um arquivo de imagem. Escolha uma extensão de arquivo compatível com o formato desejado, como .png para uma saída sem perdas ou .jpg para JPEG.
O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.
import numpy as np
from PIL import Image
pixels = np.zeros((20, 30, 3), dtype=np.uint8)
pixels[:, :15] = [255, 80, 0]
pixels[:, 15:] = [20, 90, 220]
image = Image.fromarray(pixels)
resized = image.resize((60, 40))
resized.save("resized-image.png")
print(resized.size)
O exemplo cria a entrada na memória e grava resized-image.png no diretório de trabalho atual. Em uma aplicação, use um caminho adequado ao local onde deseja salvar a saída. Salvar não modifica o array NumPy, e redimensionar altera as dimensões em pixels da imagem Pillow.
Problemas comuns de conversão
- Modo inesperado ou formato rejeitado: confira
array.shape. Use(height, width)para tons de cinza e três ou quatro canais na última dimensão para RGB ou RGBA. Remova um canal unitário indesejado ou reordene os canais se a origem usar outra convenção. - Brilho ou cores incorretos: confira o intervalo dos valores antes da conversão. Valores armazenados como ponto flutuante entre 0 e 1 precisam ser escalados deliberadamente para uma imagem de 8 bits; valores que já estejam entre 0 e 255 não devem ser multiplicados novamente. Para outros intervalos, normalize os dados com limites documentados.
- dtype inesperado: confira
array.dtypee decida como convertê-lo. Uma conversão de tipo, por si só, não redimensiona os valores; preserve uma cópia da origem se a precisão original for importante. - Resultado do mapa de cores incorreto: confirme que a entrada escalar foi normalizada como esperado e confira se o resultado tem três canais RGB ou quatro canais RGBA. Converta os canais de cor normalizados para
uint8antes de criar a imagem. - Arquivo de imagem não criado: chame
save()com um caminho gravável e uma extensão compatível. Useshow()apenas para pré-visualizar; ele não salva a imagem.