I am trying to convert a figure drawn using pyplot to an array, but I would like to eliminate any space outside of the plot before doing so. In my current approach, I am saving the figure to a temporary file (using the functionality of plt.savefig to eliminate any space outside the plot, i.e. using bbox_inches='tight' and pad_inches = 0), and then loading the image from the temporary file. Here's an MWE:
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.plot([0,1], color='black', linewidth=4)
plt.xlim([0,1])
plt.ylim([0,1])
ax.set_aspect('equal', adjustable='box')
plt.axis('off')
plt.savefig('./tmp.png', bbox_inches='tight', pad_inches = 0)
plt.close()
img_size = 128
img = Image.open('./tmp.png')
X = np.array(img)
This approach is undesirable, because of the time required to write the file and read it. I'm aware of the following method for going directly from the pixel buffer to an array:
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvas
import numpy as np
fig, ax = plt.subplots()
canvas = FigureCanvas(fig)
ax.plot([0,1], color='black', linewidth=4)
plt.xlim([0,1])
plt.ylim([0,1])
ax.set_aspect('equal', adjustable='box')
plt.axis('off')
canvas.draw()
X = np.array(canvas.renderer.buffer_rgba())
However, with this approach, I'm not sure how to eliminate the space around the plot before converting to an array. Is there an equivalent to bbox_inches='tight' and pad_inches = 0 that doesn't involve using plt.savefig()?
Improved Answer
This seems to work for your case and should be fast. There may be better ways - I am happy to delete it if anyone knows something better:
#!/usr/bin/env python3
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvas
import numpy as np
fig, ax = plt.subplots()
canvas = FigureCanvas(fig)
ax.plot([0,1], color='red', linewidth=4)
plt.xlim([0,1])
plt.ylim([0,1])
ax.set_aspect('equal', adjustable='box')
plt.axis('off')
canvas.draw()
X = np.array(canvas.renderer.buffer_rgba())
The code above is yours, the code below is mine:
# Get width and height of cnvas for reshaping
w, h = canvas.get_width_height()
Y = np.frombuffer(X,dtype=np.uint8).reshape((h,w,4))[...,0:3]
# Work out extent of image by inverting and looking for black - ASSUMES CANVAS IS WHITE
extent = np.nonzero(~Y)
top = extent[0].min()
bottom = extent[0].max()
left = extent[1].min()
right = extent[1].max()
tight_img = Y[top:bottom,left:right,:]
# Save as image just to test - you don't want this bit
Image.fromarray(tight_img).save('tight.png')
Original Answer
There may be a better way, but you could avoid writing to disk by writing to a memory-based BytesIO instead:
from io import BytesIO
buffer = BytesIO()
plt.savefig(buffer, format='png', bbox_inches='tight', pad_inches = 0)
Then do:
x = np.array(Image.open(buffer))
In fact, if you use:
plt.savefig(buffer, format='rgba', bbox_inches='tight', pad_inches = 0)
the buffer already has your array and you can avoid the PNG encoding/decoding as well as the disk I/O. The only issue is that, because it is raw, we don't know the dimensions of the image to reshape() the buffer. It is actually this on my machine but I got the dimensions by writing a PNG and checking its width and height:
arr = buffer.getvalue()
x = np.frombuffer(arr, dtype=np.uint8).reshape((398,412,4))
If someone comes up with something better, I'll delete this.
Related
I'm trying to plot two images on the same axes. Here is the code I'm experimenting with (I'm new to MatplotLib - so apologies in advance)...
import matplotlib.pyplot as plt
from matplotlib import transforms
img = plt.imread('image1.gif')
fig = plt.figure()
ax = fig.add_subplot(111)
rotation_in_degrees = 60
tr = transforms.Affine2D().rotate_deg(rotation_in_degrees)
ax.imshow(img)
ax.imshow(img, transform=tr)
plt.show()
There are two issues. Firstly, only the first image appears in the display; the second is only partially shown. Is there a way to show the two images in the same plot? That is, the axes are automatically scaled.
Secondly, as you can see, I'm trying to rotate the image but I'm sure that I'm not doing correctly in the sense I don't know if I'm rotating it around the origin of the plot etc. Any advice or links for other posts would be great!
Thanks for you help in advance.
To solve your firs problem - you need to display each image in separate subplot, now - you are plotting in the same subplot. See example of plotting in separate plots:
import matplotlib.pyplot as plt
from matplotlib import transforms
img = plt.imread('image.gif')
fig = plt.figure()
rotation_in_degrees = 60
tr = transforms.Affine2D().rotate_deg(rotation_in_degrees)
ax = fig.add_subplot(121)
ax.imshow(img)
ax = fig.add_subplot(122)
ax.imshow(img)
plt.show()
For the second issue with rotation - I would use PIL:
from PIL import Image
import matplotlib.pyplot as plt
img = Image.open('image.gif')
fig = plt.figure()
rotation_in_degrees = 60
ax = fig.add_subplot(121)
ax.imshow(img)
img2 = img.rotate(rotation_in_degrees)
ax = fig.add_subplot(122)
ax.imshow(img2)
plt.show()
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()
everyone,
I have problem when I tried to adjust colorbar to the same height with figure. I know little about the intrinsic mechanism of data visualization, or axis, fig or something like that. my code is following, sorry for unloading images,
For Figure(1), notice that input data is square, i.e., 51 by 51. The figure is satisfying.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
plt.cla()
plt.clf()
fig1 = plt.figure(1)
ax0 = plt.subplot()
im0 = ax0.imshow(np.arange(51*51).reshape((51,51)), cmap="hsv")
divider0 = make_axes_locatable(ax0)
ax_cb0 = divider0.append_axes("right", size="2%", pad=0.05)
fig1.add_axes(ax_cb0)
plt.colorbar(im0, cax=ax_cb0)
plt.savefig("tmp0.png", bbox_inches="tight")
For figure (2), the code is following, notice that now the input data is 51 by 501, the output is not satisfying,
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
plt.cla()
plt.clf()
fig2 = plt.figure(2)
ax1 = plt.subplot()
im1 = ax1.imshow(np.arange(51*501).reshape((51,501)), cmap="hsv")
divider1 = make_axes_locatable(ax1)
ax_cb1 = divider1.append_axes("right", size="2%", pad=-4)
fig2.add_axes(ax_cb1)
plt.colorbar(im1, cax=ax_cb1)
ax1.set_aspect(4)
plt.savefig("tmp1.png", bbox_inches="tight")
but still, we can make it better by manually adjusting pad parameter in this line
ax_cb0 = divider0.append_axes("right", size="2%", pad=0.05)
but which is absolutely not the recommended way, COULD anyone know the smart way of doing it or smart way of estimating the value of pad parameter? Thanks in advance.
I am generating a heat map with data that has a fixed outlier number and I need to show these outliers as a colour out of the colour palette of the cmap I use which is "hot". With the use of cmap.set_bad('green') and np.ma.masked_values(data, outlier), I get a plot which looks right but the color bar is not getting synced with the data properly even if I use cmap.set_over('green').
Here is the code I have been trying:
plt.xlim(0,35)
plt.ylim(0,35)
img=plt.imshow(data, interpolation='none',norm=norm, cmap=cmap,vmax=outlier)
cb_ax=fig.add_axes([0.85, 0.1, 0.03, 0.8])
cb=mpl.colorbar.ColorbarBase(cb_ax,cmap=cmap,norm=norm,extend='both',spacing='uniform')
cmap.set_over('green')
cmap.set_under('green')
Here is the data (outlier is 1.69 obviously):
Data;A;B;C;D;E;F;G;H;I;J;K
A;1.2;0;0;0;0;1.69;0;0;1.69;1.69;0
B;0;0;0;0;0;1.69;0;0;1.69;1.69;0
C;0;0;0;0;0;1.69;0;0.45;1.69;1.69;0.92
D;1;0;-0.7;-1.2;0;1.69;0;0;1.69;1.69;0
E;0;0;0;0;0;1.69;0;0;1.69;1.69;0
F;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
G;0;0;0;0;0;1.69;0;0;1.69;1.69;0
H;0;0;0;0;0;1.69;0;0;1.69;1.69;0
I;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
J;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
K;0;0;0;0;0;1.69;0;0;1.69;1.69;0
Appreciate any help
What's happening is that you're using a masked array where the outliers are masked.
Therefore, they don't show up on the colorbar as being "over". (i.e. as far as matplotlib is concerned, the masked values are invalid, not over the threshold)
As a stand-alone example to reproduce your problem:
import numpy as np
import matplotlib.pyplot as plt
threshold = 0.8
data = np.random.random((10,10))
data = np.ma.masked_greater(data, threshold)
fig, ax = plt.subplots()
im = ax.imshow(data, cmap=plt.cm.hot, interpolation='none')
cbar = fig.colorbar(im, extend='max')
cbar.cmap.set_over('green')
plt.show()
If we simply don't make this a masked array, and instead specify the vmax kwarg to imshow:
import numpy as np
import matplotlib.pyplot as plt
threshold = 0.8
data = np.random.random((10,10))
fig, ax = plt.subplots()
im = ax.imshow(data, cmap=plt.cm.hot, interpolation='none', vmax=threshold)
cbar = fig.colorbar(im, extend='max')
cbar.cmap.set_over('green')
plt.show()
Basically, this is the difference between set_over (or under) and set_bad.
If you did still want to use a masked array, you could just call cbar.cmap.set_bad('green') as well as set_over, and you'd get the effect you want (though all "bad" values, not just ones over the threshold, would be green). If you take that route, you'll need to manually specify the vmax. Otherwise it will be taken as the maximum of the unmasked portions of the array.
I think you need to set extend to "both" and feed in a Normalize object:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import pandas
from io import StringIO # python 3
#from StringIO import StringIO # python 2
datastring = StringIO("""\
Data;A;B;C;D;E;F;G;H;I;J;K
A;1.2;0;0;0;0;1.69;0;0;1.69;1.69;0
B;0;0;0;0;0;1.69;0;0;1.69;1.69;0
C;0;0;0;0;0;1.69;0;0.45;1.69;1.69;0.92
D;1;0;-0.7;-1.2;0;1.69;0;0;1.69;1.69;0
E;0;0;0;0;0;1.69;0;0;1.69;1.69;0
F;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
G;0;0;0;0;0;1.69;0;0;1.69;1.69;0
H;0;0;0;0;0;1.69;0;0;1.69;1.69;0
I;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
J;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
K;0;0;0;0;0;1.69;0;0;1.69;1.69;0
""")
threshold = 1.68
data = pandas.read_table(datastring, sep=';', index_col='Data')
cmap = mpl.cm.coolwarm
norm = mpl.colors.Normalize(vmin=-1 * threshold, vmax=threshold)
cmap.set_over('slategray')
cmap.set_under('forestgreen')
fig, ax = plt.subplots()
ax.set_aspect('equal')
cb_ax=fig.add_axes([0.85, 0.1, 0.03, 0.8])
img = ax.imshow(data, cmap=cmap, norm=norm, interpolation='none')
cb = mpl.colorbar.ColorbarBase(cb_ax, cmap=cmap, norm=norm, extend='both')
Gives me:
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()