HOWTO · Matplotlib

Converti un array NumPy in un'immagine PIL in Python

Questo tutorial spiega come convertire un array NumPy in un'immagine PIL utilizzando Image.fromarray() dal pacchetto PIL.

In questa pagina

Usa Image.fromarray() di Pillow per creare un’immagine PIL da un array NumPy. Un array bidimensionale rappresenta pixel in scala di grigi; gli array con forma (height, width, 3) o (height, width, 4) rappresentano pixel RGB o RGBA. Anche il dtype e i valori dell’array sono importanti: controllali prima della conversione. PIL è il nome di importazione più comune, mentre Pillow è la libreria mantenuta.

Per un’immagine RGB, crea un array a tre canali di tipo uint8 e passalo a Image.fromarray(). L’output mostra la modalità dell’immagine, le dimensioni (width, height) e il pixel nell’angolo in alto a sinistra.

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

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)

L’immagine risultante è un oggetto Pillow conservato in memoria. Puoi controllarne la modalità e le dimensioni, quindi visualizzarla in un ambiente che supporta l’anteprima delle immagini oppure salvarla su file.

Convertire un array NumPy in un’immagine PIL con Image.fromarray()

Image.fromarray(array) deduce la modalità dell’immagine dalla forma e dal dtype dell’array. Per dati in scala di grigi, usa un array bidimensionale con forma (height, width). Per dati RGB, usa (height, width, 3); un quarto canale opzionale produce RGBA. Le prime due dimensioni indicano righe e colonne: un array con 40 righe e 60 colonne crea un’immagine larga 60 pixel e alta 40.

Usa np.uint8 per i normali campioni d’immagine a 8 bit quando i valori sono già interi compresi tra 0 e 255. Non convertire il tipo alla cieca: il cast dei dati in virgola mobile tronca le parti frazionarie e i valori fuori dall’intervallo rappresentabile possono andare in overflow. Se i dati sono misurati su una scala diversa, stabilisci come mapparli all’intensità visiva prima della conversione. Per esempio, i dati nell’intervallo 0–1 possono essere moltiplicati per 255 e arrotondati; per un altro intervallo occorre una normalizzazione esplicita, basata sul significato dei dati.

Preparare forma, dtype e intervallo di valori dell’array

Un array con forma (height, width) rappresenta un’immagine in scala di grigi. Gli array RGB e RGBA aggiungono una dimensione finale per i canali, con tre o quattro valori per pixel. Una forma come (height, width, 1) non è la consueta forma bidimensionale per la scala di grigi; se quell’ultimo asse di dimensione uno rappresenta i livelli di grigio, rimuovilo con array[..., 0]. Prima della conversione, controlla array.shape, array.dtype e i valori minimo e massimo. L’array deve contenere una griglia rettangolare regolare: liste annidate irregolari non sono dati immagine.

Ecco una conversione in scala di grigi completa e autonoma. I valori sono già nell’intervallo a 8 bit, quindi non serve ridimensionarli. L’immagine risultante usa la modalità per la scala di grigi L e ha dimensioni (3, 2).

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

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 la sorgente è un array in virgola mobile con valori da 0 a 1, converti i dati in modo esplicito con np.round(array * 255).astype(np.uint8). Questa trasformazione è adatta solo quando quei valori sono intensità normalizzate. Per misurazioni come temperature o punteggi arbitrari, scegli limiti inferiore e superiore significativi, limita i valori a quell’intervallo oppure normalizzali e documenta la mappatura; altrimenti il contrasto e i valori dei pixel possono risultare fuorvianti.

Applicare una mappa di colori Matplotlib prima di creare un’immagine PIL

Usa una mappa di colori Matplotlib quando la sorgente è un campo scalare bidimensionale e vuoi rappresentarne i valori con i colori. Normalizza i dati nell’intervallo previsto dalla mappa scelta, quindi converti il risultato della mappa in un array RGB o RGBA a 8 bit. La mappa di colori plasma restituisce canali rosso, verde, blu e alfa normalizzati; eliminando il canale alfa con [..., :3] si ottiene un array RGB. È un passaggio distinto da Image.fromarray(): la mappa assegna i colori, mentre Pillow racchiude quei pixel in un’immagine.

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

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)

Questo esempio presuppone che values sia già normalizzato tra 0 e 1. Se l’intervallo effettivo dei valori scalari è diverso, normalizzali usando limiti appropriati prima di applicare la mappa di colori. Una mappa di colori è utile per la visualizzazione, ma trasforma i valori scalari in colori e non dovrebbe sostituire le misurazioni originali quando servono per l’analisi. L’immagine risultante è RGB e ha dimensioni (2, 2).

Ridimensionare e salvare l’immagine PIL

Dopo la conversione, ridimensiona l’immagine con image.resize((width, height)); Pillow prende le nuove dimensioni nell’ordine larghezza, altezza. Salvare e visualizzare sono operazioni distinte: show() apre un’anteprima nell’ambiente locale, mentre save() scrive un file immagine. Scegli un’estensione coerente con il formato desiderato, per esempio .png per un output senza perdita o .jpg per JPEG.

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

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)

L’esempio crea l’input in memoria e scrive resized-image.png nella directory di lavoro corrente. In un’applicazione, usa un percorso adatto alla posizione in cui vuoi salvare il file. Il salvataggio non modifica l’array NumPy, mentre il ridimensionamento cambia le dimensioni in pixel dell’immagine Pillow.

Problemi comuni di conversione

  • Modalità imprevista o forma rifiutata: controlla array.shape. Usa (height, width) per la scala di grigi e tre o quattro canali nell’ultima dimensione per RGB o RGBA. Rimuovi un eventuale canale singolo indesiderato oppure riordina i canali se la sorgente usa un’altra convenzione.
  • Luminosità o colori errati: controlla l’intervallo dei valori prima della conversione. I valori in virgola mobile tra 0 e 1 richiedono una scalatura deliberata per un’immagine a 8 bit; i valori già compresi tra 0 e 255 non vanno moltiplicati di nuovo. Per altri intervalli, normalizza rispetto a limiti documentati.
  • dtype inatteso: controlla array.dtype e decidi come convertire i dati. Un cast da solo non li ridimensiona; conserva una copia della sorgente se la precisione originale è importante.
  • Risultato della mappa di colori errato: verifica che l’input scalare sia stato normalizzato come previsto e controlla se il risultato ha tre canali RGB o quattro canali RGBA. Converti i canali colore normalizzati in uint8 prima di creare l’immagine.
  • Il file immagine non viene creato: chiama save() con un percorso scrivibile e un’estensione supportata. Usa show() solo per visualizzare un’anteprima: non salva l’immagine.