How to get pixel coordinates for Matplotlib-generated scatterplot?


Question

I use Matplotlib to generate PNG files of scatterplots. Now, for each scatterplot, in addition to a PNG file, I would also like to generate a list of pixel coordinates of the various points in the scatterplot.

The code I use to generate the PNG files for the scatterplots is basically like this:

from matplotlib.figure import Figure
from matplotlib.pyplot import setp
from matplotlib.backends.backend_agg import FigureCanvasAgg

...

fig = Figure(figsize=(3, 3), dpi=100)
ax = fig.gca()
for (x, y), m, c in zip(points, markers, colors):
    ax.scatter(x, y, marker=m, c=c, s=SIZE, vmin=VMIN, vmax=VMAX)

# several assorted tweaks like ax.spines['top'].set_color('none'), etc.

setp(fig, 'facecolor', 'none')

# FigureCanvasAgg(fig).print_png(FILEPATH)

...(where the variables in UPPERCASE stand for settable parameters).

How can I also produce a list of (px, py) pairs of the pixel coordinates in the resulting PNG corresponding to the points in points?

[EDIT: removed some nonsense about imshow.]

[EDIT:

OK, here's what I finally came up with, based on Joe Kington's suggestions.

# continued from above...

cnvs = FigureCanvasAgg(fig)
fig.set_canvas(cnvs)
_, ht = cnvs.get_width_height()
pcoords = [(int(round(t[0])), int(round(ht - t[1]))) for t in
           ax.transData.transform(points)]
fig.savefig(FILEPATH, dpi=fig.dpi)

The resulting pixel coords (in pcoords) are pretty close to the correct values. In fact, the y coords are exactly right. The x coords are 1 or 2 pixels off, which is good enough for my purposes.

]

1
7
12/1/2012 9:17:00 PM

Accepted Answer

Doing this is fairly simple, but to understand what's going on, you'll need to read up a bit on matplotlib's transforms. The transformations tutorial is a good place to start.

At any rate, here's an example:

import numpy as np
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
points, = ax.plot(range(10), 'ro')
ax.axis([-1, 10, -1, 10])

# Get the x and y data and transform it into pixel coordinates
x, y = points.get_data()
xy_pixels = ax.transData.transform(np.vstack([x,y]).T)
xpix, ypix = xy_pixels.T

# In matplotlib, 0,0 is the lower left corner, whereas it's usually the upper 
# right for most image software, so we'll flip the y-coords...
width, height = fig.canvas.get_width_height()
ypix = height - ypix

print 'Coordinates of the points in pixel coordinates...'
for xp, yp in zip(xpix, ypix):
    print '{x:0.2f}\t{y:0.2f}'.format(x=xp, y=yp)

# We have to be sure to save the figure with it's current DPI
# (savfig overrides the DPI of the figure, by default)
fig.savefig('test.png', dpi=fig.dpi)

This yields:

Coordinates of the points in pixel coordinates...
125.09  397.09
170.18  362.18
215.27  327.27
260.36  292.36
305.45  257.45
350.55  222.55
395.64  187.64
440.73  152.73
485.82  117.82
530.91  82.91

enter image description here

12
12/1/2012 7:31:58 PM

Try annotation box : http://matplotlib.org/examples/pylab_examples/demo_annotation_box.html

import matplotlib.pyplot as plt
from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, \
     AnnotationBbox

for (x, y), m, c in zip(points, markers, colors):
    ax.scatter(x, y, marker=m, c=c, s=SIZE, vmin=VMIN, vmax=VMAX)

    for px, py in zip(x,y):
        offsetbox = TextArea( " %s, %s" (px, py ) , minimumdescent=False)
        ab = AnnotationBbox(offsetbox,(px, py ),
                        xybox=(-20, 40),
                        xycoords='data',
                        boxcoords="offset points",
                        arrowprops=dict(arrowstyle="->"))
        ax.add_artist(ab)

I don't have matplotlib installed on my current computer, so my code might not work.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon