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

tested derivative for the OrthoMultiLMA

parent 6b986720
......@@ -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();
......
......@@ -828,7 +828,8 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
* image ones. Both are in meters relative to the "vert_meters" point
* @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.
* "vert_meters" point to the rectified metric coordinates relative to the same point.
*
*/
public static double [][] invertAffine(double [][] affine){
Matrix A = new Matrix (
......
......@@ -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 {
......
/**
**
** OrthoMultiLMA - Fit multiple scenes orthographic using pair-wise affine
** tr4ansform matrices already calculated.
**
** Copyright (C) 2024 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** OrthoMultiLMA.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.orthomosaic;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import com.elphel.imagej.cameras.CLTParameters;
import Jama.Matrix;
public class OrthoMultiLMA {
private double [] last_rms = null; // {rms, rms_pure}, matching this.vector
private double [] good_or_bad_rms = null; // just for diagnostics, to read last (failed) rms
private double [] initial_rms = null; // {rms, rms_pure}, first-calcualted rms
private double [] parameters_vector = null;
// private double [] x_vector = null; // not used. Save total weight
private double [] y_vector = null;
private double [][] tile_centers = null;
private double weight = 0; // total weight
private double pure_weight; // weight of samples only as a fraction of total weight
private double [] weights; // normalized so sum is 1.0 for all - samples and extra regularization
// private double pure_weight; // weight of samples only
private double [] last_ymfx = null;
private double [][] last_jt = null;
private PairwiseOrthoMatch [][] matches = null;
private ArrayList<Point> pairs = null;
private int [] indices = null;
private int num_scenes = 0;
private int num_pairs = 0;
public static boolean testMultiLMA(
CLTParameters clt_parameters,
OrthoMapsCollection maps_collection,
int [] indices) {
int scene0 = indices[0], scene1 = indices[1];
PairwiseOrthoMatch direct = maps_collection.ortho_maps[scene0].getMatch(maps_collection.ortho_maps[scene1].getName());
double [] enuOffset = maps_collection.ortho_maps[scene1].enuOffsetTo(maps_collection.ortho_maps[scene0]);
double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image
PairwiseOrthoMatch inv_match = direct.getInverse(rd);
double [][] affine0 = new double [2][3]; // inv_match.affine()
double [][] affine1 = new double [2][3]; // inv_match.affine()
affine0[0][0] = 0.5 * inv_match.getAffine()[0][0];
double [][] E = {{1,0,0},{0,1,0}};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
affine0[i][j] = 0.5 * (inv_match.getAffine()[i][j]+E[i][j]);
affine1[i][j] = 0.5 * (direct.getAffine()[i][j]+E[i][j]);
}
}
double [][][] derivs = new double [12][][];
double [][][] derivs_delta = new double [12][][];
double delta = 1E-7;
getAffineAndDerivetives_delta(
delta, // double delta,
affine0, // double [][] affine00,
affine1, // double [][] affine10,
rd, // double [] rd,
derivs_delta); // double [][][] derivs)
double [][] affine = getAffineAndDerivetives(
affine0, // double [][] affine00,
affine1, // double [][] affine10,
rd, // double [] rd,
derivs); // double [][][] derivs)
double [][][] err = new double [12][2][3];
for (int n = 0; n < err.length; n++) if ((derivs[n] != null) && (derivs_delta[n] != null)) {
for (int i = 0; i < err[n].length; i++) {
for (int j = 0; j < err[n][i].length; j++) {
err[n][i][j] = derivs[n][i][j] - derivs_delta[n][i][j];
}
}
}
System.out.println();
return true;
}
public int prepareLMA (
CLTParameters clt_parameters,
OrthoMapsCollection maps_collection,
int [] indices,
double [] val_coord, // 1 - valid, 0 - invalid, minimize coordinates errors
double position_pull,
boolean use_inv,
double overlap_pow, // match weight as overlap fraction to this power
int debugLevel) {
this.indices = indices;
num_scenes = indices.length;
matches = new PairwiseOrthoMatch[indices.length][indices.length];
pairs = new ArrayList<Point>();
for (int i = 0; i < num_scenes-1; i++) {
int scene0 = indices[i];
for (int j = i+1; j < num_scenes; j++){
int scene1 = indices[j];
PairwiseOrthoMatch match = maps_collection.ortho_maps[scene0].getMatch(maps_collection.ortho_maps[scene1].getName());
PairwiseOrthoMatch inv_match = use_inv?
maps_collection.ortho_maps[scene1].getMatch(maps_collection.ortho_maps[scene0].getName()):null;
if ((match != null) || (inv_match != null)){
if (match == null) {
double [] enuOffset = maps_collection.ortho_maps[scene0].enuOffsetTo(maps_collection.ortho_maps[scene1]);
double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image
match = inv_match.getInverse(rd);
}
if (inv_match == null) {
double [] enuOffset = maps_collection.ortho_maps[scene1].enuOffsetTo(maps_collection.ortho_maps[scene0]);
double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image
inv_match = match.getInverse(rd);
}
}
matches[i][j] = match;
matches[j][i] = inv_match;
pairs.add(new Point(i,j)); // only once?
}
}
num_pairs = pairs.size();
if (val_coord == null) { // maybe not needed - use weights
val_coord = new double [indices.length];
Arrays.fill(val_coord, 1.0);
}
y_vector = new double [6*num_pairs + 2 * num_scenes]; // last 2 * num_scenes will stay 0
weights = new double [6*num_pairs + 2 * num_scenes];
parameters_vector = new double [6*num_scenes]; // maybe will need move-only mode?
for (int n = 0; n < num_scenes; n++) {
double [][] affine = maps_collection.ortho_maps[indices[n]].getAffine();
System.arraycopy(affine[0], 0, parameters_vector, 6*n, 3);
System.arraycopy(affine[1], 0, parameters_vector, 6*n + 3, 3);
y_vector[6*num_pairs+2*n + 0] = affine[0][2];
y_vector[6*num_pairs+2*n + 1] = affine[1][2];
}
for (int np = 0; np < num_pairs; np++) {
Point pair_ind = pairs.get(np);
PairwiseOrthoMatch match = matches[pair_ind.x][pair_ind.y];
double [][] affine_pair = match.getAffine();
System.arraycopy(affine_pair[0], 0, y_vector, 6*np, 3);
System.arraycopy(affine_pair[1], 0, y_vector, 6*np + 3, 3);
double overlap = match.overlap;
double [][] jtj = match.jtj;
double [] w = new double [6];
w[0] = jtj[0][0];w[1]=jtj[1][1]; w[2] = jtj[4][4]; // a00, a01, b0
w[3] = jtj[2][2];w[4]=jtj[3][3]; w[5] = jtj[5][5]; // a10, a11, b1
double w_overlap = Math.pow(overlap, overlap_pow);
for (int i = 0; i < w.length; i++) {
w[i] = Math.sqrt(w[i]) * w_overlap;
}
System.arraycopy(w, 0, weights, 6*np, 6);
}
int wi = 6*num_pairs;
for (int n = 0; n < num_scenes; n++) {
double w = val_coord[n]*position_pull;
weights[wi++] = w;
weights[wi++] = w;
}
return weights.length;
}
public static double [][] getAffineAndDerivetives_delta(
double delta,
double [][] affine00,
double [][] affine10,
double [] rd,
double [][][] derivs) {
double [][] affine = getAffineAndDerivetives(
affine00, // double [][] affine0,
affine10, // double [][] affine1,
rd, // double [] rd,
null); // double [][][] derivs)
double [][][] affines0 = {affine00, affine10};
for (int naff = 0; naff < affines0.length; naff++) {
for (int row = 0; row < affines0[naff].length; row++) {
for (int col = 0; col < affines0[naff][row].length; col++) {
double [][][] affines = {
{affines0[0][0].clone(), affines0[0][1].clone()},
{affines0[1][0].clone(), affines0[1][1].clone()}};
affines[naff][row][col]= affines0[naff][row][col] - 0.5*delta;
double [][] affine_minus = getAffineAndDerivetives(
affines[0], // double [][] affine0,
affines[1], // double [][] affine1,
rd, // double [] rd,
null); // double [][][] derivs)
affines[naff][row][col]= affines0[naff][row][col] + 0.5*delta;
double [][] affine_plus = getAffineAndDerivetives(
affines[0], // double [][] affine0,
affines[1], // double [][] affine1,
rd, // double [] rd,
null); // double [][][] derivs)
double [][] daffine = new double [2][3];
for (int i = 0; i < daffine.length; i++) {
for (int j = 0; j < daffine[i].length; j++) {
daffine[i][j] = (affine_plus[i][j] - affine_minus[i][j])/delta;
}
}
derivs[naff*6+row*3+col] = daffine;
}
}
}
return affine;
}
public static double [][] getAffineAndDerivetives(
double [][] affine0,
double [][] affine1,
double [] rd,
double [][][] derivs) { // null or double[12] - will return affine derivatives by each of affine0, affine1
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);
double [][] 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)}};
if (derivs != null) {
double [][] a = A.getArray();
double [][] a0 = A0.getArray();
double [] b0 = B0.getColumnPackedCopy();
double [][] a1 = A1.getArray();
double idet = 1/(a0[0][0]*a0[1][1]-a0[1][0]*a0[0][1]);
double [] v = V.getColumnPackedCopy();
double [] vb0= {v[0]-b0[0], v[1]-b0[1]};
double d2 = -idet; // *idet; One idet is already included in a[][]
double [][] didet_da = {{d2*a0[1][1], -d2*a0[1][0]}, {-d2*a0[0][1], d2*a0[0][0]}};
/*
1/det0 * | a100, a101 | * | a011, -a001 |
| a110, a111 | |-a010, a000 |
*
1/det0 * |a100*a011 - a101*a010, -a100*a001 + a101*a000 |
|a110*a011 - a111*a010, -a110*a001 + a111*a000 |
*/
double [][] da_da000 = {{0, idet*a1[0][1]}, {0, idet*a1[1][1]}};
double [][] da_da001 = {{0, -idet*a1[0][0]}, {0,-idet*a1[1][0]}};
double [][] da_da010 = {{-idet*a1[0][1], 0}, {-idet*a1[1][1],0}};
double [][] da_da011 = {{ idet*a1[0][0], 0}, { idet*a1[1][0],0}};
double [][][][] da_da0 = {{da_da000, da_da001}, {da_da010, da_da011}};
for (int i = 0; i < da_da0.length; i++) {
for (int j = 0; j < da_da0[i].length; j++) {
for (int k = 0; k < da_da0[i][j].length; k++) {
for (int l = 0; l < da_da0[i][j][k].length; l++) {
da_da0[i][j][k][l] += a[k][l]*didet_da[i][j]; // a[][] already has idet
}
}
}
}
// some symmetry below - use to reduce calcs?
double [][] da_da100 = {{ idet*a0[1][1],-idet*a0[0][1]},{0,0}};
double [][] da_da101 = {{-idet*a0[1][0], idet*a0[0][0]},{0,0}};
double [][] da_da110 = {{ 0, 0 },{ idet*a0[1][1],-idet*a0[0][1]}};
double [][] da_da111 = {{ 0, 0 },{-idet*a0[1][0], idet*a0[0][0]}};
double [][][][] da_da1 = {{da_da100, da_da101}, {da_da110, da_da111}};
double [][][][][] da_da = {da_da0, da_da1};
double [] db_da000 = {
da_da000[0][0]*vb0[0]+da_da000[0][1]*vb0[1],
da_da000[1][0]*vb0[0]+da_da000[1][1]*vb0[1]};
double [] db_da001 = {
da_da001[0][0]*vb0[0]+da_da001[0][1]*vb0[1],
da_da001[1][0]*vb0[0]+da_da001[1][1]*vb0[1]};
double [] db_da010 = {
da_da010[0][0]*vb0[0]+da_da010[0][1]*vb0[1],
da_da010[1][0]*vb0[0]+da_da010[1][1]*vb0[1]};
double [] db_da011 = {
da_da011[0][0]*vb0[0]+da_da011[0][1]*vb0[1],
da_da011[1][0]*vb0[0]+da_da011[1][1]*vb0[1]};
double [][][] db_da0 = {{db_da000,db_da001},{db_da010,db_da011}};
double [] db_da100 = {
da_da100[0][0]*vb0[0]+da_da100[0][1]*vb0[1] - v[0],
da_da100[1][0]*vb0[0]+da_da100[1][1]*vb0[1]};
double [] db_da101 = {
da_da101[0][0]*vb0[0]+da_da101[0][1]*vb0[1] - v[1],
da_da101[1][0]*vb0[0]+da_da101[1][1]*vb0[1]};
double [] db_da110 = {
da_da110[0][0]*vb0[0]+da_da110[0][1]*vb0[1],
da_da110[1][0]*vb0[0]+da_da110[1][1]*vb0[1] - v[0]};
double [] db_da111 = {
da_da111[0][0]*vb0[0]+da_da111[0][1]*vb0[1],
da_da111[1][0]*vb0[0]+da_da111[1][1]*vb0[1] - v[1]};
double [][][] db_da1 = {{db_da100,db_da101},{db_da110,db_da111}};
double [][][][] db_da = {db_da0,db_da1};
double [] db_db00 = {-a[0][0],-a[1][0]};
double [] db_db01 = {-a[0][1],-a[1][1]};
double [] db_db10 = {1,0};
double [] db_db11 = {0,1};
double [][][] db_db = {{db_db00,db_db01},{db_db10,db_db11}};
// fill in derivatives, skipping B - only 2x2 A
for (int naff = 0; naff < da_da.length; naff++) {
for (int row = 0; row < da_da[naff].length; row++) {
for (int col = 0; col < 3; col++) {
int indx = naff*6+row*3+col;
derivs[indx] = new double[2][3];
if ((col < da_da[naff][row].length) ) {
for (int i = 0; i < 2; i++) {
for (int j = 0; j< 2; j++) {
derivs[indx][i][j] = da_da[naff][row][col][i][j];
}
derivs[indx][i][2] = db_da[naff][row][col][i];
}
} else { // d/db
for (int i = 0; i < 2; i++) {
derivs[indx][i][2] = db_db[naff][row][i];
}
}
}
}
}
}
return affine; // _alt;
}
}
......@@ -5,13 +5,16 @@ 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];
public transient double [][] jtj = new double [6][6];
public int zoom_lev;
public double rms = Double.NaN;
public transient int [] nxy = null; // not saved, just to communicate for logging
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)};
......@@ -55,14 +104,73 @@ public class PairwiseOrthoMatch implements Serializable {
affine[1][2] += corr[1];
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
}
......@@ -108,6 +108,7 @@ public class IntersceneMatchParameters {
public double rln_neib_rstr = 0.35; // minimal neighbors phase correlation maximums relative to max str
// Pairwise scenes matching
public boolean pmtch_use_affine = false; // when matching pairs, start with known scene affine matrices, false - use unity
public double pmtch_frac_remove = 0.1;
public double pmtch_metric_err = 0.05; // 0.02;// 2 cm
public double pmtch_max_std = 1.5; // maximal standard deviation to limit center area
......@@ -737,6 +738,7 @@ public class IntersceneMatchParameters {
"Minimal neighbors phase correlation maximums relative to maximal strength.");
gd.addMessage ("Pairwise scenes matching");
gd.addCheckbox ("Use scenes' affine", this.pmtch_use_affine, "Use known scenes' affine matrices, false - start from scratch (unity) ones.");
gd.addNumericField("Remove fraction of worst matches", this.pmtch_frac_remove, 3,7,"", "When fitting scenes remove this fraction of worst match tiles.");
gd.addNumericField("Maximal metric error", this.pmtch_metric_err, 3,7,"m", "Maximal tolerable fitting error caused by elevation variations.");
gd.addNumericField("Central area standard deviation", this.pmtch_max_std, 3,7,"", "Central area limit by the standard deviation.");
......@@ -1622,6 +1624,7 @@ public class IntersceneMatchParameters {
this.rln_sngl_rstr = gd.getNextNumber();
this.rln_neib_rstr = gd.getNextNumber();
this.pmtch_use_affine= gd.getNextBoolean();
this.pmtch_frac_remove = gd.getNextNumber();
this.pmtch_metric_err = gd.getNextNumber();
this.pmtch_max_std = gd.getNextNumber();
......@@ -2120,6 +2123,7 @@ public class IntersceneMatchParameters {
properties.setProperty(prefix+"rln_sngl_rstr", this.rln_sngl_rstr +""); // double
properties.setProperty(prefix+"rln_neib_rstr", this.rln_neib_rstr +""); // double
properties.setProperty(prefix+"pmtch_use_affine", this.pmtch_use_affine +""); // boolean
properties.setProperty(prefix+"pmtch_frac_remove", this.pmtch_frac_remove +""); // double
properties.setProperty(prefix+"pmtch_metric_err", this.pmtch_metric_err +""); // double
properties.setProperty(prefix+"pmtch_max_std", this.pmtch_max_std +""); // double
......@@ -2582,6 +2586,7 @@ public class IntersceneMatchParameters {
if (properties.getProperty(prefix+"rln_sngl_rstr")!=null) this.rln_sngl_rstr=Double.parseDouble(properties.getProperty(prefix+"rln_sngl_rstr"));
if (properties.getProperty(prefix+"rln_neib_rstr")!=null) this.rln_neib_rstr=Double.parseDouble(properties.getProperty(prefix+"rln_neib_rstr"));
if (properties.getProperty(prefix+"pmtch_use_affine")!=null) this.pmtch_use_affine= Boolean.parseBoolean(properties.getProperty(prefix+"pmtch_use_affine"));
if (properties.getProperty(prefix+"pmtch_frac_remove")!=null) this.pmtch_frac_remove=Double.parseDouble(properties.getProperty(prefix+ "pmtch_frac_remove"));
if (properties.getProperty(prefix+"pmtch_metric_err")!=null) this.pmtch_metric_err= Double.parseDouble(properties.getProperty(prefix+ "pmtch_metric_err"));
if (properties.getProperty(prefix+"pmtch_max_std")!=null) this.pmtch_max_std= Double.parseDouble(properties.getProperty(prefix+ "pmtch_max_std"));
......@@ -3071,6 +3076,7 @@ public class IntersceneMatchParameters {
imp.rln_sngl_rstr = this.rln_sngl_rstr;
imp.rln_neib_rstr = this.rln_neib_rstr;
imp.pmtch_use_affine = this.pmtch_use_affine;
imp.pmtch_frac_remove = this.pmtch_frac_remove;
imp.pmtch_metric_err = this.pmtch_metric_err;
imp.pmtch_max_std = this.pmtch_max_std;
......
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