Specificare e salvare una figura con dimensioni esatte in pixel


152

Supponiamo di avere un'immagine di dimensioni 3841 x 7195 pixel. Vorrei salvare il contenuto della figura su disco, dando come risultato un'immagine della dimensione esatta che specifico in pixel.

Nessun asse, nessun titolo. Solo l'immagine. Personalmente non mi preoccupo dei DPI, in quanto desidero solo specificare la dimensione che l'immagine assume sullo schermo del disco in pixel .

Ho letto altri thread e sembrano tutti fare conversioni in pollici, quindi specificare le dimensioni della figura in pollici e regolare i dpi in qualche modo. Vorrei evitare di affrontare la potenziale perdita di precisione che potrebbe derivare da conversioni da pixel a pollici.

Ho provato con:

w = 7195
h = 3841
fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig(some_path, dpi=1)

senza fortuna (Python lamenta che larghezza e altezza devono essere inferiori a 32768 (?))

Da tutto quello che ho visto, è matplotlibnecessario specificare la dimensione della figura inchese dpi, ma sono interessato solo ai pixel che la figura prende sul disco. Come posso fare questo?

Per chiarire: sto cercando un modo per farlo con matplotlib, e non con altre librerie di salvataggio delle immagini.


Con matplotlib, non è possibile impostare la dimensione della figura direttamente in pollici.
Tiago,

Risposte:


175

Matplotlib non funziona direttamente con i pixel, ma piuttosto con dimensioni fisiche e DPI. Se si desidera visualizzare una figura con una determinata dimensione di pixel, è necessario conoscere il DPI del monitor. Ad esempio questo link lo rileverà per te.

Se hai un'immagine di 3841x7195 pixel, è improbabile che il tuo monitor sia così grande, quindi non sarai in grado di mostrare una figura di quelle dimensioni (matplotlib richiede che la figura si adatti allo schermo, se chiedi una dimensione troppo grande si ridurrà alle dimensioni dello schermo). Immaginiamo di volere un'immagine di 800x800 pixel solo per un esempio. Ecco come mostrare un'immagine di 800x800 pixel sul mio monitor ( my_dpi=96):

plt.figure(figsize=(800/my_dpi, 800/my_dpi), dpi=my_dpi)

Quindi sostanzialmente dividi le dimensioni in pollici per il tuo DPI.

Se vuoi salvare una figura di una dimensione specifica, allora è una questione diversa. I DPI dello schermo non sono più così importanti (a meno che non si chieda una figura che non si adatta allo schermo). Utilizzando lo stesso esempio della cifra di 800x800 pixel, possiamo salvarlo con risoluzioni diverse utilizzando la dpiparola chiave di savefig. Per salvarlo con la stessa risoluzione dello schermo basta usare lo stesso dpi:

plt.savefig('my_fig.png', dpi=my_dpi)

Per salvarlo come immagine di 8000x8000 pixel, utilizzare un dpi 10 volte più grande:

plt.savefig('my_fig.png', dpi=my_dpi * 10)

Si noti che l'impostazione del DPI non è supportata da tutti i backend. Qui viene utilizzato il backend PNG, ma i backend pdf e ps implementeranno le dimensioni in modo diverso. Inoltre, la modifica del DPI e delle dimensioni influenzerà anche cose come la dimensione del carattere. Un DPI più grande manterrà le stesse dimensioni relative di caratteri ed elementi, ma se si desidera caratteri più piccoli per una figura più grande, è necessario aumentare la dimensione fisica anziché il DPI.

Tornando al tuo esempio, se vuoi salvare un'immagine con 3841 x 7195 pixel, puoi fare quanto segue:

plt.figure(figsize=(3.841, 7.195), dpi=100)
( your code ...)
plt.savefig('myfig.png', dpi=1000)

Nota che ho usato la cifra in dpi di 100 per adattarmi alla maggior parte degli schermi, ma ho salvato con dpi=1000per ottenere la risoluzione richiesta. Nel mio sistema questo produce un png con 3840x7190 pixel - sembra che il DPI salvato sia sempre 0,02 pixel / pollice più piccolo del valore selezionato, il che avrà un effetto (piccolo) su immagini di grandi dimensioni. Qualche altra discussione su questo qui .


5
È utile ricordare che le dimensioni del monitor (e quindi le dimensioni standard del browser e dell'interfaccia utente) sono normalmente in termini di 96 dpi - multipli di 96. Improvvisamente numeri come 1440 pixel sono significativi (15 pollici) se pensati in questo modo.
Danny Staple,

Impossibile ottenere questo al lavoro passando figsizea plt.figure. La soluzione era fare come suggeriscono le altre risposte e dopo averlo chiamato senza figsize , quindi chiamarefig.set_size_inches(w,h)
Trinidad

Documenti per figuree savefig.
gestire il

Il link non mostra il valore corretto per Apple Thunderbolt Display.
Dmitry,

Mi piace questa soluzione, ma ho un avvertimento. Le dimensioni del testo vengono ridimensionate inversamente come dpi. (Il mio sistema è MacBook Pro, OS X), quindi per la stampa interattiva che rende i dpi di grandi dimensioni (come 10 * my_dpi), il testo si riduce quasi all'invisibilità.
Prof Huster,

16

Questo ha funzionato per me, in base al tuo codice, generando un'immagine png di 93 Mb con disturbo del colore e le dimensioni desiderate:

import matplotlib.pyplot as plt
import numpy

w = 7195
h = 3841

im_np = numpy.random.rand(h, w)

fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig('figure.png', dpi=1)

Sto usando le ultime versioni PIP delle librerie Python 2.7 in Linux Mint 13.

Spero che aiuti!


4
L'impostazione di un dpi molto basso significa che i caratteri saranno difficilmente visibili, a meno che non vengano utilizzati in modo esplicito dimensioni di caratteri molto grandi.
Tiago,

1
Probabilmente è meglio impostare un dpi più alto e dividere la dimensione in pollici (che è comunque arbitraria) per quel dpi. A parte questo, la tua configurazione produce un pixel esatto per la riproduzione dei pixel, grazie!
FrenchKheldar,

Sto cercando di usarlo prima di salvare le immagini con elementi della trama (cerchi, linee, ...). Ciò disturba la larghezza della linea, in modo che gli elementi siano appena visibili.
Gauthier,

5

Sulla base della risposta accettata da Tiago, ecco una piccola funzione generica che esporta un array intorpidito in un'immagine con la stessa risoluzione dell'array:

import matplotlib.pyplot as plt
import numpy as np

def export_figure_matplotlib(arr, f_name, dpi=200, resize_fact=1, plt_show=False):
    """
    Export array as figure in original resolution
    :param arr: array of image to save in original resolution
    :param f_name: name of file where to save figure
    :param resize_fact: resize facter wrt shape of arr, in (0, np.infty)
    :param dpi: dpi of your screen
    :param plt_show: show plot or not
    """
    fig = plt.figure(frameon=False)
    fig.set_size_inches(arr.shape[1]/dpi, arr.shape[0]/dpi)
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.set_axis_off()
    fig.add_axes(ax)
    ax.imshow(arr)
    plt.savefig(f_name, dpi=(dpi * resize_fact))
    if plt_show:
        plt.show()
    else:
        plt.close()

Come detto nella precedente risposta di Tiago, è necessario prima trovare lo schermo DPI, che può essere fatto qui ad esempio: http://dpi.lv

Ho aggiunto un ulteriore argomento resize_factnella funzione in base al quale puoi esportare l'immagine al 50% (0,5) della risoluzione originale, ad esempio.


2

L'OP vuole conservare i dati 1: 1 pixel. Come astronomo che lavora con le immagini scientifiche non posso permettere alcuna interpolazione dei dati delle immagini in quanto introdurrebbe rumore o errori sconosciuti e imprevedibili. Ad esempio, ecco uno snippet di un'immagine 480x480 salvata tramite pyplot.savefig (): dettaglio dei pixel ricampionati con matplotlib circa 2 x 2, ma notare la colonna di 1 x 2 pixel

Puoi vedere che la maggior parte dei pixel è stata semplicemente raddoppiata (quindi un pixel 1x1 diventa 2x2) ma alcune colonne e righe sono diventate 1x2 o 2x1 per pixel, il che significa che i dati scientifici originali sono stati modificati.

Come suggerito da Alka, plt.imsave () che realizzerà ciò che l'OP chiede. Supponiamo che tu abbia dei dati di immagine memorizzati nell'array di immagini, quindi si può fare qualcosa del genere

plt.imsave(fname='my_image.png', arr=im, cmap='gray_r', format='png')

dove il nome del file ha l'estensione "png" in questo esempio (ma è comunque necessario specificare il formato con format = 'png' per quanto ne so), l'array di immagini è arr e abbiamo scelto la scala di grigi invertita "gray_r" come la mappa dei colori. Di solito aggiungo vmin e vmax per specificare l'intervallo dinamico ma questi sono opzionali.

Il risultato finale è un file png con esattamente le stesse dimensioni in pixel dell'array im.

Nota: l'OP non ha specificato assi, ecc. Che è esattamente ciò che questa soluzione fa. Se si desidera aggiungere assi, segni di spunta, ecc. Il mio approccio preferito è quello di farlo su un grafico separato, salvando con transparent = True (PNG o PDF), quindi sovrapporre quest'ultimo sull'immagine. Ciò garantisce che i pixel originali siano stati mantenuti intatti.


Grazie. Sono passati alcuni anni da quando ho chiesto questo Q, ma da quello che ricordo e vedo nella tua risposta, questo funzionerebbe bene quando stai cercando di salvare una matrice di dati come immagine, ma cosa succede se vuoi salvare è un vero figura stessa (indipendentemente dal suo contenuto) e controlla ancora esattamente le dimensioni in pixel del file di immagine risultante?
Amelio Vazquez-Reina,

-1

plt.imsave ha funzionato per me. Puoi trovare la documentazione qui: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.imsave.html

#file_path = directory address where the image will be stored along with file name and extension
#array = variable where the image is stored. I think for the original post this variable is im_np
plt.imsave(file_path, array)

Aggiungi un codice di esempio che mostri esattamente quale parametro stai impostando e i valori consigliati per il caso d'uso del post originale.
Sarah Messer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.