I am finding that the get_window_extent method for an image object gives all zeros.
For example
import numpy as np
import matplotlib.pyplot as plt
im = np.array([[0.25, 0.75, 1.0, 0.75], [0.1, 0.65, 0.5, 0.4], \
[0.6, 0.3, 0.0, 0.2], [0.7, 0.9, 0.4, 0.6]])
fig = plt.figure()
ax = plt.subplot()
ax.set_xlim(0,1)
ax.set_ylim(0,1)
im_obj = ax.imshow(im, extent = [0.25, 0.75, 0.25, 0.75], interpolation = 'nearest')
produces the following plot
Now I want to get the image size in display coordinates, so I do the following:
fig.canvas.draw()
renderer = fig.canvas.renderer
im_bbox = im_obj.get_window_extent(renderer)
The trouble is print im_bbox
produces Bbox('array([[ 0., 0.],\n [ 0., 0.]])')
. The method .get_window_extent(renderer)
works fine with text, lines, and patches, so I was a bit surprised to see it does not work with images. Is this a bug, or am I doing something wrong?
In case it matters, I am using Matplotlib 1.3.0 with the TkAgg backend.
EDIT:
Here is a work around to get the image bounding box in display coordinates
im_ext = im_obj.get_extent()
im_pts = np.array([[im_ext[0], im_ext[2]], [im_ext[1], im_ext[3]]])
bbox = mpl.transforms.Bbox(im_pts)
fig.canvas.draw()
bbox = bbox.transformed(ax.transData)
Now print bbox
produces Bbox('array([[ 236.375, 148.2 ],\n [ 433.975, 345.8 ]])')
. I have also confirmed that these values are correct. The following code:
from matplotlib.patches import Rectangle
rect = Rectangle([bbox.x0, bbox.y0], \
bbox.width, bbox.height, \
linewidth = 8, color = [1,0,0], \
fill = False)
fig.patches.append(rect)
fig.canvas.draw()
plt.savefig('wtf.png', dpi = mpl.rcParams['figure.dpi'])
produces the following plot
This workaround should be robust, but it isn't simple. It took me a while to figure out that I had to do fig.canvas.draw()
before I transformed the image bounding box from axes coordinates to display (pixel) coordinates. Without fig.canvas.draw()
the transformation is done incorrectly. I guess matplotlib needs to draw everything first so it knows how to convert to display coordinates.
Thus, the original question remains. Why does im_obj.get_window_extent(renderer)
give a bounding box with all zeros?
The reason that you get all zeros is that until you have rendered the artist (done by calling draw()
) at least once, it has no way of knowing how big it is. This is a consequence of matplotlib being lazy and not doing the work of rendering the artists until it has to.
Your workaround is correct and is essentially what mpl does internally for things like tight_layout
and saving with a tight bounding box.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With