4

我正在用条形图制作图,并试图在图上找到它们的绝对位置(以像素为单位),以便稍后进行进一步处理。在我看来,这应该可以从 matplotlib 在轴实例上的转换信息中计算出来。具体来说,我使用ax.transData从数据坐标(我知道盒子位置)到显示坐标。这是一些代码:

import matplotlib.pyplot as plt

x = range(10)
y = range(1, 11)

fig = plt.figure()
ax = fig.add_subplot(111)
bars = ax.bar(x, y, width=.5, label="foo")
ax.monkey_rectangles = bars
ax.legend()

def get_useful_info(fig):
    for ax in fig.get_axes():
        for rect in ax.monkey_rectangles:
            corners = rect.get_bbox().corners()[::3]
            pos = ax.transData.transform(corners)
            left = pos[0,0]
            width = pos[1,0] - pos[0,0]
            bottom = pos[0,1]
            height = pos[1,1] - pos[0,1]
            yield left, width, bottom, height

fig.savefig('foo.png')
for l, w, b, h in get_useful_info(fig):
    print l, w, b, h

这将打印以下内容:

80.0 24.8 48.0 38.4
129.6 24.8 48.0 76.8
179.2 24.8 48.0 115.2
228.8 24.8 48.0 153.6
278.4 24.8 48.0 192.0
328.0 24.8 48.0 230.4
377.6 24.8 48.0 268.8
427.2 24.8 48.0 307.2
476.8 24.8 48.0 345.6
526.4 24.8 48.0 384.0

所以,matplotlib 认为我的盒子是 24.8 单位(我假设是像素)宽。这很好,除非当我实际测量盒子宽度时,我得到的东西更像是 32 像素宽。这里的差异是什么?

4

2 回答 2

3

像往常一样,发布问题时会发生启示......这里的问题是单位不是像素,它们是点。初始化图形时,会设置默认的每英寸点数(fig.set_dpi()fig.get_dpi()允许您更改/查询当前图形的分辨率)。当然,事情不可能那么容易——当你保存图形时,dpi 会根据特定后端的 rc 设置而变化。对于使用默认后端和 png 输出的我来说,这是 100 dpi。然而,这里不变的一件事是图形大小fig.get_size_inches()。所以,如果我扩展我的结果,事情似乎会变得更加一致......

def get_useful_info(fig):
    dpi = fig.get_dpi()
    pngdpi = 100
    scale_x = pngdpi / dpi
    scale_y = pngdpi / dpi
    for ax in fig.get_axes():
        for rect in ax.monkey_rectangles:
            corners = rect.get_bbox().corners()[::3]
            pos = ax.transData.transform(corners)
            left = pos[0,0] * scale_x
            width = (pos[1,0] - pos[0,0]) * scale_x
            bottom = pos[0,1] * scale_y
            height = (pos[1,1] - pos[0,1]) * scale_y
            yield left, width, bottom, height

我有一种下沉的感觉,尽管这不是完整的解决方案——毕竟,我们仍在点点滴滴地工作。我不完全确定一个点如何转换为 png 图像的一个像素......我希望显示引擎(在我的特定情况下是一个网络浏览器)将每个点呈现在一个像素中并且不关心图像的报告大小。我想只有一些实验才能解决这个问题......

于 2013-09-18T10:40:19.997 回答
1

当您指的是网络浏览器时,我假设您正在构建图像地图?

import matplotlib.pyplot as plt

x = range(10)
y = range(1, 11)

fig = plt.figure()
ax = fig.add_subplot(111)
bars = ax.bar(x, y, width=.5, label="foo")
ax.monkey_rectangles = bars
ax.legend()

def fig_fraction_info(fig):
    for ax in fig.get_axes():
        inv = fig.transFigure.inverted()
        # transformations are applied left to right
        my_trans = ax.transData + inv 
        for rect in ax.monkey_rectangles:
            corners = rect.get_bbox().corners()[::3]
            pos = my_trans.transform(corners)
            left = pos[0,0]
            width = pos[1,0] - pos[0,0]
            bottom = pos[0,1]
            height = pos[1,1] - pos[0,1]
            yield left, width, bottom, height

我认为这样的事情是你想要的。如果您将此数据和 png 输入到下一级代码中,您可以准确地找出所有内容的位置。

像这样的问题(我认为)是 matplotlib 从前端抽象后端的方式的结果。考虑以像素为单位的大小对于矢量输出没有多大意义。

交替:

dpi = 300
...
fig.set_dpi(dpi)
...
fig.savefig(..., dpi=dpi)
于 2013-09-18T17:13:59.847 回答