357

I need to take an image and save it after some process. The figure looks fine when I display it, but after saving the figure, I got some white space around the saved image. I have tried the 'tight' option for savefig method, did not work either. The code:

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

fig = plt.figure(1)
img = mpimg.imread("image.jpg")
plt.imshow(img)
ax = fig.add_subplot(1, 1, 1)

extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.savefig('1.png', bbox_inches=extent)

plt.axis('off') 
plt.show()

I am trying to draw a basic graph by using NetworkX on a figure and save it. I realized that without a graph it works, but when added a graph I get white space around the saved image;

import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import networkx as nx

G = nx.Graph()
G.add_node(1)
G.add_node(2)
G.add_node(3)
G.add_edge(1, 3)
G.add_edge(1, 2)
pos = {1:[100, 120], 2:[200, 300], 3:[50, 75]}

fig = plt.figure(1)
img = mpimg.imread("image.jpg")
plt.imshow(img)
ax = fig.add_subplot(1, 1, 1)

nx.draw(G, pos=pos)

extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.savefig('1.png', bbox_inches=extent)

plt.axis('off') 
plt.show()
1

16 Answers 16

402

You can remove the white space padding by setting bbox_inches="tight" in savefig:

plt.savefig("test.png",bbox_inches='tight')

You'll have to put the argument to bbox_inches as a string, perhaps this is why it didn't work earlier for you.


Possible duplicates:

Matplotlib plots: removing axis, legends and white spaces

How to set the margins for a matplotlib figure?

Reduce left and right margins in matplotlib plot

Sign up to request clarification or add additional context in comments.

13 Comments

If you have multiple subplots and want to save each of them, you can use this with fig.savefig() too. (plt.savefig() will not work in that case.)
That's not quite right. When you use that bbox_inches option, there's another default that leaves some space. If you really want to get rid of everything, you need to also use pad_inches=0.0. Of course, such tight padding frequently cuts off, e.g., exponents...
To remove the black edge as well, you may need to set pad_inches=-0.1
This simply doesn't work, you still get whitespace around the figure. Setting the transparent option (as mentioned in some answers) doesn't really help either, the whitespace is still there, it's only transparent.
@piperchester that's a good question, but probably should be asked as a new question altogether so it doesn't get lost in the comments. You should link the new question to the old one though!
|
312

I cannot claim I know exactly why or how my “solution” works, but this is what I had to do when I wanted to plot the outline of a couple of aerofoil sections — without white margins — to a PDF file. (Note that I used matplotlib inside an IPython notebook, with the -pylab flag.)

plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, 
            hspace = 0, wspace = 0)
plt.margins(0,0)
plt.gca().xaxis.set_major_locator(plt.NullLocator())
plt.gca().yaxis.set_major_locator(plt.NullLocator())
plt.savefig("filename.pdf", bbox_inches = 'tight',
    pad_inches = 0)

I have tried to deactivate different parts of this, but this always lead to a white margin somewhere. You may even have modify this to keep fat lines near the limits of the figure from being shaved by the lack of margins.

16 Comments

Finally something that works, thank you so much! By the way, in my case only the two lines using set_major_locator were necessary.
I've spent the last hour trying various things and could not get rid of a 1px white border. This was the only thing which worked - specifically the pad_inches=0 which other answers do not mention.
set_major_locator was key for me.
pad_inches helped me.
matplotlib.ticker.NullLocator()
|
41

After trying the above answers with no success (and a slew of other stack posts) what finally worked for me was just

plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, 
            hspace = 0, wspace = 0)
plt.margins(0,0)
plt.savefig("myfig.pdf")

Importantly this does not include the bbox or padding arguments.

3 Comments

This should be the accepted answer. Additionally, you don't even need to call set_axis_off. It does not affect the saved image since after subplots_adjust the axis lies outside figure's extent and henve won't be plotten anyway. In Jupyter notebooks, however, you need to explicitly disable the axis, since the inline backend overwrites these settings.
Agreed. This should be accepted as answer. I have struggled several days with this concern only this code has solved my problem. I have tried a lot (several) stackoverflow tricks and tips, workaround etc. without success. Thank You so much @SuaveSouris.
@MaxPowers set_axis_off will remove the background color and make the plot transparent. This may not be desirable in some cases. Do you know how to keep the background color?
32

I found something from Arvind Pereira (http://robotics.usc.edu/~ampereir/wordpress/?p=626) and seemed to work for me:

plt.savefig(filename, transparent = True, bbox_inches = 'tight', pad_inches = 0)

4 Comments

transparent=True will make it seem like there's no problem but it will just hide white space, image dimensions won't be ok.
Thanks for mentioning pad_inches! I wish I had known of this option earlier!
This works for most plots, but this removed the right border for my confusion matrix. Just add a small padding pad_inches=.25
None of the options below worked for me so I too went with transparent=True. Im on matplotlib==3.5.1
20

The most straightforward method is to use plt.tight_layout transformation which is actually more preferable as it doesn't do unnecessary cropping when using plt.savefig

import matplotlib as plt    
plt.plot([1,2,3], [1,2,3])
plt.tight_layout(pad=0)
plt.savefig('plot.png')

However, this may not be preferable for complex plots that modifies the figure. Refer to Johannes S's answer that uses plt.subplots_adjust if that's the case.

1 Comment

That's a clean way to do it. For me, pad=0 and tight did it in combination. You can use it inside or outside the savefig, it did not matter to me, but it might for other cases.
18

The following function incorporates johannes-s answer above. I have tested it with plt.figure and plt.subplots() with multiple axes, and it works nicely.

def save(filepath, fig=None):
    '''Save the current image with no whitespace
    Example filepath: "myfig.png" or r"C:\myfig.pdf" 
    '''
    import matplotlib.pyplot as plt
    if not fig:
        fig = plt.gcf()

    plt.subplots_adjust(0,0,1,1,0,0)
    for ax in fig.axes:
        ax.axis('off')
        ax.margins(0,0)
        ax.xaxis.set_major_locator(plt.NullLocator())
        ax.yaxis.set_major_locator(plt.NullLocator())
    fig.savefig(filepath, pad_inches = 0, bbox_inches='tight')

1 Comment

Worked like a charm. The previous answer was some required commands in my export.
18

This worked for me plt.savefig(save_path,bbox_inches='tight', pad_inches=0, transparent=True)

2 Comments

Not sure this is any different from other answers posted here.
Because one thing is when saving the image and another when plotting. This was the nly one that worked for me. While the others looked plausible when plotting in a notebook, the saved images had a weird big empty space, with this solution, the bbox_inches='tight', pad_inches=0 removed all that useless empty space
11

I found the following codes work perfectly for the job.

fig = plt.figure(figsize=[6,6])
ax = fig.add_subplot(111)
ax.imshow(data)
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.set_frame_on(False)
plt.savefig('data.png', dpi=400, bbox_inches='tight',pad_inches=0)

1 Comment

Generally, answers are much more helpful if they include an explanation of what the code is intended to do, and why that solves the problem without introducing others.
9

i followed this sequence and it worked like a charm.

plt.axis("off")
fig=plt.imshow(image array,interpolation='nearest')
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
plt.savefig('destination_path.pdf',
    bbox_inches='tight', pad_inches=0, format='pdf', dpi=1200)

3 Comments

Actually, I found this answer is easy and more convenient to use.
This one worked for me; the accepted answer did not.
In my case, using the two arguments bbox_inches='tight', pad_inches=0 in the savefig command made it work perfectly.
6

A much simpler approach I found is to use plt.imsave :

    import matplotlib.pyplot as plt
    arr = plt.imread(path)
    plt.imsave('test.png', arr)

3 Comments

Underrated answer. This helped me after a long search for how to retain resolution and remove whitespace with plt.savefig().
This works only in case you want to save an array (!) as image. This does not allow to save an arbitrary figure.
What do you mean by arbitrary image? Isn't an image an array of values?
4

For anyone who wants to work in pixels rather than inches this will work.

Plus the usual you will also need

from matplotlib.transforms import Bbox

Then you can use the following:

my_dpi = 100 # Good default - doesn't really matter

# Size of output in pixels
h = 224
w = 224

fig, ax = plt.subplots(1, figsize=(w/my_dpi, h/my_dpi), dpi=my_dpi)

ax.set_position([0, 0, 1, 1]) # Critical!

# Do some stuff
ax.imshow(img)
ax.imshow(heatmap) # 4-channel RGBA
ax.plot([50, 100, 150], [50, 100, 150], color="red")

ax.axis("off")

fig.savefig("saved_img.png",
            bbox_inches=Bbox([[0, 0], [w/my_dpi, h/my_dpi]]),
            dpi=my_dpi)

enter image description here

1 Comment

You don't have to specify dpi, you can use the default one fig.dpi instead
3

So the solution depend on whether you adjust the subplot. If you specify plt.subplots_adjust (top, bottom, right, left), you don't want to use the kwargs of bbox_inches='tight' with plt.savefig, as it paradoxically creates whitespace padding. It also allows you to save the image as the same dims as the input image (600x600 input image saves as 600x600 pixel output image).

If you don't care about the output image size consistency, you can omit the plt.subplots_adjust attributes and just use the bbox_inches='tight' and pad_inches=0 kwargs with plt.savefig.

This solution works for matplotlib versions 3.0.1, 3.0.3 and 3.2.1. It also works when you have more than 1 subplot (eg. plt.subplots(2,2,...).

def save_inp_as_output(_img, c_name, dpi=100):
    h, w, _ = _img.shape
    fig, axes = plt.subplots(figsize=(h/dpi, w/dpi))
    fig.subplots_adjust(top=1.0, bottom=0, right=1.0, left=0, hspace=0, wspace=0) 
    axes.imshow(_img)
    axes.axis('off')
    plt.savefig(c_name, dpi=dpi, format='jpeg') 

Comments

2

In a Jupyter notebook, one can add this line:

%config InlineBackend.print_figure_kwargs = {'pad_inches':0}

Here is a minimal example

import matplotlib.pyplot as plt
import numpy as np

%config InlineBackend.print_figure_kwargs = {'pad_inches':0}

fig, ax = plt.subplots()
ax.axis("off")
ax.imshow(np.fromfunction(lambda i, j: np.sin(j), (15, 15)), cmap="YlGnBu")

enter image description here

Comments

0

You may try this. It solved my issue.

import matplotlib.image as mpimg
img = mpimg.imread("src.png")
mpimg.imsave("out.png", img, cmap=cmap)

Comments

0

I usually need to crop whitespace from PDF files for scientific papers. Tweaking matplotlib to truly remove all the whitespace never worked perfectly, and some whitespace remained… If you are using pdflatex, the included pdfcrop executable proved very useful to remove whitespace from exported figures. Hence, after exporting the PDF, pdfcrop removes remaining whitespace. Here is a Python snippet to automatically crop your saved figure using pdfcrop. Just call the function instead of plt.savefig:

import subprocess
import os

def save_and_crop(path, *args, **kwargs):
    filename, file_extension = os.path.splitext(path)
    plt.savefig(path, *args, **kwargs)
    if file_extension == ".pdf":
        cropped_path = filename + "_cropped" + file_extension
        subprocess.run(["pdfcrop", path, cropped_path], stdout=subprocess.DEVNULL)

Comments

-4

This works for me saving a numpy array plotted with imshow to file

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(10,10))
plt.imshow(img) # your image here
plt.axis("off")
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, 
        hspace = 0, wspace = 0)
plt.savefig("example2.png", box_inches='tight', dpi=100)
plt.show()

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.