Gráficos de Matplotlib: eliminación de ejes, leyendas y espacios en blanco

285

Soy nuevo en Python y Matplotlib, me gustaría simplemente aplicar un mapa de colores a una imagen y escribir la imagen resultante, sin usar ejes, etiquetas, títulos o cualquier cosa que Matplotlib agregue automáticamente. Aquí esta lo que hice:

def make_image(inputname,outputname):
    data = mpimg.imread(inputname)[:,:,0]
    fig = plt.imshow(data)
    fig.set_cmap('hot')
    fig.axes.get_xaxis().set_visible(False)
    fig.axes.get_yaxis().set_visible(False)
    plt.savefig(outputname)

Elimina con éxito el eje de la figura, pero la figura guardada presenta un relleno blanco y un marco alrededor de la imagen real. ¿Cómo puedo eliminarlos (al menos el relleno blanco)? Gracias

sunmat
fuente
2
Todas las soluciones a esta pregunta están enfocadas imshow. Si tiene un diagrama de dispersión, la siguiente respuesta podría ayudarlo: stackoverflow.com/a/40727744/4124317
ImportanceOfBeingErnest

Respuestas:

373

Creo que el comando axis('off')se ocupa de uno de los problemas de manera más sucinta que cambiar cada eje y el borde por separado. Sin embargo, todavía deja el espacio en blanco alrededor de la frontera. Agregar bbox_inches='tight'al savefigcomando casi lo lleva allí, puede ver en el siguiente ejemplo que el espacio en blanco que queda es mucho más pequeño, pero aún está presente.

Tenga en cuenta que las versiones más recientes de matplotlib pueden requerir en bbox_inches=0lugar de la cadena 'tight'(a través de @episodeyang y @kadrach)

from numpy import random
import matplotlib.pyplot as plt

data = random.random((5,5))
img = plt.imshow(data, interpolation='nearest')
img.set_cmap('hot')
plt.axis('off')
plt.savefig("test.png", bbox_inches='tight')

ingrese la descripción de la imagen aquí

Enganchado
fuente
44
Siguiendo la sugerencia de unutbu, se puede usar fig = plt.figure(), ax = plt.axes([0,0,1,1])y, a continuación plt.imshow(data,interpolation="nearest". Combinado con plt.axis("off"), debería deshacerse de todo junto a la imagen en sí, ¡con suerte!
PhilMacKay
55
La combinación de los métodos de la pregunta ({fig.axes.get_xaxis (). Set_visible (False) & fig.axes.get_yaxis (). Set_visible (False)} con {plt.axis ('off')}) solucionó el problema para yo. (Ya no hay espacios en blanco). Y no olvide establecer sus {pad_inches} en savefig en 0.
Domagoj
3
Acabo de encontrarme con este problema, ninguna de las soluciones en este hilo y las vinculadas funcionaron. Sin embargo, la especificación explícita bbox_inches=0hizo el truco.
kadrach
1
Tenga en cuenta que si está 'trazando' cosas sobre plt.imshow, como en plt.plot (x, y), debe asegurarse de establecer xlim e ylim en el tamaño de la matriz.
Mathusalem el
1
Esto ya no funciona. Me llevó una hora resolverlo. Ver la respuesta a continuación.
episodeyang
107

Aprendí este truco de matehat, aquí :

import matplotlib.pyplot as plt
import numpy as np

def make_image(data, outputname, size=(1, 1), dpi=80):
    fig = plt.figure()
    fig.set_size_inches(size)
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.set_axis_off()
    fig.add_axes(ax)
    plt.set_cmap('hot')
    ax.imshow(data, aspect='equal')
    plt.savefig(outputname, dpi=dpi)

# data = mpimg.imread(inputname)[:,:,0]
data = np.arange(1,10).reshape((3, 3))

make_image(data, '/tmp/out.png')

rendimientos

ingrese la descripción de la imagen aquí

unutbu
fuente
3
Estoy bastante seguro de que tiene la respuesta correcta (aunque probablemente haya más de una forma de hacerlo), pero me pregunto si puede explicar por qué es la respuesta correcta. ¿Qué pasa con su código elimina el espacio en blanco?
Yann
44
@ Yann, además de la documentación, me resulta muy útil comentar una línea a la vez para ver qué efecto tiene cada comando. ¡Es la forma empírica!
unutbu
44
La línea que elimina el borde blanco es plt.Axes(fig, [0,0,1,1]). Esto le dice a matplotlib que cree un conjunto de ejes con la esquina inferior izquierda en el punto ubicado en (0%, 0%), y con un ancho y una altura de (100%, 100%).
PhilMacKay
Esta es la respuesta más correcta, ya que no solo elimina el espacio en blanco para la imagen guardada sino también para los trazados en pantalla.
MaxNoe
Gracias, esta es la ÚNICA solución que funciona bien en mi Ubuntu :)
AlphaGoMK
56

Posible solución más simple:

Simplemente combiné el método descrito en la pregunta y el método de la respuesta de Hooked.

fig = plt.imshow(my_data)
plt.axis('off')
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
plt.savefig('pict.png', bbox_inches='tight', pad_inches = 0)

Después de este código no hay espacios en blanco ni marco.

Sin espacios en blanco, ejes o marco

Domagoj
fuente
3
Su método eliminó todos los espacios en blanco alrededor de esta imagen, buen trabajo, pero aún no sé por qué agregar el comando fig.axes.get_xaxis().set_visible(False)resuelve el problema. Gracias
ollydbg23
55
Sí, si no llama a estos comandos, se eliminarán la mayoría de los espacios en blanco. Sin embargo, en mi caso todavía había algunos espacios entre mi trama y el borde de la imagen .png (quizás 2px de ancho). Al llamar a los siguientes comandos, me he librado de esos tercos espacios.
Domagoj
3
Sin embargo, tenga en cuenta que lo anterior fallará en presencia de múltiples ejes en la misma figura (en mi caso, una imagen incrustada en el gráfico). Solución: fig.axes[0]y, en general, todos o ejes seleccionados.
Ioannis Filippidis
29

Nadie lo mencionó imsaveaún, lo que lo convierte en una frase:

import matplotlib.pyplot as plt
import numpy as np

data = np.arange(10000).reshape((100, 100))
plt.imsave("/tmp/foo.png", data, format="png", cmap="hot")

Almacena directamente la imagen tal como está, es decir, no agrega ningún eje o borde / relleno.

ingrese la descripción de la imagen aquí

luator
fuente
Esto es lo que hago la mayor parte del tiempo, pero hay casos en los que necesito la barra de colores, y en estos casos imsave no puede salvarme.
Elias
9

También puede especificar la extensión de la figura al bbox_inchesargumento. Esto eliminaría el relleno blanco alrededor de la figura.

def make_image(inputname,outputname):
    data = mpimg.imread(inputname)[:,:,0]
    fig = plt.imshow(data)
    fig.set_cmap('hot')
    ax = fig.gca()
    ax.set_axis_off()
    ax.autoscale(False)
    extent = ax.get_window_extent().transformed(plt.gcf().dpi_scale_trans.inverted())
    plt.savefig(outputname, bbox_inches=extent)
Gypaetus
fuente
8

Esto debería eliminar todo el relleno y los bordes:

from matplotlib import pyplot as plt

fig = plt.figure()
fig.patch.set_visible(False)

ax = fig.add_subplot(111)

plt.axis('off')
plt.imshow(data)

extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.savefig("../images/test.png", bbox_inches=extent)
Vlady Veselinov
fuente
Respuesta subestimada! Esto me llevó al 99% del camino. Debería estar más votado porque es el más actualizado
Nathan el
También funciona si no tiene un objeto Axes ( ax) con extent = plt.gca().get_window_extent().transformed(fig.dpi_scale_trans.inverted()).
Brindis
3

La respuesta votada ya no funciona. Para que funcione, debe agregar manualmente un conjunto de ejes a [0, 0, 1, 1] o eliminar el parche de la figura.

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(5, 5), dpi=20)
ax = plt.Axes(fig, [0., 0., 1., 1.])
fig.add_axes(ax)
plt.imshow([[0, 1], [0.5, 0]], interpolation="nearest")
plt.axis('off')                                # same as: ax.set_axis_off()

plt.savefig("test.png")

Alternativamente, puede eliminar el parche. No es necesario agregar una subtrama para eliminar los rellenos. Esto se simplifica a partir de la respuesta de Vlady a continuación.

fig = plt.figure(figsize=(5, 5))
fig.patch.set_visible(False)                   # turn off the patch

plt.imshow([[0, 1], [0.5, 0]], interpolation="nearest")
plt.axis('off')

plt.savefig("test.png", cmap='hot')

Esto se prueba con la versión 3.0.3el 19/06/2019. Imagen a continuación:

ingrese la descripción de la imagen aquí

Una cosa mucho más simple de hacer es usar pyplot.imsave. Para más detalles, vea la respuesta de Luator a continuación.

episodeyang
fuente
para mí solo funciona la versión Axes. me sale relleno con la versión de parche desactivada. gracias de todos modos!
save_jeff
2

Me gustó la respuesta de ubuntu , pero no mostraba explícitamente cómo configurar el tamaño de las imágenes no cuadradas de forma inmediata, así que lo modifiqué para copiar y pegar fácilmente:

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np

def save_image_fix_dpi(data, dpi=100):
    shape=np.shape(data)[0:2][::-1]
    size = [float(i)/dpi for i in shape]

    fig = plt.figure()
    fig.set_size_inches(size)
    ax = plt.Axes(fig,[0,0,1,1])
    ax.set_axis_off()
    fig.add_axes(ax)
    ax.imshow(data)
    fig.savefig('out.png', dpi=dpi)
    plt.show()

Guardar imágenes sin borde es fácil sea cual sea el dpi que elija si se mantiene pixel_size / dpi = size.

data = mpimg.imread('test.png')
save_image_fix_dpi(data, dpi=100)

ingrese la descripción de la imagen aquí

Sin embargo, la visualización es espeluznante. Si elige ppp pequeños, el tamaño de su imagen puede ser mayor que su pantalla y obtendrá un borde durante la visualización. Sin embargo, esto no afecta el ahorro.

Entonces para

save_image_fix_dpi(data, dpi=20)

La pantalla se vuelve confinada (pero guarda trabajos): ingrese la descripción de la imagen aquí

csnemes
fuente
1

Primero, para ciertos formatos de imagen (es decir, TIFF ), puede guardar el mapa de colores en el encabezado y la mayoría de los espectadores mostrarán sus datos con el mapa de colores.

Para guardar una matplotlibimagen real , que puede ser útil para agregar anotaciones u otros datos a las imágenes, he usado la siguiente solución:

fig, ax = plt.subplots(figsize=inches)
ax.matshow(data)  # or you can use also imshow
# add annotations or anything else
# The code below essentially moves your plot so that the upper
# left hand corner coincides with the upper left hand corner
# of the artist
fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0, hspace=0)
# now generate a Bbox instance that is the same size as your
# single axis size (this bbox will only encompass your figure)
bbox = matplotlib.transforms.Bbox(((0, 0), inches))
# now you can save only the part of the figure with data
fig.savefig(savename, bbox_inches=bbox, **kwargs)
David Hoffman
fuente
0

Gracias por las asombrosas respuestas de todos ... Tuve exactamente el mismo problema con querer trazar solo una imagen sin relleno / espacio adicional, etc., así que estaba muy feliz de encontrar las ideas de todos aquí.

Además de la imagen sin relleno, también quería poder agregar fácilmente anotaciones, etc., más allá de un simple diagrama de imagen.

Entonces, lo que terminé haciendo fue combinar la respuesta de David con csnemes para hacer una envoltura simple en el momento de la creación de la figura. Cuando usas eso, no necesitas ningún cambio posterior con imsave () o cualquier otra cosa:

def get_img_figure(image, dpi):
    """
    Create a matplotlib (figure,axes) for an image (numpy array) setup so that
        a) axes will span the entire figure (when saved no whitespace)
        b) when saved the figure will have the same x/y resolution as the array, 
           with the dpi value you pass in.

    Arguments:
        image -- numpy 2d array
        dpi -- dpi value that the figure should use

    Returns: (figure, ax) tuple from plt.subplots
    """

    # get required figure size in inches (reversed row/column order)
    inches = image.shape[1]/dpi, image.shape[0]/dpi

    # make figure with that size and a single axes
    fig, ax = plt.subplots(figsize=inches, dpi=dpi)

    # move axes to span entire figure area
    fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0, hspace=0)

    return fig, ax
Ricardo
fuente