Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib: image.get_window_extent(renderer) produces all zeros

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

image

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

whew

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?

like image 986
Stretch Avatar asked Oct 20 '22 10:10

Stretch


1 Answers

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.

like image 60
tacaswell Avatar answered Oct 23 '22 04:10

tacaswell