Matplotlib:image.get_window_extent(renderer) 返回全零
我发现,图像对象的 get_window_extent 方法返回的结果全是零。
比如说:
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')
这段代码生成了下面的图:
现在我想获取图像在显示坐标中的大小,所以我这样做:
fig.canvas.draw()
renderer = fig.canvas.renderer
im_bbox = im_obj.get_window_extent(renderer)
问题是,print im_bbox
输出的是 Bbox('array([[ 0., 0.],\n [ 0., 0.]])')
。这个 .get_window_extent(renderer)
方法在处理文本、线条和补丁时都很好用,所以我有点惊讶它在图像上不管用。这是个bug,还是我做错了什么?
顺便说一下,我使用的是 Matplotlib 1.3.0 和 TkAgg 后端。
编辑:
这里有个解决办法,可以获取图像在显示坐标中的边界框:
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)
现在 print bbox
输出的是 Bbox('array([[ 236.375, 148.2 ],\n [ 433.975, 345.8 ]])')
。我也确认这些值是正确的。下面的代码:
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'])
生成了以下图:
这个解决办法应该是有效的,但并不简单。我花了一些时间才弄明白,在把图像的边界框从坐标轴坐标转换为显示(像素)坐标之前,我必须先执行 fig.canvas.draw()
。如果不调用 fig.canvas.draw()
,转换就会出错。我想 matplotlib 需要先绘制所有内容,这样它才能知道如何转换为显示坐标。
所以,最初的问题依然存在。为什么 im_obj.get_window_extent(renderer)
返回的边界框全是零呢?
1 个回答
你看到的全是零的原因是,在你至少调用一次 draw()
来渲染艺术家之前,它并不知道自己的大小。这是因为 matplotlib 采取了一种懒惰的方式,只有在必须的时候才会去渲染这些艺术家。
你找到的解决办法是正确的,实际上这就是 mpl 在处理像 tight_layout
和保存时使用紧凑边界框时内部所做的事情。