Especificar y guardar una figura con tamaño exacto en píxeles

152

Digamos que tengo una imagen de tamaño 3841 x 7195 píxeles. Me gustaría guardar el contenido de la figura en el disco, lo que da como resultado una imagen del tamaño exacto que especifico en píxeles.

Sin eje, sin títulos. Solo la imagen. Personalmente, no me importan los DPI, ya que solo quiero especificar el tamaño que toma la imagen en la pantalla en el disco en píxeles .

He leído otros hilos , y todos parecen hacer conversiones a pulgadas y luego especifican las dimensiones de la figura en pulgadas y ajustan los ppp de alguna manera. Me gustaría evitar lidiar con la posible pérdida de precisión que podría resultar de las conversiones de píxeles a pulgadas.

He intentado 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)

sin suerte (Python se queja de que el ancho y la altura deben estar por debajo de 32768 (?))

Por todo lo que he visto, matplotlibrequiere que se especifique el tamaño de la figura inchesy dpi, pero solo me interesan los píxeles que la figura toma en el disco. ¿Cómo puedo hacer esto?

Para aclarar: estoy buscando una manera de hacer esto matplotlib, y no con otras bibliotecas para guardar imágenes.

Amelio Vazquez-Reina
fuente
Con matplotlib, no es posible establecer el tamaño de la figura directamente en pulgadas.
tiago

Respuestas:

175

Matplotlib no funciona con píxeles directamente, sino con tamaños físicos y DPI. Si desea mostrar una figura con un cierto tamaño de píxel, debe conocer el DPI de su monitor. Por ejemplo, este enlace lo detectará por usted.

Si tiene una imagen de 3841x7195 píxeles, es poco probable que su monitor sea tan grande, por lo que no podrá mostrar una figura de ese tamaño (matplotlib requiere que la figura quepa en la pantalla, si solicita un tamaño demasiado grande se reducirá al tamaño de la pantalla). Imaginemos que desea una imagen de 800x800 píxeles solo como ejemplo. Aquí le mostramos cómo mostrar una imagen de 800x800 píxeles en mi monitor ( my_dpi=96):

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

Básicamente, simplemente divide las dimensiones en pulgadas por su DPI.

Si desea guardar una figura de un tamaño específico, entonces es un asunto diferente. Los DPI de pantalla ya no son tan importantes (a menos que solicite una figura que no cabe en la pantalla). Usando el mismo ejemplo de la figura de 800x800 píxeles, podemos guardarlo en diferentes resoluciones usando la dpipalabra clave de savefig. Para guardarlo en la misma resolución que la pantalla, solo use el mismo dpi:

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

Para guardarlo como una imagen de 8000x8000 píxeles, use un dpi 10 veces más grande:

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

Tenga en cuenta que la configuración del DPI no es compatible con todos los backends. Aquí, se utiliza el backend PNG, pero los backends pdf y ps implementarán el tamaño de manera diferente. Además, cambiar el DPI y los tamaños también afectarán cosas como el tamaño de fuente. Un DPI más grande mantendrá los mismos tamaños relativos de fuentes y elementos, pero si desea fuentes más pequeñas para una figura más grande, debe aumentar el tamaño físico en lugar del DPI.

Volviendo a su ejemplo, si desea guardar una imagen con 3841 x 7195 píxeles, puede hacer lo siguiente:

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

Tenga en cuenta que utilicé la cifra de ppp de 100 para caber en la mayoría de las pantallas, pero la guardé dpi=1000para lograr la resolución requerida. En mi sistema, esto produce un png con 3840x7190 píxeles; parece que el DPI guardado siempre es 0,02 píxeles / pulgada más pequeño que el valor seleccionado, lo que tendrá un efecto (pequeño) en los tamaños de imagen grandes. Un poco más de discusión sobre esto aquí .

tiago
fuente
55
Es útil recordar que los tamaños de monitor (y, por lo tanto, los tamaños estándar de ventana de navegador y ui) son normalmente en términos de 96 ppp, múltiplos de 96. De repente, números como 1440 píxeles son significativos (15 pulgadas) cuando se piensa así.
Danny Staple,
No se pudo hacer que esto funcione pasando figsizea plt.figure. La solución fue hacer que las otras respuestas sugieren que y después de llamar sin figsize , a continuación, llamarfig.set_size_inches(w,h)
Trinidad
Documentos para figurey savefig.
manejar
El enlace no muestra el valor correcto para Apple Thunderbolt Display.
Dmitry
Me gusta esta solución, pero tengo una advertencia. El tamaño del texto se escala inversamente como ppp. (Mi sistema es MacBook Pro, OS X), por lo que para la impresión interactiva, hacer que el dpi sea grande (como 10 * my_dpi) reduce el texto a una invisibilidad cercana.
Prof Huster
16

Esto funcionó para mí, basado en su código, generando una imagen png de 93Mb con ruido de color y las dimensiones deseadas:

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)

Estoy usando las últimas versiones PIP de las bibliotecas Python 2.7 en Linux Mint 13.

¡Espero que ayude!

heltonbiker
fuente
44
Establecer un ppp muy bajo significará que las fuentes apenas serán visibles, a menos que se utilicen explícitamente tamaños de fuente muy grandes.
tiago
1
Probablemente sea mejor establecer un dpi más alto y dividir el tamaño de su pulgada (que es arbitrario de todos modos) por ese dpi. Aparte de eso, su configuración produce un píxel exacto para la reproducción de píxeles, ¡gracias!
FrenchKheldar
Estoy tratando de usar esto antes de guardar imágenes con elementos de la trama (círculos, líneas, ...). Esto perturba el ancho de línea, de modo que los elementos son apenas visibles.
Gauthier
5

Según la respuesta aceptada por tiago, aquí hay una pequeña función genérica que exporta una matriz numpy a una imagen que tiene la misma resolución que la matriz:

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

Como se dijo en la respuesta anterior de tiago, la pantalla DPI debe encontrarse primero, lo que se puede hacer aquí, por ejemplo: http://dpi.lv

Agregué un argumento adicional resize_facten la función que permite exportar la imagen al 50% (0.5) de la resolución original, por ejemplo.

Cirilo
fuente
-1

plt.imsave funcionó para mí. Puede encontrar la documentación aquí: 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)
Alka
fuente
Agregue un código de muestra que muestre exactamente qué parámetro está configurando y los valores recomendados para el caso de uso de la publicación original.
Sarah Messer