Commit 6b986720 authored by Andrey Filippov's avatar Andrey Filippov

Automatic matching of multiple scene sets

parent ba9466e1
......@@ -8,6 +8,7 @@ import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
import ij.IJ;
import ij.Prefs;
import ij.io.OpenDialog;
......@@ -187,6 +188,13 @@ public class CalibrationFileManagement {
}
public static void saveStringToFile (String path,String data, boolean append){
int dir_sep = path.lastIndexOf(Prefs.getFileSeparator());
if (dir_sep >=0) { // create directory if it does not exist
File dir = new File(path.substring(0, dir_sep));
if (!dir.exists()) {
dir.mkdirs();
}
}
BufferedWriter writer = null;
try {
writer = new BufferedWriter( new FileWriter( path, append));
......
......@@ -3,13 +3,18 @@ package com.elphel.imagej.orthomosaic;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import com.elphel.imagej.calibration.CalibrationFileManagement;
import com.elphel.imagej.cameras.CLTParameters;
import com.elphel.imagej.cameras.ColorProcParameters;
import com.elphel.imagej.cameras.EyesisCorrectionParameters;
......@@ -28,6 +33,7 @@ import com.elphel.imagej.tileprocessor.TDCorrTile;
import Jama.Matrix;
import ij.ImagePlus;
import ij.ImageStack;
import ij.Prefs;
import ij.WindowManager;
import ij.gui.PointRoi;
import ij.process.FloatPolygon;
......@@ -166,12 +172,16 @@ public class ComboMatch {
boolean bounds_to_indices = true;
int temp_mode = 1;
boolean restore_temp = true;
double frac_remove = 0.15;
double metric_error = 0.05; // 0.02;// 2 cm
double frac_remove = clt_parameters.imp.pmtch_frac_remove; // 0.15;
double metric_error = clt_parameters.imp.pmtch_metric_err; // 0.05; // 0.02;// 2 cm
boolean update_lla = false; // re-read file metadata
boolean update_kernel_patterns = false;
boolean update_bl_bc = false;
String [] suffixes_bl_bc= {"","-BL","-BC"};
boolean log_append = clt_parameters.imp.pwise_log_append;
String log_path = clt_parameters.imp.pwise_log_path;
int suffix_bc_bl_indx = 2;
if (!use_marked_image) {
process_correlation=false; // use already adjusted by default
......@@ -182,7 +192,7 @@ public class ComboMatch {
}
GenericJTabbedDialog gd = new GenericJTabbedDialog("Set image pair",1200,600);
GenericJTabbedDialog gd = new GenericJTabbedDialog("Set image pair",1200,700);
gd.addChoice ("Files list/data path (w/o extension):", files_lists_paths, files_lists_paths[default_list_choice]);
gd.addCheckbox ("Use saved maps collection", use_saved_collection, "If false - use files list.");
gd.addCheckbox ("Save maps collection", save_collection, "Save maps collection to be able to restore.");
......@@ -235,6 +245,8 @@ public class ComboMatch {
suffixes_bl_bc,
suffixes_bl_bc[suffix_bc_bl_indx],
"Select interpolation mode of the source files: old bilinear (empty), bilinear (-BL), or bicubic (-BC)", 0);
gd.addCheckbox ("Write log file", log_append, "Enable writing log file with matching results.");
gd.addStringField ("Log file full path", log_path, 150, "Path of the log file to be appended.");
......@@ -282,6 +294,8 @@ public class ComboMatch {
update_kernel_patterns= gd.getNextBoolean();
update_bl_bc= gd.getNextBoolean();
suffix_bc_bl_indx = gd.getNextChoiceIndex();
log_append = gd.getNextBoolean();
log_path = gd.getNextString();
OrthoMapsCollection maps_collection=null;
if (use_saved_collection) {
try {
......@@ -656,37 +670,6 @@ public class ComboMatch {
output_patt_titles); // test_titles,
imp_inv_patt.show();
// select kernel and correlatde with every cut image?
// Test invert patterns
/*
boolean test_invert = debugLevel > -1000;
if (test_invert) {
double invert_fz = 0;
double [][] inverted_patterns = new double [output_patterns.length][];
for (int i = 0; i < output_patterns.length; i++) {
inverted_patterns[i] = output_patterns[i].clone();
double [] dfr = new double [corr_size*corr_size];
dfr[corr_size/2*(corr_size+1)] = corr_size*corr_size;
double [][] data_pair = new double[][] {inverted_patterns[i],dfr};
inverted_patterns[i]=OrthoMap.deconvolvePair(
data_pair,
invert_fz,
1);
}
ImagePlus imp_inv_patt = ShowDoubleFloatArrays.makeArrays(
inverted_patterns,
corr_size,
corr_size,
OrthoMap.removeKnownExtension(imp_sel.getTitle())+"-INV_PATTERN"+settings_str+"_"+corr_size+"x"+corr_size+".tiff",
output_patt_titles); // test_titles,
imp_inv_patt.show();
}
*/
ImagePlus imp_out_patt = ShowDoubleFloatArrays.makeArrays(
output_patterns,
corr_size,
......@@ -765,8 +748,30 @@ public class ComboMatch {
maps_collection.processComboMap(debugLevel);
return true;
}
// for all modes - needed for create_overlaps || process_correlation || render_match || pattern_match
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);
}
if (create_overlaps) {
boolean ok =maps_collection.getIntersectedPairs(debugLevel);
boolean ok =maps_collection.getIntersectedPairs(
clt_parameters, // CLTParameters clt_parameters,
orthoMapsCollection_path); // String orthoMapsCollection_path);
if (!ok) return false;
}
if (process_correlation || render_match || pattern_match) {
......@@ -780,6 +785,17 @@ public class ComboMatch {
maps_collection.getIndex(other_name)));
}
}
// sort pairs_list by x then y
Collections.sort(pairs_list, new Comparator<Point>() {
@Override
public int compare(Point lhs, Point rhs) {
return (rhs.x > lhs.x) ? -1 : (rhs.x < lhs.x) ? 1 :
((rhs.y > lhs.y) ? -1 : (rhs.y < lhs.y) ? 1 : 0); // increasing
}
});
int [][] available_pairs = new int [pairs_list.size()][2];
for (int i = 0; i < available_pairs.length; i++) {
available_pairs[i][0] = pairs_list.get(i).x;
......@@ -803,7 +819,7 @@ public class ComboMatch {
if (pair >= choices.length) {
int default_choice = 0;
int num_scene_lines = 50;
if (process_correlation) { // select a second image to match
if (process_correlation || render_match) { // select a second image to match
// select a second image and set gpu_spair
int default_choice1 = 0;
gpu_spair = maps_collection.selectTwoScenes(
......@@ -843,9 +859,9 @@ 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?
System.out.println("Setting up GPU");
/*
if (GPU_QUAD_AFFINE == null) {
System.out.println("Setting up GPU");
try {
GPU_QUAD_AFFINE = new GpuQuad(//
GPU_TILE_PROCESSOR, // GPUTileProcessor gpuTileProcessor,
......@@ -860,6 +876,7 @@ public class ComboMatch {
return false;
} // final int debugLevel);
}
*/
double [][] affine0 = {{1,0,0},{0,1,0}}; // will always stay the same
double [][] affine1 = null;
......@@ -901,7 +918,7 @@ public class ComboMatch {
affine1 = pairwiseOrthoMatch.getAffine();
double [][][] affines = {affine0,affine1};
int [] zooms = {initial_zoom, min_zoom_lev, 1000,1000}; // make automatic
double scale = 2.0; // scale vectors when warping;
// double scale = 2.0; // scale vectors when warping;
// int num_tries = 5; // make configurable
if (!process_correlation || !use_marked_image) { // skip low-res
zooms = new int[] {min_zoom_lev, 1000};
......@@ -912,12 +929,17 @@ public class ComboMatch {
}
// debugLevel = 0;
boolean batch_mode = true; // false; // true;
boolean ignore_prev_rms = true;
boolean ignore_prev_rms = clt_parameters.imp.pmtch_ignore_rms; // true;
Rectangle woi = new Rectangle(); // used to return actual woi from correlateOrthoPair()
double [][] ground_planes = null;
double max_std = 1.5; // maximal standard deviation to limit center area
double min_std_rad = 2.0; // minimal radius of the central area (if less - fail)
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
double max_tile_rad = clt_parameters.imp.pmtch_max_cent_rad;// maximal center radius in tiles (limit pmtch_cent_rad)
double fill_fraction = clt_parameters.imp.pmtch_cent_fill; // should be populated not less than this
double fill_fraction_final = clt_parameters.imp.pmtch_cent_final; // should be populated not less than this during final pass
double ease_nosfm = clt_parameters.imp.pmtch_ease_nosfm; // ease metric_error when no SfM gain == 0;
for (int zi = 0; zi < zooms.length; zi++) {
zoom_lev = zooms[zi];
if (zoom_lev >=1000) {
......@@ -947,6 +969,12 @@ public class ComboMatch {
zoom_lev, // int zoom_lev,
show_vf, // boolean show_vf,
ground_planes, // double [][] ground_planes, // null or double[2] - will return ground planes
rad_fraction, // double rad_fraction,
max_tile_rad, // double max_tile_rad, // = 30;
fill_fraction, // double fill_fraction,
fill_fraction_final, // double fill_fraction_final,
ease_nosfm, // double ease_nosfm,
null, // double [] max_rms_iter, // = {1.0, 0.6};//
debugLevel); // final int debugLevel)
if ((warp == null) || ((pmatch != null) && Double.isNaN(pmatch.rms))) {
System.out.println("Failed correlateOrthoPair()");
......@@ -978,6 +1006,16 @@ public class ComboMatch {
}
System.out.println();
}
if (log_append && (log_path != null)) { // assuming directory exists
StringBuffer sb = new StringBuffer();
sb.append(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime())+"\n");
sb.append(String.format("%d\t%d", gpu_pair[0], gpu_pair[1]));
sb.append(String.format("\t%6.4f\t%d\n", pairwiseOrthoMatch.rms, pairwiseOrthoMatch.zoom_lev));
CalibrationFileManagement.saveStringToFile (
log_path, //String path,
sb.toString(), // data,
true); // boolean append)
}
}
if (pattern_match) {
......@@ -1053,8 +1091,19 @@ public class ComboMatch {
int min_overlap = clt_parameters.imp.ospir_overlap; // 3000; // do not try to match if there is too small overlap (scaled pixels)
int num_iter_lma = clt_parameters.imp.ospir_num_iter; // 5;
boolean ignore_rms = clt_parameters.imp.ospir_ignore_rms; // false
int spiral_debug = clt_parameters.imp.ospir_debug; // -3;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Setup SpiralMatch",1200,300);
int spiral_debug = clt_parameters.imp.ospir_debug; // 0;
boolean log_append = clt_parameters.imp.pwise_log_append;
String log_path = clt_parameters.imp.pwise_log_path;
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
double max_tile_rad = clt_parameters.imp.pmtch_max_cent_rad;// maximal center radius in tiles (limit pmtch_cent_rad)
double fill_fraction = clt_parameters.imp.pmtch_cent_fill; // should be populated not less than this
double fill_fraction_final = clt_parameters.imp.pmtch_cent_final; // should be populated not less than this during final pass
double ease_nosfm = clt_parameters.imp.pmtch_ease_nosfm; // ease metric_error when no SfM gain == 0;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Setup Spiral Match",1200,350);
if (pairwiseOrthoMatch != null) {
gd.addCheckbox ("Use existing image pair", use_existing_pair, "Use existing affine settings for this pair, do not use spiral search.");
} else {
......@@ -1072,8 +1121,20 @@ public class ComboMatch {
gd.addNumericField("Minimal overlap", min_overlap, 0,4,"scaled pix ^ 2","Minimal overlap area in square scaled pixels.");
gd.addNumericField("LMA iterations", num_iter_lma, 0,2,"", "Number of LMA iterations.");
gd.addCheckbox ("Ignore worsening RMSE", ignore_rms, "Ignore worsening/not improving RMSE during spiral search.");
gd.addCheckbox ("Write log file", log_append, "Enable writing log file with matching results.");
gd.addStringField ("Log file full path", log_path, 150, "Path of the log file to be appended.");
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.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.");
gd.addNumericField("Maximal central area radius", max_tile_rad, 3,7,"tiles", "Absolute limit to the center area radius (eases bad peripheral matching).");
gd.addNumericField("Central area minimal fill", fill_fraction, 3,7,"", "Central area minimal fill for all but the last iteration.");
gd.addNumericField("Central area minimal fill final", fill_fraction_final, 3,7,"", "Central area minimal fill for the last iteration.");
gd.addNumericField("Relax metric error for no-SfM", ease_nosfm, 3,7,"", "Relax metric error for no-SfM scenes (sfm_gain==0).");
gd.showDialog();
if (gd.wasCanceled()) return null;
if (pairwiseOrthoMatch != null) {
......@@ -1089,7 +1150,18 @@ public class ComboMatch {
min_overlap = (int) gd.getNextNumber();
num_iter_lma = (int) gd.getNextNumber();
ignore_rms = gd.getNextBoolean();
log_append = gd.getNextBoolean();
log_path = gd.getNextString();
spiral_debug = (int) gd.getNextNumber();
search_step= gd.getNextNumber();
max_std= gd.getNextNumber();
min_std_rad= gd.getNextNumber();
rad_fraction= gd.getNextNumber();
max_tile_rad = gd.getNextNumber();
fill_fraction= gd.getNextNumber();
fill_fraction_final= gd.getNextNumber();
ease_nosfm= gd.getNextNumber();
if (use_existing_pair) {
if (invert_exixting_pair) {
System.out.println("Both direct and inverted matches are selected, using direct match");
......@@ -1104,11 +1176,17 @@ public class ComboMatch {
double [][] affine0 = {{1,0,0},{0,1,0}}; // will always stay the same
double [][] affine1 = {{1,0,0},{0,1,0}}; // here (manual mode) start from the center, may use prediction in auto
double [][][] affines = new double[][][] {affine0,affine1};
pairwiseOrthoMatch = maps_collection.SpiralMatch (
clt_parameters, // CLTParameters clt_parameters,
frac_remove, // double frac_remove, // = 0.25
metric_error, // double metric_error,
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,
max_tile_rad, //double max_tile_rad, // = 30;
fill_fraction, // double fill_fraction,
fill_fraction_final, // double fill_fraction_final,
ease_nosfm, // double ease_nosfm,
gpu_pair, // int [] gpu_pair,
affines, // double [][][] affines_init, // here in meters, relative to vertical points
initial_zoom, // int zoom_lev,
......@@ -1119,12 +1197,26 @@ public class ComboMatch {
num_iter_lma, // int num_tries, // = 5
min_overlap, // int min_overlap, // 3000
ignore_rms, // boolean ignore_rms,
null,//double [] max_rms_iter, // = {1.0, 0.6};//
spiral_debug); // int debugLevel){
if (log_append && (log_path != null)) { // assuming directory exists
StringBuffer sb = new StringBuffer();
sb.append(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime())+"\n");
sb.append(String.format("%d\t%d", gpu_pair[0], gpu_pair[1]));
if ((pairwiseOrthoMatch != null) && !Double.isNaN(pairwiseOrthoMatch.rms)) {
sb.append(String.format("\t%d\t%d\t%6.4f\t%d\n",
pairwiseOrthoMatch.nxy[0], pairwiseOrthoMatch.nxy[1], pairwiseOrthoMatch.rms, pairwiseOrthoMatch.zoom_lev));
} else {
sb.append("\tFAILED");
}
CalibrationFileManagement.saveStringToFile (
log_path, //String path,
sb.toString(), // data,
true); // boolean append)
}
return pairwiseOrthoMatch;
}
public static boolean updateBlBcFileNames(
String suffix,
String before,
......
......@@ -191,6 +191,13 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
PairwiseOrthoMatch match) {
pairwise_matches.put(name, match);
}
public void unsetMatch(
String name) {
pairwise_matches.remove(name);
}
public PairwiseOrthoMatch getMatch(String name) {
return pairwise_matches.get(name);
}
......
......@@ -940,56 +940,18 @@ public class OrthoMapsCollection implements Serializable{
return gpu_pair_img;
}
/*
public double [][] correlateOrthoPair(
CLTParameters clt_parameters,
String first_name,
String second_name,
int zoom_lev,
double [] offset_xy_second, // on top of affine and GPS
final int debugLevel){
int first_index = map_index_string.get(first_name);
int second_index = map_index_string.get(second_name);
return correlateOrthoPair(
clt_parameters, // CLTParameters clt_parameters,
first_index, // int first_index,
second_index, // int second_index,
zoom_lev, // int zoom_lev,
offset_xy_second, // double [] offset_xy_second, // on top of affine and GPS
debugLevel); // final int debugLevel)
}
private double [][] correlateOrthoPair(
CLTParameters clt_parameters,
int first_index,
int second_index,
int zoom_lev,
double [] offset_xy_second, // on top of affine and GPS
final int debugLevel){
int [] gpu_pair = {first_index,second_index};
double [][][] bounds_out = new double [2][][];
float [][] gpu_pair_img = new float [2][];
for (int n = 0; n < gpu_pair.length; n++) {
gpu_pair_img[n] = ortho_maps[gpu_pair[n]].getPaddedGPU (
zoom_lev); // int zoom_level,
}
if (debugLevel > 1) {
String [] map_names = {ortho_maps[gpu_pair[0]].getName(),ortho_maps[gpu_pair[1]].getName()};
ShowDoubleFloatArrays.showArrays(
gpu_pair_img,
OrthoMap.gpu_width,
OrthoMap.gpu_height,
true,
"gpu_pair-zoom"+zoom_lev+"-"+ortho_maps[gpu_pair[0]].getName()+"-"+ortho_maps[gpu_pair[1]].getName(),
map_names);
}
return null;
} */
public PairwiseOrthoMatch SpiralMatch (
CLTParameters clt_parameters,
double frac_remove, // = 0.25
double metric_error,
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,
double max_tile_rad, // = 30;
double fill_fraction,
double fill_fraction_final,
double ease_nosfm,
int [] gpu_pair,
double [][][] affines_init, // here in meters, relative to vertical points
int zoom_lev,
......@@ -1000,6 +962,7 @@ public class OrthoMapsCollection implements Serializable{
int num_tries, // = 5
int min_overlap, // = 5
boolean ignore_rms,
double [] max_rms_iter, // = {1.0, 0.6};//
int debugLevel){
double [][] affine1 = new double [][] {affines_init[1][0].clone(),affines_init[1][1].clone()};
double [][][] affines = new double [][][] {affines_init[0],affine1};
......@@ -1014,8 +977,8 @@ public class OrthoMapsCollection implements Serializable{
null, // double [][] jtj,
Double.NaN, // double rms,
zoom_lev); // int zoom_lev);
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)
// 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;
PairwiseOrthoMatch best_pom = null;
while ((Math.abs(nx) <= nabs) && (Math.abs(ny) <= nabs)) {
......@@ -1044,19 +1007,27 @@ public class OrthoMapsCollection implements Serializable{
zoom_lev, // int zoom_lev,
show_vf, // boolean show_vf,
ground_planes, // double [][] ground_planes, // null or double[2] - will return ground planes
rad_fraction, // double rad_fraction,
max_tile_rad, // double max_tile_rad, // = 30;
fill_fraction, // double fill_fraction,
fill_fraction_final, // double fill_fraction_final,
ease_nosfm, // double ease_nosfm,
max_rms_iter, // double [] max_rms_iter, // = {1.0, 0.6};//
debugLevel-4); // final int debugLevel)
pairwiseOrthoMatch.affine = affines[1]; // modified by correlateOrthoPair
if (debugLevel > -4) {
System.out.println(String.format("SpiralMatch(): nx = %3d, ny=%3d, RMSE=%8.6f",
nx,ny,pairwiseOrthoMatch.rms));
}
if (pairwiseOrthoMatch.rms < good_rms) {
break;
System.out.println(String.format("SpiralMatch(): %3d-%3d nx = %3d, ny=%3d, RMSE=%8.6f",
gpu_pair[0], gpu_pair[1], nx,ny,pairwiseOrthoMatch.rms)); // if NaN - provide reason
}
if (!Double.isNaN(pairwiseOrthoMatch.rms) && ((best_pom == null) || !(best_pom.rms <= pairwiseOrthoMatch.rms))) {
pairwiseOrthoMatch.nxy = new int [] {nx,ny};
best_pom = pairwiseOrthoMatch.clone();
best_nx = nx;
best_ny = ny;
// System.out.println("best_pom.rms = "+best_pom.rms);
}
if (pairwiseOrthoMatch.rms < good_rms) {
break;
}
// update nx, ny
if ((nx > ny) && (nx > -ny)){
......@@ -1069,7 +1040,8 @@ public class OrthoMapsCollection implements Serializable{
nx++;
}
}
if (pairwiseOrthoMatch.rms < good_rms) { // failed
if (pairwiseOrthoMatch.rms < good_rms) { // immediately OK
return pairwiseOrthoMatch; // pairwiseOrthoMatch.affine will have adjusted affine[1]
}
if ((best_pom != null) && (best_pom.rms <= max_rms)) {
......@@ -1103,6 +1075,11 @@ public class OrthoMapsCollection implements Serializable{
int zoom_lev,
boolean show_vf,
double [][] ground_planes, // null or double[2] - will return ground planes
double rad_fraction,
double fill_fraction,
double fill_fraction_final,
double ease_nosfm,
double [] max_rms_iter, // = {1.0, 0.6};//
final int debugLevel){
int [] gpu_pair = new int[gpu_spair.length];
for (int i = 0; i < gpu_pair.length; i++) {
......@@ -1128,6 +1105,12 @@ public class OrthoMapsCollection implements Serializable{
zoom_lev, // int zoom_lev,
show_vf, // boolean show_vf,
ground_planes, // double [][] ground_planes, // null or double[2] - will return ground planes
rad_fraction, // double rad_fraction,
30, // double max_tile_rad, // = 30;
fill_fraction, // double fill_fraction,
fill_fraction_final, // double fill_fraction_final,
ease_nosfm, // double ease_nosfm,
max_rms_iter, // = {1.0, 0.6};// double [] max_rms_iter, // = {1.0, 0.6};//
debugLevel); // final int debugLevel)
}
......@@ -1149,7 +1132,19 @@ public class OrthoMapsCollection implements Serializable{
int zoom_lev,
boolean show_vf,
double [][] ground_planes, // null or double[2] - will return ground planes:
double rad_fraction,
double max_tile_rad, // = 30;
double fill_fraction,
double fill_fraction_final,
double ease_nosfm,
double [] max_rms_iter, // = {1.0, 0.6};//
int debugLevel){
double vf_drop_big = 0.9; // 0.5? If vector_field threshold drops more, allow RMS to increase
// double rad_fraction = 0.5; // center circle radius fraction of 0.5* min(width, height) in tiles
// double fill_fraction = 0.25; // should be populated not less than this
// double fill_fraction_final = 0.4; // should be populated not less than this
// double ease_nosfm = 2.0; // ease metric_error when no SfM gain == 0;
// double max_tile_rad = 30;
double center_radius_change = 1.5; // if increases by more than this, disregard RMSE worsening
boolean show_lma_dbg = !batch_mode && (debugLevel > 1);
if (woi == null) {
......@@ -1177,13 +1172,15 @@ public class OrthoMapsCollection implements Serializable{
if ((bounds_overlap_meters == null) || (bounds_overlap_meters[0] == null) || (bounds_overlap_meters[1] == null)) {
if (debugLevel > -4) {
System.out.println("correlateOrthoPair(): no overlap");
return null;
}
return null;
}
double pix_size = OrthoMap.getPixelSizeMeters (zoom_lev);
int [] overlap_wh_pixel = new int [2];
for (int i = 0; i < 2; i++) {
overlap_wh_pixel[i] = ((int) Math.ceil(bounds_overlap_meters[i][1]/pix_size)) - ((int) Math.floor(bounds_overlap_meters[i][0]/pix_size));
overlap_wh_pixel[i] = (
(int) Math.ceil(bounds_overlap_meters[i][1]/pix_size))
- ((int) Math.floor(bounds_overlap_meters[i][0]/pix_size));
}
// convert to pixels,shift top-left to [0,0] (remember offsets, limit w,h,
// change to pixels last, remember TL in meters?
......@@ -1265,6 +1262,11 @@ public class OrthoMapsCollection implements Serializable{
}
woi.height = OrthoMap.gpu_height;
}
double tile_rad = rad_fraction * 0.5*Math.min(woi.width, woi.height)/GPUTileProcessor.DTT_SIZE;
tile_rad = Math.min(tile_rad, max_tile_rad);
int min_tiles_rad = (int) (fill_fraction * 4 * tile_rad * tile_rad);
final int gpu_width = OrthoMap.gpu_width; // static
final int gpu_height = OrthoMap.gpu_height; // static
int tilesX = gpu_width/GPUTileProcessor.DTT_SIZE;
......@@ -1288,6 +1290,12 @@ public class OrthoMapsCollection implements Serializable{
double rms = Double.NaN;
double last_center_radius = 0;
double previous_center_radius = 1.0;
OrthoPairLMA orthoPairLMA = null;
double [][][] vector_field = new double[gpu_pair.length][][];
double [][] tile_centers = null;
double [] ground_planes_weight = null;
double previous_vf_threshold = Double.POSITIVE_INFINITY;
double vf_threshold = 0;
for (int ntry = 0; ntry < num_tries; ntry++) {
if (!batch_mode) {
if (debugLevel>-3) {
......@@ -1295,7 +1303,7 @@ public class OrthoMapsCollection implements Serializable{
}
}
String dbg_suffix = batch_mode? null: String.format("_%02d", ntry);
double [][][] vector_field =
vector_field =
ComboMatch.rectilinearVectorField(//rectilinearCorrelate_TD( // scene0/scene1
clt_parameters, // final CLTParameters clt_parameters,
gpu_pair_img, // final float [][] fpixels, // to check for empty
......@@ -1318,11 +1326,21 @@ public class OrthoMapsCollection implements Serializable{
}
// get elevations
// double ease_nosfm = 3.0;
int zoom_lev_tiles = zoom_lev-3;
double [] ground_planes_weight = null;
ground_planes_weight = null;
for (int num_elevations = 0; num_elevations < 1; num_elevations++) {
for (int n = 0; n < gpu_pair.length; n++) {
elevations[n] = ortho_maps[gpu_pair[n]]. getTileElevations(
double sfm_gain = ortho_maps[gpu_pair[n]].getSfmGain();
double metric_error_adj = metric_error;
if (sfm_gain == 0.0) {
metric_error_adj *= ease_nosfm;
if (debugLevel > -3) {
System.out.println("SfM gain == 0 for scene #"+gpu_pair[n]+
", icreasing metric_error to "+metric_error_adj);
}
}
elevations[n] = ortho_maps[gpu_pair[n]].getTileElevations(
zoom_lev, // final int zoom_level,
tp_tasks[n], // final TpTask [] tp_tasks,
tilesX, // final int tilesX,
......@@ -1345,7 +1363,7 @@ public class OrthoMapsCollection implements Serializable{
elevations[n], // double [] elev,
tilesX, // int width,
ground_planes_metric[n], // double [] plane_metric, // tiltx,tilty, offs - in meters
metric_error); // double metric_error);
metric_error_adj); // double metric_error);
}
ground_planes_weight = new double[ ground_planes_masks[0].length];
int num_left = 0; // , num_was = tp_tasks[0].length;
......@@ -1361,7 +1379,9 @@ public class OrthoMapsCollection implements Serializable{
}
if (num_left < tp_tasks[0].length/4) {
num_elevations--;
if (debugLevel > -4) {
System.out.println("too few tiles remain, try again. metric_error="+metric_error);
}
pairwiseOrthoMatch.rms = Double.NaN; // failed
return null;
}
......@@ -1403,6 +1423,9 @@ public class OrthoMapsCollection implements Serializable{
max_err, // final double max_err,
tilesX, // final int width, // tilesX
tile_woi); //final Rectangle twoi); // in tiles
if (nvf_mode == 0) {
vf_threshold = threshold; // if it drops significantly - ignore rms increase
}
if (debugLevel>-3) {
System.out.println("vector_field layer= "+nvf_mode+" error threshold = "+threshold+" pix.");
}
......@@ -1466,7 +1489,7 @@ public class OrthoMapsCollection implements Serializable{
// may use tl_rect_metric to remap to the original image
double [][] tile_centers = new double [vector_field[0].length][];
tile_centers = new double [vector_field[0].length][];
for (TpTask task: tp_tasks[1]) {
int ti = task.getTileY() * tilesX + task.getTileX();
tile_centers[ti] = task.getDoubleCenterXY();
......@@ -1501,7 +1524,7 @@ public class OrthoMapsCollection implements Serializable{
}
boolean origin_center = true; // false - old mode
OrthoPairLMA orthoPairLMA = new OrthoPairLMA(origin_center);
orthoPairLMA = new OrthoPairLMA(origin_center);
// vector_field[1] - neighbors
double lambda = 0.1;
double lambda_scale_good = 0.5;
......@@ -1520,12 +1543,13 @@ public class OrthoMapsCollection implements Serializable{
tilesX, // int width,
vector_field[1], // double [][] vector_XYS, // optical flow X,Y, confidence obtained from the correlate2DIterate()
tile_centers, // double [][] centers, // tile centers (in pixels)
null, // double [] weights_extra, // optional, may be null
ground_planes_weight, // null, // double [] weights_extra, // optional, may be null
true, // boolean first_run,
min_good_tiles, // int min_good_tiles,
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)
tile_rad , // double tile_rad,
min_tiles_rad, // int min_tiles_rad,
debugLevel); // final int debug_level)
last_center_radius = orthoPairLMA.getCenterRadius();
double center_radius_increase =
......@@ -1574,6 +1598,17 @@ public class OrthoMapsCollection implements Serializable{
if (debugLevel > -3) {
System.out.println("RMS= "+rms+" ("+orthoPairLMA.getInitialRms()+")");
}
if (max_rms_iter != null) {
int max_rms_index = Math.min(max_rms_iter.length-1, ntry);
if (rms > max_rms_iter[max_rms_index]) {
if (debugLevel > -3) {
System.out.println("RMS= "+rms+" > max_rms_iter["+
max_rms_index+"] = "+max_rms_iter[max_rms_index]);
}
return null;
}
}
if (rms > prev_rms) {
if (debugLevel > -3) {
if ((rms-prev_rms)/prev_rms < rel_improve) {
......@@ -1593,14 +1628,22 @@ public class OrthoMapsCollection implements Serializable{
previous_center_radius+" to "+ last_center_radius +" (more than "+
center_radius_change+"x).");
}
} else {
if (vf_threshold < (vf_drop_big * previous_vf_threshold)) {
if (debugLevel > -3) {
System.out.println("Will continue, as vf_threshold dropped from "+
previous_vf_threshold+" to "+ vf_threshold +" (more than "+
vf_drop_big+"x).");
}
} else {
break;
}
}
}
}
affines_gpu[1]=OrthoMap.combineAffine(affines_gpu[1], orthoPairLMA.getAffine());
jtj = orthoPairLMA.getLastJtJ();
if ((prev_rms - rms)/prev_rms < rel_improve) {
if ((rms <= prev_rms) && ((prev_rms - rms)/prev_rms < rel_improve)) {
if (debugLevel > -2) {
System.out.println("LMA relative RMSE improvement = "+((prev_rms - rms)/prev_rms)+" < "+rel_improve+", exiting.");
}
......@@ -1615,11 +1658,19 @@ public class OrthoMapsCollection implements Serializable{
previous_center_radius+" to "+ last_center_radius +" (more than "+
center_radius_change+"x).");
}
} else {
if (vf_threshold < (vf_drop_big * previous_vf_threshold)) {
if (debugLevel > -3) {
System.out.println("Will continue, as vf_threshold dropped from "+
previous_vf_threshold+" to "+ previous_vf_threshold +" (more than "+
vf_drop_big+"x).");
}
} else {
break;
}
}
}
}
if (center_radius_increase > center_radius_change) {
num_tries ++;
......@@ -1633,8 +1684,20 @@ public class OrthoMapsCollection implements Serializable{
prev_rms=rms;
previous_center_radius = last_center_radius;
previous_vf_threshold = vf_threshold;
} // for (int ntry = 0; ntry < num_tries; ntry++) {
// recheck here
// double tile_rad = rad_fraction * 0.5*Math.min(woi.width, woi.height)/GPUTileProcessor.DTT_SIZE;
if (num_tries > 0) {
min_tiles_rad = (int) (fill_fraction_final * 4 * tile_rad * tile_rad);
last_center_radius = orthoPairLMA.getCenterRadius(
max_std, // final double max_std, // maximal standard deviation to limit center area
tile_rad, // final double min_radius,
min_tiles_rad, // final int min_tiles,
vector_field[1], // final double [][] vector_XYS,
ground_planes_weight, //, // null, // final double [] weights_extra, // null or additional weights (such as elevation-based)
tile_centers); // final double [][] centers)
if ((min_std_rad > 0) && (last_center_radius < min_std_rad)) {
if (debugLevel>-3) {
......@@ -1643,10 +1706,7 @@ public class OrthoMapsCollection implements Serializable{
}
return null;
}
}
if (pairwiseOrthoMatch != null) {
pairwiseOrthoMatch.jtj = jtj;
......@@ -3844,6 +3904,13 @@ public class OrthoMapsCollection implements Serializable{
int [] sel_50 = {125,126,127,129,135,136,137,138,139,140,141,144,145,146,147,148,155,156,157,158};
int [] sel_75 = {170,177,178,183,187,188,189,192,193,199,200,204};
int [] sel_100 = {207,208,212,213,214,215,218,220};
int [] range_50_evn= { 0, 48};
int [] range_25_mor= { 49,124};
int [] range_50_mor= {125,164};
int [] range_75_mor= {166,204};
int [] range_100_mor= {205,221};
int [][] ranges = {null,null,null,null,range_50_evn,range_25_mor,range_50_mor,range_75_mor,range_100_mor};
boolean [] selection = new boolean [lines.length];
if (pre_selects != null) {
if (pre_selects[0]) {
......@@ -3864,23 +3931,34 @@ public class OrthoMapsCollection implements Serializable{
selection[i] = true;
}
}
for (int n = 4; n < 9; n++) if (pre_selects[n]){
for (int i = ranges[n][0]; i<=ranges[n][1]; i++) {
selection[i] = true;
}
}
}
pre_selects = new boolean[4];
pre_selects = new boolean[9];
GenericJTabbedDialog gd = new GenericJTabbedDialog("Select scenes",1200,1000);
gd.addCheckbox ("Select all maps", pre_selects[0], "Select all scenes, reopen this dialog.");
gd.addCheckbox ("Select 50m used maps", pre_selects[1], "Select 50m scenes, reopen this dialog.");
gd.addCheckbox ("Select 75m used maps", pre_selects[2], "Select 50m scenes, reopen this dialog.");
gd.addCheckbox ("Select 100m used maps", pre_selects[3], "Select 50m scenes, reopen this dialog.");
gd.addCheckbox ("Select all 50m evening", pre_selects[4], "Select all 50m evening scenes, reopen this dialog.");
gd.addCheckbox ("Select all 25m morning", pre_selects[5], "Select all 25m morning scenes, reopen this dialog.");
gd.addCheckbox ("Select all 50m morning", pre_selects[6], "Select all 50m morning scenes, reopen this dialog.");
gd.addCheckbox ("Select all 75m morning", pre_selects[7], "Select all 75m morning scenes, reopen this dialog.");
gd.addCheckbox ("Select all 100m morning", pre_selects[8], "Select all 100m morning scenes, reopen this dialog.");
for (int i = 0; i < lines.length; i++) {
gd.addCheckbox (lines[i], selection[i], "Select scene number "+i);
}
gd.showDialog();
if (gd.wasCanceled()) return null;
pre_selects[0]= gd.getNextBoolean();
pre_selects[1]= gd.getNextBoolean();
pre_selects[2]= gd.getNextBoolean();
pre_selects[3]= gd.getNextBoolean();
if (pre_selects[0] ||pre_selects[1] || pre_selects[2] || pre_selects[3]) {
boolean presel_any = false;
for (int i = 0; i < pre_selects.length; i++) {
pre_selects[i]= gd.getNextBoolean();
presel_any |= pre_selects[i];
}
if (presel_any) {
return getScenesSelection(pre_selects, purpose);
}
int num_sel = 0;
......@@ -3899,26 +3977,31 @@ public class OrthoMapsCollection implements Serializable{
}
public boolean getIntersectedPairs(
int debugLevel) {
CLTParameters clt_parameters,
String orthoMapsCollection_path) {
int [] indices = getScenesSelection(
null, // boolean select_all,
" to find intersects"); // String purpose)
if (indices == null) {
return false;
}
int zoom_lev = -5;
double min_overlap = 0.25;
int zoom_lev = clt_parameters.imp.pwise_zoom; // -5;
double min_overlap_frac = clt_parameters.imp.pwise_overlap; // 0.25;
boolean bounds_to_indices = true;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Combo map/stats generation",1200,1000);
int debugLevel = clt_parameters.imp.pwise_debug;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Get scene intersections",1200,300);
gd.addNumericField("Zoom level", zoom_lev, 0,4,"",
"Zoom level: +1 - zoom in twice, -1 - zoom out twice");
gd.addNumericField("Minimal overlap", min_overlap, 3,7,"",
gd.addNumericField("Minimal overlap", min_overlap_frac, 3,7,"",
"Minimal overlap as a fraction of the smaller image.");
gd.addNumericField("Pairwise match debug level", debugLevel, 0,3,"","Debug level during Spiral search.");
gd.showDialog();
if (gd.wasCanceled()) return false;
zoom_lev = (int) gd.getNextNumber();
min_overlap = gd.getNextNumber();
min_overlap_frac = gd.getNextNumber();
debugLevel= (int) gd.getNextNumber();
int [] wh = new int[2];
int [] origin = new int[2];
double [][] centers =new double [indices.length][];
......@@ -3937,9 +4020,9 @@ public class OrthoMapsCollection implements Serializable{
dmulti); // final double [][] dmulti) {
int num_overlaps_all = 0, num_overlaps_sel = 0;
boolean [][] intersects = new boolean [dmulti.length][dmulti.length];
for (int i = 0; i < overlaps.length; i++) if (!Double.isNaN(overlaps[i])){
for (int i = 0; i < overlaps.length; i++) if (overlaps[i] > 0){
num_overlaps_all++;
if (overlaps[i] >= min_overlap) {
if (overlaps[i] >= min_overlap_frac) {
num_overlaps_sel++;
int row = i / dmulti.length;
int col = i % dmulti.length;
......@@ -3951,7 +4034,7 @@ public class OrthoMapsCollection implements Serializable{
groups[i] = i;
}
for (int i = 0; i< (groups.length-1); i++) {
for (int j = i+1; j < groups.length; j++) if (groups[j] != groups[i]){
for (int j = i+1; j < groups.length; j++) if (intersects[i][j] && (groups[j] != groups[i])){
int g0 = groups[i];
int g1 = groups[j];
for (int k = 0; k < groups.length; k++) {
......@@ -3967,11 +4050,500 @@ public class OrthoMapsCollection implements Serializable{
}
System.out.println("getIntersectedPairs(): num_overlaps_all="+num_overlaps_all+
", num_overlaps_sel="+num_overlaps_sel+", number of disconnected groups="+hs.size());
if (debugLevel > 0) {
if (debugLevel > -4) {
ShowDoubleFloatArrays.showArrays(
overlaps,
"Overlaps");
}
boolean matches_ok= generatePairwiseMatches(
clt_parameters, // CLTParameters clt_parameters,
min_overlap_frac, // double min_overlap_frac
indices, // int [] indices,
overlaps, // double [] overlaps,
// intersects, // boolean [][] boverlaps)
orthoMapsCollection_path); // String orthoMapsCollection_path
return matches_ok;
}
public boolean generatePairwiseMatches(
CLTParameters clt_parameters,
double min_overlap_frac,
int [] indices,
double [] overlaps,
// boolean [][] boverlaps,
String orthoMapsCollection_path) {
// gen number of pairs:
int num_scenes = indices.length;
int num_pairs = 0;
int num_new = 0;
for (int i = 0; i < indices.length-1; i++) {
for (int j = i+1; j < indices.length; j++) if (overlaps[i * indices.length +j] >= min_overlap_frac){
num_pairs++;
int [] ipair = {indices[i], indices[j]};
if ((ortho_maps[ipair[0]].getMatch(ortho_maps[ipair[1]].getName()) == null) &&
(ortho_maps[ipair[1]].getMatch(ortho_maps[ipair[0]].getName()) == null)){
num_new++;
}
}
}
boolean skip_exist = clt_parameters.imp.pwise_skip_exist; //
boolean refine_exist = clt_parameters.imp.pwise_refine_exist; // if false, start from scratch, true - start from previous
boolean delete_failed = clt_parameters.imp.pwise_delete_fail; // delete existing match if now failed
boolean gen_inverse = clt_parameters.imp.pwise_gen_inverse; // generate inverse matches
boolean save_each = clt_parameters.imp.pwise_save_each; // save state file after each match
boolean log_append = clt_parameters.imp.pwise_log_append; //
String log_path = clt_parameters.imp.pwise_log_path; //
int debugLevel = clt_parameters.imp.pwise_debug; //
//Initial spiral search for image matching
double search_step = clt_parameters.imp.ospir_step; // 8.0; // pix
double search_range = clt_parameters.imp.ospir_range; // 50.0; // pix
double double_threshold = clt_parameters.imp.ospir_double; //
double good_rms = clt_parameters.imp.ospir_good_rms; // 0.27; //
double max_rms = clt_parameters.imp.ospir_max_rms; // 0.35; //
int min_overlap = clt_parameters.imp.ospir_overlap; // 3000; // do not try to match if there is too small overlap (scaled pixels)
int num_iter_lma = clt_parameters.imp.ospir_num_iter; // 5;
double [] max_rms_iter = clt_parameters.imp.ospir_rms_iter; // {1.0, 0.6};//
boolean spiral_ignore_rms = clt_parameters.imp.ospir_ignore_rms; // false
int spiral_debug = clt_parameters.imp.ospir_debug; // 0;
//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
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;
int num_tries = clt_parameters.imp.pmtch_num_iter;// 10;
double rad_fraction = clt_parameters.imp.pmtch_cent_rad; // center circle radius fraction of 0.5* min(width, height) in tiles
double max_tile_rad = clt_parameters.imp.pmtch_max_cent_rad;// maximal center radius in tiles (limit pmtch_cent_rad)
double fill_fraction = clt_parameters.imp.pmtch_cent_fill; // should be populated not less than this
double fill_fraction_final = clt_parameters.imp.pmtch_cent_final; // should be populated not less than this during final pass
double ease_nosfm = clt_parameters.imp.pmtch_ease_nosfm; // ease metric_error when no SfM gain == 0;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Pairwise Match Parameters",1200,1000);
gd.addMessage("Number of scenes - "+num_scenes+
", number of pairs (w/o inverse) - "+num_pairs+
", number of new pairs - "+num_new);
gd.addCheckbox ("Skip existing", skip_exist, "Do not regenerate if match with same or higher resolution exists.");
gd.addCheckbox ("Refine existing", refine_exist, "Refine existing matches (false - start from scratch with spiral search).");
gd.addCheckbox ("Delete failed", delete_failed, "Delete previous matches if it failed now.");
gd.addCheckbox ("Generate inverse matches", gen_inverse, "Generate (refine if exist and enabled) inverse matches.");
gd.addCheckbox ("Save state after each match", save_each, "Update state file after each match generation to mitigate possible crashes.");
gd.addCheckbox ("Write log file", log_append, "Enable writing log file with matching results.");
gd.addStringField ("Log file full path", log_path, 150, "Path of the log file to be appended.");
gd.addNumericField("Pairwise match debug level", debugLevel, 0,3,"","Debug level during Spiral search.");
gd.addMessage ("Initial spiral search for image matching");
gd.addNumericField("Spiral search step", search_step, 3,7,"scaled pix", "Distance between spiral search probes, in scaled pixels.");
gd.addNumericField("Spiral search radius", search_range, 3,7,"scaled pix", "Maximal radius of the spiral search, in scaled pixels.");
gd.addNumericField("Mitigate small overlap", double_threshold, 3,7,"","For small overlaps increase zoom by 1 and range - twice.");
gd.addNumericField("RMSE to end search", good_rms, 3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels.");
gd.addNumericField("Satisfactory RMSE", max_rms, 3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels.");
gd.addNumericField("Minimal overlap", min_overlap, 0,4,"scaled pix ^ 2","Minimal overlap area in square scaled pixels.");
gd.addNumericField("LMA iterations", num_iter_lma, 0,2,"", "Number of LMA iterations during spiral search.");
gd.addNumericField("RMSE at first iteration", max_rms_iter[0], 3,7,"scaled pix","Maximal RMSE at first iteration.");
gd.addNumericField("RMSE at second iteration", max_rms_iter[1], 3,7,"scaled pix","Maximal RMSE at second iteration.");
gd.addCheckbox ("Ignore worsening RMSE", spiral_ignore_rms, "Ignore worsening/not improving RMSE during spiral search.");
gd.addNumericField("Spiral search debug level", spiral_debug, 0,3,"","Debug level during Spiral search.");
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.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.");
gd.addNumericField("Number of fitting iterations", num_tries, 0,3,"","number of full fittng iterations.");
gd.addNumericField("Central area radius as fraction", rad_fraction, 3,7,"", "Central area radius as fraction of half minimal WOI dimension.");
gd.addNumericField("Maximal central area radius", max_tile_rad, 3,7,"tiles", "Absolute limit to the center area radius (eases bad peripheral matching).");
gd.addNumericField("Central area minimal fill", fill_fraction, 3,7,"", "Central area minimal fill for all but the last iteration.");
gd.addNumericField("Central area minimal fill final", fill_fraction_final, 3,7,"", "Central area minimal fill for the last iteration.");
gd.addNumericField("Relax metric error for no-SfM", ease_nosfm, 3,7,"", "Relax metric error for no-SfM scenes (sfm_gain==0).");
gd.showDialog();
if (gd.wasCanceled()) return false;
skip_exist = gd.getNextBoolean();
refine_exist = gd.getNextBoolean();
delete_failed = gd.getNextBoolean();
gen_inverse = gd.getNextBoolean();
save_each = gd.getNextBoolean();
log_append = gd.getNextBoolean();
log_path = gd.getNextString();
debugLevel = (int) gd.getNextNumber();
search_step = gd.getNextNumber();
search_range = gd.getNextNumber();
double_threshold = gd.getNextNumber();
good_rms = gd.getNextNumber();
max_rms = gd.getNextNumber();
min_overlap = (int) gd.getNextNumber();
num_iter_lma = (int) gd.getNextNumber();
max_rms_iter[0] = gd.getNextNumber();
max_rms_iter[1] = gd.getNextNumber();
spiral_ignore_rms= gd.getNextBoolean();
spiral_debug = (int) gd.getNextNumber();
frac_remove = gd.getNextNumber();
metric_err = gd.getNextNumber();
max_std = gd.getNextNumber();
min_std_rad = gd.getNextNumber();
ignore_prev_rms = gd.getNextBoolean();
num_tries = (int) gd.getNextNumber();
rad_fraction = gd.getNextNumber();
max_tile_rad = gd.getNextNumber();
fill_fraction = gd.getNextNumber();
fill_fraction_final= gd.getNextNumber();
ease_nosfm = gd.getNextNumber();
return generatePairwiseMatches(
clt_parameters, // CLTParameters clt_parameters,
min_overlap_frac, // double min_overlap_frac,
indices, // int [] indices,
overlaps, // double [] overlaps,
skip_exist, // boolean skip_existing,
refine_exist, // boolean refine_existing, // if false, start from scratch, true - start from previous
delete_failed, // boolean delete_failed, // if false, start from scratch, true - start from previous
gen_inverse, // boolean gen_inverse, // generate inverse matches
save_each, // boolean save_each, // save state file after each match
log_append, // boolean log_append, //
log_path, // String log_path,
orthoMapsCollection_path, // String orthoMapsCollection_path
search_step, // double search_step,
search_range, // double search_range,
good_rms, // double good_rms,
max_rms, // double max_rms,
min_overlap, // int min_overlap,
num_iter_lma, // int num_iter_lma,
spiral_ignore_rms, // boolean ignore_rms,
spiral_debug, // int spiral_debug,
frac_remove, // double frac_remove,
metric_err, // double metric_error,
max_std, // double max_std,
min_std_rad, // double min_std_rad,
ignore_prev_rms, // boolean ignore_prev_rms,
num_tries, // int num_tries,
rad_fraction, // double rad_fraction,
max_tile_rad, // double max_tile_rad,
fill_fraction, // double fill_fraction,
fill_fraction_final, // double fill_fraction_final,
ease_nosfm, // double ease_nosfm,
double_threshold, // double double_threshold,
max_rms_iter, // double [] max_rms_iter,
debugLevel); // int debugLevel
}
public boolean generatePairwiseMatches(
CLTParameters clt_parameters,
double min_overlap_frac,
int [] indices,
double [] overlaps,
boolean skip_existing,
boolean refine_existing, // if false, start from scratch, true - start from previous
boolean delete_failed, // if false, start from scratch, true - start from previous
boolean gen_inverse, // generate inverse matches
boolean save_each, // save state file after each match
boolean log_append, //
String log_path,
String orthoMapsCollection_path,
double search_step,
double search_range_in,
double good_rms,
double max_rms,
int min_overlap,
int num_iter_lma,
boolean ignore_rms,
int spiral_debug,
double frac_remove,
double metric_error,
double max_std,
double min_std_rad,
boolean ignore_prev_rms,
int num_tries,
double rad_fraction,
double max_tile_rad,
double fill_fraction,
double fill_fraction_final,
double ease_nosfm,
double double_threshold,
double [] max_rms_iter,
int debugLevel) {
boolean batch_mode = true;
int num_scenes = indices.length;
int num_pairs = 0;
int num_new = 0;
ArrayList<Point> pairs = new ArrayList<Point>();
for (int i = 0; i < indices.length-1; i++) {
for (int j = i+1; j < indices.length; j++) if (overlaps[i * indices.length +j] >= min_overlap_frac){
pairs.add(new Point(i,j));
if (gen_inverse) {
pairs.add(new Point(j,i));
}
num_pairs++;
int [] ipair = {indices[i], indices[j]};
if ((ortho_maps[ipair[0]].getMatch(ortho_maps[ipair[1]].getName()) == null) &&
(ortho_maps[ipair[1]].getMatch(ortho_maps[ipair[0]].getName()) == null)){
num_new++;
}
}
}
if (log_append && (log_path != null)) { // assuming directory exists
StringBuffer sb = new StringBuffer();
sb.append(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime())+"\n");
sb.append("num_scenes\t"+ num_scenes+"\n");
sb.append("pairs.size()\t"+pairs.size()+"\n");
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"));
CalibrationFileManagement.saveStringToFile (
log_path, //String path,
sb.toString(), // data,
true); // boolean append)
if (debugLevel>-3) {
System.out.print(sb.toString());
}
}
ArrayList<Point> failed_pairs = new ArrayList<Point>();
ArrayList<Point> new_pairs = new ArrayList<Point>(); // started from spiral, not from the same or inverse
for (Point pair:pairs) {
int [] ipair = {indices[pair.x], indices[pair.y]};
boolean direct = indices[pair.x] < indices[pair.y];
int min_zoom_lev = ortho_maps[ipair[0]].getOriginalZoomLevel();
int max_zoom_lev = ortho_maps[ipair[0]].getOriginalZoomLevel();
double max_agl = ortho_maps[ipair[0]].getAGL();
for (int i = 0; i < ipair.length; i++) {
max_agl = Math.max(max_agl, ortho_maps[ipair[i]].getAGL());
min_zoom_lev = Math.min(min_zoom_lev, ortho_maps[ipair[i]].getOriginalZoomLevel());
max_zoom_lev = Math.max(max_zoom_lev, ortho_maps[ipair[i]].getOriginalZoomLevel());
}
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?
// overlaps
int overlap_indx = direct ? (pair.x * indices.length + pair.y) : (pair.y * indices.length + pair.x);
double overlap_frac = overlaps[overlap_indx];
double search_range = search_range_in;
while (overlap_frac < double_threshold) {
overlap_frac *= 2;
initial_zoom += 1;
search_range *= 2;
}
PairwiseOrthoMatch direct_match = ortho_maps[ipair[0]].getMatch(ortho_maps[ipair[1]].getName());
PairwiseOrthoMatch inv_match = ortho_maps[ipair[1]].getMatch(ortho_maps[ipair[0]].getName());
boolean use_direct = false;
boolean use_inverse = false;
boolean skip = false;
if (!direct && (inv_match != null)) { // prefer inverse
use_inverse = true;
} else if (direct_match != null) {
if (skip_existing) {
skip = true;
} else if (refine_existing) {
use_direct = true;
} // else will start from scratch
} else if (inv_match != null) {
if (direct && refine_existing) {
use_inverse = true;
}
}
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",
ipair[0], ipair[1],
ortho_maps[ipair[0]].getName(), ortho_maps[ipair[1]].getName(),
overlaps[overlap_indx], initial_zoom,
direct_match.rms));
CalibrationFileManagement.saveStringToFile (
log_path, //String path,
sb.toString(), // data,
true); // boolean append)
}
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
double [][][] affines = new double[][][] {affine0,affine1};
double spiral_rms = Double.NaN;
if (use_direct) {
pairwiseOrthoMatch=direct_match;
} else if (use_inverse) {
double [] enuOffset = ortho_maps[ipair[0]].enuOffsetTo(ortho_maps[ipair[1]]);
double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image
// create inverted pairwiseOrthoMatch
pairwiseOrthoMatch = inv_match.getInverse(rd);
} else { // run spiral search
// now always unity, but after partial adjustment may be non-unity
// TODO: Not yet tested with intermediate adjustment.
pairwiseOrthoMatch = SpiralMatch (
clt_parameters, // CLTParameters clt_parameters,
frac_remove, // double frac_remove, // = 0.25
metric_error_adj,// double metric_error,
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,
max_tile_rad, // double max_tile_rad, // = 30;
fill_fraction, // double fill_fraction,
fill_fraction_final, // double fill_fraction_final,
ease_nosfm, // double ease_nosfm,
ipair, // int [] gpu_pair,
affines, // double [][][] affines_init, // here in meters, relative to vertical points
initial_zoom, // int zoom_lev,
search_step, // double pix_step,
search_range, // double pix_range,
good_rms, // double good_rms,
max_rms, // double max_rms,
num_iter_lma, // int num_tries, // = 5
min_overlap, // int min_overlap, // 3000
ignore_rms, // boolean ignore_rms,
max_rms_iter, // double [] max_rms_iter, // = {1.0, 0.6};//
spiral_debug); // int debugLevel)
if ((pairwiseOrthoMatch == null) || Double.isNaN(pairwiseOrthoMatch.rms)) {
if (delete_failed) {
ortho_maps[ipair[0]].unsetMatch(ortho_maps[ipair[1]].getName());
}
String str_failed = delete_failed?"%4d\t%4d\t%s\t%s\t%6.4f\t%3d\tFAILED\tREMOVED\n":"%4d\t%4d\t%s\t%s\t%6.4f\t%3d\tFAILED\n";
if (log_append && (log_path != null)) { // assuming directory exists
StringBuffer sb = new StringBuffer();
sb.append(String.format(str_failed,
ipair[0], ipair[1], ortho_maps[ipair[0]].getName(), ortho_maps[ipair[1]].getName(),
overlaps[overlap_indx], initial_zoom));
CalibrationFileManagement.saveStringToFile (
log_path, //String path,
sb.toString(), // data,
true); // boolean append)
}
failed_pairs.add(pair);
continue;
} else {
spiral_rms = pairwiseOrthoMatch.rms;
new_pairs.add(pair); // adjusted with spiral
}
}
// refine match with high resolution
affines[1] = pairwiseOrthoMatch.getAffine();
affines[1][0] = affines[1][0].clone();
affines[1][1] = affines[1][1].clone();
Rectangle woi = new Rectangle(); // used to return actual woi from correlateOrthoPair()
correlateOrthoPair(
clt_parameters, // CLTParameters clt_parameters,
pairwiseOrthoMatch, //PairwiseOrthoMatch pairwiseOrthoMatch, // will return statistics
0, // int min_overlap,
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)
frac_remove, // double frac_remove, // = 0.25
metric_error_adj,// double metric_error,
ignore_prev_rms, // boolean ignore_prev_rms,
num_tries, // = 5int num_tries, // = 5
false, // , // boolean calc_warp, (will return null if false)
batch_mode, // boolean batch_mode,
ipair, // String [] gpu_spair,
affines, // double [][][] affines, // on top of GPS offsets
woi, // Rectangle woi,
min_zoom_lev, // int zoom_lev,
false, // show_vf, // boolean show_vf,
null, // ground_planes, // double [][] ground_planes, // null or double[2] - will return ground planes
rad_fraction, // double rad_fraction,
max_tile_rad, // double max_tile_rad, // = 30;
fill_fraction, // double fill_fraction,
fill_fraction_final, // double fill_fraction_final,
ease_nosfm, // double ease_nosfm,
null, // double [] max_rms_iter, // = {1.0, 0.6};//
debugLevel-4); // final int debugLevel)
boolean failed_refine = Double.isNaN(pairwiseOrthoMatch.rms);
// Create log line and write it
StringBuffer sb = new StringBuffer();
sb.append(String.format("%4d\t%4d\t%s\t%s\t%6.4f\t%3d",
ipair[0], ipair[1], ortho_maps[ipair[0]].getName(), ortho_maps[ipair[1]].getName(),
overlaps[overlap_indx], initial_zoom));
if (use_direct || use_inverse) {
sb.append("\t\t\t");
} else {
sb.append(String.format("\t%3d\t%3d\t%6.4f",
pairwiseOrthoMatch.nxy[0],pairwiseOrthoMatch.nxy[1],spiral_rms));
}
if (failed_refine) {
if (delete_failed) {
ortho_maps[ipair[0]].unsetMatch(ortho_maps[ipair[1]].getName());
}
String str_failed = delete_failed?"\tFAILED\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];
ortho_maps[ipair[0]].setMatch(ortho_maps[ipair[1]].getName(),pairwiseOrthoMatch);
if (save_each && (orthoMapsCollection_path != null)) {
try {
writeOrthoMapsCollection(orthoMapsCollection_path);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (debugLevel > -4) {
System.out.println("Saved data to "+ orthoMapsCollection_path);
}
}
}
if (log_append && (log_path != null)) { // assuming directory exists
CalibrationFileManagement.saveStringToFile (
log_path, //String path,
sb.toString(), // data,
true); // boolean append)
}
// save if save_each
// does it have direct or inverted pair
}
if (log_append && (log_path != null)) { // assuming directory exists
StringBuffer sb = new StringBuffer();
sb.append("\nSUMMARY\n");
sb.append("good\t"+(pairs.size()-failed_pairs.size())+"\n");
sb.append("new\t"+ new_pairs.size()+"\n");
sb.append("failed\t"+failed_pairs.size()+"\n");
if (!failed_pairs.isEmpty()) {
for (int i = 0; i < failed_pairs.size(); i++) {
int [] ipair = {indices[failed_pairs.get(i).x], indices[failed_pairs.get(i).y]};
sb.append(String.format("%4d\t%4d\t%s\t%s\n",
ipair[0], ipair[1], ortho_maps[ipair[0]].getName(), ortho_maps[ipair[1]].getName()));
}
}
CalibrationFileManagement.saveStringToFile (
log_path, //String path,
sb.toString(), // data,
true); // boolean append)
if (debugLevel > -4) {
System.out.println("Appended log file "+ log_path);
}
}
if (orthoMapsCollection_path != null) {
try {
writeOrthoMapsCollection(orthoMapsCollection_path);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (debugLevel > -4) {
System.out.println("Saved data to "+ orthoMapsCollection_path);
}
}
return true;
}
......
......@@ -27,6 +27,7 @@
package com.elphel.imagej.orthomosaic;
import java.awt.Rectangle;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import com.elphel.imagej.common.ShowDoubleFloatArrays;
......@@ -105,6 +106,8 @@ public class OrthoPairLMA {
int min_good_tiles,
double max_std, // maximal standard deviation to limit center area
double min_std_rad, // minimal radius of the central area (if less - fail)
double tile_rad,
int min_tiles_rad,
final int debug_level) {
tile_centers = centers;
this.width = width;
......@@ -127,16 +130,17 @@ public class OrthoPairLMA {
origin = new double[2];
if (origin_center) {
origin = new double [] {
woi.x + 0.5 * woi.width * GPUTileProcessor.DTT_SIZE,
woi.y + 0.5 * woi.height * GPUTileProcessor.DTT_SIZE};
(woi.x + 0.5 * woi.width) * GPUTileProcessor.DTT_SIZE,
(woi.y + 0.5 * woi.height) * GPUTileProcessor.DTT_SIZE};
}
N = woi.width * woi.height;
parameters_vector = new double [] {1,0,0,1,0,0};
if (min_std_rad > 0) {
int min_tiles = 4;
// int min_tiles = 4;
getCenterRadius(
max_std, // final double max_std, // maximal standard deviation to limit center area
min_tiles, // final int min_tiles,
tile_rad, // min_std_rad, // final double min_radius,
min_tiles_rad, // min_tiles, // final int min_tiles,
vector_XYS, // final double [][] vector_XYS,
weights_extra, // final double [] weights_extra, // null or additional weights (such as elevation-based)
centers); // final double [][] centers)
......@@ -251,6 +255,11 @@ public class OrthoPairLMA {
* In that case try to adjust only the central area first, then increase that area
* in next iterations.
* @param max_std maximal standard deviation (average for X and Y) inside center area
* @param min_radius minimal central zone radius that has to have >= min_tiles.
* Should be increased (with min_tiles) for high resolution images.
* Also there can be a bush right in the center - need to handle it too.
* Maybe for the final it should be a fraction of the minimal overlap
* dimension?
* @param min_tiles minimal tiles in the center zone
* @param vector_XYS 2D correlation-measured X,Y, and strength
* @param weights_extra null or optional additional weights of the samples
......@@ -258,9 +267,10 @@ public class OrthoPairLMA {
* @return maximal radius from the center (in pixels) where standard deviation of the inside samples is
* below max_std. Returns Double.POSITIVE_INFINITY if std is not reached
*/
private double getCenterRadius(
public double getCenterRadius(
final double max_std, // maximal standard deviation to limit center area
// final double min_std_rad, // minimal radius of the central area (if less - fail)
final double min_radius,
final int min_tiles,
final double [][] vector_XYS,
final double [] weights_extra, // null or additional weights (such as elevation-based)
......@@ -276,7 +286,53 @@ public class OrthoPairLMA {
final double [][] sy_arr = new double [threads.length][rad_length];
final double [][] sy2_arr = new double [threads.length][rad_length];
final int [][] sn_arr = new int [threads.length][rad_length];
boolean dbg = false;
if (dbg) {
String [] titles = {"fx","fy","vw","w","cent-x","cent-y","dx","dy","r_t","irt"};
final double [][] dbg_img = new double [titles.length][woi.width*woi.height];
for (int i = 0; i < dbg_img.length; i++) {
Arrays.fill(dbg_img[i], Double.NaN);
}
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int iTile = ai.getAndIncrement(); iTile < N; iTile = ai.getAndIncrement()) {
int tileX = iTile % woi.width + woi.x;
int tileY = iTile / woi.width + woi.y;
int aTile = tileY * width + tileX;
if ((vector_XYS[aTile] != null) && (centers[aTile] != null)) {
double w = vector_XYS[aTile][2];
if (weights_extra != null) w *= weights_extra[aTile];
if (Double.isNaN(w)) w = 0;
double dx = centers[aTile][0] - origin[0];
double dy = centers[aTile][1] - origin[1];
double r_t = Math.sqrt(dx*dx+dy*dy)/GPUTileProcessor.DTT_SIZE; // radius in tiles
int irt = (int) Math.round(r_t);
dbg_img[0][iTile] = vector_XYS[aTile][0];
dbg_img[1][iTile] = vector_XYS[aTile][1];
dbg_img[2][iTile] = vector_XYS[aTile][2];
dbg_img[3][iTile] = w;
dbg_img[4][iTile] = centers[aTile][0];
dbg_img[5][iTile] = centers[aTile][1];
dbg_img[6][iTile] = dx;
dbg_img[7][iTile] = dy;
dbg_img[8][iTile] = r_t;
dbg_img[9][iTile] = irt;
}
}
}
};
}
ImageDtt.startAndJoin(threads);
ai.set(0);
ShowDoubleFloatArrays.showArrays(
dbg_img,
woi.width,
woi.height,
true,
"getCenterRadius",
titles);
}
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
......@@ -335,6 +391,7 @@ public class OrthoPairLMA {
ImageDtt.startAndJoin(threads);
double sc0=0, scx=0,scx2=0,scy=0,scy2=0,std_prev=0, std=0;
int scn=0;
int scnc = 0;
center_radius = Double.POSITIVE_INFINITY;
for (int iRad=0; iRad < rad_length; iRad++) {
sc0+= s0 [iRad];
......@@ -343,7 +400,10 @@ public class OrthoPairLMA {
scy+= sy [iRad];
scy2+=sy2[iRad];
scn+= sn [iRad];
if ((scn > min_tiles) && (sc0 > 0)) {// for one tile gets scrt() of a small negative error
if (iRad <= ((int) Math.round(min_radius))) {
scnc = scn;
}
if ((scn > min_tiles) && (sc0 > 0)) {// for one tile gets sqcrt() of a small negative error
std = Math.sqrt((scx2*sc0 - scx*scx + scy2*sc0 - scy*scy)/2)/sc0;
}
if ((scn >= min_tiles) && (std >= max_std)) {
......@@ -356,6 +416,9 @@ public class OrthoPairLMA {
}
std_prev = std;
}
if ((min_tiles > 0) && (min_radius > 0) && (scnc < min_tiles)) {
center_radius = 0;
}
return center_radius;
}
......
......@@ -11,6 +11,7 @@ public class PairwiseOrthoMatch implements Serializable {
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 PairwiseOrthoMatch() {
}
......@@ -21,7 +22,8 @@ public class PairwiseOrthoMatch implements Serializable {
int zoom_lev) {
this.affine = affine;
this.jtj = jtj;
this.zoom_lev=zoom_lev;
this.zoom_lev= zoom_lev;
this.rms = rms;
}
public PairwiseOrthoMatch clone() {
double [][] affine = {this.affine[0].clone(),this.affine[1].clone()};
......@@ -34,6 +36,9 @@ public class PairwiseOrthoMatch implements Serializable {
jtj,
this.rms,
this.zoom_lev);
if (nxy != null) {
pom.nxy = nxy.clone();
}
return pom;
}
public PairwiseOrthoMatch getInverse(double [] rd) {
......
......@@ -104,21 +104,48 @@ public class IntersceneMatchParameters {
public double rln_radius_frac = 0.1; // rln_neib_radius not less than this fraction of woi width/height
public double rln_cent_radius = 4.0; // centroids center radius
public int rln_n_recenter = 2; // when cosine window, re-center window these many times
public double rln_sngl_rstr = 0.3; // minimal single-tile phase correlation maximums relative to max str
public double rln_neib_rstr = 0.4; // minimal neighbors phase correlation maximums relative to max str
public double rln_sngl_rstr = 0.25; // minimal single-tile phase correlation maximums relative to max str
public double rln_neib_rstr = 0.35; // minimal neighbors phase correlation maximums relative to max str
// Pairwise scenes matching
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
public double pmtch_min_std_rad = 2.0; // minimal radius of the central area (if less - fail)
public boolean pmtch_ignore_rms = true;
public int pmtch_num_iter = 10;
public double pmtch_cent_rad = 0.6; // center circle radius fraction of 0.5* min(width, height) in tiles
public double pmtch_max_cent_rad =40; // 30; // maximal center radius in tiles (limit pmtch_cent_rad)
public double pmtch_cent_fill = 0.2; // 25; // should be populated not less than this
public double pmtch_cent_final = 0.23; // 4; // should be populated not less than this
public double pmtch_ease_nosfm = 2.0; // ease metric_error when no SfM gain == 0;
// spiral search parameters
public boolean ospir_existing = false; // use existing pair
public boolean ospir_invert = false; // invert existing pair
public double ospir_step = 8.0; // spiral step (pix)
public double ospir_range = 50.0; // spiral radius (pix)
public double ospir_good_rms = 0.27; // maximal immediately acceptable LMA RMS for the initial search
public double ospir_double = 0.5; // if overlap < this, increase zoom by 1, range - twice
public double ospir_good_rms = 0.3; // 27; // maximal immediately acceptable LMA RMS for the initial search
public double ospir_max_rms = 0.35; // maximal acceptable LMA RMS - best during initial search
public int ospir_overlap = 3000; // do not try to match if there is too small overlap (scaled pixels)
public int ospir_num_iter = 5; // maximal number of LMA iterations during initial search
public double []ospir_rms_iter = {1.0, 0.6};// maximal rms per iteration
public boolean ospir_ignore_rms = false; // ignore RMS worsening during spiral search
public int ospir_debug = 0; // Debug level during sppiral search
// pairwise match parameters
public int pwise_zoom = -5;
public double pwise_overlap = 0.25;
public boolean pwise_skip_exist = true;
public boolean pwise_refine_exist = true; // if false, start from scratch, true - start from previous
public boolean pwise_delete_fail = true; // delete existing match if now failed
public boolean pwise_gen_inverse = false; // generate inverse matches
public boolean pwise_save_each = true; // save state file after each match
public boolean pwise_log_append = true; //
public String pwise_log_path = "/media/elphel/NVME/lwir16-proc/ortho_videos/debug/sept12-13/overlaps/pairwise_01.log";
public int pwise_debug = 0;
public double [] getImsMountATR() {
......@@ -708,21 +735,53 @@ public class IntersceneMatchParameters {
"Minimal single-tile phase correlation maximums relative to maximal strength.");
gd.addNumericField("Minimal relative strength (neighbors)", this.rln_neib_rstr, 5,8,"",
"Minimal neighbors phase correlation maximums relative to maximal strength.");
gd.addMessage ("Initial spiral search for image matching");
gd.addMessage ("Pairwise scenes matching");
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.");
gd.addNumericField("Central area minimal radius", this.pmtch_min_std_rad, 3,7,"tile", "Minimal radius of the central area after all LMA passes.");
gd.addCheckbox ("Ignore previous RMSE", this.pmtch_ignore_rms, "Do not exit full fitting cycles if the RMSE worsened/not improved.");
gd.addNumericField("Number of fitting iterations", this.pmtch_num_iter, 0,3,"","number of full fittng iterations.");
gd.addNumericField("Central area radius as fraction", this.pmtch_cent_rad, 3,7,"", "Central area radius as fraction of half minimal WOI dimension.");
gd.addNumericField("Maximal central area radius", this.pmtch_max_cent_rad, 3,7,"tiles", "Absolute limit to the center area radius (eases bad peripheral matching).");
gd.addNumericField("Central area minimal fill", this.pmtch_cent_fill, 3,7,"", "Central area minimal fill for all but the last iteration.");
gd.addNumericField("Central area minimal fill final", this.pmtch_cent_final, 3,7,"", "Central area minimal fill for the last iteration.");
gd.addNumericField("Relax metric error for no-SfM", this.pmtch_ease_nosfm, 3,7,"", "Relax metric error for no-SfM scenes (sfm_gain==0).");
gd.addMessage ("Initial spiral search for image matching");
gd.addCheckbox ("Use existing image pair", this.ospir_existing, "Use existing affine settings for this pair, do not use spiral search.");
gd.addCheckbox ("Invert existing image pair", this.ospir_invert, "Invert existing image pair affine transform, do not use spiral search.");
gd.addNumericField("Spiral search step", this.ospir_step, 3,7,"scaled pix", "Distance between spiral search probes, in scaled pixels.");
gd.addNumericField("Spiral search radius", this.ospir_range, 3,7,"scaled pix", "Maximal radius of the spiral search, in scaled pixels.");
gd.addNumericField("Mitigate small overlap", this.ospir_double, 3,7,"","For small overlaps increase zoom by 1 and range - twice.");
gd.addNumericField("RMSE to end search", this.ospir_good_rms, 3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels.");
gd.addNumericField("Satisfactory RMSE", this.ospir_max_rms, 3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels.");
gd.addNumericField("Minimal overlap", this.ospir_overlap, 0,4,"scaled pix ^ 2","Minimal overlap area in square scaled pixels.");
gd.addNumericField("LMA iterations", this.ospir_num_iter, 0,2,"", "Number of LMA iterations during spiral search.");
gd.addNumericField("RMSE at first iteration", this.ospir_rms_iter[0], 3,7,"scaled pix","Maximal RMSE at first iteration.");
gd.addNumericField("RMSE at second iteration", this.ospir_rms_iter[1], 3,7,"scaled pix","Maximal RMSE at second iteration.");
gd.addCheckbox ("Ignore worsening RMSE", this.ospir_ignore_rms, "Ignore worsening/not improving RMSE during spiral search.");
gd.addNumericField("Spiral search debug level", this.ospir_debug, 0,3,"","Debug level during Spiral search.");
gd.addMessage ("Pairwise image matching");
gd.addNumericField("Zoom level", this.pwise_zoom, 0,3,"","Low-res images to determine overlaps.");
gd.addNumericField("Minimal overlap fraction", this.pwise_overlap, 3,7,"", "Minimal overlap area as a fraction of the smaller of the two images.");
gd.addCheckbox ("Skip existing", this.pwise_skip_exist, "Do not regenerate if match with same or higher resolution exists.");
gd.addCheckbox ("Refine existing", this.pwise_refine_exist, "Refine existing matches (false - start from scratch with spiral search).");
gd.addCheckbox ("Delete failed", this.pwise_delete_fail, "Delete previous matches if it failed now.");
gd.addCheckbox ("Generate inverse matches", this.pwise_gen_inverse, "Generate (refine if exist and enabled) inverse matches.");
gd.addCheckbox ("Save state after each match", this.pwise_save_each, "Update state file after each match generation to mitigate possible crashes.");
gd.addCheckbox ("Write log file", this.pwise_log_append, "Enable writing log file with matching results.");
gd.addStringField ("Log file full path", this.pwise_log_path, 150, "Path of the log file to be appended.");
gd.addNumericField("Pairwise match debug level", this.pwise_debug, 0,3,"","Debug level during Spiral search.");
//
gd.addTab ("Scene Series", "Processing series of scenes and multi-series sets");
gd.addMessage ("Build series options");
......@@ -1563,17 +1622,43 @@ public class IntersceneMatchParameters {
this.rln_sngl_rstr = gd.getNextNumber();
this.rln_neib_rstr = gd.getNextNumber();
this.pmtch_frac_remove = gd.getNextNumber();
this.pmtch_metric_err = gd.getNextNumber();
this.pmtch_max_std = gd.getNextNumber();
this.pmtch_min_std_rad = gd.getNextNumber();
this.pmtch_ignore_rms = gd.getNextBoolean();
this.pmtch_num_iter = (int) gd.getNextNumber();
this.pmtch_cent_rad = gd.getNextNumber();
this.pmtch_max_cent_rad = gd.getNextNumber();
this.pmtch_cent_fill = gd.getNextNumber();
this.pmtch_cent_final = gd.getNextNumber();
this.pmtch_ease_nosfm = gd.getNextNumber();
this.ospir_existing = gd.getNextBoolean();
this.ospir_invert = gd.getNextBoolean();
this.ospir_step = gd.getNextNumber();
this.ospir_range = gd.getNextNumber();
this.ospir_double = gd.getNextNumber();
this.ospir_good_rms = gd.getNextNumber();
this.ospir_max_rms = gd.getNextNumber();
this.ospir_overlap = (int) gd.getNextNumber();
this.ospir_num_iter = (int) gd.getNextNumber();
this.ospir_rms_iter[0] = gd.getNextNumber();
this.ospir_rms_iter[1] = gd.getNextNumber();
this.ospir_ignore_rms = gd.getNextBoolean();
this.ospir_debug = (int) gd.getNextNumber();
this.pwise_zoom = (int) gd.getNextNumber();
this.pwise_overlap = gd.getNextNumber();
this.pwise_skip_exist = gd.getNextBoolean();
this.pwise_refine_exist = gd.getNextBoolean();
this.pwise_delete_fail = gd.getNextBoolean();
this.pwise_gen_inverse = gd.getNextBoolean();
this.pwise_save_each = gd.getNextBoolean();
this.pwise_log_append = gd.getNextBoolean();
this.pwise_log_path = gd.getNextString();
this.pwise_debug = (int) gd.getNextNumber();
this.center_reference = gd.getNextBoolean();
this.overlap_sequences = gd.getNextBoolean();
this.reset_photometric = gd.getNextBoolean();
......@@ -2033,19 +2118,46 @@ public class IntersceneMatchParameters {
properties.setProperty(prefix+"rln_cent_radius", this.rln_cent_radius +""); // double
properties.setProperty(prefix+"rln_n_recenter", this.rln_n_recenter+""); // int
properties.setProperty(prefix+"rln_sngl_rstr", this.rln_sngl_rstr +""); // double
properties.setProperty(prefix+"rln_neib_rstr", this.rln_neib_rstr +"");
properties.setProperty(prefix+"rln_neib_rstr", this.rln_neib_rstr +""); // double
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
properties.setProperty(prefix+"pmtch_min_std_rad", this.pmtch_min_std_rad +""); // double
properties.setProperty(prefix+"pmtch_ignore_rms", this.pmtch_ignore_rms +""); // boolean
properties.setProperty(prefix+"pmtch_num_iter", this.pmtch_num_iter +""); // int
properties.setProperty(prefix+"pmtch_cent_rad", this.pmtch_cent_rad + ""); // double
properties.setProperty(prefix+"pmtch_max_cent_rad", this.pmtch_max_cent_rad + ""); // double
properties.setProperty(prefix+"pmtch_cent_fill", this.pmtch_cent_fill + ""); // double
properties.setProperty(prefix+"pmtch_cent_final", this.pmtch_cent_final + ""); // double
properties.setProperty(prefix+"pmtch_ease_nosfm", this.pmtch_ease_nosfm + ""); // double
properties.setProperty(prefix+"ospir_existing", this.ospir_existing +""); // boolean
properties.setProperty(prefix+"ospir_invert", this.ospir_invert +""); // boolean
properties.setProperty(prefix+"ospir_step", this.ospir_step +""); // double
properties.setProperty(prefix+"ospir_range", this.ospir_range +""); // double
properties.setProperty(prefix+"ospir_double", this.ospir_double +""); // double
properties.setProperty(prefix+"ospir_good_rms", this.ospir_good_rms +""); // double
properties.setProperty(prefix+"ospir_max_rms", this.ospir_max_rms +""); // double
properties.setProperty(prefix+"ospir_overlap", this.ospir_overlap +""); // int
properties.setProperty(prefix+"ospir_num_iter", this.ospir_num_iter +""); // int
properties.setProperty(prefix+"ospir_rms_iter_0", this.ospir_rms_iter[0] +""); // double
properties.setProperty(prefix+"ospir_rms_iter_1", this.ospir_rms_iter[1] +""); // double
properties.setProperty(prefix+"ospir_ignore_rms", this.ospir_ignore_rms +""); // boolean
properties.setProperty(prefix+"ospir_debug", this.ospir_debug +""); // int
properties.setProperty(prefix+"pwise_zoom", this.pwise_zoom + ""); // int
properties.setProperty(prefix+"pwise_overlap", this.pwise_overlap + ""); // double
properties.setProperty(prefix+"pwise_skip_exist", this.pwise_skip_exist + ""); // boolean
properties.setProperty(prefix+"pwise_refine_exist", this.pwise_refine_exist + ""); // boolean
properties.setProperty(prefix+"pwise_delete_fail", this.pwise_delete_fail + ""); // boolean
properties.setProperty(prefix+"pwise_gen_inverse", this.pwise_gen_inverse + ""); // boolean
properties.setProperty(prefix+"pwise_save_each", this.pwise_save_each + ""); // boolean
properties.setProperty(prefix+"pwise_log_append", this.pwise_log_append + ""); // boolean
properties.setProperty(prefix+"pwise_log_path", this.pwise_log_path + ""); // String
properties.setProperty(prefix+"pwise_debug", this.pwise_debug + ""); // int
properties.setProperty(prefix+"center_reference", this.center_reference + ""); // boolean
properties.setProperty(prefix+"overlap_sequences", this.overlap_sequences + ""); // boolean
properties.setProperty(prefix+"reset_photometric", this.reset_photometric + ""); // boolean
......@@ -2470,17 +2582,43 @@ 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_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"));
if (properties.getProperty(prefix+"pmtch_min_std_rad")!=null) this.pmtch_min_std_rad=Double.parseDouble(properties.getProperty(prefix+ "pmtch_min_std_rad"));
if (properties.getProperty(prefix+"pmtch_ignore_rms")!=null) this.pmtch_ignore_rms= Boolean.parseBoolean(properties.getProperty(prefix+"pmtch_ignore_rms"));
if (properties.getProperty(prefix+"pmtch_num_iter")!=null) this.pmtch_num_iter= Integer.parseInt(properties.getProperty(prefix+ "pmtch_num_iter"));
if (properties.getProperty(prefix+"pmtch_cent_rad" )!=null) this.pmtch_cent_rad= Double.parseDouble(properties.getProperty(prefix+ "pmtch_cent_rad"));
if (properties.getProperty(prefix+"pmtch_max_cent_rad")!=null) this.pmtch_max_cent_rad=Double.parseDouble(properties.getProperty(prefix+ "pmtch_max_cent_rad"));
if (properties.getProperty(prefix+"pmtch_cent_fill" )!=null) this.pmtch_cent_fill= Double.parseDouble(properties.getProperty(prefix+ "pmtch_cent_fill"));
if (properties.getProperty(prefix+"pmtch_cent_final" )!=null) this.pmtch_cent_final= Double.parseDouble(properties.getProperty(prefix+ "pmtch_cent_final"));
if (properties.getProperty(prefix+"pmtch_ease_nosfm" )!=null) this.pmtch_ease_nosfm= Double.parseDouble(properties.getProperty(prefix+ "pmtch_ease_nosfm"));
if (properties.getProperty(prefix+"ospir_existing")!=null) this.ospir_existing=Boolean.parseBoolean(properties.getProperty(prefix+ "ospir_existing"));
if (properties.getProperty(prefix+"ospir_invert")!=null) this.ospir_invert=Boolean.parseBoolean(properties.getProperty(prefix+ "ospir_invert"));
if (properties.getProperty(prefix+"ospir_step")!=null) this.ospir_step=Double.parseDouble(properties.getProperty(prefix+ "ospir_step"));
if (properties.getProperty(prefix+"ospir_range")!=null) this.ospir_range=Double.parseDouble(properties.getProperty(prefix+ "ospir_range"));
if (properties.getProperty(prefix+"ospir_double")!=null) this.ospir_double=Double.parseDouble(properties.getProperty(prefix+ "ospir_double"));
if (properties.getProperty(prefix+"ospir_good_rms")!=null) this.ospir_good_rms=Double.parseDouble(properties.getProperty(prefix+ "ospir_good_rms"));
if (properties.getProperty(prefix+"ospir_max_rms")!=null) this.ospir_max_rms=Double.parseDouble(properties.getProperty(prefix+ "ospir_max_rms"));
if (properties.getProperty(prefix+"ospir_overlap")!=null) this.ospir_overlap=Integer.parseInt(properties.getProperty(prefix+ "ospir_overlap"));
if (properties.getProperty(prefix+"ospir_num_iter")!=null) this.ospir_num_iter=Integer.parseInt(properties.getProperty(prefix+ "ospir_num_iter"));
if (properties.getProperty(prefix+"ospir_rms_iter_0")!=null) this.ospir_rms_iter[0]=Double.parseDouble(properties.getProperty(prefix+ "ospir_rms_iter_0"));
if (properties.getProperty(prefix+"ospir_rms_iter_1")!=null) this.ospir_rms_iter[1]=Double.parseDouble(properties.getProperty(prefix+ "ospir_rms_iter_1"));
if (properties.getProperty(prefix+"ospir_ignore_rms")!=null) this.ospir_ignore_rms=Boolean.parseBoolean(properties.getProperty(prefix+"ospir_ignore_rms"));
if (properties.getProperty(prefix+"ospir_debug")!=null) this.ospir_debug=Integer.parseInt(properties.getProperty(prefix+ "ospir_debug"));
if (properties.getProperty(prefix+"pwise_zoom")!=null) this.pwise_zoom=Integer.parseInt(properties.getProperty(prefix+ "pwise_zoom"));
if (properties.getProperty(prefix+"pwise_overlap")!=null) this.pwise_overlap=Double.parseDouble(properties.getProperty(prefix+ "pwise_overlap"));
if (properties.getProperty(prefix+"pwise_skip_exist")!=null) this.pwise_skip_exist=Boolean.parseBoolean(properties.getProperty(prefix+ "pwise_skip_exist"));
if (properties.getProperty(prefix+"pwise_refine_exist")!=null) this.pwise_refine_exist=Boolean.parseBoolean(properties.getProperty(prefix+"pwise_refine_exist"));
if (properties.getProperty(prefix+"pwise_delete_fail")!=null) this.pwise_delete_fail=Boolean.parseBoolean(properties.getProperty(prefix+ "pwise_delete_fail"));
if (properties.getProperty(prefix+"pwise_gen_inverse")!=null) this.pwise_gen_inverse=Boolean.parseBoolean(properties.getProperty(prefix+ "pwise_gen_inverse"));
if (properties.getProperty(prefix+"pwise_save_each")!=null) this.pwise_save_each=Boolean.parseBoolean(properties.getProperty(prefix+ "pwise_save_each"));
if (properties.getProperty(prefix+"pwise_log_append")!=null) this.pwise_log_append=Boolean.parseBoolean(properties.getProperty(prefix+ "pwise_log_append"));
if (properties.getProperty(prefix+"pwise_log_path")!=null) this.pwise_log_path=(String) properties.getProperty(prefix+ "pwise_log_path");
if (properties.getProperty(prefix+"pwise_debug")!=null) this.pwise_debug=Integer.parseInt(properties.getProperty(prefix+ "pwise_debug"));
if (properties.getProperty(prefix+"center_reference")!=null) this.center_reference=Boolean.parseBoolean(properties.getProperty(prefix+"center_reference"));
if (properties.getProperty(prefix+"overlap_sequences")!=null) this.overlap_sequences=Boolean.parseBoolean(properties.getProperty(prefix+"overlap_sequences"));
if (properties.getProperty(prefix+"reset_photometric")!=null) this.reset_photometric=Boolean.parseBoolean(properties.getProperty(prefix+"reset_photometric"));
......@@ -2928,22 +3066,47 @@ public class IntersceneMatchParameters {
imp.rln_neibs_fill = this.rln_neibs_fill;
imp.rln_neib_radius = this.rln_neib_radius;
imp.rln_radius_frac = this.rln_radius_frac;
imp.rln_cent_radius = this.rln_cent_radius;
imp.rln_n_recenter = this.rln_n_recenter;
imp.rln_sngl_rstr = this.rln_sngl_rstr;
imp.rln_neib_rstr = this.rln_neib_rstr;
imp.pmtch_frac_remove = this.pmtch_frac_remove;
imp.pmtch_metric_err = this.pmtch_metric_err;
imp.pmtch_max_std = this.pmtch_max_std;
imp.pmtch_min_std_rad = this.pmtch_min_std_rad;
imp.pmtch_ignore_rms = this.pmtch_ignore_rms;
imp.pmtch_num_iter = this.pmtch_num_iter;
imp.pmtch_cent_rad = this.pmtch_cent_rad;
imp.pmtch_max_cent_rad = this.pmtch_max_cent_rad;
imp.pmtch_cent_fill = this.pmtch_cent_fill;
imp.pmtch_cent_final = this.pmtch_cent_final;
imp.pmtch_ease_nosfm = this.pmtch_ease_nosfm;
imp.ospir_existing = this.ospir_existing;
imp.ospir_step = this.ospir_step;
imp.ospir_range = this.ospir_range;
imp.ospir_double = this.ospir_double;
imp.ospir_good_rms = this.ospir_good_rms;
imp.ospir_max_rms = this.ospir_max_rms;
imp.ospir_overlap = this.ospir_overlap;
imp.ospir_num_iter = this.ospir_num_iter;
imp.ospir_rms_iter = this.ospir_rms_iter.clone();
imp.ospir_ignore_rms = this.ospir_ignore_rms;
imp.ospir_debug = this.ospir_debug;
imp.pwise_zoom = this.pwise_zoom;
imp.pwise_overlap = this.pwise_overlap;
imp.pwise_skip_exist = this.pwise_skip_exist;
imp.pwise_refine_exist = this.pwise_refine_exist;
imp.pwise_delete_fail = this.pwise_delete_fail;
imp.pwise_gen_inverse = this.pwise_gen_inverse;
imp.pwise_save_each = this.pwise_save_each;
imp.pwise_log_append = this.pwise_log_append;
imp.pwise_log_path = this.pwise_log_path;
imp.pwise_debug = this.pwise_debug;
imp.center_reference = this.center_reference;
imp.overlap_sequences = this.overlap_sequences;
imp.reset_photometric = this.reset_photometric;
......
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