ChatGPT解决这个技术问题 Extra ChatGPT

Matplotlib plots: removing axis, legends and white spaces

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

All the solutions on this question are focussed on imshow. If you have a scatterplot instead the following answer might help you: stackoverflow.com/a/40727744/4124317

H
Hooked

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

https://i.stack.imgur.com/4ZMuW.png


Following unutbu's suggestion, you could use fig = plt.figure(), ax = plt.axes([0,0,1,1]), then plt.imshow(data,interpolation="nearest". Combined with plt.axis("off"), it should get rid of everything beside the image itself, hopefully!
Combining the methods from the question ({fig.axes.get_xaxis().set_visible(False) & fig.axes.get_yaxis().set_visible(False)} with {plt.axis('off')}) fixed the problem for me. (No whitespaces anymore). And dont forget to set your {pad_inches} in savefig to 0.
I just ran across this issue, none of the solutions in this thread, and linked ones, worked. However explicitly specifying bbox_inches=0 did the trick.
Note that if you are 'plotting' stuff over the plt.imshow, as in plt.plot(x,y), you need to make sure you set the xlim and ylim to the size of the matrix.
this does not work anymore. Took me an hour to figure it out. See answer below.
u
unutbu

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

https://i.stack.imgur.com/dyCKl.png


I'm pretty sure you have the correct answer (though there's probably more than one way to do it), but I'm wondering if you can explain why it's the right answer? What about your code removes the white space?
@Yann, in addition to the documentation, I find it very helpful to comment out one line at a time to see what effect each command has. It's the empirical way!
The line that removes the white border is plt.Axes(fig, [0,0,1,1]). This tells matplotlib to create a set of axes with bottom left corner at the point located at (0 %, 0 %), and with a width and height of (100 %, 100 %).
This is the more correct answer, as it not only removes white space for the saved image but also for onscreen plots.
Thanks, this is the ONLY solution works fine on my Ubuntu:)
D
Domagoj

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.

https://i.stack.imgur.com/kzrdo.png


Your method did remove all the white spaces around this image, good work, but I still don't know why add the command fig.axes.get_xaxis().set_visible(False) solve the issue. Thanks
Yes if you do not call this commands most of the white spaces will be removed. However in my case there was still some spaces between my plot and edge of the .png image (maybe 2px wide). By calling following commands I've got rid of those stubborn spaces.
Note, though, that the above will fail in the presence of multiple axes in the same figure (in my case an embedded image in the plot). Solution: fig.axes[0], and in general all or selected axes.
l
luator

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.

https://i.stack.imgur.com/NQv8Z.png


This is what I do most of the time, but there are instances when I need the colorbar, and in these cases imsave can't save me.
N
Nazmus Sakib Akash
plt.axis('off')

plt.savefig('example.png',bbox_inches='tight',pad_inches = 0)

gets me the borderless image.


p
paulj

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

V
Vlady Veselinov

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)

Underrated answer! This got me like 99% of the way there. Should be more upvoted because it's the most up-to-date
It also works if you don't have an Axes object (ax) with extent = plt.gca().get_window_extent().transformed(fig.dpi_scale_trans.inverted()).
g
gypaetus

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)

e
episodeyang

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:

https://i.stack.imgur.com/tFV46.png

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


for me only the Axes version works. i get padding with the patch turned off version. thx anyways!
c
csnemes

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)

https://i.stack.imgur.com/viokB.png

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)

https://i.stack.imgur.com/jtkq2.png


D
Dgomn D

This is what finally worked for me:

ax.margins(x=0, y=0, tight=True) was the key line.

    fig = plt.figure(figsize=(8, 8))
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.set_axis_off()
    ax.margins(x=0, y=0, tight=True)
    fig.add_axes(ax)
    for triangle in list_of_triangles:
        x_points = [point[0] for point in triangle]
        y_points = [point[1] for point in triangle]
        plt.fill(x_points, y_points, 'k', edgecolor='k')
    plt.savefig("test.png", bbox_inches=0, pad_inches=0)
    plt.show()

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
D
David Hoffman

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)

R
Richard

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

H
Hulya

I have been looking for several codes to solve this problem and the verified answer to this question is the only code that helped me.

This is useful for scatter plots and triplots. All you have to do is change the margins to zero and you are all done.


B
Ben

This worked for me to remove the ticks:

fig, axes = plt.subplots(2, figsize=(15, 20))

for ax in axes:
    ax.get_xaxis().set_ticks([])
    ax.get_yaxis().set_ticks([])

t
tdy

Use this at the end of the code:

plt.imsave(filename, img)

A single line answer does not explain the solution, better if you can elaborate it.
plt.imsave has already been answered by @luator in 2017. When answering old questions, please check for duplicates before posting.