sunmat
sunmat

Reputation: 7268

How to remove axis, legends, and white padding

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

Upvotes: 501

Views: 1035526

Answers (18)

G M
G M

Reputation: 22510

I prefer this solution which is in my opinion the cleanest:

from numpy import random
import matplotlib.pyplot as plt

data = random.random((5,5))
fig,ax = plt.subplots()
ax.imshow(data)
ax.set_axis_off()

It offers more control without using plt and it need only one line of code.

enter image description here

Upvotes: 0

AaronJPung
AaronJPung

Reputation: 1144

Surprised this didn't turn up. For plotting N images, you could use:

N = 5
data_array = np.random.rand(15,15,N)
plt.figure(figsize=(5*N,5))
for i in np.arange(N):
    img = data_array[:,:,i]
    plt.subplot(1,N,i+1)
    plt.imshow(img)             # Generate image
    plt.axis("off")             # Remove axes
plt.tight_layout()              # Remove white space between images
plt.show()

which produces something like

enter image description here

Upvotes: -1

leenremm
leenremm

Reputation: 1464

This works:

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

Upvotes: 3

Sirius Koan
Sirius Koan

Reputation: 61

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.

Upvotes: 1

Hooked
Hooked

Reputation: 88228

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

enter image description here

Upvotes: 657

Ben
Ben

Reputation: 61

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

Upvotes: 1

Dgomn D
Dgomn D

Reputation: 131

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

Upvotes: 3

Nazmus Sakib Akash
Nazmus Sakib Akash

Reputation: 327

plt.axis('off')

plt.savefig('example.png',bbox_inches='tight',pad_inches = 0)

gets me the borderless image.

Upvotes: 20

paulj
paulj

Reputation: 151

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

Upvotes: 14

Richard
Richard

Reputation: 3424

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

Upvotes: 1

episodeyang
episodeyang

Reputation: 782

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:

enter image description here

A much simpler thing to do is to use pyplot.imsave. For details, see luator's answer bellow

Upvotes: 10

Vlady Veselinov
Vlady Veselinov

Reputation: 5431

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)

Upvotes: 12

csnemes
csnemes

Reputation: 71

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)

enter image description here

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): enter image description here

Upvotes: 6

David Hoffman
David Hoffman

Reputation: 2343

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)

Upvotes: 1

unutbu
unutbu

Reputation: 880717

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

enter image description here

Upvotes: 201

luator
luator

Reputation: 5027

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.

enter image description here

Upvotes: 51

Domagoj
Domagoj

Reputation: 1705

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 whitespaces, axes or frame

Upvotes: 92

gypaetus
gypaetus

Reputation: 7349

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)

Upvotes: 9

Related Questions