318

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()
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Ahmet Tuğrul Bayrak
  • 3,466
  • 2
  • 15
  • 27
  • 1
    Possible duplicate of [Saving a matplotlib/networkx figure without margins](http://stackoverflow.com/questions/11298909/saving-a-matplotlib-networkx-figure-without-margins) – Joel Oct 13 '15 at 10:03

15 Answers15

367

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

Community
  • 1
  • 1
Hooked
  • 84,485
  • 43
  • 192
  • 261
  • What I try to do is to draw a graph on a figure by NetworkX then save it as an image. The problem - whiteSpace could be related to NetworkX commands. Anyway, I updated the question. Thanks – Ahmet Tuğrul Bayrak Aug 07 '12 at 16:46
  • This works great... until I have a suptitle which gets ignored by it. – Elliot Jan 03 '13 at 22:11
  • 1
    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.) – Abhranil Das Apr 21 '13 at 12:06
  • 56
    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... – Mike Dec 19 '14 at 16:46
  • 6
    To remove the black edge as well, you may need to set `pad_inches=-0.1` – lenhhoxung Oct 12 '17 at 14:09
  • 15
    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. – BjornW Apr 07 '18 at 10:50
  • This is great - I wonder if there's something comparable for `.svg`s? Doesn't seem to have the same effect as `.png` or `.pdf`. – Piper Dec 31 '18 at 12:27
  • 1
    @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! – Hooked Jan 02 '19 at 17:43
  • 1
    Why is this not the default setting?? So much better. – Aditya Kashi Feb 26 '19 at 04:10
  • with Pad inches = 0 at the end! ```plt.savefig("filename.pdf", bbox_inches = 'tight', pad_inches = 0)``` – Javi Apr 10 '20 at 09:44
  • What does `pad_inches = 0` do? – Rylan Schaeffer May 26 '20 at 02:56
  • Next issue is that output image size now does not matches requested.... Had to specify figure_size(2422, 1344) to get 1920x1080 graph.... – BarsMonster Jul 25 '21 at 05:06
  • cool, it worked for me with png. Is there a way to set it by default in rcParams? – Vincenzooo Jul 15 '22 at 17:27
293

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.

duhaime
  • 25,611
  • 17
  • 169
  • 224
Johannes S.
  • 2,954
  • 1
  • 11
  • 2
  • 9
    Finally something that works, thank you so much! By the way, in my case only the two lines using `set_major_locator` were necessary. – Florian Brucker Oct 28 '15 at 17:54
  • 10
    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. – AnnanFay May 16 '16 at 17:54
  • 1
    `set_major_locator` was key for me. – kmac Aug 07 '16 at 21:44
  • 26
    `pad_inches` helped me. – Myles Baker Aug 15 '16 at 04:56
  • 1
    I'm getting name 'NullLocator' is not defined. Is there a version dependency on this? I'm on 1.5.1 :( – FrenchKheldar Jun 04 '17 at 14:43
  • 5
    matplotlib.ticker.NullLocator() – Joop Jun 14 '17 at 13:14
  • Can this be used outside of a notebook, like in a normal script? Also, using pylab is not recommended by the matplotlib docs: https://matplotlib.org/faq/usage_faq.html#matplotlib-pyplot-and-pylab-how-are-they-related – Vlady Veselinov Jul 28 '18 at 16:33
  • However, there are still very small blank for me, thanks very much – Statham Dec 14 '18 at 03:35
  • the answer would be better if you add more "import blabla" code – Statham Dec 14 '18 at 03:36
  • The crucial part is the call to `subplots_adjust`. Afterwards, the axis and the white space lie outside the figure's extent and hence won't be plotten. Thus, all the other stuff is not necessary. Jupyter notebooks, however, you need to explicitly disable the axis with `set_axis_off`, since the inline backend overwrites the subplot settings. and actually shows the axis and some white space. – MaxPowers Nov 08 '19 at 11:23
  • `pad_inches` and `bbox_inches` worked great, thanks! You can also add `transparent=True` to remove the background to use the image on non white background. – takacsmark Feb 20 '20 at 12:14
  • nice, but a bit too long I think. The thing that worked for me was: ```plt.savefig("filename.pdf", bbox_inches = 'tight', pad_inches = 0)``` – Javi Apr 10 '20 at 09:43
  • Note that in my case, `pad_inches=0` often cuts off the box-line on the right side of the plot. As a result, I often have to do `pad_inches=0.01` or something similar so that plot elements don't get cut out. – Gene Burinsky Feb 22 '21 at 20:47
  • In my case, using the two arguments `bbox_inches='tight'`, `pad_inches=0` in the last line made it work perfectly ! – Basilique Mar 12 '21 at 16:50
  • Would love to know how to execute `bbox_inches='tight'` outside of `savefig`, i.e. I am trying to generate an image without the whitespace, but not saving it to disk until later. – sh37211 Aug 06 '22 at 22:50
  • Exactly what I need. I used `plt.figure(figsize=(16,9), constrained_layout=True)` before, but sometimes when axes collapse, my previous way did not work. Johannes S. solution helps a lot. – Wei Shan Lee Jan 24 '23 at 14:50
39

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.

SuaveSouris
  • 1,302
  • 17
  • 20
  • 9
    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. – MaxPowers Nov 08 '19 at 11:11
  • 1
    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. – MGB.py Sep 26 '20 at 01:21
29

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)
mkl
  • 90,588
  • 15
  • 125
  • 265
unclerico
  • 415
  • 4
  • 2
  • 11
    `transparent=True` will make it seem like there's no problem but it will just hide white space, image dimensions won't be ok. – Vlady Veselinov Jul 28 '18 at 16:34
  • Thanks for mentioning `pad_inches`! I wish I had known of this option earlier! – ingomueller.net Feb 19 '20 at 17:11
  • 1
    This works for most plots, but this removed the right border for my confusion matrix. Just add a small padding `pad_inches=.25` – YTZ Jun 13 '20 at 15:32
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')
TomNorway
  • 2,584
  • 1
  • 19
  • 26
15

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.

Keto
  • 1,470
  • 1
  • 12
  • 25
13

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

Mohammed
  • 2,215
  • 1
  • 9
  • 8
  • 3
    Not sure this is any different from other answers posted here. – BigBen Jul 24 '20 at 16:09
  • 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 – lesolorzanov Jul 18 '23 at 09:33
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)
Richard Yu Liu
  • 119
  • 1
  • 4
  • 2
    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. – Tim Diekmann May 24 '18 at 15:26
8

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)
Evan
  • 2,121
  • 14
  • 27
Khan
  • 1,288
  • 12
  • 11
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)
Parth92
  • 369
  • 3
  • 4
  • Underrated answer. This helped me after a long search for how to retain resolution and remove whitespace with `plt.savefig()`. – nihal111 Sep 05 '19 at 04:26
  • 1
    This works only in case you want to save an array (!) as image. This does not allow to save an arbitrary figure. – MaxPowers Nov 08 '19 at 10:57
  • 1
    What do you mean by arbitrary image? Isn't an image an array of values? – Parth92 Nov 12 '19 at 07:17
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

Simon Thomas
  • 187
  • 1
  • 4
2

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') 
Razorocean
  • 378
  • 2
  • 13
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)
user1410665
  • 719
  • 7
  • 23
0

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

Kolibril
  • 1,096
  • 15
  • 19
-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()
Alejandro Sazo
  • 796
  • 15
  • 31