Commit 352aa9e1 authored by Andrey Filippov's avatar Andrey Filippov
Browse files

tested derivative for the OrthoMultiLMA

parent 6b986720
Loading
Loading
Loading
Loading
+20 −22
Original line number Diff line number Diff line
@@ -166,7 +166,7 @@ public class ComboMatch {
		int num_tries_fit =           10;
		boolean update_match =        true; // use false to save new version of data
		boolean render_match =        false; // true;

		boolean test_multi_lma =      false;
		boolean pattern_match =       true; // false;
		
		boolean bounds_to_indices =   true;
@@ -202,7 +202,8 @@ public class ComboMatch {

		gd.addCheckbox    ("Update match if calculated", update_match, "Will update correlation match for a pair if found.");
		gd.addCheckbox    ("Render match", render_match, "Render a pair of matched images.");
		
		gd.addCheckbox    ("Test multi LMA", test_multi_lma, "Temporary debug.");
		//
		gd.addCheckbox    ("Pattern match", pattern_match, "Search for patterns for both images in a pair, first is primary.");
		
		gd.addCheckbox    ("Bounds to selected images", bounds_to_indices, "Set combo image bounds to selected images only. False - all images.");
@@ -262,7 +263,7 @@ public class ComboMatch {
		num_tries_fit =      (int) gd.getNextNumber();
		update_match=              gd.getNextBoolean();
		render_match=              gd.getNextBoolean();
		
		test_multi_lma=            gd.getNextBoolean();
		pattern_match=             gd.getNextBoolean();
		
		bounds_to_indices=         gd.getNextBoolean();
@@ -774,7 +775,7 @@ public class ComboMatch {
					orthoMapsCollection_path); // String orthoMapsCollection_path);
			if (!ok) return false;
		}
        if (process_correlation || render_match || pattern_match) {
        if (process_correlation || render_match || pattern_match || test_multi_lma) {
//        	int [] gpu_pair;
        	if (gpu_spair == null) {
        		ArrayList<Point> pairs_list = new ArrayList<Point>(); 
@@ -859,24 +860,8 @@ public class ComboMatch {
        	double agl_ratio = max_agl/50.0;
        	double metric_error_adj = metric_error * agl_ratio * agl_ratio; // metric_error settings is good for 50m. Increase for higher Maybe squared?
        	int initial_zoom = max_zoom_lev - 4;  // another algorithm?
/*
        	if (GPU_QUAD_AFFINE == null) {
            	System.out.println("Setting up GPU");
        		try {
        			GPU_QUAD_AFFINE = new GpuQuad(//
        					GPU_TILE_PROCESSOR,   // GPUTileProcessor gpuTileProcessor,
        					gpu_max_width,        // final int        max_width,
        					gpu_max_height,       // final int        max_height,
        					1,                    // final int        num_colors, // normally 1?
        					clt_parameters.gpu_debug_level);
        		} catch (Exception e) {
        			System.out.println("Failed to initialize GpuQuad class");
        			// TODO Auto-generated catch block
        			e.printStackTrace();
        			return false;
        		} // final int debugLevel);
        	}
*/        	
        	
        	// Here - always start with unity affine0 and affine1 from possibly matched pair 
    		double [][] affine0 = {{1,0,0},{0,1,0}}; // will always stay the same
    		double [][] affine1 = null;

@@ -893,6 +878,13 @@ public class ComboMatch {
				}
        		
        	} else {
        		if (test_multi_lma) {
        			OrthoMultiLMA.testMultiLMA(
        					clt_parameters,  // CLTParameters       clt_parameters,
        					maps_collection, // OrthoMapsCollection maps_collection,
        					gpu_pair);       // int []              indices)
        			return true;
        		}
        		if (process_correlation && !use_marked_image) { // match may or may not exist
        			// if match exists - ask if use it. If not - open dialog and start spiral
        			pairwiseOrthoMatch = initialPairAdjust(
@@ -1095,6 +1087,7 @@ public class ComboMatch {
		boolean log_append =           clt_parameters.imp.pwise_log_append;
		String log_path =              clt_parameters.imp.pwise_log_path;
		
		boolean     pmtch_use_affine =    clt_parameters.imp.pmtch_use_affine;
		double      max_std =             clt_parameters.imp.pmtch_max_std;    // 1.5;      // maximal standard deviation to limit center area  
		double      min_std_rad =         clt_parameters.imp.pmtch_min_std_rad;// 2.0;  // minimal radius of the central area (if less - fail)
		double      rad_fraction =        clt_parameters.imp.pmtch_cent_rad; // center circle radius fraction of 0.5* min(width, height) in tiles
@@ -1126,6 +1119,7 @@ public class ComboMatch {
		gd.addNumericField("Spiral search debug level",spiral_debug, 0,3,"","Debug level during Spiral search.");
		
		gd.addMessage("Parameters, common to all matching, not only spiral");
		gd.addCheckbox    ("Use scenes' affine",               pmtch_use_affine, "Use known scenes' affine matrices, false - start from scratch (unity) ones.");
		gd.addNumericField("Central area standard deviation",  max_std,  3,7,"",	"Central area limit by the standard deviation.");
		gd.addNumericField("Central area minimal radius",      min_std_rad,  3,7,"tile",	"Minimal radius of the central area after all LMA passes.");
		gd.addNumericField("Central area radius as fraction",  rad_fraction,   3,7,"",	"Central area radius as fraction of half minimal WOI dimension.");
@@ -1154,6 +1148,7 @@ public class ComboMatch {
		log_path =                 gd.getNextString();
		spiral_debug =       (int) gd.getNextNumber();
		search_step=               gd.getNextNumber();
		pmtch_use_affine=          gd.getNextBoolean();
		max_std=                   gd.getNextNumber();
		min_std_rad=               gd.getNextNumber();
		rad_fraction=              gd.getNextNumber();
@@ -1180,6 +1175,8 @@ public class ComboMatch {
				clt_parameters,  // CLTParameters    clt_parameters,
				frac_remove,     // double           frac_remove, //  =        0.25
				metric_error,    // double           metric_error,
				pmtch_use_affine, // boolean          pmtch_use_affine,

				max_std,         // double           max_std,     // maximal standard deviation to limit center area  
				min_std_rad,     // double           min_std_rad, // minimal radius of the central area (if less - fail)
				rad_fraction,    // double           rad_fraction,
@@ -1198,6 +1195,7 @@ public class ComboMatch {
				min_overlap,     // int              min_overlap, // 3000
				ignore_rms,      // boolean          ignore_rms,
				null,//double []        max_rms_iter, //  = {1.0, 0.6};//
				1.0, // double           overlap,
				spiral_debug);     // int              debugLevel){
		if (log_append && (log_path != null)) { // assuming directory exists
			StringBuffer sb = new StringBuffer();
+2 −1
Original line number Diff line number Diff line
@@ -829,6 +829,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
	 * @param affine [2][3] array were the last column is made of the offsets (in meters)
	 * @return [2][3] array that converts from the image metric coordinates relative to the
	 * "vert_meters" point to the rectified metric coordinates relative to the same point.
	 * 
	 */
	public static double [][] invertAffine(double [][] affine){
		Matrix A = new Matrix (
+33 −13
Original line number Diff line number Diff line
@@ -115,6 +115,11 @@ public class OrthoMapsCollection implements Serializable{
		Arrays.sort(ortho_maps);
		reindex();
	}
	
	public static double [][] unityAffine() {
		return new double [][] {{1,0,0},{0,1,0}};
	}
	
	// add update kernels, update patterns
	public void updateKernels (
			String path) {
@@ -945,6 +950,7 @@ public class OrthoMapsCollection implements Serializable{
			CLTParameters    clt_parameters,
			double           frac_remove, //  =        0.25
			double           metric_error,
			boolean          pmtch_use_affine,
			double           max_std,     // maximal standard deviation to limit center area  
			double           min_std_rad, // minimal radius of the central area (if less - fail)
			double           rad_fraction,
@@ -963,6 +969,7 @@ public class OrthoMapsCollection implements Serializable{
			int              min_overlap, //  = 5
			boolean          ignore_rms,
			double []        max_rms_iter, //  = {1.0, 0.6};//
			double           overlap,
			int              debugLevel){
		double [][] affine1 = new double [][] {affines_init[1][0].clone(),affines_init[1][1].clone()};
		double [][][] affines = new double [][][] {affines_init[0],affine1};
@@ -976,7 +983,8 @@ public class OrthoMapsCollection implements Serializable{
				affines[1], //  double [][] affine,
				null, // double [][] jtj,
				Double.NaN, // double rms,
				zoom_lev); // int zoom_lev); 
				zoom_lev, // int zoom_lev);
				overlap); // double overlap);
//		double           max_std =     0.5;      // maximal standard deviation to limit center area  
//		double           min_std_rad = 2.0;  // minimal radius of the central area (if less - fail)
		int best_nx = -1, best_ny = -1;
@@ -1059,7 +1067,7 @@ public class OrthoMapsCollection implements Serializable{
	}
	
	
	
/*	
	public FineXYCorr correlateOrthoPair( // not used
			CLTParameters    clt_parameters,
			PairwiseOrthoMatch pairwiseOrthoMatch, // will return statistics			
@@ -1113,7 +1121,7 @@ public class OrthoMapsCollection implements Serializable{
				max_rms_iter, //  = {1.0, 0.6};// double []        max_rms_iter, //  = {1.0, 0.6};//        						
				debugLevel);     // final int        debugLevel)
	}
	
*/	
	public FineXYCorr correlateOrthoPair(
			CLTParameters    clt_parameters,
			PairwiseOrthoMatch pairwiseOrthoMatch, // will return statistics, may be null if not needed
@@ -1711,6 +1719,7 @@ public class OrthoMapsCollection implements Serializable{
		if (pairwiseOrthoMatch != null) {
			pairwiseOrthoMatch.jtj = jtj;
			pairwiseOrthoMatch.rms=  rms;
			pairwiseOrthoMatch.zoom_lev = zoom_lev;
		}
		
		double [] tlo_src_metric_other = new double[2]; // check affines[][][] here - 
@@ -4112,6 +4121,7 @@ public class OrthoMapsCollection implements Serializable{
		//Final pairwise scenes matching
		double  frac_remove  =        clt_parameters.imp.pmtch_frac_remove;// 0.15;
		double  metric_err =          clt_parameters.imp.pmtch_metric_err;// 0.05; // 0.02;//  2 cm
		boolean pmtch_use_affine =    clt_parameters.imp.pmtch_use_affine;		
		double  max_std =             clt_parameters.imp.pmtch_max_std;// 1.5;      // maximal standard deviation to limit center area  
		double  min_std_rad =         clt_parameters.imp.pmtch_min_std_rad;// 2.0;  // minimal radius of the central area (if less - fail)
		boolean ignore_prev_rms =     clt_parameters.imp.pmtch_ignore_rms;// true; 
@@ -4154,6 +4164,7 @@ public class OrthoMapsCollection implements Serializable{
		gd.addMessage  ("Final pairwise scenes matching");
		gd.addNumericField("Remove fraction of worst matches", frac_remove,  3,7,"",	"When fitting scenes remove this fraction of worst match tiles.");
		gd.addNumericField("Maximal metric error",             metric_err,  3,7,"m",	"Maximal tolerable fitting error caused by elevation variations.");
		gd.addCheckbox    ("Use scenes' affine",               pmtch_use_affine, "Use known scenes' affine matrices, false - start from scratch (unity) ones.");
		gd.addNumericField("Central area standard deviation",  max_std,  3,7,"",	"Central area limit by the standard deviation.");
		gd.addNumericField("Central area minimal radius",      min_std_rad,  3,7,"tile",	"Minimal radius of the central area after all LMA passes.");
		gd.addCheckbox    ("Ignore previous RMSE",             ignore_prev_rms, "Do not exit full fitting cycles if the RMSE worsened/not improved.");
@@ -4190,6 +4201,7 @@ public class OrthoMapsCollection implements Serializable{
		                         
		frac_remove      =       gd.getNextNumber();
		metric_err       =       gd.getNextNumber();
		pmtch_use_affine=        gd.getNextBoolean();
		max_std          =       gd.getNextNumber();
		min_std_rad      =       gd.getNextNumber();
		ignore_prev_rms  =       gd.getNextBoolean();
@@ -4224,6 +4236,7 @@ public class OrthoMapsCollection implements Serializable{
				spiral_debug,             // int           spiral_debug,
				frac_remove,              // double        frac_remove,
				metric_err,               // double        metric_error,
				pmtch_use_affine,         // boolean       pmtch_use_affine,				
				max_std,                  // double        max_std,  
				min_std_rad,              // double        min_std_rad,
				ignore_prev_rms,          // boolean       ignore_prev_rms, 
@@ -4263,6 +4276,7 @@ public class OrthoMapsCollection implements Serializable{
			
			double        frac_remove,
			double        metric_error,
			boolean       pmtch_use_affine,			
			double        max_std,  
			double        min_std_rad,
			boolean       ignore_prev_rms, 
@@ -4305,7 +4319,7 @@ public class OrthoMapsCollection implements Serializable{
			sb.append("num_pairs\t"+   num_pairs+"\n");
			sb.append("num_new\t"+     num_new+"\n");
			sb.append(String.format("%4s\t%4s\t%17s\t%17s\t%6s\t%3s\t%4s,\t%4s\t%6s\t%6s\t%7s\n",
					"scn1","scn2","timestamp1","timestamp2","ovrlp","zl","nx","ny","RMS-sp","RMSfin","removed"));
					"scn1","scn2","timestamp1","timestamp2","ovrlp","zl","nx","ny","RMS-sp","RMSfin","fzl","removed"));
			CalibrationFileManagement.saveStringToFile (
					log_path,          //String path,
					sb.toString(), // data,
@@ -4361,11 +4375,12 @@ public class OrthoMapsCollection implements Serializable{
			if (skip) {
				if (log_append && (log_path != null)) { // assuming directory exists
					StringBuffer sb = new StringBuffer();
					sb.append(String.format("%4d\t%4d\t%s\t%s\t%6.4f\t%3d\tSKIP\t\t\t%6.4f\n",
					sb.append(String.format("%4d\t%4d\t%s\t%s\t%6.4f\t%3d\tSKIP\t\t\t%6.4f\t%3d\n",
							ipair[0], ipair[1],
							ortho_maps[ipair[0]].getName(), ortho_maps[ipair[1]].getName(),
							overlaps[overlap_indx],  initial_zoom,
							direct_match.rms));
							direct_match.rms,
							direct_match.zoom_lev));
					CalibrationFileManagement.saveStringToFile (
							log_path,          //String path,
							sb.toString(), // data,
@@ -4374,8 +4389,11 @@ public class OrthoMapsCollection implements Serializable{
				continue;
			}
			PairwiseOrthoMatch pairwiseOrthoMatch = null;
			double [][] affine0 = ortho_maps[ipair[0]].getAffine(); // {{1,0,0},{0,1,0}}; // will always stay the same
			double [][] affine1 = ortho_maps[ipair[0]].getAffine(); // {{1,0,0},{0,1,0}}; // here (manual mode) start from the center, may use prediction in auto
			
			// unityAffine()
			
			double [][] affine0 = pmtch_use_affine?ortho_maps[ipair[0]].getAffine():unityAffine(); // {{1,0,0},{0,1,0}}; // will always stay the same
			double [][] affine1 = pmtch_use_affine?ortho_maps[ipair[0]].getAffine():unityAffine(); // {{1,0,0},{0,1,0}}; // here (manual mode) start from the center, may use prediction in auto
			double [][][] affines = new double[][][] {affine0,affine1};
			double spiral_rms = Double.NaN;
			if (use_direct) {
@@ -4392,6 +4410,7 @@ public class OrthoMapsCollection implements Serializable{
						clt_parameters,  // CLTParameters    clt_parameters,
						frac_remove,     // double           frac_remove, //  =        0.25
						metric_error_adj,// double           metric_error,
						pmtch_use_affine, // boolean          pmtch_use_affine,
						max_std,         // double           max_std,     // maximal standard deviation to limit center area  
						min_std_rad,     // double           min_std_rad, // minimal radius of the central area (if less - fail)
						rad_fraction,    // double           rad_fraction,
@@ -4410,6 +4429,7 @@ public class OrthoMapsCollection implements Serializable{
						min_overlap,     // int              min_overlap, // 3000
						ignore_rms,      // boolean          ignore_rms,
						max_rms_iter,    // double []        max_rms_iter, //  = {1.0, 0.6};//
						overlaps[overlap_indx], // double           overlap,
						spiral_debug);   // int              debugLevel)
				if ((pairwiseOrthoMatch == null) || Double.isNaN(pairwiseOrthoMatch.rms)) {
					if (delete_failed) {
@@ -4480,16 +4500,16 @@ public class OrthoMapsCollection implements Serializable{
				if (delete_failed) {
					ortho_maps[ipair[0]].unsetMatch(ortho_maps[ipair[1]].getName());
				}
				String str_failed = delete_failed?"\tFAILED\tREMOVED\n":"\tFAILED\n";
				String str_failed = delete_failed?"\tFAILED\t\tREMOVED\n":"\tFAILED\n";

				failed_pairs.add(pair);
				sb.append(String.format(str_failed));
				if (debugLevel > -4) System.out.print("Final adjustment"+str_failed);				
			} else {
				sb.append(String.format("\t%6.4f\n",pairwiseOrthoMatch.rms)); 
				if (debugLevel > -4) System.out.println("Final adjustment RMSE="+pairwiseOrthoMatch.rms);				
//				pairwiseOrthoMatch.zoom_lev = zoom_lev;
//				pairwiseOrthoMatch.affine = affines[1];
				pairwiseOrthoMatch.overlap = overlaps[overlap_indx]; // needed here if refining old/manual w/o overlap 
				sb.append(String.format("\t%6.4f\t%3d\n",pairwiseOrthoMatch.rms,pairwiseOrthoMatch.zoom_lev)); 
				if (debugLevel > -4) System.out.println("Final adjustment RMSE="+pairwiseOrthoMatch.rms+
						", overlap = "+pairwiseOrthoMatch.overlap);				
				ortho_maps[ipair[0]].setMatch(ortho_maps[ipair[1]].getName(),pairwiseOrthoMatch);
				if (save_each && (orthoMapsCollection_path != null)) {
		        	try {
+337 −0

File added.

Preview size limit exceeded, changes collapsed.

+123 −13
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import Jama.Matrix;

public class PairwiseOrthoMatch implements Serializable {
	private static final long serialVersionUID = 1L;
	public double [][]           affine = new double[2][3];
@@ -12,6 +14,7 @@ public class PairwiseOrthoMatch implements Serializable {
	public int zoom_lev;
	public double rms = Double.NaN;
	public transient int [] nxy = null; // not saved, just to communicate for logging
	public transient double overlap = 0.0;
	public PairwiseOrthoMatch() {
		
	}
@@ -19,11 +22,13 @@ public class PairwiseOrthoMatch implements Serializable {
			double [][] affine,
			double [][] jtj,
			double rms,
			int zoom_lev) {
			int zoom_lev,
			double overlap) {
		this.affine =  affine;
		this.jtj =     jtj;
		this.zoom_lev= zoom_lev;
		this.rms =     rms;
		this.overlap = overlap;
	}
	public PairwiseOrthoMatch clone() {
		double [][] affine = {this.affine[0].clone(),this.affine[1].clone()};
@@ -35,19 +40,63 @@ public class PairwiseOrthoMatch implements Serializable {
				affine,
				jtj,
				this.rms,
				this.zoom_lev);
				this.zoom_lev,
				this.overlap);
		if (nxy != null) {
			pom.nxy = nxy.clone();
		}
		return pom;
	}
	/**
	 * If this match is calculated from scene0 to scene1, inverse one is from scene1 to scene0  
	 * @param rd displacement from scene0 reference (vertical) point to scene1 reference point
	 * (GNSS-derived), referenced as -V in the notes below. (it is calculated as 0 from 1)
	 * @return inverted 3x2 matrix
	 * 
		Xs0, Xs1 - metric coordinates (from top-left) in each of the source images
		V - image center offset of the second scene from the first in rectified space (GPS-derived)
		S0, S1 - vertical point offset from the top-left in image coordinates
		A0,A1 - affine transform matrices (sensor coordinates from rectified coordinates)
		B0, B1 centers offsets in image coordinates
		Xr - rectified coordinates. Xr1, Xr2 - just different points
		
		Xsi = Ai*(Xr - V) + Bi + Si
		
		1) A00 = E, A1, B1 - second image match when the first is unity:
		
		Xs0 = Xr1 + S0
		Xs1 = A1 * (Xr1 - V) + B1 + S1;
		
		2) calculate inverted A1,B1 of the first scene relative to the second one. In this case Xs0
		of the first scene should still correspond to Xs1
		Xs0 = A0 * (Xr2 + V) + B0 + S0;
		Xs1 = Xr2 + S1
		
		----
		Xs1 =  A1 * (Xs0 - S0 - V) + B1 + S1;
		Xs1 - B1 - S1 =  A1 * (Xs0 - S0 - V);
		A1inv*(Xs1 - B1 - S1) =  Xs0 - S0 - V;
		Xs0 = A1inv*(Xs1 - B1 - S1) + S0 + V
		---
		Xr2 = Xs1 - S1
		Xs0 = A0 * (Xs1 - S1 + V) + B0 + S0;
		
		A1inv*(Xs1 - B1 - S1) + S0 + V = A0 * (Xs1 - S1 + V) + B0 + S0
		=> A0=A1inv
		A1inv*(Xs1 - B1 - S1) + S0 + V = A1inv * (Xs1 - S1 + V) + B0 + S0
		-A1inv*( B1)  + V = A1inv * ( V) + B0
		A1inv * ( V) + B0 + A1inv*(B1) - V = 0
		B0 = -((A1inv-E)*V + A1inv*B1)
	 */
	
	public PairwiseOrthoMatch getInverse(double [] rd) {
		double [][] affine = OrthoMap.invertAffine(getAffine());
		PairwiseOrthoMatch inverted_match = new PairwiseOrthoMatch(
				affine, // double [][] affine,
				null, // double [][] jtj,
				jtj, // double [][] jtj,
				rms, // double rms,
				zoom_lev); // int zoom_lev)
				zoom_lev, // int zoom_lev)
				overlap); // 
		double [] corr = {
				rd[0] * (affine[0][0]-1.0)+ rd[1]*affine[0][1],
				rd[0] * affine[1][0]+       rd[1]*(affine[1][1]-1.0)};
@@ -56,13 +105,72 @@ public class PairwiseOrthoMatch implements Serializable {
		return inverted_match;	
	}
	
	public PairwiseOrthoMatch getInverse() {
		PairwiseOrthoMatch inverted_match = new PairwiseOrthoMatch(
				OrthoMap.invertAffine(getAffine()), // double [][] affine,
				null, // double [][] jtj,
				rms, // double rms,
				zoom_lev); // int zoom_lev)
		return inverted_match;
	/**
	 * Create differential PairwiseOrthoMatch instance from 2 affines of the scenes and an offset vector
	 * @param affine0
	 * @param affine1
	 * @param rd
	 * Continued from getInverse notes:
	 * =========================== V = -rd
		Xsi = Ai*(Xr - V) + Bi + Si
		
		Xs0 = A0*(Xr - 0) + B0 + S0
		Xs1 = A1*(Xr - V) + B1 + S1
		
		--- difference Ad, Bd 
		Xs0 = Xr1 + S0
		Xs1 = Ad * (Xr1 - V) + Bd + S1;
		
		
		Xs0 = A0*(Xr - 0) + B0 + S0
		Xs0 - (B0 + S0) = A0*Xr
		Xr = A0inv*(Xs0 - (B0 + S0))
		Xs1 = A1*((A0inv*(Xs0 - (B0 + S0))) - V) + B1 + S1
		
		Xr1 = Xs0-S0
		
		Xs1 = Ad * ((Xs0-S0) - V) + Bd + S1;
		
		A1*((A0inv*(Xs0 - (B0 + S0))) - V) + B1 + S1 = Ad * ((Xs0-S0) - V) + Bd + S1
		
		A1*((A0inv*(Xs0 - (B0 + S0))) - V) + B1 = Ad * ((Xs0-S0) - V) + Bd
		
		A1*((A0inv*(Xs0 - (B0 + S0))) - V) + B1 = Ad * ((Xs0-S0) - V) + Bd
		A1 * A0inv * Xs0 - A1 * A0inv * (B0 + S0) - A1*V + B1 - Ad *Xs0 + Ad * S0 +Ad*V -Bd
		=> Ad = A1 * A0inv:
		 - A1 * A0inv * (B0 + S0) - A1*V + B1  + Ad * S0 +Ad*V -Bd =
		 - Ad * (B0 + S0) - A1*V + B1  + Ad * S0 + Ad*V - Bd =
		 Ad * (S0 + V - B0 - S0) - A1*V + B1- Bd = 
		  Ad * (V - B0) - A1 * V + B1 - Bd
		Bd =  Ad * (V - B0) - A1 * V + B1
		
		--- check if A0 = E, B0 = 0
		Ad = A1inv
		Bd = A1 * V - A1 *V + B1 = B1
		--- check if A1 = E, B1 = 0, V1= -V
		Bd =  A0inv * (V1 - B0) - V1 =
		(A0inv-E)*V1 -A0inv*B0 = 
		-(A0inv-E)*V -A0inv*B0 = 
	 */
	public PairwiseOrthoMatch (
			double [][] affine0,
			double [][] affine1,
			double [] rd) {
		Matrix A0 = new Matrix (
				new double [][] {{affine0[0][0],affine0[0][1]},{affine0[1][0],affine0[1][1]}});
		Matrix B0 = new Matrix(new double [][] {{affine0[0][2]},{affine0[1][2]}});
		Matrix A1 = new Matrix (
				new double [][] {{affine1[0][0],affine1[0][1]},{affine1[1][0],affine1[1][1]}});
		Matrix B1 = new Matrix(new double [][] {{affine1[0][2]},{affine1[1][2]}});
		Matrix V =  new Matrix(new double [][] {{-rd[0]},{-rd[1]}});
		Matrix A =  A1.times(A0.inverse());
		Matrix B = A.times(V.minus(B0)).minus(A1.times(V)).plus(B1);
		affine = new double[][] {
				{A.get(0,0),A.get(0,1), B.get(0,0)},
				{A.get(1,0),A.get(1,1), B.get(1,0)}};
//				jtj = null;
//				rms = Double.NaN; // double rms,
//				zoom_lev = 0; // int zoom_lev)
	}
	
	public double [][] getAffine(){
@@ -76,6 +184,7 @@ public class PairwiseOrthoMatch implements Serializable {
				oos.writeObject(jtj[i][j]);
			}
		}
		oos.writeObject(overlap);
	}
	private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
		ois.defaultReadObject();
@@ -88,6 +197,7 @@ public class PairwiseOrthoMatch implements Serializable {
				}
			}
		}
		overlap = (Double) ois.readObject();
	}
	//private void readObjectNoData() throws ObjectStreamException; // used to modify default values
}
Loading