114

In numpy/scipy I have an image stored in an array. I can display it, I want to save it using savefig without any borders, axes, labels, titles,... Just pure image, nothing else.

I want to avoid packages like PyPNG or scipy.misc.imsave, they are sometimes problematic (they do not always install well, only basic savefig() for me

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Jakub M.
  • 32,471
  • 48
  • 110
  • 179

13 Answers13

141

EDIT

Changed aspect='normal to aspect='auto' since that changed in more recent versions of matplotlib (thanks to @Luke19).


Assuming :

import matplotlib.pyplot as plt

To make a figure without the frame :

fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)

To make the content fill the whole figure

ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)

Then draw your image on it :

ax.imshow(your_image, aspect='auto')
fig.savefig(fname, dpi)

The aspect parameter changes the pixel size to make sure they fill the figure size specified in fig.set_size_inches(…). To get a feel of how to play with this sort of things, read through matplotlib's documentation, particularly on the subject of Axes, Axis and Artist.

matehat
  • 5,214
  • 2
  • 29
  • 40
  • 6
    nope, I still have some small transparent border, and what I want is _no border at all_, pure image – Jakub M. Nov 21 '11 at 21:42
  • Maybe, it's because I forgot about the case where the image is not square :P. Just edited to add the `aspect` param. How's it now? – matehat Nov 21 '11 at 21:49
  • 1
    grrr, no, still the same. There is a small, transparent border around the image, few pixels on _each_ side – Jakub M. Nov 21 '11 at 21:58
  • 1
    almost! I figured out that the border was added by `bbox_inches='tight'`. But, now the images instead of desired 24x24px are 800x600px. Still looking... – Jakub M. Nov 21 '11 at 22:05
  • 5
    If you manually set the `w` and `h` parameters in `fig.set_size_inches(w,h)` and the `dpi` parameter in `fig.savefig(fname, dpi)` so that it result in 24px by 24px, it should work just fine. For example, `w = h = 1` and `dpi = 24` – matehat Nov 21 '11 at 22:27
  • 5
    I had to combine both this answer, and the answer below by Mostafa Pakparvar. Not only do you need to turn off the axes, but you need to set_visible to false to make sure the white space disappears. (wtf?) – Bryce Guinta Jan 17 '16 at 19:13
  • 6
    Just tried this using matplotlib v2.2.2 an it worked perfectly (except that `imshow`'s syntax changed to `aspect='auto'` instead of `'normal'`). – Luke19 Aug 18 '18 at 19:09
  • 2
    This is the right approach. `ax = plt.Axes(fig, [0., 0., 1., 1.])` is what makes it work. – greatvovan May 23 '20 at 05:28
  • I had to change the last line to `fig.savefig(fname, dpi=dpi)`. – smcs Jan 24 '22 at 14:32
106

An easier solution seems to be:

import matplotlib.pyplot as plt

    plt.imshow(img)
    plt.axis('off')
    fig.savefig('out.png', bbox_inches='tight', pad_inches=0)
aemonge
  • 2,289
  • 1
  • 24
  • 26
weatherfrog
  • 2,970
  • 2
  • 19
  • 17
  • 2
    This worked out great for me. Also, pad_inches can be changed to desired size easily. Thanks! – Curious2learn Dec 31 '12 at 16:46
  • 1
    +1 worked out great for me too :) And this is actually way simpler than the accepted answer – El Ninja Trepador Apr 10 '14 at 22:26
  • 37
    I still got white margins with this. – Fábio Perez Feb 13 '17 at 13:03
  • 1
    Yes, for newer versions of Matplotlib the above line seemingly results in small margins. Setting the extent manually is probably the cleanest solution: `fig.set_size_inches((width, height))` `extent = mpl.transforms.Bbox(((0, 0), (width, height)))` `fig.savefig([...], bbox_inches=extent)` – weatherfrog Feb 14 '17 at 13:39
  • 10
    I was able to update this for newer versions/remaining margins issue simply by adding transparent=True `fig.savefig('out.png', bbox_inches='tight',transparent=True, pad_inches=0)` – mdoc-2011 Feb 14 '18 at 19:21
  • 4
    Still got axes and tickmarks and everything with this :( – BjornW Apr 07 '18 at 09:56
  • 3
    This does not do anything at all. It will give you the figure with all axes and labels. – 1313e Nov 05 '18 at 02:45
  • 4
    I recommend borrowing `plt.axis('off')` from another answer in combination with this one. – craq Aug 13 '20 at 02:46
33

You can find the bbox of the image inside the axis (using get_window_extent), and use the bbox_inches parameter to save only that portion of the image:

import numpy as np
import matplotlib.pyplot as plt

data=np.arange(9).reshape((3,3))
fig=plt.figure()
ax=fig.add_subplot(1,1,1)
plt.axis('off')
plt.imshow(data)

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

I learned this trick from Joe Kington here.

Community
  • 1
  • 1
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
22

I've tried several options in my case, and the best solution was this:

fig.subplots_adjust(bottom = 0)
fig.subplots_adjust(top = 1)
fig.subplots_adjust(right = 1)
fig.subplots_adjust(left = 0)

then save your figure with savefig

Cagri Sarigoz
  • 366
  • 3
  • 7
10

Actually I have tried this recently and instead of all these lines, you can use

plt.imsave(image_path, image)

Works like a charm. just one line and problem solved.

imsave()

Documentation ( https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.imsave.html )

9

For anybody trying to do this in Jupyter Notebook

 plt.axis('off')

 spec = plt.imshow

 plt.savefig('spec',bbox_inches='tight',transparent=True, pad_inches=0)
Community
  • 1
  • 1
Krackle
  • 363
  • 1
  • 3
  • 9
8

I will suggest heron13 answer with a slight addition borrowed from here to remove the padding left after setting the bbox to tight mode, therefore:

axes = fig.axes()
axes.get_xaxis().set_visible(False)
axes.get_yaxis().set_visible(False)
fig.savefig('out.png', bbox_inches='tight', pad_inches=0)
rominf
  • 2,719
  • 3
  • 21
  • 39
8

This one work for me

plt.savefig('filename',bbox_inches='tight',transparent=True, pad_inches=0)
Cathy Yang
  • 81
  • 1
  • 1
4

For me, this code made similar the input image size without frame and axes. I combined snippets from matehat, unutbu, and WHZW:

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
plt.axis('off')
viridis = cm.get_cmap('gist_gray', 256)
plt.imshow(data, aspect='auto', cmap=viridis)
plt.tight_layout()
plt.savefig(out_file, bbox_inches='tight', transparent=True, pad_inches=0)

Runtime environment:
  Python: 3.6.10
  Matplotlib: 3.2.1
  OS: Windows 10

Cloud Cho
  • 1,594
  • 19
  • 22
3

I had the same problem while doing some visualization using librosa where I wanted to extract content of the plot without any other information. So this my approach. unutbu answer also helps me to make to work.

    figure = plt.figure(figsize=(500, 600), dpi=1)
    axis = plt.subplot(1, 1, 1)
    plt.axis('off')
    plt.tick_params(axis='both', left='off', top='off', right='off', bottom='off', labelleft='off', labeltop='off',
                    labelright='off', labelbottom='off')

     # your code goes here. e.g: I used librosa function to draw a image
    result = np.array(clip.feature_list['fft'].get_logamplitude()[0:2])
    librosa.display.specshow(result, sr=api.Clip.RATE, x_axis='time', y_axis='mel', cmap='RdBu_r')


    extent = axis.get_window_extent().transformed(figure.dpi_scale_trans.inverted())
    plt.savefig((clip.filename + str("_.jpg")), format='jpg', bbox_inches=extent, pad_inches=0)
    plt.close()
GPrathap
  • 7,336
  • 7
  • 65
  • 83
  • This got me on the right track, but I had two problems: 1) I had to set the dpi to a number greater than 1 to avoid a font error in my jupyter notebook; and 2) there was still a small border so I have to manually change the extent Bbox to `extent.get_points()*np.array([[1.1],[.9]])`. – Bob Baxley Dec 09 '17 at 15:00
  • thanks for the fulfiling the answer which may help to somebody else. – GPrathap Dec 09 '17 at 20:34
2

While the above answers address removing margins and padding, they did not work for me in removing labels. Here's what worked, for anyone who stumbles upon this question later:

Assuming you want a 2x2 grid of subplots from four images stored in images:

matplotlib.pyplot.figure(figsize = (16,12)) # or whatever image size you require
for i in range(4):
    ax = matplotlib.pyplot.subplot(2,2,i+1)
    ax.axis('off')
    imshow(images[i])
matplotlib.pyplot.savefig(path, bbox_inches='tight')
curious
  • 125
  • 1
  • 12
1

I tried to get rid of the border too, using tips here but nothing really worked. Some fiddling about and I found that changing the faceolor gave me no border in jupyter labs (Any color resulted in getting rid of the white border). Hope this helps.

def show_num(data):
data = np.rot90(data.reshape((16,16)), k=3)
data = np.fliplr(data)
fig = plt.figure(frameon=False, facecolor='white')
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(data)
plt.show()

enter image description here

Atom Scott
  • 59
  • 1
  • 10
0

I am working on a CNN model which needs simulated images as input training data. Thus, I need axes to be removed completely. I tried all presented answers and found below combination the most effective in my case for outputting a certain size figure by pixels (in my case 512*512). As an example:

import numpy as np
import matplotlib.pyplot as plt

time = np.arange(0, 10, 0.1)
amplitude = np.sin(time)

plt.plot(amplitude, c = 'black')

plt.savefig('no_content_data.png')
plt.close()

which saves below figure in a normal way with axes and no specific size: enter image description here

While below codes remove everything and output figure in 512*512 pixel size:

pixels = 512
px = 1/plt.rcParams['figure.dpi']  # pixel in inches
fig, ax = plt.subplots(frameon=False)
fig.set_size_inches(pixels*px, pixels*px)
ax = plt.Axes(fig, [0., 0., 1., 1.]) # This is needed for fitting content to 512*512 figure frame otherwise output figure will be less than desired size
ax.set_axis_off()
fig.add_axes(ax)

plt.plot(amplitude, c = 'black')

fig.subplots_adjust(bottom = 0)
fig.subplots_adjust(top = 0.00001) # This value should be very small so that marginal axes of subplot would not overlay on the main figure
fig.subplots_adjust(right = 1)
fig.subplots_adjust(left = 0)

plt.savefig('no_content_data.png')
plt.close()

which gives this: enter image description here

hamflow
  • 27
  • 6