From 2060db49be3bbf63b36bc19ac8a7c7074b8e8bfe Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 18 Nov 2010 09:35:14 -0500 Subject: [PATCH] Add clickloc post and bring in plotpick.py. --- posts/clicklock.mdwn | 22 +++ posts/clicklock/clickloc.tk | 33 +++++ posts/clicklock/scale_click.sh | 29 ++++ .../{PlotPick_program.mdwn => plotpick.mdwn} | 9 +- posts/plotpick/plotpick.py | 126 ++++++++++++++++++ 5 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 posts/clicklock.mdwn create mode 100644 posts/clicklock/clickloc.tk create mode 100644 posts/clicklock/scale_click.sh rename posts/{PlotPick_program.mdwn => plotpick.mdwn} (64%) create mode 100644 posts/plotpick/plotpick.py diff --git a/posts/clicklock.mdwn b/posts/clicklock.mdwn new file mode 100644 index 0000000..1383f38 --- /dev/null +++ b/posts/clicklock.mdwn @@ -0,0 +1,22 @@ +[[!meta title="Image click locations"]] + +The [[clickloc.tk]] micro-app just opens an image file and prints out +the pixel coordinates of any mouse clicks upon it. I use it to get +rough numbers from journal figures. You can use [[scale_click.sh]] to +convert the output to units of your choice, if you use your first four +clicks to mark out the coordinate system (xmin,anything), +(xmax,anything), (anything,ymin), and (anything,ymax). + + $ pdfimages article.pdf fig + $ clickloc.tk fig-000.ppm > fig-000.pixels + $ scale_click.sh 0 10 5 20 fig-000.pixels > fig-000.data + +Take a look at [[plotpick]] for grabbing points from raw datafiles +(which is more accurate and easier than reverse engineering images). + +[[!tag tags/bash]] +[[!tag tags/code]] +[[!tag tags/linux]] +[[!tag tags/programming]] +[[!tag tags/tcl-tk]] +[[!tag tags/theory]] diff --git a/posts/clicklock/clickloc.tk b/posts/clicklock/clickloc.tk new file mode 100644 index 0000000..f8ec6a7 --- /dev/null +++ b/posts/clicklock/clickloc.tk @@ -0,0 +1,33 @@ +#!/usr/bin/wish +# +# from http://wiki.tcl.tk/876 +# http://www.tcl.tk/man/tcl8.4/TkCmd/canvas.htm +# http://wiki.tcl.tk/1623 +# +# Open an image and record the pixel coordinates of mouseclicks on it. +# Useful for extracting rough numbers from journal figures. +# +# usage: clickloc.tk imagefile + +# The image package lets you open more image formats. +# You can invoke this package with either of the following lines +#load /usr/local/lib/Img1.2/libimg1.2.so +#package require Img + +if { $argc != 1 } { + puts "usage: clickloc.tk imagefile" + exit 1 +} + +set imagefile $argv + +set picture [image create photo -file $imagefile] +set height [image height $picture] +set width [image width $picture] +canvas .can -height $height -width $width +.can create image 0 0 -image $picture -anchor nw -tag img +pack .can + +.can bind img { + puts "%x\t%y" +} diff --git a/posts/clicklock/scale_click.sh b/posts/clicklock/scale_click.sh new file mode 100644 index 0000000..ee51550 --- /dev/null +++ b/posts/clicklock/scale_click.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +if [ $# -ne 5 ] +then + echo "usage: scale_click.sh xmin xmax ymin ymax file" + exit 1 +fi + +# reference points in the final units from the command line +XMIN=$1 +XMAX=$2 +YMIN=$3 +YMAX=$4 +FILE=$5 + +# reference points in pixels from the first 4 clicks +XMINP=`sed -n '1p' $FILE | cut -f1` +XMAXP=`sed -n '2p' $FILE | cut -f1` +YMINP=`sed -n '3p' $FILE | cut -f2` +YMAXP=`sed -n '4p' $FILE | cut -f2` + +echo "# Xp -> ($XMAX-$XMIN)/($XMAXP-$XMINP)*(Xp-$XMINP) + $XMIN" +echo "# Yp -> ($YMAX-$YMIN)/($YMAXP-$YMINP)*(Yp-$YMINP) + $YMIN" + +awk "{if(NR > 4){print ($XMAX-$XMIN)/($XMAXP-$XMINP)*(\$1-$XMINP) + $XMIN, \ + ($YMAX-$YMIN)/($YMAXP-$YMINP)*(\$2-$YMINP) + $YMIN}}" \ + $FILE + +exit 0 diff --git a/posts/PlotPick_program.mdwn b/posts/plotpick.mdwn similarity index 64% rename from posts/PlotPick_program.mdwn rename to posts/plotpick.mdwn index 015b9a6..581d048 100644 --- a/posts/PlotPick_program.mdwn +++ b/posts/plotpick.mdwn @@ -6,12 +6,11 @@ set boundaries programmatically at the moment, I thought I'd write up a little utility to record clicks on key data points, so I could pick out the “good data”. -Enter -[plotpick.py](http://www.physics.drexel.edu/~wking/code/index.shtml#plotpick), -the raw-data version of -[clicklock.tk](http://www.physics.drexel.edu/~wking/blog/archives/2008/09/05/index.html#e2008-09-05T20_20_30.txt). -Hope you like it :). +Enter [[plotpick.py]] the raw-data version of [[clicklock]]. Hope you +like it :). +[[!tag tags/code]] [[!tag tags/linux]] [[!tag tags/programming]] +[[!tag tags/python]] [[!tag tags/theory]] diff --git a/posts/plotpick/plotpick.py b/posts/plotpick/plotpick.py new file mode 100644 index 0000000..8564b71 --- /dev/null +++ b/posts/plotpick/plotpick.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +""" +Pick data-points with a cursor. The cursor snaps to the nearest +datapoint. Developed from Matplotlib's cursor_demo.py. + +Faster cursoring is possible using native GUI drawing, as in +wxcursor_demo.py. You could also write up a more efficient +Cursor._get_xy() method. +""" + +from pylab import subplot, connect, show, draw +from numpy import array, fromfile, double + +class Cursor: + """ + Like Cursor but the crosshair snaps to the nearest x,y point + For simplicity, I'm assuming x is sorted + """ + def __init__(self, ax, x, y): + self.ax = ax + self.lx, = ax.plot( (0,0), (0,0), 'k-' ) # the horiz line + self.ly, = ax.plot( (0,0), (0,0), 'k-' ) # the vert line + self.x = x + self.y = y + self.xscale = max(self.x) - min(self.x) + self.yscale = max(self.y) - min(self.y) + self._sort() + # text location in axes coords + #self.txt = ax.text( 0.6, 0.9, '', transform=ax.transAxes) + self.txt = self.ax.title + def _sort(self): + "ideally, here is where you build the Voronoi lookup tree" + pass + def _get_xy(self, x, y): + """terrible hack. Should compute Voronoi diagram with + some sort of lookup tree. Work for my free time... + http://en.wikipedia.org/wiki/Voronoi_diagram + http://en.wikipedia.org/wiki/Point_location#Triangulation_refinement + http://www.cs.cmu.edu/~quake/triangle.html + """ + dist = float("infinity") + indx = -1 + xs = self.xscale + ys = self.yscale + for xp,yp,i in zip(self.x, self.y,range(len(self.x))): + d = (((x-xp)/xs)**2 + ((y-yp)/ys)**2)**0.5 + if d < dist: + dist = d + xpm = xp + ypm = yp + indx = i + return (xpm,ypm,indx) + def mouse_move(self, event): + if not event.inaxes: return + ax = event.inaxes + minx, maxx = ax.get_xlim() + miny, maxy = ax.get_ylim() + x, y, i = self._get_xy(event.xdata, event.ydata) + # update the line positions + self.lx.set_data( (minx, maxx), (y, y) ) + self.ly.set_data( (x, x), (miny, maxy) ) + # update the label + self.txt.set_text( 'x=%1.2g, y=%1.2g, indx=%d'%(x,y,i) ) + draw() + def mouse_click(self, event): + if not event.inaxes: return + x, y, i = self._get_xy(event.xdata, event.ydata) + if event.button != 3: return # ignore non-button-3 clicks + print '%g\t%g\t%d'%(x,y,i) + +def readFile(fid, cols=2, sep='\t'): + """This is the lower level reader. It works on all file types, but + you need to know the number of columns in the file ahead of time.""" + data = fromfile(file=fid, dtype=double, sep=sep) + rows = len(data)/cols + data = data.reshape((rows,cols)) + x = data[:,0] + y = data[:,1] + return x,y + +def readFilename(filename, sep='\t'): + """Requires rewinding file if first line is not a comment. + Therefore only useful on + * seekable files (e.g. read from disk) + * FIFOs/piped-data with headers""" + fid = file(filename,'r') + headline = fid.readline() + if headline[0] != "#": fid.seek(0) # rewind if headline was data + cols = headline.count(sep)+1 + x,y = readFile(fid, cols=cols, sep=sep) + fid.close() + return x,y + +if __name__ == "__main__" : + import sys + + if len(sys.argv) > 2 : + print "usage: plotpick.py [datafile]" + print """ +Reads in ASCII data from datafile, or, if datafile is not given, from +stdin. Data files define an array of (x,y) points and consist of two +columns (x&y) seperated by TABs, with ENDLINEs between the points: +x0 y0 +x1 y1 +... +The points are plotted in a window, and mousing over the plot displays +a cursor that snaps to the nearest point. Right-clicking (button 3) +will print the TAB seperated coordinates (x, y, point-index) to stdout. +""" + sys.exit(1) + elif len(sys.argv) == 1: + x,y = readFile(sys.stdin) + else: # len(sys.argv) == 2 + datafile = file(sys.argv[1], 'r') + x,y = readFile(datafile) + datafile.close + + ax = subplot(111) + cursor = Cursor(ax, x, y) + connect('motion_notify_event', cursor.mouse_move) + connect('button_press_event', cursor.mouse_click) + ax.plot(x, y, '.') + ax.axis([min(x), max(x), min(y), max(y)]) + + print '#x\ty\tindex' + show() -- 2.26.2