def error_shading(calibration, fn): """Plot the GP offset model for x and y, after removing the constant correction""" proj = proj_wrapper(pyproj.Proj("+proj=robin")) plot_reference_grid(pyproj.Proj("+proj=robin")) xs, ys, zs = [], [], [] for ix, row in calibration.iterrows(): tlon, tlat = row["target_lon"], row["target_lat"] lon, lat = row["touch_lon"], row["touch_lat"] # compute offset from constant corrected px,py = proj(lon, lat) lonc, latc = fn(lon, lat) d = np.degrees(sphere.spherical_distance((tlon, tlat), (lonc, latc))) xs.append(px) ys.append(py) zs.append(d) xi = np.linspace(np.min(xs), np.max(xs), 500) yi = np.linspace(np.min(ys), np.max(ys), 500) zi = matplotlib.mlab.griddata(xs, ys, zs, xi, yi, interp='linear') #xr, yr = np.meshgrid(xi,yi) #iv = scipy.interpolate.interp2d(xs,ys,zs,kind='cubic') #zi = iv(xi, yi) #zi = scipy.interpolate.griddata((np.array(xs).flatten(),np.array(ys).flatten()),np.array(zs).flatten(),(xr,yr), method='cubic') levels = np.linspace(np.min(zi), np.max(zi), 30) plt.contourf(xi, yi, zi, cmap="bone", levels=levels, vmin=0, vmax=9) plt.colorbar()
def valid(a, b, name): d = sphere.spherical_distance(a,b) if d<1e-6: print "Testing %s: OK, error is within %.4e" % (name, d) return True else: print "Testing %s: FAIL! error exceeds 1e-6 at %.4e" % (name, d) return False
def process_calibration(calibration_name=None, fit=None): if calibration_name is None: print "WARNING: No calibration file specified." latest_csv = latest_file("calibration", ".csv") print "Using latest calibration file found: %s" % latest_csv calibration_name = latest_csv print "Processing calibration %s" % calibration_name print # load the calibration data calibration = pd.io.parsers.read_table(os.path.join("calibration", calibration_name), delimiter=",", skipinitialspace=True) augment_calibration(calibration) calibration["distance"] = [sphere.spherical_distance((r["target_lon"], r["target_lat"]), (r["touch_lon"], r["touch_lat"])) for ix, r in calibration.iterrows()] total_targets = len(calibration) print "Read %d calibration targets" % total_targets calibration = calibration[calibration["distance"]<np.radians(22)] print "%d targets excluded as being outliers with >25 degree offset\n%d calibration targets remain" % (total_targets-len(calibration), len(calibration)) grouped = calibration.groupby(["target_x", "target_y"]) print "%d unique targets identified; %d repeats per target" % (len(grouped), int(0.5+total_targets/float(len(grouped)))) print true_rms = estimate_touch_error(calibration, grouped) if fit is None: correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y = fit_models(calibration) else: correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y = fit errors = {} errors["none"] = error_distribution(calibration, lambda x,y:polar_adjust(x,y,s=1)) errors["constant"] = error_distribution(calibration, lambda x,y:polar_adjust(x,y,s=correction_factor)) errors["quadratic"] = error_distribution(calibration, lambda x,y:quadratic_polar_adjust(x,y,quadratic_coeff)) errors["cubic"] = error_distribution(calibration, lambda x,y:cubic_polar_adjust(x,y,cubic_coeff)) errors["gp"] = error_distribution(calibration, lambda x,y:gp_adjust(x,y,gp_x,gp_y)) report_errors(errors) #plot_calibration_state(calibration, (correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y)) #plot_error_distribution(calibration, ["Constant", "Quadratic", "Cubic", "GP"], [constant, quadratic, cubic, gp]) #plot_offset_shading(calibration, gp_x, gp_y, correction_factor) # only write the calibration if we fitted new models if fit is None: generate_module(calibration_name, calibration, errors, (correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y)) #generate_offset_map("gp", lambda x,y:gp_adjust(x,y,gp_x,gp_y)) #generate_offset_map_gp(gp_x, gp_y) mode_times = time_module(calibration) for mode in mode_times: print "%s took %d ms" %(mode, 1000*mode_times[mode]) return (correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y), calibration
def spherical_mse(fn): """Return the mse error using the given correction""" distances = [] for ix,row in calibration.iterrows(): corr_touch_lon, corr_touch_lat = fn(row["touch_lon"], row["touch_lat"]) distances.append(sphere.spherical_distance((row["target_lon"],row["target_lat"]), (corr_touch_lon, corr_touch_lat))) #return np.median(distances) return np.sqrt(np.mean(np.array(distances)**2))
def error_distribution(calibration, fn): """Return the error distribution after applying fn""" ds = [] for ix, row in calibration.iterrows(): lon, lat = row["target_lon"], row["target_lat"] tlon, tlat = row["touch_lon"], row["touch_lat"] # compute offset from constant corrected lonc, latc = fn(tlon, tlat) d = sphere.spherical_distance((lon, lat), (lonc, latc)) ds.append(np.degrees(d)) return ds
def spherical_mean_rms(lonlats): """Compute spherical mean, rms error and geodesic distances, using Robinson projection method""" proj = pyproj.Proj("+proj=robin") pts = [] for lon, lat in lonlats: pts.append(proj(lon, lat)) pts = np.array(pts) proj_mean = np.mean(pts, axis=0) lon_mean, lat_mean = proj(proj_mean[0], proj_mean[1], inverse=True) distances = [] for lon, lat in lonlats: distances.append(sphere.spherical_distance((lon_mean, lat_mean), (lon, lat))) rms = np.sqrt(np.mean(np.array(distances)**2)) return (lon_mean, lat_mean), rms, distances
def process_calibration(calibration_name=None, fit=None): global calibration if calibration_name is None: print "WARNING: No calibration file specified." latest_csv = latest_file("calibration", ".csv") print "Using latest calibration file found: %s" % latest_csv calibration_name = latest_csv print "Processing calibration %s" % calibration_name print # load the calibration data calibration = pd.io.parsers.read_table(os.path.join("calibration", calibration_name), delimiter=",", skipinitialspace=True) augment_calibration(calibration) calibration["distance"] = [sphere.spherical_distance((r["target_lon"], r["target_lat"]), (r["touch_lon"], r["touch_lat"])) for ix, r in calibration.iterrows()] total_targets = len(calibration) print "Read %d calibration targets" % total_targets calibration = calibration[calibration["distance"]<np.radians(25)] print "%d targets excluded as being outliers with >25 degree offset\n%d calibration targets remain" % (total_targets-len(calibration), len(calibration)) grouped = calibration.groupby(["target_x", "target_y"]) print "%d unique targets identified; %d repeats per target" % (len(grouped), int(0.5+total_targets/float(len(grouped)))) print true_rms = estimate_touch_error(calibration, grouped) if fit is None: correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y = fit_models() else: correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y = fit uncorrected_rmse = np.degrees(correction_error(1)) constant_rmse = np.degrees((correction_error(correction_factor))) quadratic_rmse = np.degrees(quadratic_error(quadratic_coeff)) cubic_rmse = np.degrees(cubic_error(cubic_coeff)) gp_rmse = np.degrees(gp_error(gp_x, gp_y)) print "RMS Uncorrected error: %.2f degrees" % uncorrected_rmse print "RMS Corrected error: %.2f degrees" % constant_rmse print "RMS Quadratic corrected error: %.2f degrees" % quadratic_rmse print "RMS Cubic corrected error: %.2f degrees" % cubic_rmse print "RMS GP corrected error: %.2f degrees" % gp_rmse print plot_correction_models(calibration, correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y) fname = "calibration_plot.pdf" print print "Writing calibration plot to %s" % fname try: plt.savefig(fname,bbox_inches='tight', pad_inches=0) except IOError: print "WARNING: Could not write to %s" % fname plt.figtext(0.04, 0.16, "Calibration data from %s" % calibration_name, fontdict={"size":5}) plt.figtext(0.04, 0.13, "%d calibration targets; %d unique" % (len(calibration), len(grouped)), fontdict={"size":5}) plt.figtext(0.04, 0.1, "%.2f degrees intra-target error" % np.degrees(true_rms), fontdict={"size":5}) error_labels(calibration, uncorrected_rmse, constant_rmse, quadratic_rmse, cubic_rmse, gp_rmse) fname = "calibration_state.pdf" print print "Writing calibration plot to %s" % fname try: plt.savefig(fname) except IOError: print "WARNING: Could not write to %s" % fname plt.figure(figsize=(6,9)) errors = [ #error_distribution(calibration, lambda x,y: polar_adjust(x,y,1)), error_distribution(calibration, lambda x,y:polar_adjust(x,y,s=correction_factor)), error_distribution(calibration, lambda x,y:quadratic_polar_adjust(x,y,quadratic_coeff)), error_distribution(calibration, lambda x,y:cubic_polar_adjust(x,y,cubic_coeff)), error_distribution(calibration, lambda x,y:gp_adjust(x,y,gp_x,gp_y))] plt.xlabel("Correction method") plt.ylabel("Error (degrees)") plt.gca().set_frame_on(False) simpleaxis(plt.gca()) seaborn.boxplot(errors) plt.xticks([0,1,2,3,4], ["","Constant", "Quadratic", "Cubic", "GP"]) fname = "calibration_dist.pdf" print "Writing calibration boxplot to %s" % fname try: plt.savefig(fname,bbox_inches='tight', pad_inches=0) except IOError: print "WARNING: Could not write to %s" % fname gp_offset_shading(calibration, gp_x, gp_y, correction_factor) fname = "gp_offset.pdf" plt.savefig("gp_offset.svg",bbox_inches='tight', pad_inches=0) print "Writing GP offset plot to %s" % fname try: plt.savefig(fname,bbox_inches='tight', pad_inches=0) except IOError: print "WARNING: Could not write to %s" % fname cal = calibration # only write the calibration if we fitted new models if fit is None: print print "Generating calibration.py" write_module("calibration.py", calibration_name, calibration, correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y) print print "Testing calibration.py..." import calibration as cal def test_calibration(test_x, test_y): def valid(a, b, name): d = sphere.spherical_distance(a,b) if d<1e-6: print "Testing %s: OK, error is within %.4e" % (name, d) return True else: print "Testing %s: FAIL! error exceeds 1e-6 at %.4e" % (name, d) return False lat, lon = sphere.tuio_to_polar(test_x, test_y) lon += np.pi assert(valid(cal.test_calibrated_touch(test_x, test_y, 'gp'),gp_adjust(lon, lat, gp_x, gp_y), 'GP')) assert(valid(cal.test_calibrated_touch(test_x, test_y, 'cubic'),cubic_polar_adjust(lon, lat, cubic_coeff), 'cubic')) assert(valid(cal.test_calibrated_touch(test_x, test_y, 'quadratic'),quadratic_polar_adjust(lon, lat, quadratic_coeff),'quadratic')) assert(valid(cal.test_calibrated_touch(test_x, test_y, 'constant'),polar_adjust(lon, lat, correction_factor), 'constant')) assert(valid(cal.test_calibrated_touch(test_x, test_y, 'none'),polar_adjust(lon, lat, 1), 'none')) test_calibration(0.5, 0.5) print "calibration.py seems to be working" import calibration as cal tuio_time = timeit.timeit("lon, lat = sphere.polar_to_tuio(0.5, 0.5)", setup="import sphere", number=1000) for mode in ["none", "constant", "quadratic", "cubic", "gp"]: ds = [] tx,ty=0.5,0.5 mode_time = timeit.timeit("lon, lat = calibration.test_calibrated_touch(0.5, 0.5, mode='%s')"%mode, setup="import calibration", number=1000) print "%s took %d ms, %d ms more thant TUIO time" %(mode, 1000*mode_time, 1000*(mode_time-tuio_time)) for ix, row in calibration.iterrows(): tx, ty = row["tuio_x"], row["tuio_y"] lon, lat = cal.test_calibrated_touch(tx, ty, mode=mode) d = np.degrees(sphere.spherical_distance((lon, lat), (row["target_lon"], row["target_lat"]))) ds.append(d) print "calibration.py median error for mode %s is %.2f degrees" % (mode, np.median(ds)) return (correction_factor, cubic_coeff, quadratic_coeff, gp_x, gp_y)