Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
283 views
in Technique[技术] by (71.8m points)

python - Incorrect rectangle location in matplotlib

I'm making a plot with bars and I'm trying to find their absolute location (in pixels) on the plot for further processing later. It seems to me that this should be able to be figured out from matplotlib's transformation information on the axes instance. Specifically, I'm using ax.transData to go from data coordinates (where I know the box positions) to display coordinates. Here is some code:

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

This prints the following:

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

So, matplotlib thinks my boxes are 24.8 units (pixels I assume) wide. That's fine except when I actually go to measure the box width, I get something more like 32 pixels wide. What is the discrepancy here?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As usual, the revelation happens when posting the question ... The problem here is that the units are not pixels, they're dots. When the figure is initialized, it is set up with a default dots per inch (fig.set_dpi() and fig.get_dpi() allow you to change/inquire the current figure's resolution). Of course, things can't be that easy -- When you save the figure, the dpi changes based on your rc settings for the particular backend. For me with the default backend and png output, this goes to 100 dpi. One thing which is invariant here, however is the figure size fig.get_size_inches(). So, if I scale my results, things seem to turn our a little more consistently ...

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

I have a sinking feeling that this isn't the full solution though -- After all, we're still working in dots. I'm not completely sure how a dot translates to a pixel for png images ... My hope is that the display engine (in my particular case, a web browser) renders each dot in a single pixel and doesn't care about the image's reported size. I suppose that only some experimentation will sort that one out ...


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...