Commit 8ecf7566 authored by Russ Fish's avatar Russ Fish

Factor out the global world coordinate tranform from dump_analyzer so

blend triangles can be plotted overlaid on the all-cameras composite figures.
Add camera_data Makefile targets to do the composite figures.
parent c9879a25
......@@ -7,23 +7,37 @@
# Makefile for robots/vmcd/camera_data.
# See README.txt for directory contents.
default: show-figs
SRCDIR = ..
analyze := $(SRCDIR)/dump_analyzer.py
w_plot := $(SRCDIR)/analysis_w_plot.awk
PARAMDIR = ../etc
default: plots
# Dollar-sign for variable reference in shell looping actions.
dollar = $
show: plots
# A selected subset.
figs = camera1_blend8_tris_mag50.eps \
all_mag50.eps all_blend8_mag50.eps all_blend8_tris_mag50.eps
show-figs: figs
for f in $(figs); do gv $(value dollar)f & sleep 1; done
# Raw pixel data with cosine dewarping.
show-plots: plots
gv all_$(mag).eps & sleep 1
for f in $(cam_plt); do gv $(value dollar)f & sleep 1; done
# Cosine dewarping with error blending.
show-blend8: blend8
gv all_blend8_$(mag).eps & sleep 1
for f in $(cam_blp); do gv $(value dollar)f & sleep 1; done
# Superimpose the error blending triangles as well.
show-blend8-tris: blend8-tris
gv all_blend8_tris_$(mag).eps & sleep 1
for f in $(cam_blt); do gv $(value dollar)f & sleep 1; done
clean:
rm -f analysis_* *.error_lines *.grid_points *.tris_output *.eps \
rm -f analysis_* *.error_lines *.grid_points *.tri_lines* *.eps \
*.pyc *_blend8 *_calpts
# Plot magnification factor for error_lines files.
......@@ -40,10 +54,18 @@ analysis: analysis_all $(cam_anl)
cam_plt := $(foreach cam, $(cams), camera$(cam)_$(mag).eps )
plots: all_$(mag).eps $(cam_plt)
cam_anl := $(foreach cam, $(cams), analysis_camera$(cam)_blend8 )
cam_blo := $(foreach cam, $(cams), output_camera$(cam)_blend8 )
cam_bla := $(foreach cam, $(cams), analysis_camera$(cam)_blend8 )
cam_blp := $(foreach cam, $(cams), camera$(cam)_blend8_$(mag).eps )
blend8: all_blend8_$(mag).eps $(cam_anl) $(cam_blp)
blend8: all_blend8_$(mag).eps $(cam_bla) $(cam_blp)
cam_bll := $(foreach cam, $(cams), camera$(cam)_blend8_tris.tri_lines )
cam_blw := $(foreach cam, $(cams), camera$(cam)_blend8_tris.tri_lines_world )
cam_blt := $(foreach cam, $(cams), camera$(cam)_blend8_tris_$(mag).eps )
blend8-tris: all_blend8_tris_$(mag).eps $(cam_anl) $(cam_bll) $(cam_blt)
figs: $(figs)
# Per-camera analysis.
analysis_camera%: output_camera%
......@@ -59,6 +81,7 @@ analysis_all_blend8: $(cam_blo)
# The Y axis is reversed in world coords. (Grumble.)
all_bounds := [3:14] [15:5]
all_blend8_bounds := [3:14] [15:5]
all_blend8_tris_bounds := [3:14] [15:5]
# Generate plots through gnuplot.
# "w_plot -v file=base." makes base.grid_points and base.error_lines .
......@@ -80,10 +103,28 @@ calpts: $(cam_dwp)
camera%_calpts: analysis_camera%_calpts
./cal_pts.py $< > $@
# Apply the Python error blender to generate and show the blending triangles.
camera%_blend8.tri_lines: analysis_camera%_calpts output_camera%
./test_lin_blend2.py -t $^ > $@
# Apply the Python error blender to error-cancel the grid data.
output_camera%_blend8: analysis_camera%_calpts output_camera%
./test_lin_blend2.py $^ > $@
# Apply the Python error blender to generate and show the blending triangles.
%_blend8_tris.tri_lines: analysis_%_calpts output_%
./test_lin_blend2.py -t $^ > $@
%_blend8_tris.tri_lines_world: analysis_%_calpts output_%
@# What's the better way to tell Python this file is upstairs?
@if [ ! -e worldTransform.py ]; then ln -s ../worldTransform.py .; fi
./test_lin_blend2.py -t -g $^ > $@
all_blend8_tris.tri_lines: $(cam_blw)
cat $(cam_blw) > $@
# Superimpose the error blending triangles onto the plots.
%_tris_$(mag).eps: analysis_% %_tris.tri_lines
$(w_plot) -v file=$(@:eps=) -v mag=$(magval) < $<
echo 'set output "$@"; \
set terminal postscript eps color; \
plot $(value $(subst _$(mag).eps,,$@)_bounds) \
"$(@:eps=grid_points)" with points pt 7 ps 1, \
"$(@:eps=error_lines)" with lines lt 1 lw 3, \
"$(@:_tris_$(mag).eps=_tris.tri_lines)" with lines lt 3 lw 1' \
| gnuplot
......@@ -3,13 +3,23 @@ The camera_data directory contains camera calibration files and data.
----------------------------------------------------------------
GNU Makefile targets
plots (default): Does all analysis and plotting for the raw data.
show: Runs gv on the plots, both per-camera and composite.
figs: Does just the first paper figure, and some more composite figures.
camera1_blend8_tris_mag50.eps # Camera 1 only, grid pts, error vecs, tris.
all_mag50.eps # All cameras "before" (cos dewarp only.)
all_blend8_mag50.eps # All cameras "after" (pts and error vecs.)
all_blend8_tris_mag50.eps # All "after", (pts, vecs, and triangles.)
show-figs (default): Runs gv on the above figs.
You can use "killall gv", or just type "q" in each gv to quit.
plots: Does analysis and plotting for all raw cosine-dewarped data.
show-plots: Runs gv on the plots, both per-camera and composite.
blend8: Pushes the raw data through error blending, both analysis and plots.
show-blend8: gv.
blend8-tris: Adds error cancellation blending triangles to the plots.
show-blend8-tris: gv.
calpts: Extracts the calibration points in mezzanine.opt format.
----------------------------------------------------------------
......@@ -58,13 +68,14 @@ output_camera[012367] - Measured grid points (file_dumper output).
analysis_{camera*,all} - Output from dump_analyzer on output_camera files.
*_mag*.eps - Gnuplot figures, corresponding to the analysis*
*_mag*.eps - Gnuplot figures, corresponding to the analysis_* files.
camera*_calpts - Calibration points in mezzanine.opt format
camera*_calpts - Calibration points in mezzanine.opt format.
*.{grid_points,error_lines} - Data for gnuplot.
*.{grid_points,error_lines} - Plot data in gnuplot format.
*.tri_lines - Blending triangles for gnuplot.
*.tri_lines - Per-camera Error cancellation blending triangles for gnuplot.
camera*.tri_lines_world - Ditto, in global coords for all_blend8_tris_mag50.eps.
----------------------------------------------------------------
Processing camera data gathered at measured grid points
......
......@@ -20,7 +20,16 @@
# corners and origin above are part of the fine grid, you can extract a subset
# of this file_dumper data and analyze it to make the above file.)
#
# Usage: There are two filename args:
# Usage: test_lin_blend2.py [-t [-g]] [-d] ptAnal gridData
#
# Dash option flags:
# -t - Output only gnuplot lines for the triangles.
# -g - Output triangle lines in global world coordinates for composite plots.
# The world_{x,y,theta} in the gridData file are applied.
# Otherwise the "world" coords here are local to the camera.
# -d - Enable debugging output.
#
# There are two filename args:
#
# ptAnal - The dump_analyzer output file for the center and corner points.
# The order must be as above. The section headers give the target
......@@ -44,14 +53,20 @@ import re
import geom
import blend_tris
import read_analysis
import worldTransform
opts,args = getopt.getopt(sys.argv[1:], 'td')
opts,args = getopt.getopt(sys.argv[1:], 'tgd')
printTris = False
debug = False
globalCoords = False
for o,a in opts:
if o == "-t":
printTris = True
pass
if o == "-g":
# Global coords: apply offsets from config data in input file.
globalCoords = True
pass
if o == "-d":
debug = True
pass
......@@ -108,20 +123,17 @@ def mkTri(i0, i1, i2):
triangles = [ mkTri(4,2,1), mkTri(4,1,0), mkTri(4,0,3), mkTri(4,3,6),
mkTri(4,6,7), mkTri(4,7,8), mkTri(4,8,5), mkTri(4,5,2) ]
# Optionally output only gnuplot lines for the triangles.
if printTris:
for tri in triangles:
for v in tri.target:
print '%f, %f'%tuple(v)
print "%f, %f\n"%tuple(tri.target[0])
pass
sys.exit(0)
pass
#================================================================
# Regexes for parsing file_dumper output.
fpnum = "\s*(\-*\d+\.\d+)\s*"
re_config_option_float = re.compile(" \- ([ \w]+):"+fpnum)
options = dict({ 'camera_x': 0.0, # Camera offset, in meters.
'camera_y': 0.0,
'world_x': 0.0, # Offset in meters to camera center.
'world_y': 0.0,
'world_theta': 0.0, # Rotation of camera in radians CW.
})
reDumperSection = re.compile("section:\s*\("+fpnum+","+fpnum+"\)")
reFrameData_line = re.compile("(\[[0-9]+\] a\("+fpnum+","+fpnum+"\)\s*"
"b\("+fpnum+","+fpnum+"\)\s*-- wc)"
......@@ -137,12 +149,40 @@ while gridLine != "":
m1 = reFrameData_line.match(gridLine)
if m1 == None:
# Everything else streams straight through.
print gridLine
if not printTris:
print gridLine
# Pick off the world coord offset and rotation parameters.
m2 = re_config_option_float.match(gridLine)
if m2 != None:
options[m2.group(1)] = float(m2.group(2))
pass
pass
else:
# Frame data.
lineHead = m1.group(1)
data = [float(f) for f in m1.groups()[1:]]
# Optionally output only gnuplot lines for the triangles.
# Moved here from above because we first need world params for -t -g.
if printTris:
for tri in triangles:
for v in tri.target:
if globalCoords:
wc = worldTransform.worldTransform(
options,
v[0] + options['camera_x'],
v[1] + options['camera_y'], 0.0)
print '%f, %f'%(wc[0], wc[1])
else:
print '%f, %f'%tuple(v)
# No need to output first vertex again in this triangle pattern.
# (And doing so messes up dashed triangle lines in the plot.)
print "" # Blank line separates line chains in gnuplot.
pass
sys.exit(0)
pass
# XXX Horrid Hack Warning - We need right-handed coordinates,
# but the image Y coordinate goes down from 0 at the top.
# Negate it internally.
......
......@@ -13,6 +13,7 @@ import os.path
import random
import re
import math
import worldTransform
# Parse options and (perhaps multiple) filename args.
DEBUG = False
......@@ -64,7 +65,8 @@ class ObjectData:
if world_coords:
# Match up grid points with other cameras globally.
self.wx, self.wy, self.wa = worldTransform(wx, wy, wa)
self.wx, self.wy, self.wa = worldTransform.worldTransform(
options, wx, wy, wa)
else:
# Subtract local camera offset to match nominal grid point coords.
self.wx = wx - options['camera_x']
......@@ -108,8 +110,8 @@ class Section:
if world_coords:
# Match up nominal grid points with other cameras globally.
# Add in the local offset from camera center to surveyed grid point.
self.lx, self.ly, self.lt = worldTransform(
lx + options['camera_x'], ly + options['camera_y'], 0)
self.lx, self.ly, self.lt = worldTransform.worldTransform(
options, lx + options['camera_x'], ly + options['camera_y'], 0)
else:
# Leave the nominal grid point coords alone.
self.lx = lx
......@@ -172,30 +174,6 @@ def stddev(list):
m = mean(list)
return math.sqrt(msqerr([n-m for n in list]))
# Transform a point from camera-local into global world coords.
# (Same logic as local2global_posit_trans in vmc-client.c)
def worldTransform(cx, cy, ca):
ct = math.cos(options['world_theta'])
st = math.sin(options['world_theta'])
# Notice this is a left-handed rotation, followed by an offset.
wx = ct * cx + st * -cy + options['world_x']
wy = ct * -cy + st * -cx + options['world_y']
# Rotate 90 more degrees because the fiducials are now mounted crosswise.
wa = mtp_theta(ca + options['world_theta'] + math.pi/2);
return wx, wy, wa
# Put orientation into the +-PI radians range.
# (Same logic as mtp_theta in mtp.c)
def mtp_theta(theta):
if theta < -math.pi:
retval = theta + (2.0 * math.pi)
elif theta > math.pi:
retval = theta - (2.0 * math.pi)
else:
retval = theta
pass
return retval
def analyze(section):
# takes a Section, and does crap on it, and puts the result of the crap
# in the results global, under this section string title:
......@@ -318,10 +296,10 @@ def analyze(section):
pass
# set up the regexes
re_config_option_float = re.compile(" \- ([ \w]+): (\-*\d+\.\d+)")
fpnum = "\s*(\-*\d+\.\d+)\s*"
re_config_option_float = re.compile(" \- ([ \w]+):"+fpnum)
re_config_option_int = re.compile(" \- ([ \w]+): (\-*\d+)")
re_config_option_string = re.compile(" \- ([ \w]+): (.*)")
fpnum = "\s*(\-*\d+\.\d+)\s*"
re_section = re.compile("section:\s*\("+fpnum+","+fpnum+"\)")
re_section_sep = re.compile("\+\+\+")
re_frame_title = re.compile("frame (\d+) \(timestamp"+fpnum+"\):")
......
#!/usr/local/bin/python
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005 University of Utah and the Flux Group.
# All rights reserved.
#
import math
# Transform a point from camera-local into global world coords.
# (Same logic as local2global_posit_trans in vmc-client.c)
def worldTransform(options, cx, cy, ca):
ct = math.cos(options['world_theta'])
st = math.sin(options['world_theta'])
# Notice this is a left-handed rotation, followed by an offset.
wx = ct * cx + st * -cy + options['world_x']
wy = ct * -cy + st * -cx + options['world_y']
# Rotate 90 more degrees because the fiducials are now mounted crosswise.
wa = mtp_theta(ca + options['world_theta'] + math.pi/2);
return wx, wy, wa
# Put orientation into the +-PI radians range.
# (Same logic as mtp_theta in mtp.c)
def mtp_theta(theta):
if theta < -math.pi:
retval = theta + (2.0 * math.pi)
elif theta > math.pi:
retval = theta - (2.0 * math.pi)
else:
retval = theta
pass
return retval
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment