Matplotlib图:删除轴,图例和空白

问题:Matplotlib图:删除轴,图例和空白

我是Python和Matplotlib的新手,我想简单地将colormap应用于图像并写入结果图像,而无需使用轴,标签,标题或通常由matplotlib自动添加的任何内容。这是我所做的:

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)

它成功删除了图形的轴,但是保存的图形在实际图像周围显示了白色填充和边框。如何删除它们(至少是白色填充)?谢谢

I’m new to Python and Matplotlib, I would like to simply apply colormap to an image and write the resulting image, without using axes, labels, titles or anything usually automatically added by matplotlib. Here is what I did:

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)

It successfully removes the axis of the figure, but the figure saved presents a white padding and a frame around the actual image. How can I remove them (at least the white padding)? Thanks


回答 0

我认为该命令axis('off')比单独更改每个轴和边框更简洁地解决了其中一个问题。但是,它仍然在边框周围留有空白区域。添加bbox_inches='tight'savefig命令几乎可以使您到达那里,在下面的示例中您可以看到剩余的空白空间要小得多,但仍然存在。

请注意,可能需要更新版本的matplotlib bbox_inches=0而不是字符串'tight'(通过@episodeyang和@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')

I think that the command axis('off') takes care of one of the problems more succinctly than changing each axis and the border separately. It still leaves the white space around the border however. Adding bbox_inches='tight' to the savefig command almost gets you there, you can see in the example below that the white space left is much smaller, but still present.

Note that newer versions of matplotlib may require bbox_inches=0 instead of the string 'tight' (via @episodeyang and @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')


回答 1

我从matehat那里学到了这个技巧:

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

Yield

I learned this trick from matehat, here:

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

yields


回答 2

可能最简单的解决方案:

我只是简单地结合了问题中描述的方法和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)

在此代码之后,没有空格和框架。

Possible simplest solution:

I simply combined the method described in the question and the method from the answer by 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)

After this code there is no whitespaces and no frame.


回答 3

现在还没有人提到imsave,这使它成为一线客:

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

它直接按原样存储图像,即不添加任何轴或边框/填充。

No one mentioned imsave yet, which makes this a one-liner:

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

It directly stores the image as it is, i.e. does not add any axes or border/padding.


回答 4

您还可以指定bbox_inches参数的数字范围。这样可以消除图形周围的白色填充。

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)

You can also specify the extent of the figure to the bbox_inches argument. This would get rid of the white padding around the figure.

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)

回答 5

这应该删除所有填充和边框:

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)

This should remove all padding and borders:

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)

回答 6

我发现所有文件都记录在案…

https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.axes.Axes.axis.html#matplotlib.axes.Axes.axis

我的代码…。“ bcK”是512×512的图片

plt.figure()
plt.imshow(bck)
plt.axis("off")   # turns off axes
plt.axis("tight")  # gets rid of white border
plt.axis("image")  # square up the image instead of filling the "figure" space
plt.show()

I found that it is all documented…

https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.axes.Axes.axis.html#matplotlib.axes.Axes.axis

My code…. “bcK” is a 512×512 image

plt.figure()
plt.imshow(bck)
plt.axis("off")   # turns off axes
plt.axis("tight")  # gets rid of white border
plt.axis("image")  # square up the image instead of filling the "figure" space
plt.show()

回答 7

我喜欢ubuntu的答案,但没有明确显示如何直接设置非正方形图像的大小,因此我对其进行了修改,以方便复制粘贴:

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

如果保留pixel_size / dpi = size,则无论选择哪种dpi,都可以轻松保存无边界图像。

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

但是显示是怪异的。如果选择小dpi,则图像尺寸可能大于屏幕尺寸,并且在显示过程中会出现边框。但是,这不会影响保存。

因此对于

save_image_fix_dpi(data, dpi=20)

显示屏变成边框(但保存有效):

I liked ubuntu’s answer, but it was not showing explicitly how to set the size for non-square images out-of-the-box, so I modified it for easy copy-paste:

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

Saving images without border is easy whatever dpi you choose if pixel_size/dpi=size is kept.

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

However displaying is spooky. If you choose small dpi, your image size can be larger than your screen and you get border during display. Nevertheless, this does not affect saving.

So for

save_image_fix_dpi(data, dpi=20)

The display becomes bordered (but saving works):


回答 8

已投票的答案不再起作用。要使其正常工作,您需要手动添加设置为[0,0,1,1]的轴,或删除下图的面片。

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

或者,您可以只删除补丁。您无需添加子图即可删除填充。这是从下面弗拉迪的答案简化

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

这是3.0.3在2019/06/19 版本上进行测试的。图片见下面:

使用起来要简单得多pyplot.imsave。有关详细信息,请参见下面的luator回答

The upvoted answer does not work anymore. To get it to work you need to manually add an axis set to [0, 0, 1, 1], or remove the patch under figure.

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

Alternatively, you could just remove the patch. You don’t need to add a subplot in order to remove the paddings. This is simplified from Vlady’s answer below

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

This is tested with version 3.0.3 on 2019/06/19. Image see bellow:

A much simpler thing to do is to use pyplot.imsave. For details, see luator’s answer bellow


回答 9

首先,对于某些图像格式(即TIFF),您实际上可以将颜色图保存在标题中,并且大多数查看器将使用颜色图显示数据。

为了保存实际matplotlib图像,这对于在图像中添加注释或其他数据很有用,我使用了以下解决方案:

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)

First, for certain image formats (i.e. TIFF) you can actually save the colormap in the header and most viewers will show your data with the colormap.

For saving an actual matplotlib image, which can be useful for adding annotations or other data to images, I’ve used the following solution:

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)

回答 10

感谢每个人的出色回答…我只想绘制没有多余填充/空格等图像的问题,同样遇到了同样的问题,因此非常高兴在这里找到每个人的想法。

除了没有填充的图像外,我还希望能够轻松添加注释等,而不仅仅是简单的图像图。

因此,我最终要做的是在图形创建时将David的答案csnemes相结合,以制作一个简单的包装。使用它时,以后不需要使用imsave()或其他任何方式进行任何更改:

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

Thanks for the awesome answers from everyone …I had exactly the same problem with wanting to plot just an image with no extra padding/space etc, so was super happy to find everyone’s ideas here.

Apart from image with no padding, I also wanted to be able to easily add annotations etc, beyond just a simple image plot.

So what I ended up doing was combining David’s answer with csnemes’ to make a simple wrapper at the figure creation time. When you use that, you don’t need any changes later with imsave() or anything else:

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