Matplotlib:image.get_window_extent(renderer)产生全零

Str*_*tch 5 python matplotlib

我发现图像对象的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')
Run Code Online (Sandbox Code Playgroud)

产生以下情节

图片

现在,我想获取显示坐标中的图像大小,因此我执行以下操作:

fig.canvas.draw()
renderer = fig.canvas.renderer
im_bbox = im_obj.get_window_extent(renderer)
Run Code Online (Sandbox Code Playgroud)

麻烦是print im_bbox产生的Bbox('array([[ 0., 0.],\n [ 0., 0.]])')。该方法.get_window_extent(renderer)适用于文本,行和补丁,因此我惊讶地发现它不适用于图像。这是一个错误,还是我做错了什么?

万一重要,我将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)
Run Code Online (Sandbox Code Playgroud)

现在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'])
Run Code Online (Sandbox Code Playgroud)

产生以下情节

ew

该解决方法应该很可靠,但是并不简单。我花了一段时间才能弄清楚fig.canvas.draw()在将图像边界框从轴坐标转换为显示(像素)坐标之前必须要做的事情。没有fig.canvas.draw()转换就做错了。我想matplotlib需要先绘制所有内容,以便它知道如何转换为显示坐标。

因此,原来的问题仍然存在。为什么要im_obj.get_window_extent(renderer)给出一个全零的边界框?

tac*_*ell 5

你得到全零的原因是,在你渲染艺术家(通过调用完成draw())至少一次之前,它无法知道它有多大。这是 matplotlib 懒惰的结果,直到必要时才进行渲染艺术家的工作。

您的解决方法是正确的,本质上是 mpl 在内部所做的事情,例如tight_layout使用紧密的边界框进行保存。

  • 是的,在我找到解决方法后,我也想到了这一点。但是,我尝试在“renderer = Fig.canvas.renderer”之前放置“fig.canvas.draw()”,但它没有执行任何操作。`im_obj.get_window_extent(renderer)` 仍然产生全零。我已经更新了原来的帖子以反映这一点。 (2认同)