Removing white space around a saved image
Asked Answered
H

16

339

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()
Heterochromatin answered 7/8, 2012 at 1:2 Comment(1)
Possible duplicate of Saving a matplotlib/networkx figure without marginsIncommodious
A
304

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.

Apyretic answered 1/12, 2014 at 11:45 Comment(16)
Finally something that works, thank you so much! By the way, in my case only the two lines using set_major_locator were necessary.Jonasjonathan
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.Gamin
set_major_locator was key for me.Crudden
pad_inches helped me.Hildredhildreth
I'm getting name 'NullLocator' is not defined. Is there a version dependency on this? I'm on 1.5.1 :(Ooze
matplotlib.ticker.NullLocator()Tedtedd
Can this be used outside of a notebook, like in a normal script? Also, using pylab is not recommended by the matplotlib docs: matplotlib.org/faq/…Leff
However, there are still very small blank for me, thanks very muchFulmar
the answer would be better if you add more "import blabla" codeFulmar
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.Appealing
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.Cyclic
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)Loyalty
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.Deep
In my case, using the two arguments bbox_inches='tight', pad_inches=0 in the last line made it work perfectly !Thingumabob
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.Calumniation
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.Lehr
S
389

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

Scenery answered 7/8, 2012 at 13:39 Comment(13)
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. ThanksSlipway
This works great... until I have a suptitle which gets ignored by it.Virescence
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.)Welkin
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...Schoen
To remove the black edge as well, you may need to set pad_inches=-0.1Jill
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.Mustee
This is great - I wonder if there's something comparable for .svgs? Doesn't seem to have the same effect as .png or .pdf.Diagenesis
@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!Scenery
Why is this not the default setting?? So much better.Gulledge
with Pad inches = 0 at the end! plt.savefig("filename.pdf", bbox_inches = 'tight', pad_inches = 0)Loyalty
What does pad_inches = 0 do?Clough
Next issue is that output image size now does not matches requested.... Had to specify figure_size(2422, 1344) to get 1920x1080 graph....Laspisa
cool, it worked for me with png. Is there a way to set it by default in rcParams?Personnel
A
304

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.

Apyretic answered 1/12, 2014 at 11:45 Comment(16)
Finally something that works, thank you so much! By the way, in my case only the two lines using set_major_locator were necessary.Jonasjonathan
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.Gamin
set_major_locator was key for me.Crudden
pad_inches helped me.Hildredhildreth
I'm getting name 'NullLocator' is not defined. Is there a version dependency on this? I'm on 1.5.1 :(Ooze
matplotlib.ticker.NullLocator()Tedtedd
Can this be used outside of a notebook, like in a normal script? Also, using pylab is not recommended by the matplotlib docs: matplotlib.org/faq/…Leff
However, there are still very small blank for me, thanks very muchFulmar
the answer would be better if you add more "import blabla" codeFulmar
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.Appealing
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.Cyclic
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)Loyalty
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.Deep
In my case, using the two arguments bbox_inches='tight', pad_inches=0 in the last line made it work perfectly !Thingumabob
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.Calumniation
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.Lehr
C
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.

Constitutive answered 15/11, 2018 at 0:19 Comment(3)
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.Appealing
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.Plateau
@Appealing 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?Acclimate
W
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)
Webfoot answered 11/9, 2017 at 15:5 Comment(4)
transparent=True will make it seem like there's no problem but it will just hide white space, image dimensions won't be ok.Leff
Thanks for mentioning pad_inches! I wish I had known of this option earlier!Theomania
This works for most plots, but this removed the right border for my confusion matrix. Just add a small padding pad_inches=.25Synchro
None of the options below worked for me so I too went with transparent=True. Im on matplotlib==3.5.1Baseboard
V
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')
Vociferous answered 28/11, 2018 at 9:23 Comment(1)
Worked like a charm. The previous answer was some required commands in my export.Automata
D
17

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.

Domesticate answered 14/8, 2019 at 15:59 Comment(1)
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.Serum
H
17

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

Hagiographer answered 24/7, 2020 at 15:20 Comment(2)
Not sure this is any different from other answers posted here.Negris
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 spacePrintmaker
C
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)
Chou answered 24/5, 2018 at 15:16 Comment(1)
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.Corriveau
E
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)
Eldreda answered 27/11, 2018 at 13:6 Comment(3)
Actually, I found this answer is easy and more convenient to use.Benisch
This one worked for me; the accepted answer did not.Mendenhall
In my case, using the two arguments bbox_inches='tight', pad_inches=0 in the savefig command made it work perfectly.Thingumabob
B
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)
Byler answered 28/6, 2019 at 9:18 Comment(3)
Underrated answer. This helped me after a long search for how to retain resolution and remove whitespace with plt.savefig().Echt
This works only in case you want to save an array (!) as image. This does not allow to save an arbitrary figure.Appealing
What do you mean by arbitrary image? Isn't an image an array of values?Byler
B
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

Blameworthy answered 20/9, 2019 at 2:5 Comment(1)
You don't have to specify dpi, you can use the default one fig.dpi insteadAccomplice
S
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') 
Syncopation answered 27/12, 2020 at 19:6 Comment(0)
H
1

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

Hebrides answered 4/8, 2022 at 6:41 Comment(0)
C
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)
Checklist answered 17/7, 2020 at 15:59 Comment(0)
C
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)
Ceaseless answered 8/2 at 10:56 Comment(0)
S
-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()
Surmise answered 2/5, 2018 at 1:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.