Gráficos Matplotlib: removendo eixos, legendas e espaços em branco

285

Eu sou novo no Python e no Matplotlib, gostaria de simplesmente aplicar o mapa de cores a uma imagem e escrever a imagem resultante, sem usar eixos, rótulos, títulos ou qualquer coisa normalmente adicionada automaticamente pelo matplotlib. Aqui está o que eu fiz:

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)

Ele remove com êxito o eixo da figura, mas a figura salva apresenta um preenchimento branco e uma moldura em torno da imagem real. Como posso removê-los (pelo menos o preenchimento branco)? obrigado

sunmat
fonte
2
Todas as soluções nesta questão estão focadas imshow. Se você tiver um gráfico de dispersão, a seguinte resposta poderá ajudá-lo: stackoverflow.com/a/40727744/4124317
ImportanceOfBeingErnest

Respostas:

373

Penso que o comando axis('off')cuida de um dos problemas de maneira mais sucinta do que mudar cada eixo e a borda separadamente. No entanto, ele ainda deixa o espaço em branco ao redor da borda. A adição bbox_inches='tight'ao savefigcomando quase o leva até lá, você pode ver no exemplo abaixo que o espaço em branco restante é muito menor, mas ainda está presente.

Observe que as versões mais recentes do matplotlib podem exigir, em bbox_inches=0vez da string 'tight'(via @episodeyang e @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')

insira a descrição da imagem aqui

Hooked
fonte
4
Seguindo a sugestão de unutbu, você poderia usar fig = plt.figure(), ax = plt.axes([0,0,1,1])e, em seguida plt.imshow(data,interpolation="nearest". Combinado com plt.axis("off"), ele deve se livrar de tudo que estiver ao lado da imagem, espero!
22413 PhilMacKay
5
A combinação dos métodos da pergunta ({fig.axes.get_xaxis (). Set_visible (False) e fig.axes.get_yaxis (). Set_visible (False)} com {plt.axis ('off')} resolveu o problema de mim. (Não há mais espaços em branco). E não se esqueça de definir o seu {pad_inches} em savefig a 0.
Domagoj
3
Acabei de me deparar com esse problema, nenhuma das soluções neste segmento, e as vinculadas, funcionaram. No entanto, especificando explicitamente bbox_inches=0o truque.
23415 kadrach
1
Observe que, se você estiver 'plotando' coisas sobre o plt.imshow, como no plt.plot (x, y), é necessário definir o xlim e o ylim para o tamanho da matriz.
Mathusalem
1
isso não funciona mais. Levei uma hora para descobrir. Veja a resposta abaixo.
episodeyang
107

Eu aprendi esse truque com matehat, aqui :

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

rendimentos

insira a descrição da imagem aqui

unutbu
fonte
3
Tenho certeza de que você tem a resposta correta (embora provavelmente exista mais de uma maneira de fazê-lo), mas estou me perguntando se você pode explicar por que é a resposta certa. E o seu código remove o espaço em branco?
Yann
4
@ Yann, além da documentação, acho muito útil comentar uma linha de cada vez para ver qual efeito cada comando tem. É o caminho empírico!
23412 unutbu
4
A linha que remove a borda branca é plt.Axes(fig, [0,0,1,1]). Isso indica ao matplotlib para criar um conjunto de eixos com o canto inferior esquerdo no ponto localizado em (0%, 0%) e com largura e altura (100%, 100%).
22813 PhilMacKay
Essa é a resposta mais correta, pois ela não apenas remove o espaço em branco da imagem salva, mas também as plotagens na tela.
MaxNoe
Obrigado, esta é a única solução funciona bem no meu Ubuntu :)
AlphaGoMK
56

Possível solução mais simples:

Simplesmente combinei o método descrito na pergunta e o método da resposta 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)

Após esse código, não há espaços em branco nem quadro.

Sem espaços em branco, eixos ou estrutura

Domagoj
fonte
3
Seu método removeu todos os espaços em branco em torno desta imagem, bom trabalho, mas ainda não sei por que adicionar o comando fig.axes.get_xaxis().set_visible(False)resolver o problema. Graças
ollydbg23
5
Sim, se você não chamar esses comandos, a maioria dos espaços em branco será removida. No entanto, no meu caso, ainda havia alguns espaços entre minha plotagem e a borda da imagem .png (talvez 2px de largura). Ao chamar os seguintes comandos, me livrei desses espaços teimosos.
Domagoj
3
Note, no entanto, que o acima falhará na presença de vários eixos na mesma figura (no meu caso, uma imagem incorporada no gráfico). Solução:, fig.axes[0]e em geral todos ou eixos selecionados.
Ioannis Filippidis 06/06
29

Ninguém mencionado imsaveainda, o que torna isso uma linha:

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

Ele armazena diretamente a imagem como ela é, ou seja, não adiciona nenhum eixo ou borda / preenchimento.

insira a descrição da imagem aqui

luator
fonte
É isso que faço na maioria das vezes, mas há casos em que preciso da barra de cores e, nesses casos, o imsave não pode me salvar.
Elias
9

Você também pode especificar a extensão da figura no bbox_inchesargumento. Isso eliminaria o preenchimento branco ao redor da 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
fonte
8

Isso deve remover todos os preenchimentos e bordas:

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
fonte
Resposta subestimada! Isso me deixou 99% do caminho até lá. Deve ser mais upvoted porque é a data de up-to-mais
Nathan
Também funciona se você não tiver um objeto Axes ( ax) com extent = plt.gca().get_window_extent().transformed(fig.dpi_scale_trans.inverted()).
Brinde
3

A resposta votada não funciona mais. Para que funcione, você precisa adicionar manualmente um eixo definido como [0, 0, 1, 1] ou remover o patch da 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")

Como alternativa, você pode apenas remover o patch. Você não precisa adicionar uma subparcela para remover os preenchimentos. Isso é simplificado a partir da resposta de Vlady abaixo

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

Isso foi testado com a versão 3.0.3em 2019/06/19. Imagem veja abaixo:

insira a descrição da imagem aqui

Uma coisa muito mais simples de fazer é usar pyplot.imsave. Para detalhes, veja a resposta do luator abaixo

episodeyang
fonte
para mim, apenas a versão do Axes funciona. eu recebo preenchimento com o patch desativado. thx de qualquer maneira!
save_jeff
2

Gostei da resposta do ubuntu , mas ele não estava mostrando explicitamente como definir o tamanho de imagens não quadradas prontas para uso, então modifiquei-a para facilitar a cópia e colar:

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

É fácil salvar imagens sem borda, seja qual for o dpi escolhido, se o tamanho de pixel_size / dpi = for mantido.

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

insira a descrição da imagem aqui

No entanto, a exibição é assustadora. Se você escolher dpi pequenos, o tamanho da imagem poderá ser maior que a tela e você terá margens durante a exibição. No entanto, isso não afeta a economia.

Então para

save_image_fix_dpi(data, dpi=20)

A exibição fica delimitada (mas salvar obras): insira a descrição da imagem aqui

csnemes
fonte
1

Primeiro, para determinados formatos de imagem (por exemplo, TIFF ), você pode realmente salvar o mapa de cores no cabeçalho e a maioria dos visualizadores mostrará seus dados com o mapa de cores.

Para salvar uma matplotlibimagem real , que pode ser útil para adicionar anotações ou outros dados às imagens, usei a seguinte solução:

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
fonte
0

Obrigado pelas respostas impressionantes de todos ... Eu tive exatamente o mesmo problema em querer plotar apenas uma imagem sem preenchimento / espaço extra, etc., então fiquei super feliz em encontrar as idéias de todos aqui.

Além da imagem sem preenchimento, eu também queria poder adicionar anotações facilmente, etc, além de um simples gráfico de imagem.

Então, o que acabei fazendo foi combinar a resposta de David com a csnemes ' para criar um invólucro simples no momento da criação da figura. Quando você usa isso, não precisa de alterações posteriormente com imsave () ou qualquer outra coisa:

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
Richard
fonte