3 """Generate an image from ASCII data.
5 Step 1: make this file executable::
9 Step 2: pipe data into python script::
11 ./gen_data | ./plot_image.py -s nx,ny -c nc -t 'image title'
13 Data should be one ASCII float per line, in the following order::
25 where `x` increases from `x1` to `xN` and `y` increases from `y1`
28 You can use the `--xyz` option to read an alternative data format,
35 If you use this method, the ordering of lines in the data file is
36 irrelevant, and `Nx` and `Ny` are extracted from the data file itself.
37 However, you still need to use a rectangular grid (i.e. for every
38 `xi`, you need to have entries for every `yi`).
42 ./plot_image.py --help
44 When run with interactive output (i.e. no `-o ...` option), the
45 interactive figure is displayed using `pylab.show`, which means that
46 you'll have to kill `plot_image.py` using ^C or similar [1]_.
48 For other ideas, see the Matplotlib website [2]_.
50 .. [1] http://matplotlib.sourceforge.net/faq/howto_faq.html#use-show
51 .. [2] http://matplotlib.sourceforge.net/
58 import matplotlib.image
61 # Depending on your Matplotlib configuration, you may need to adjust
62 # your backend. Do this before importing pylab or matplotlib.backends.
63 #matplotlib.use('Agg') # select backend that doesn't require X Windows
64 #matplotlib.use('GTKAgg') # select backend that supports pylab.show()
72 def read_data_1d(stream, nx, ny):
73 """Read in data, one entry per line.
75 >>> from StringIO import StringIO
76 >>> s = StringIO('\\n'.join(map(str, range(10)))+'\\n')
77 >>> X,Y,Z = read_data_1d(s, 5, 2)
79 array([[0, 1, 2, 3, 4, 5],
83 array([[0, 0, 0, 0, 0, 0],
87 array([[ 0., 1., 2., 3., 4.],
88 [ 5., 6., 7., 8., 9.]])
90 X,Y = pylab.meshgrid(range(nx+1), range(ny+1))
91 Z = numpy.loadtxt(stream)
92 assert Z.size == nx*ny, 'Z.size = %d != %d = %dx%d' % (
93 Z.size, nx*ny, nx, ny)
94 Z = Z.reshape([x-1 for x in X.shape])
97 def read_data_3d(stream):
98 """Read in data, one `(x, y, z)` tuple per line.
100 >>> from StringIO import StringIO
102 >>> for x in range(5):
103 ... for y in range(2):
104 ... lines.append('\t'.join(map(str, [x, y, x+y*5])))
105 >>> s = StringIO('\\n'.join(lines)+'\\n')
106 >>> X,Y,Z = read_data_3d(s)
108 array([[ 0., 1., 2., 3., 4., 5.],
109 [ 0., 1., 2., 3., 4., 5.],
110 [ 0., 1., 2., 3., 4., 5.]])
112 array([[ 0., 0., 0., 0., 0., 0.],
113 [ 1., 1., 1., 1., 1., 1.],
114 [ 2., 2., 2., 2., 2., 2.]])
116 array([[ 0., 1., 2., 3., 4.],
117 [ 5., 6., 7., 8., 9.]])
119 XYZ = numpy.loadtxt(stream)
120 assert len(XYZ.shape) == 2 and XYZ.shape[1] == 3, XYZ.shape
121 Xs = numpy.array(sorted(set(XYZ[:,0])))
122 Ys = numpy.array(sorted(set(XYZ[:,1])))
123 Z = numpy.ndarray((len(Ys), len(Xs)), dtype=float)
124 xyz = {} # dict of z values keyed by (x,y) tuples
125 for i in range(XYZ.shape[0]):
126 xyz[(XYZ[i,0], XYZ[i,1])] = XYZ[i,2]
127 for i,x in enumerate(Xs):
128 for j,y in enumerate(Ys):
130 # add dummy row/column for pcolor
133 Xs = numpy.append(Xs, Xs[-1] + dx)
134 Ys = numpy.append(Ys, Ys[-1] + dy)
135 X,Y = pylab.meshgrid(Xs, Ys)
138 def plot(X, Y, Z, full=False, title=None, contours=None, interpolation=None,
140 """Plot Z over the mesh X, Y.
142 >>> X, Y = pylab.meshgrid(range(6), range(2))
143 >>> Z = X[:-1,:-1]**2 + Y[:-1,:-1]
144 >>> plot(X, Y, Z) # doctest: +ELLIPSIS
145 <matplotlib.figure.Figure object at 0x...>
154 axes = fig.add_axes([0, 0, 1, 1])
156 axes = fig.add_subplot(1, 1, 1)
158 axes.set_title(title)
162 cset = axes.contour(X[:-1,:-1], Y[:-1,:-1], Z, contours, cmap=cmap)
163 # [:-1,:-1] to strip dummy last row & column from X&Y.
164 axes.clabel(cset, inline=1, fmt='%1.1f', fontsize=10)
166 # pcolor() is much slower than imshow.
167 #plot = axes.pcolor(X, Y, Z, cmap=cmap, edgecolors='none')
168 #axes.autoscale_view(tight=True)
169 plot = axes.imshow(Z, aspect='auto', interpolation=interpolation,
170 origin='lower', cmap=cmap,
171 extent=(X_min, X_max, Y_min, Y_max))
177 def get_possible_interpolations():
178 try: # Matplotlib v1.0.1
179 return sorted(matplotlib.image.AxesImage._interpd.keys())
180 except AttributeError:
181 try: # Matplotlib v0.91.2
182 return sorted(matplotlib.image.AxesImage(None)._interpd.keys())
183 except AttributeError:
190 results = doctest.testmod()
191 return results.failed
195 """Read in data and plot it.
197 >>> from tempfile import NamedTemporaryFile
198 >>> i = NamedTemporaryFile(prefix='tmp-input', suffix='.dat')
199 >>> i.write('\\n'.join([str(x) for x in range(10)])+'\\n')
201 >>> o = NamedTemporaryFile(prefix='tmp-output', suffix='.png')
202 >>> main(['-i', i.name, '-s', '5,2', '-o', o.name, '-m', 'binary'])
204 Title: Some like it hot
207 X range: 0 4 (6 steps)
208 Y range: 0 1 (3 steps)
211 >>> img.startswith('\\x89PNG')
219 usage = '%prog [options]'
221 p = optparse.OptionParser(usage=usage, epilog=epilog)
222 p.format_epilog = lambda formatter: epilog+'\n'
224 p.add_option('-i', '--input', dest='input', metavar='FILE',
225 help='If set, read data from FILE rather than stdin.')
226 p.add_option('-o', '--output', dest='output', metavar='FILE',
227 help=('If set, save the figure to FILE rather than '
228 'displaying it immediately'))
229 p.add_option('-s', '--size', dest='size', default='%d,%d' % (16, 16),
230 help='Data size (columns,rows; default: %default)')
231 p.add_option('-3', '--xyz', dest='xyz', default=False, action='store_true',
232 help=('If set, read (x,y,z) tuples from the input data rather'
233 'then reading `z` and calculating `x` and `y` from '
235 p.add_option('-c', '--contours', dest='contours', type='int',
236 help=('Number of contour lines (if not set, draw false color '
237 'instead of contour lines; default: %default)'))
238 p.add_option('-f', '--full-figure', dest='full', action='store_true',
239 help=('Set axes to fill the figure (i.e. no title or color '
241 p.add_option('-t', '--title', dest='title', default='Some like it hot',
242 help='Title (%default)')
243 p.add_option('--test', dest='test', action='store_true',
244 help='Run internal tests and exit.')
245 interpolations = get_possible_interpolations()
246 p.add_option('--interpolation', dest='interpolation', default='nearest',
247 help=('Interpolation scheme (for false color images) from %s '
248 '(%%default)') % ', '.join(interpolations))
249 maps=[m for m in pylab.cm.datad if not m.endswith("_r")]
251 p.add_option('-m', '--color-map', dest='cmap', default='jet',
252 help='Select color map from %s (%%default)' % ', '.join(maps))
254 options,args = p.parse_args(argv)
259 nx,ny = [int(x) for x in options.size.split(',')]
261 cmap = getattr(pylab.cm, options.cmap)
262 except AttributeError:
263 raise Exception('no color map named %s in %s'
264 % (options.cmap, ', '.join(maps)))
267 print 'Title: ', options.title
269 print 'Image size: ', nx, ny
271 print '# countour lines: ', options.contours
275 fin = open(options.input, 'r')
280 X,Y,Z = read_data_3d(fin)
282 X,Y,Z = read_data_1d(fin, nx, ny)
287 Z_min = numpy.min(Z.flat)
288 Z_max = numpy.max(Z.flat)
289 print 'X range: {} {} ({} steps)'.format(
290 X[0,0], X[0,-2], X.shape[1])
291 print 'Y range: {} {} ({} steps)'.format(
292 Y[0,0], Y[-2,0], Y.shape[0])
293 print 'Z range: ', Z_min, Z_max
295 fig = plot(X, Y, Z, full=options.full, title=options.title,
296 contours=options.contours, interpolation=options.interpolation,
300 fig.savefig(options.output)
305 if __name__ == '__main__':