How to remove axis, legends, and white padding - python

I would like to apply colormap to an image, and write the resulting image, without using axes, labels, titles, or anything 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)?

The axis('off') method resolves one of the problems more succinctly than separately changing each axis and border. 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.
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')

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

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.

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.

plt.axis('off')
plt.savefig('example.png',bbox_inches='tight',pad_inches = 0)
gets me the borderless image.

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

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)

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)

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

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

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

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)

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

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.

This works:
plot.axis('off')
ax = plot.gca()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)

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

I tried
plt.rcParams['axes.spines.left'] = False
plt.rcParams['axes.spines.right'] = False
plt.rcParams['axes.spines.top'] = False
plt.rcParams['axes.spines.bottom'] = False
plt.rcParams['ytick.major.left'] = False
plt.rcParams['ytick.major.right'] = False
plt.rcParams['ytick.minor.left'] = False
plt.rcParams['ytick.minor.left'] = False
plt.rcParams['xtick.major.top'] = False
plt.rcParams['xtick.major.bottom'] = False
plt.rcParams['xtick.minor.top'] = False
plt.rcParams['xtick.minor.bottom'] = False
fig = plt.figure()
And it removes all border and axes.
I get this from another question on Stack Overflow.

Related

Matplotlib: remove white border from edge of saved image [duplicate]

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()
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
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.
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.
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)
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')
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.
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)
This worked for me
plt.savefig(save_path,bbox_inches='tight', pad_inches=0, transparent=True)
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)
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)
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)
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')
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)
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")
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()

Completely removing axes from contour plot

I am trying to create axesless contour plot of my raster data. I managed to create the contour plot however I can't remove the axes completely. I can turn them off with plt.axis('off') but axes whitespaces are still there.
What I do:
cnt = plt.contour(my_data)
plt.clabel(cnt, inline=1, fontsize=10)
plt.axis('off')
Edit
My output method
plt.savefig(image_path, transparent=False, bbox_inches='tight', pad_inches=0)
Results:
Before plt.axis('off')
After plt.axis('off')
I had the same issue with the imshow but I've managed to solve it here, however the same technique can't be used with contours.
So how can I plot contours without axes and any whitespaces they leave behind?
Edit
So I managed to determine that the problem is not in 'plt.axis('off')' part of the code. The line does in fact completely remove the axes and it is visible when I call plt.show() however when I try to save the plot with 'plt.savefig()' I get that undesirable whitespaces. Why is that?
My code with output:
cnt = plt.contour(my_data)
plt.clabel(cnt, inline=1, fontsize=10)
plt.axis('off')
# no whitespaces
plt.show()
# whitespaces are present
plt.savefig(image_path, transparent=False, bbox_inches='tight', pad_inches=0)
Possible solution!?
I did find the way to make my images almost what I wanted with:
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
plt.contour(data)
extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.axis('off')
plt.savefig(image_path, transparent=False, bbox_inches=extent, pad_inches=0)
However I cant change the aspect ratio of the plot. I think that I do not understand this solution to the fullest.
This is actually due to savefig's defaults. The figure can have a transparent background (e.g. try fig.patch.set(facecolor='none'); fig.canvas.print_png), but it's being overridden when you call plt.savefig.
If you want a transparent background, you'll need to specify transparent=True in your call to savefig. Otherwise, it will override the figure's current background color and set it to opaque white.
Have a look at the documentation for savefig for more details.
As an example:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.random((5, 5))
fig, ax = plt.subplots()
cnt = ax.contour(data)
ax.clabel(cnt)
ax.axis('off')
fig.savefig('test.png', bbox_inches='tight', transparent=True)
Of course, this looks identical on this page, but if you open it up in an image viewer you'll notice that it has a proper transparent background:
Edit:
I may have misunderstood what you're asking. If you want the contour plot to take up the entire figure with no room left for tick labels, etc on the side, it's easiest to define the plot that way to begin with.
For example (note that this applies to any type of plot, not just contouring):
import numpy as np
import matplotlib.pyplot as plt
data = np.random.random((5, 5))
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
cnt = ax.contour(data)
ax.clabel(cnt)
ax.axis('off')
plt.show()
If you're still having issues, it's probably because you're using fig.savefig(..., bbox_inches='tight'). That specifically requires the tick labels to be included in the saved image, even if they're invisible and outside of the bounds of the figure.
Try something similar to:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.random((5, 5))
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
cnt = ax.contour(data)
ax.clabel(cnt)
ax.axis('off')
fig.savefig('test.png')

get_position() does strange things when using a colorbar

I have a matrix of data in which the x and y axes are logarithmic. I'm trying to use imshow to display the matrix, but since I want log axes I'm setting the ticks in the imshow axes to [], and then overlaying another set of axes:
import matplotlib.pyplot as plt
import numpy as np
# the x,y max and min are the log values
array = np.zeros((2,2))
array[1,1] = -1
fig = plt.figure()
ax = plt.imshow(
array,
extent = (0,1, 1, 0),
interpolation = 'nearest').get_axes()
ax.invert_yaxis()
# add a colorbar
# cb = plt.colorbar() # <----- THIS CAUSES TROUBLE
# cb.set_label('zbar')
ax.set_aspect(1)
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
position = ax.get_position()
aspect = ax.get_aspect()
# overlay another set of axes
ax_log = fig.add_subplot(111, frameon = False)
ax_log.set_xscale('log')
ax_log.set_yscale('log')
ax_log.axis((10**0, 10**1, 10**0, 10**1)) # old min and max but exponentiated
ax_log.set_position(position)
ax_log.set_aspect(aspect)
plt.savefig('test.png', bbox_inches = 'tight')
plt.close()
without the colorbar this works fine:
but when I uncomment the lines adding a colorbar, I get a weird shift:
It looks like the colorbar somehow shifts the image slightly to the left, but given that I'm calling get_position() after I create the colorbar this seems weird. Am I overlooking an easier way to make this plot? Is there some easy fix?
Searching around a bit, I found a workaround, maybe there's a better one...
The issue seems to be that plt.colorbar() will 'steal' space from the plot it's drawn on. It's still a bit strange, because I'd still expect get_position() to return the proper coordinates. But as a workaround I used GridSpec and the raw Colorbar constructor.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec
from matplotlib.colorbar import Colorbar
# the x,y max and min are the log values
array = np.zeros((2,2))
array[1,1] = -1
fig = plt.figure()
gs = GridSpec(10,11) # create a 10 x 11 grid
ax = plt.subplot(gs[:,0:-1]) # make subplot on 10 x 10 part
im = plt.imshow(
array,
extent = (0,1, 1, 0),
interpolation = 'nearest',
axes = ax)
ax.invert_yaxis()
# add a colorbar
cb_ax = plt.subplot(gs[:,-1]) # put the colorbar on the last column
cb = Colorbar(ax = cb_ax, mappable = im ) # use the raw colorbar constructor
cb.set_label('zbar')
ax.set_aspect(1)
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
position = ax.get_position()
aspect = ax.get_aspect()
# overlay another set of axes
ax_log = fig.add_subplot(111, frameon = False) # can't use gridspec?
ax_log.set_xscale('log')
ax_log.set_yscale('log')
ax_log.axis((10**0, 10**1, 10**0, 10**1)) # old min and max but exponentiated
ax_log.set_position(position)
ax_log.set_aspect(aspect)
plt.savefig('test.pdf', bbox_inches = 'tight')
plt.close()
It's also quite strange that I can't use the GridSpec object to initialize the second set of axes (doing so makes the image disappear).

Remove padding from matplotlib plotting

I am plotting an image in matplotlib, and it keeps giving me some padding. This is what I have tried:
def field_plot():
x = [i[0] for i in path]
y = [i[1] for i in path]
plt.clf()
plt.axis([0, 560, 0, 820])
im = plt.imread('field.jpg')
field = plt.imshow(im)
for i in range(len(r)):
plt.plot(r[i][0],r[i][1],c=(rgb_number(speeds[i]),0,1-rgb_number(speeds[i])),linewidth=1)
plt.axis('off')
plt.savefig( IMG_DIR + 'match.png',bbox_inches='tight', transparent="True")
plt.clf()
Try using pad_inches=0, i.e.
plt.savefig( IMG_DIR + 'match.png',bbox_inches='tight', transparent="True", pad_inches=0)
From the documentation:
pad_inches: Amount of padding around the figure when bbox_inches is
‘tight’.
I think the default is pad_inches=0.1
Just add plt.tight_layout() before plt.savefig() !!
plt.figure(figsize=(16, 10))
# ... Doing Something ...
plt.tight_layout()
plt.savefig('wethers.png')
plt.show()
This worked for me. After plotting, get the Axes object from plt using ax = plt.gca(). Then set the xlim, and ylim of ax object to match image width and image height. Matplotlib seems to automatically increase xlim and ylim of viewing area when you plot. Note that while setting y_lim you have to invert the order of coordinates.
for i in range(len(r)):
plt.plot(r[i][0],r[i][1],c=(rgb_number(speeds[i]),0,1-rgb_number(speeds[i])),linewidth=1)
plt.axis('off')
ax = plt.gca();
ax.set_xlim(0.0, width_of_im);
ax.set_ylim(height_of_im, 0.0);
plt.savefig( IMG_DIR + 'match.png',bbox_inches='tight', transparent="True")
All previous approaches didn't quite work for me, they all left some padding around the figure.
The following lines successfully removed the white or transparent padding that was left:
plt.axis('off')
ax = plt.gca()
ax.xaxis.set_major_locator(matplotlib.ticker.NullLocator())
ax.yaxis.set_major_locator(matplotlib.ticker.NullLocator())
plt.savefig(IMG_DIR + 'match.png', pad_inches=0, bbox_inches='tight', transparent=True)
Use plt.gca().set_position((0, 0, 1, 1)) to let the axes span the whole figure, see reference.
If plt.imshow is used, this requires that the figure has the correct aspect ratio.
import matplotlib as mpl
import matplotlib.pyplot as plt
# set the correct aspect ratio
dpi = mpl.rcParams["figure.dpi"]
plt.figure(figsize=(560/dpi, 820/dpi))
plt.axis('off')
plt.gca().set_position((0, 0, 1, 1))
im = plt.imread('field.jpg')
plt.imshow(im)
plt.savefig("test.png")
plt.close()

Remove colorbar from figure

This should be easy but I'm having a hard time with it. Basically, I have a subplot in matplotlib that I'm drawing a hexbin plot in every time a function is called, but every time I call the function I get a new colorbar, so what I'd really like to do is update the colorbar. Unfortunately, this doesn't seem to work since the object the colorbar is attached to is being recreated by subplot.hexbin.
def foo(self):
self.subplot.clear()
hb = self.subplot.hexbin(...)
if self.cb:
self.cb.update_bruteforce() # Doesn't work (hb is new)
else:
self.cb = self.figure.colorbar(hb)
I'm now in this annoying place where I'm trying to delete the colorbar axes altogether and simply recreate it. Unfortunately, when I delete the colorbar axes, the subplot axes don't reclaim the space, and calling self.subplot.reset_position() isn't doing what I thought it would.
def foo(self):
self.subplot.clear()
hb = self.subplot.hexbin(...)
if self.cb:
self.figure.delaxes(self.figure.axes[1])
del self.cb
# TODO: resize self.subplot so it fills the
# whole figure before adding the new colorbar
self.cb = self.figure.colorbar(hb)
I think the problem is that with del you cancel the variable, but not the referenced object colorbar.
If you want the colorbar to be removed from plot and disappear, you have to use the method remove of the colorbar instance and to do this you need to have the colorbar in a variable, for which you have two options:
holding the colorbar in a value at the moment of creation, as shown in other answers e.g. cb=plt.colorbar()
retrieve an existing colorbar, that you can do following (and upvoting :)) what I wrote here: How to retrieve colorbar instance from figure in matplotlib
then:
cb.remove() plt.draw() #update plot
Full code and result:
from matplotlib import pyplot as plt
import numpy as np
plt.ion()
plt.imshow(np.random.random(15).reshape((5,3)))
cb = plt.colorbar()
plt.savefig('test01.png')
cb.remove()
plt.savefig('test02.png')
Alright, here's my solution. Not terribly elegant, but not a terrible hack either.
def foo(self):
self.subplot.clear()
hb = self.subplot.hexbin(...)
if self.cb:
self.figure.delaxes(self.figure.axes[1])
self.figure.subplots_adjust(right=0.90) #default right padding
self.cb = self.figure.colorbar(hb)
This works for my needs since I only ever have a single subplot. People who run into the same problem when using multiple subplots or when drawing the colorbar in a different position will need to tweak.
I managed to solve the same issue using fig.clear() and display.clear_output()
import matplotlib.pyplot as plt
import IPython.display as display
import matplotlib.tri as tri
from pylab import *
%matplotlib inline
def plot_res(fig):
ax=fig.add_axes([0,0,1,1])
ax.set_xlabel("x")
ax.set_ylabel('y')
plotted=ax.imshow(rand(250, 250))
ax.set_title("title")
cbar=fig.colorbar(mappable=plotted)
display.clear_output(wait=True)
display.display(plt.gcf())
fig.clear()
fig=plt.figure()
N=20
for j in range(N):
plot_res(fig)
If you have a matplotlib figure object you just need to do fig.delaxes(fig.axes[1])
For example:
Plot with colorbar
import matplotlib.pyplot as plt
# setup some generic data
N = 37
x, y = np.mgrid[:N, :N]
Z = (np.cos(x*0.2) + np.sin(y*0.3))
# mask out the negative and positive values, respectively
Zpos = np.ma.masked_less(Z, 0)
Zneg = np.ma.masked_greater(Z, 0)
fig, ax1 = plt.subplots(figsize=(13, 3), ncols=1)
# plot just the positive data and save the
# color "mappable" object returned by ax1.imshow
pos = ax1.imshow(Zpos, cmap='Blues', interpolation='none')
# add the colorbar using the figure's method,
# telling which mappable we're talking about and
# which axes object it should be near
fig.colorbar(pos, ax=ax1)
Remove colorbar
import matplotlib.pyplot as plt
# setup some generic data
N = 37
x, y = np.mgrid[:N, :N]
Z = (np.cos(x*0.2) + np.sin(y*0.3))
# mask out the negative and positive values, respectively
Zpos = np.ma.masked_less(Z, 0)
Zneg = np.ma.masked_greater(Z, 0)
fig, ax1 = plt.subplots(figsize=(13, 3), ncols=1)
# plot just the positive data and save the
# color "mappable" object returned by ax1.imshow
pos = ax1.imshow(Zpos, cmap='Blues', interpolation='none')
# add the colorbar using the figure's method,
# telling which mappable we're talking about and
# which axes object it should be near
fig.colorbar(pos, ax=ax1)
fig.delaxes(fig.axes[1])
I had a similar problem and played around a little bit. I came up with two solutions which might be slightly more elegant:
Clear the whole figure and add the subplot (+colorbar if wanted) again.
If there's always a colorbar, you can simply update the axes with autoscale which also updates the colorbar.
I've tried this with imshow, but I guess it works similar for other plotting methods.
from pylab import *
close('all') #close all figures in memory
#1. Figures for fig.clf method
fig1 = figure()
fig2 = figure()
cbar1=None
cbar2=None
data = rand(250, 250)
def makefig(fig,cbar):
fig.clf()
ax = fig.add_subplot(111)
im = ax.imshow(data)
if cbar:
cbar=None
else:
cbar = fig.colorbar(im)
return cbar
#2. Update method
fig_update = figure()
cbar3=None
data_update = rand(250, 250)
img=None
def makefig_update(fig,im,cbar,data):
if im:
data*=2 #change data, so there is change in output (look at colorbar)
#im.set_data(data) #use this if you use new array
im.autoscale()
#cbar.update_normal(im) #cbar is updated automatically
else:
ax = fig.add_subplot(111)
im = ax.imshow(data)
cbar=fig.colorbar(im)
return im,cbar,data
#Execute functions a few times
for i in range(3):
print i
cbar1=makefig(fig1,cbar1)
cbar2=makefig(fig2,cbar2)
img,cbar3,data_update=makefig_update(fig_update,img,cbar3,data_update)
cbar2=makefig(fig2,cbar2)
fig1.show()
fig2.show()
fig_update.show()
I needed to remove colorbars because I was plotting a pcolormesh and adding colorbar to a figure in a loop. Each loop would create a new colorbar and after ten loops I would have ten colorbars. That was bad.
To remove colorbars, I name the pcolormesh and colorbar a variable, then at the end of my loop I remove each. It is important to remove the colorbar before removing the pcolormesh.
Psudo Code:
for i in range(0,10):
p = plt.pcolormesh(datastuff[i])
cb = plt.colorbar(p)
plt.savefig('name_'+i)
cb.remove()
p.remove()
Again, it was necessary to remove the colorbar before the pcolormesh
I am using matplotlib 1.4.0. This is how I solve this problem:
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
# A contour plot example:
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10.0 * (Z2 - Z1)
#
# first drawing
fig = plt.figure()
ax = fig.add_subplot(111) # drawing axes
c = ax.contourf(Z) # contour fill c
cb = fig.colorbar(c) # colorbar for contour c
# clear first drawimg
ax.clear() # clear drawing axes
cb.ax.clear() # clear colorbar axes
# replace with new drawing
# 1. drawing new contour at drawing axes
c_new = ax.contour(Z)
# 2. create new colorbar for new contour at colorbar axes
cb_new = ax.get_figure().colorbar(c_new, cax=cb.ax)
plt.show()
Above code draws a contour fill plot with colorbar, clear it and draw a new contour plot with new colorbar at the same figure.
By using
cb.ax
i am able to identify the colorbar axes and clear the old colorbar.
And specifying cax=cb.ax simply draws the new colorbar in the old colorbar axes.
Don't want to take anything away from the author of this blog post (Joseph Long) but this is clearly the best solution I've found so far. It includes pieces of code, great explanations and many examples.
To summarize, from any output of an axis ax of the command: plot, image, scatter, collection, etc. such as:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5,5), dpi=300)
ax = fig.add_subplot(1, 1, 1)
data = ax.plot(x,y)
# or
data = ax.scatter(x, y, z)
# or
data = ax.imshow(z)
# or
data = matplotlib.collection(patches)
ax.add_collection(data)
You create a color bar axis using the make_axes_locatable and the original axis of the plot.
from mpl_toolkits.axes_grid1 import make_axes_locatable
# the magical part
divider = make_axes_locatable(ax)
caxis = divider.append_axes("right", size="5%", pad=0.05)
fig.colorbar(data, cax=caxis)
plt.show()
The created colorbar will have the same size as the figure or subplot and you can modify it's width, location, padding when using the divider.append_axes command.
My solution consists in having an Axes whose only purpose is to hold the colorbar, and clear it entirely when needed.
For example, define those once:
figure, ax = plt.subplots() # All the plotting is done on `ax`.
cax = ax.inset_axes([1.03, 0, 0.1, 1], transform=ax.transAxes) # Colorbar is held by `cax`.
Then do this as many times as needed:
cax.clear()
colorbar = figure.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap),
ax=ax,
cax=cax,
**kwargs)
"on_mappable_changed" worked in my case. However, according to docs, the method "Typically ... should not be called manually."
if self.cb:
self.cb.on_mappable_changed(hb)
else:
self.cb = self.fig.colorbar(hb)

Categories

Resources