buffer_rgba() mysteriously adds whitespace to matplotlib figure

Viewed 736

I have some simple code in a notebook to visualize an image with matplotlib

f = plt.figure()
plt.imshow(rgb_img)
# f.tight_layout(pad=0) doesn't fix the issue
f.canvas.draw()
# save figure as a np array for easy visualization w/ imshow later
fig_as_np_array = np.array(f.canvas.renderer.buffer_rgba())

At this point everything looks fine:

enter image description here

I then try to view the saved np array (plt.imshow(fig_as_np_array)) which I expect to display the same thing but instead I get odd whitespace plus a new sets of axis:

enter image description here

I can't for the life of me figure out what is adding the extra whitespace/axis, the shapes are slightly different as well:

print(f'rgb shape: {rgb_img.shape}') # prints: rgb shape: (480, 640, 3)
print(f'saved fig shape: {fig_as_np_array.shape}') # prints: saved fig shape: (288, 432, 4)

Any idea what is going on (fwiw I am visualizing this in a notebook). Thanks for your time

1 Answers

If I understood your question correctly, you'll have to ensure to create the figure with the correct dimensions and then remove the axes (via ax.set_axis_off()) and the frame of the figure around the image (via frameon=False) before writing to buffer, see the comments below:

import matplotlib as mpl
mpl.use("tkagg") # <— you may not need this, 
                 #    but I had to specify an agg backend manually
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np

## image taken from
# "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Empty_road_at_night.jpg/1024px-Empty_road_at_night.jpg"
filename = "1024px-Empty_road_at_night.jpg"
im = mpimg.imread(filename)

## create the figure with the correct dpi & resolution
#  and make sure that you specify to show "no frame" around the image
figure_dpi = 72
fig = plt.figure(figsize=(1024/figure_dpi,768/figure_dpi),dpi=figure_dpi,frameon=False,facecolor="w")
ax = fig.add_subplot()

## turn of axes, make imshow use the whole frame
ax.set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, hspace = 0, wspace = 0)
plt.margins(0,0)

## show image
ax.imshow(im,zorder=0,alpha=1.0,origin="upper")
## add some text label
ax.text(300,600,"this is the middle lane",fontsize=30,color="w")

def fig2rgb_array(fig):
    """adapted from: https://stackoverflow.com/questions/21939658/"""
    fig.canvas.draw()
    buf = fig.canvas.tostring_rgb()
    ncols, nrows = fig.canvas.get_width_height()
    print("to verify, our resolution is: ",ncols,nrows)
    return np.frombuffer(buf, dtype=np.uint8).reshape(nrows, ncols, 3)

## make a new figure and read from buffer
fig2,ax2 = plt.subplots()
ax2.imshow(fig2rgb_array(fig))
plt.show()

yields (note there is now only one set of axes around the image, not two):

buffered image with text on top

Related