Commit ba9466e1 authored by Andrey Filippov's avatar Andrey Filippov

Improving matching stability, added parameters

parent f468c5ba
...@@ -150,7 +150,7 @@ public class ComboMatch { ...@@ -150,7 +150,7 @@ public class ComboMatch {
int zoom_lev = -3; // 0; // +1 - zoom in twice, -1 - zoom out twice int zoom_lev = -3; // 0; // +1 - zoom in twice, -1 - zoom out twice
// boolean show_map_stats = false; // boolean show_map_stats = false;
boolean show_combo = false; // true; boolean show_combo = false; // true;
boolean create_overlaps = false;
// boolean show_combo_mask = false; // generate image mas (where it is defined)_ // boolean show_combo_mask = false; // generate image mas (where it is defined)_
// boolean use_alt = false; // boolean use_alt = false;
// boolean show_centers = true; // boolean show_centers = true;
...@@ -182,7 +182,7 @@ public class ComboMatch { ...@@ -182,7 +182,7 @@ public class ComboMatch {
} }
GenericJTabbedDialog gd = new GenericJTabbedDialog("Set image pair",1200,900); GenericJTabbedDialog gd = new GenericJTabbedDialog("Set image pair",1200,600);
gd.addChoice ("Files list/data path (w/o extension):", files_lists_paths, files_lists_paths[default_list_choice]); 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 ("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."); gd.addCheckbox ("Save maps collection", save_collection, "Save maps collection to be able to restore.");
...@@ -219,6 +219,7 @@ public class ComboMatch { ...@@ -219,6 +219,7 @@ public class ComboMatch {
// gd.addCheckbox ("Show transformation centers", show_centers, "Mark verticals from the UAS on the ground."); // gd.addCheckbox ("Show transformation centers", show_centers, "Mark verticals from the UAS on the ground.");
// gd.addCheckbox ("Show statistics for ortho images", show_map_stats, "Generate and show statistics for ortho maps."); // gd.addCheckbox ("Show statistics for ortho images", show_map_stats, "Generate and show statistics for ortho maps.");
gd.addCheckbox ("Show combo maps/stats", show_combo, "Generate/save combo maps and stats."); gd.addCheckbox ("Show combo maps/stats", show_combo, "Generate/save combo maps and stats.");
gd.addCheckbox ("Create overlap pairs", create_overlaps, "Create scene pairs overlaps.");
// gd.addCheckbox ("Show combo image mask", show_combo_mask, "Display combo binary image."); // gd.addCheckbox ("Show combo image mask", show_combo_mask, "Display combo binary image.");
// gd.addCheckbox ("Show altitude combo image", use_alt, "Load and process altitude maps."); // gd.addCheckbox ("Show altitude combo image", use_alt, "Load and process altitude maps.");
gd.addNumericField("Remove fraction of worst matches", frac_remove, 3,7,"", "When fitting scenes remove this fraction of worst match."); gd.addNumericField("Remove fraction of worst matches", frac_remove, 3,7,"", "When fitting scenes remove this fraction of worst match.");
...@@ -270,14 +271,8 @@ public class ComboMatch { ...@@ -270,14 +271,8 @@ public class ComboMatch {
OrthoMap.setGPUWidthHeight(gpu_width,gpu_height); OrthoMap.setGPUWidthHeight(gpu_width,gpu_height);
// show_centers = gd.getNextBoolean();
// show_map_stats = gd.getNextBoolean();
show_combo = gd.getNextBoolean(); show_combo = gd.getNextBoolean();
// show_combo_mask = gd.getNextBoolean(); create_overlaps = gd.getNextBoolean();
// use_alt = gd.getNextBoolean();
// gpu_spair[0] = gd.getNextString();
// gpu_spair[1] = gd.getNextString();
frac_remove = gd.getNextNumber(); frac_remove = gd.getNextNumber();
metric_error= gd.getNextNumber(); metric_error= gd.getNextNumber();
if (use_marked_image ) { // will only be used if found and asked if (use_marked_image ) { // will only be used if found and asked
...@@ -770,6 +765,10 @@ public class ComboMatch { ...@@ -770,6 +765,10 @@ public class ComboMatch {
maps_collection.processComboMap(debugLevel); maps_collection.processComboMap(debugLevel);
return true; return true;
} }
if (create_overlaps) {
boolean ok =maps_collection.getIntersectedPairs(debugLevel);
if (!ok) return false;
}
if (process_correlation || render_match || pattern_match) { if (process_correlation || render_match || pattern_match) {
// int [] gpu_pair; // int [] gpu_pair;
if (gpu_spair == null) { if (gpu_spair == null) {
...@@ -792,7 +791,7 @@ public class ComboMatch { ...@@ -792,7 +791,7 @@ public class ComboMatch {
String [] choices_all = new String[choices.length+1]; String [] choices_all = new String[choices.length+1];
System.arraycopy(choices, 0, choices_all, 0, choices.length); System.arraycopy(choices, 0, choices_all, 0, choices.length);
choices_all[choices_all.length-1] = "--- select a single image ---"; choices_all[choices_all.length-1] = "--- select a single image ---";
GenericJTabbedDialog gdc = new GenericJTabbedDialog("Select image pair",1200,400); GenericJTabbedDialog gdc = new GenericJTabbedDialog("Select image pair",1200,100);
int num_choice_lines = 50; int num_choice_lines = 50;
gdc.addChoice("Image pair:", gdc.addChoice("Image pair:",
choices_all, choices_all,
...@@ -804,25 +803,23 @@ public class ComboMatch { ...@@ -804,25 +803,23 @@ public class ComboMatch {
if (pair >= choices.length) { if (pair >= choices.length) {
int default_choice = 0; int default_choice = 0;
int num_scene_lines = 50; int num_scene_lines = 50;
String scene_name = maps_collection.selectOneScene(
0, // int num_scene,
default_choice, // int default_choice,
num_scene_lines); // int num_choice_lines)
if (scene_name == null) {
return false;
}
if (process_correlation) { // select a second image to match if (process_correlation) { // select a second image to match
// select a second image and set gpu_spair // select a second image and set gpu_spair
int default_choice1 = 0; int default_choice1 = 0;
String scene_name1 = maps_collection.selectOneScene( gpu_spair = maps_collection.selectTwoScenes(
1, // int num_scene,
default_choice1, // int default_choice, default_choice1, // int default_choice,
num_scene_lines); // int num_choice_lines) num_scene_lines); // int num_choice_lines)
if (scene_name1 == null) { if (gpu_spair == null) {
return false;
}
} else {
String scene_name = maps_collection.selectOneScene(
0, // int num_scene,
default_choice, // int default_choice,
num_scene_lines); // int num_choice_lines)
if (scene_name == null) {
return false; return false;
} }
gpu_spair = new String[] {scene_name, scene_name1};
} else { // pattern match - single image
gpu_spair = new String[] {scene_name}; gpu_spair = new String[] {scene_name};
} }
} else { } else {
...@@ -913,7 +910,7 @@ public class ComboMatch { ...@@ -913,7 +910,7 @@ public class ComboMatch {
num_tries_fit = 0; num_tries_fit = 0;
update_match = false; update_match = false;
} }
debugLevel = 0; // debugLevel = 0;
boolean batch_mode = true; // false; // true; boolean batch_mode = true; // false; // true;
boolean ignore_prev_rms = true; boolean ignore_prev_rms = true;
Rectangle woi = new Rectangle(); // used to return actual woi from correlateOrthoPair() Rectangle woi = new Rectangle(); // used to return actual woi from correlateOrthoPair()
...@@ -976,6 +973,9 @@ public class ComboMatch { ...@@ -976,6 +973,9 @@ public class ComboMatch {
System.out.println("adjusted affines[1] for a pair: "+gpu_spair[0]+"/"+gpu_spair[1]); System.out.println("adjusted affines[1] for a pair: "+gpu_spair[0]+"/"+gpu_spair[1]);
System.out.println("[["+affines[1][0][0]+","+affines[1][0][1]+","+affines[1][0][2]+"],"); System.out.println("[["+affines[1][0][0]+","+affines[1][0][1]+","+affines[1][0][2]+"],");
System.out.println(" ["+affines[1][1][0]+","+affines[1][1][1]+","+affines[1][1][2]+"]]"); System.out.println(" ["+affines[1][1][0]+","+affines[1][1][1]+","+affines[1][1][2]+"]]");
if (pmatch != null) {
System.out.println("RMSE="+pmatch.rms);
}
System.out.println(); System.out.println();
} }
} }
...@@ -1040,45 +1040,57 @@ public class ComboMatch { ...@@ -1040,45 +1040,57 @@ public class ComboMatch {
PairwiseOrthoMatch pairwiseOrthoMatch = maps_collection.ortho_maps[gpu_pair[0]].getMatch( PairwiseOrthoMatch pairwiseOrthoMatch = maps_collection.ortho_maps[gpu_pair[0]].getMatch(
maps_collection.ortho_maps[gpu_pair[1]].getName()); maps_collection.ortho_maps[gpu_pair[1]].getName());
// boolean has_match = pairwiseOrthoMatch != null;
PairwiseOrthoMatch inv_match = maps_collection.ortho_maps[gpu_pair[1]].getMatch( PairwiseOrthoMatch inv_match = maps_collection.ortho_maps[gpu_pair[1]].getMatch(
maps_collection.ortho_maps[gpu_pair[0]].getName()); maps_collection.ortho_maps[gpu_pair[0]].getName());
// dialog - ask parameters and if has_match -ask if to use it (then just return true) // dialog - ask parameters and if has_match -ask if to use it (then just return true)
// if has inv - ask and, if yes, = create inverted as initial // if has inv - ask and, if yes, = create inverted as initial
boolean use_exixting_pair = false; boolean use_existing_pair = clt_parameters.imp.ospir_existing; // false
boolean invert_exixting_pair = false; boolean invert_exixting_pair = clt_parameters.imp.ospir_invert; // false
double search_step = 8.0; // pix double search_step = clt_parameters.imp.ospir_step; // 8.0; // pix
double search_range = 50.0; // pix double search_range = clt_parameters.imp.ospir_range; // 50.0; // pix
double maximal_rms = 0.25; // double good_rms = clt_parameters.imp.ospir_good_rms; // 0.27; //
int min_overlap = 3000; // do not try to match if there is too small overlap (scaled pixels) double max_rms = clt_parameters.imp.ospir_max_rms; // 0.35; //
int num_iter_lma = 5; 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); GenericJTabbedDialog gd = new GenericJTabbedDialog("Setup SpiralMatch",1200,300);
if (pairwiseOrthoMatch != null) { if (pairwiseOrthoMatch != null) {
gd.addCheckbox ("Use existing image pair", use_exixting_pair, "Use existing affine settings for this pair, do not use spiral search."); gd.addCheckbox ("Use existing image pair", use_existing_pair, "Use existing affine settings for this pair, do not use spiral search.");
} else {
use_existing_pair = false;
} }
if (inv_match != null) { if (inv_match != null) {
gd.addCheckbox ("Invert existing image pair", invert_exixting_pair, "Invert existing image pair affine transform, do not use spiral search."); gd.addCheckbox ("Invert existing image pair", invert_exixting_pair, "Invert existing image pair affine transform, do not use spiral search.");
} else {
invert_exixting_pair = false;
} }
gd.addNumericField("Spiral search step", search_step, 3,7,"scaled pix", "Distance between spiral search probes, in scaled pixels."); 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("Spiral search radius", search_range, 3,7,"scaled pix", "Maximal radius of the spiral search, in scaled pixels.");
gd.addNumericField("Maximal RMSE", maximal_rms, 3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels."); 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("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.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.addNumericField("Spiral search debug level",spiral_debug, 0,3,"","Debug level during Spiral search.");
gd.showDialog(); gd.showDialog();
if (gd.wasCanceled()) return null; if (gd.wasCanceled()) return null;
if (pairwiseOrthoMatch != null) { if (pairwiseOrthoMatch != null) {
use_exixting_pair = gd.getNextBoolean(); use_existing_pair = gd.getNextBoolean();
} }
if (inv_match != null) { if (inv_match != null) {
invert_exixting_pair = gd.getNextBoolean(); invert_exixting_pair = gd.getNextBoolean();
} }
search_step= gd.getNextNumber(); search_step= gd.getNextNumber();
search_range= gd.getNextNumber(); search_range= gd.getNextNumber();
maximal_rms = gd.getNextNumber(); good_rms = gd.getNextNumber();
max_rms = gd.getNextNumber();
min_overlap = (int) gd.getNextNumber(); min_overlap = (int) gd.getNextNumber();
num_iter_lma = (int) gd.getNextNumber(); num_iter_lma = (int) gd.getNextNumber();
if (use_exixting_pair) { ignore_rms = gd.getNextBoolean();
spiral_debug = (int) gd.getNextNumber();
if (use_existing_pair) {
if (invert_exixting_pair) { if (invert_exixting_pair) {
System.out.println("Both direct and inverted matches are selected, using direct match"); System.out.println("Both direct and inverted matches are selected, using direct match");
} }
...@@ -1095,7 +1107,6 @@ public class ComboMatch { ...@@ -1095,7 +1107,6 @@ public class ComboMatch {
pairwiseOrthoMatch = maps_collection.SpiralMatch ( pairwiseOrthoMatch = maps_collection.SpiralMatch (
clt_parameters, // CLTParameters clt_parameters, clt_parameters, // CLTParameters clt_parameters,
// PairwiseOrthoMatch pairwiseOrthoMatch, // will return statistics, may be null if not needed
frac_remove, // double frac_remove, // = 0.25 frac_remove, // double frac_remove, // = 0.25
metric_error, // double metric_error, metric_error, // double metric_error,
gpu_pair, // int [] gpu_pair, gpu_pair, // int [] gpu_pair,
...@@ -1103,10 +1114,12 @@ public class ComboMatch { ...@@ -1103,10 +1114,12 @@ public class ComboMatch {
initial_zoom, // int zoom_lev, initial_zoom, // int zoom_lev,
search_step, // double pix_step, search_step, // double pix_step,
search_range, // double pix_range, search_range, // double pix_range,
maximal_rms, // double need_rms, good_rms, // double good_rms,
max_rms, // double max_rms,
num_iter_lma, // int num_tries, // = 5 num_iter_lma, // int num_tries, // = 5
min_overlap, // int min_overlap, // 3000 min_overlap, // int min_overlap, // 3000
debugLevel); // int debugLevel){ ignore_rms, // boolean ignore_rms,
spiral_debug); // int debugLevel){
return pairwiseOrthoMatch; return pairwiseOrthoMatch;
} }
......
...@@ -22,6 +22,7 @@ import java.util.ArrayList; ...@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
...@@ -987,7 +988,6 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -987,7 +988,6 @@ public class OrthoMapsCollection implements Serializable{
public PairwiseOrthoMatch SpiralMatch ( public PairwiseOrthoMatch SpiralMatch (
CLTParameters clt_parameters, CLTParameters clt_parameters,
// PairwiseOrthoMatch pairwiseOrthoMatch, // will return statistics, may be null if not needed
double frac_remove, // = 0.25 double frac_remove, // = 0.25
double metric_error, double metric_error,
int [] gpu_pair, int [] gpu_pair,
...@@ -995,15 +995,16 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -995,15 +995,16 @@ public class OrthoMapsCollection implements Serializable{
int zoom_lev, int zoom_lev,
double pix_step, double pix_step,
double pix_range, double pix_range,
double need_rms, double good_rms,
double max_rms,
int num_tries, // = 5 int num_tries, // = 5
int min_overlap, // = 5 int min_overlap, // = 5
boolean ignore_rms,
int debugLevel){ int debugLevel){
double [][] affine1 = new double [][] {affines_init[1][0].clone(),affines_init[1][1].clone()}; double [][] affine1 = new double [][] {affines_init[1][0].clone(),affines_init[1][1].clone()};
double [][][] affines = new double [][][] {affines_init[0],affine1}; double [][][] affines = new double [][][] {affines_init[0],affine1};
boolean show_vf = false; boolean show_vf = false;
boolean batch_mode = true; boolean batch_mode = true;
boolean ignore_prev_rms = false; // true;
double [][] ground_planes = null; // null or double[2] - will return ground planes: double [][] ground_planes = null; // null or double[2] - will return ground planes:
double pix_size = OrthoMap.getPixelSizeMeters (zoom_lev); double pix_size = OrthoMap.getPixelSizeMeters (zoom_lev);
int nx = 0, ny=0; // number of steps (pix_step size) in hor and vert directions (both pos and neg) int nx = 0, ny=0; // number of steps (pix_step size) in hor and vert directions (both pos and neg)
...@@ -1015,7 +1016,8 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -1015,7 +1016,8 @@ public class OrthoMapsCollection implements Serializable{
zoom_lev); // int zoom_lev); zoom_lev); // int zoom_lev);
double max_std = 0.5; // maximal standard deviation to limit center area 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 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)) { while ((Math.abs(nx) <= nabs) && (Math.abs(ny) <= nabs)) {
for (int i = 0; i < affines_init.length; i++) { for (int i = 0; i < affines_init.length; i++) {
System.arraycopy(affines_init[1][i], 0, affine1[i], 0, affine1[i].length); System.arraycopy(affines_init[1][i], 0, affine1[i], 0, affine1[i].length);
...@@ -1032,7 +1034,7 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -1032,7 +1034,7 @@ public class OrthoMapsCollection implements Serializable{
min_std_rad, // double min_std_rad, // minimal radius of the central area (if less - fail) min_std_rad, // double min_std_rad, // minimal radius of the central area (if less - fail)
frac_remove, // double frac_remove, // = 0.25 frac_remove, // double frac_remove, // = 0.25
metric_error, // double metric_error, metric_error, // double metric_error,
ignore_prev_rms, // boolean ignore_prev_rms, ignore_rms, // boolean ignore_prev_rms,
num_tries, // = 5int num_tries, // = 5 num_tries, // = 5int num_tries, // = 5
false, // boolean calc_warp, (will return null if false) false, // boolean calc_warp, (will return null if false)
batch_mode, // boolean batch_mode, batch_mode, // boolean batch_mode,
...@@ -1042,15 +1044,20 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -1042,15 +1044,20 @@ public class OrthoMapsCollection implements Serializable{
zoom_lev, // int zoom_lev, zoom_lev, // int zoom_lev,
show_vf, // boolean show_vf, show_vf, // boolean show_vf,
ground_planes, // double [][] ground_planes, // null or double[2] - will return ground planes ground_planes, // double [][] ground_planes, // null or double[2] - will return ground planes
debugLevel); // final int debugLevel) debugLevel-4); // final int debugLevel)
pairwiseOrthoMatch.affine = affines[1]; // modified by correlateOrthoPair pairwiseOrthoMatch.affine = affines[1]; // modified by correlateOrthoPair
if (debugLevel > -4) { if (debugLevel > -4) {
System.out.println(String.format("SpiralMatch(): nx = %3d, ny=%3d, RMSE=%8.6f", System.out.println(String.format("SpiralMatch(): nx = %3d, ny=%3d, RMSE=%8.6f",
nx,ny,pairwiseOrthoMatch.rms)); nx,ny,pairwiseOrthoMatch.rms));
} }
if (pairwiseOrthoMatch.rms < need_rms) { if (pairwiseOrthoMatch.rms < good_rms) {
break; break;
} }
if (!Double.isNaN(pairwiseOrthoMatch.rms) && ((best_pom == null) || !(best_pom.rms <= pairwiseOrthoMatch.rms))) {
best_pom = pairwiseOrthoMatch.clone();
best_nx = nx;
best_ny = ny;
}
// update nx, ny // update nx, ny
if ((nx > ny) && (nx > -ny)){ if ((nx > ny) && (nx > -ny)){
ny++; ny++;
...@@ -1062,11 +1069,22 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -1062,11 +1069,22 @@ public class OrthoMapsCollection implements Serializable{
nx++; nx++;
} }
} }
if (!(pairwiseOrthoMatch.rms < need_rms)) { // failed if (pairwiseOrthoMatch.rms < good_rms) { // failed
return null;
}
return pairwiseOrthoMatch; // pairwiseOrthoMatch.affine will have adjusted affine[1] return pairwiseOrthoMatch; // pairwiseOrthoMatch.affine will have adjusted affine[1]
} }
if ((best_pom != null) && (best_pom.rms <= max_rms)) {
if (debugLevel > -4) {
System.out.println("SpiralMatch(): best RMSE="+best_pom.rms+" < "+max_rms+
" for nx = "+best_nx+", ny="+best_ny+", using it.");
}
return best_pom;
}
if (debugLevel > -4) {
System.out.println("SpiralMatch(): Failed to find a good match candidate. Best RMSE="+
((best_pom != null)? best_pom.rms:"N/A")+", max_rms= "+max_rms+"");
}
return null; // pairwiseOrthoMatch.affine will have adjusted affine[1]
}
...@@ -1132,6 +1150,7 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -1132,6 +1150,7 @@ public class OrthoMapsCollection implements Serializable{
boolean show_vf, boolean show_vf,
double [][] ground_planes, // null or double[2] - will return ground planes: double [][] ground_planes, // null or double[2] - will return ground planes:
int debugLevel){ int debugLevel){
double center_radius_change = 1.5; // if increases by more than this, disregard RMSE worsening
boolean show_lma_dbg = !batch_mode && (debugLevel > 1); boolean show_lma_dbg = !batch_mode && (debugLevel > 1);
if (woi == null) { if (woi == null) {
woi = new Rectangle(); woi = new Rectangle();
...@@ -1268,6 +1287,7 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -1268,6 +1287,7 @@ public class OrthoMapsCollection implements Serializable{
double [][] jtj = null; double [][] jtj = null;
double rms = Double.NaN; double rms = Double.NaN;
double last_center_radius = 0; double last_center_radius = 0;
double previous_center_radius = 1.0;
for (int ntry = 0; ntry < num_tries; ntry++) { for (int ntry = 0; ntry < num_tries; ntry++) {
if (!batch_mode) { if (!batch_mode) {
if (debugLevel>-3) { if (debugLevel>-3) {
...@@ -1508,6 +1528,13 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -1508,6 +1528,13 @@ public class OrthoMapsCollection implements Serializable{
debugLevel); // final int debug_level) debugLevel); // final int debug_level)
last_center_radius = orthoPairLMA.getCenterRadius(); last_center_radius = orthoPairLMA.getCenterRadius();
double center_radius_increase =
Double.isInfinite(previous_center_radius)?
(Double.isInfinite(last_center_radius)?1:0):
last_center_radius/previous_center_radius;
if ((ntry == 0) && Double.isInfinite(last_center_radius)) {
center_radius_increase = 1.0; // if starts with infinity - no increase
}
if ((min_std_rad >0) && (last_center_radius < 1)) { if ((min_std_rad >0) && (last_center_radius < 1)) {
if (debugLevel>-3) { if (debugLevel>-3) {
System.out.println("correlateOrthoPair(): center_radius="+ System.out.println("correlateOrthoPair(): center_radius="+
...@@ -1550,7 +1577,7 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -1550,7 +1577,7 @@ public class OrthoMapsCollection implements Serializable{
if (rms > prev_rms) { if (rms > prev_rms) {
if (debugLevel > -3) { if (debugLevel > -3) {
if ((rms-prev_rms)/prev_rms < rel_improve) { if ((rms-prev_rms)/prev_rms < rel_improve) {
System.out.println("LMA RMSE worsened, but less than improvement threshold: new"+rms+" ("+ orthoPairLMA.getInitialRms()+"), prev="+prev_rms); System.out.println("LMA RMSE worsened, but less than improvement threshold: new "+rms+" ("+ orthoPairLMA.getInitialRms()+"), prev="+prev_rms);
} else { } else {
System.out.println("LMA RMSE worsened: new "+rms+" ("+ orthoPairLMA.getInitialRms()+"), prev="+prev_rms); System.out.println("LMA RMSE worsened: new "+rms+" ("+ orthoPairLMA.getInitialRms()+"), prev="+prev_rms);
} }
...@@ -1559,10 +1586,18 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -1559,10 +1586,18 @@ public class OrthoMapsCollection implements Serializable{
if (debugLevel > -3) { if (debugLevel > -3) {
System.out.println("Will continue, as ignore_prev_rms is set"); System.out.println("Will continue, as ignore_prev_rms is set");
} }
} else {
if (center_radius_increase > center_radius_change) {
if (debugLevel > -3) {
System.out.println("Will continue, as center_radius increased from "+
previous_center_radius+" to "+ last_center_radius +" (more than "+
center_radius_change+"x).");
}
} else { } else {
break; break;
} }
} }
}
affines_gpu[1]=OrthoMap.combineAffine(affines_gpu[1], orthoPairLMA.getAffine()); affines_gpu[1]=OrthoMap.combineAffine(affines_gpu[1], orthoPairLMA.getAffine());
jtj = orthoPairLMA.getLastJtJ(); jtj = orthoPairLMA.getLastJtJ();
if ((prev_rms - rms)/prev_rms < rel_improve) { if ((prev_rms - rms)/prev_rms < rel_improve) {
...@@ -1573,11 +1608,32 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -1573,11 +1608,32 @@ public class OrthoMapsCollection implements Serializable{
if (debugLevel > -3) { if (debugLevel > -3) {
System.out.println("Will continue, as ignore_prev_rms is set"); System.out.println("Will continue, as ignore_prev_rms is set");
} }
} else {
if (center_radius_increase > center_radius_change) {
if (debugLevel > -3) {
System.out.println("Will continue, as center_radius increased from "+
previous_center_radius+" to "+ last_center_radius +" (more than "+
center_radius_change+"x).");
}
} else { } else {
break; break;
} }
} }
}
if (center_radius_increase > center_radius_change) {
num_tries ++;
if (debugLevel > -2) {
System.out.println("Increasing num_tries to "+num_tries+", as center_radius increased from "+
previous_center_radius+" to "+ last_center_radius +" (more than "+
center_radius_change+"x).");
}
}
prev_rms=rms; prev_rms=rms;
previous_center_radius = last_center_radius;
} // for (int ntry = 0; ntry < num_tries; ntry++) { } // for (int ntry = 0; ntry < num_tries; ntry++) {
if ((min_std_rad > 0) && (last_center_radius < min_std_rad)) { if ((min_std_rad > 0) && (last_center_radius < min_std_rad)) {
...@@ -3728,6 +3784,42 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -3728,6 +3784,42 @@ public class OrthoMapsCollection implements Serializable{
return ortho_maps[scene_number].getName(); return ortho_maps[scene_number].getName();
} }
public String[] selectTwoScenes(
int default_choice,
int num_choice_lines) {
String [] fs = {"first","second"};
String [] lines = new String [ortho_maps.length];
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss.SS zzz");// may be VV instead of zzz
for (int indx = 0; indx < ortho_maps.length; indx++) {
OrthoMap map = ortho_maps[indx];
LocalDateTime ldt = map.getLocalDateTime();
ZonedDateTime zonedUTC = ldt.atZone(ZoneId.of("UTC")); // that time is UTC
ZonedDateTime zonedDateTime = zonedUTC.withZoneSameInstant(ZoneId.of("Europe/Kyiv"));
String sdt = zonedDateTime.format(formatter);
String name = map.getName();
double agl = map.getAGL();
lines[indx]=String.format(
"%3d %17s %26s %6.2f",indx, name, sdt, agl);
}
GenericJTabbedDialog gd = new GenericJTabbedDialog("Select two image scenes",1200,150);
for (int num_scene = 0; num_scene < 2; num_scene++) {
gd.addChoice(fs[num_scene]+" image:",
lines,
lines[default_choice],
"Select "+fs[num_scene]+" scene", num_choice_lines);
}
gd.showDialog();
if (gd.wasCanceled()) return null;
String [] names = new String[2];
for (int num_scene = 0; num_scene < 2; num_scene++) {
int scene_number= gd.getNextChoiceIndex();
names[num_scene] = ortho_maps[scene_number].getName();
}
return names;
}
public String [] getScenesList() { public String [] getScenesList() {
String [] lines = new String [ortho_maps.length]; String [] lines = new String [ortho_maps.length];
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss.SS zzz");// may be VV instead of zzz DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss.SS zzz");// may be VV instead of zzz
...@@ -3806,6 +3898,84 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -3806,6 +3898,84 @@ public class OrthoMapsCollection implements Serializable{
return indices; return indices;
} }
public boolean getIntersectedPairs(
int debugLevel) {
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;
boolean bounds_to_indices = true;
GenericJTabbedDialog gd = new GenericJTabbedDialog("Combo map/stats generation",1200,1000);
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,"",
"Minimal overlap as a fraction of the smaller image.");
gd.showDialog();
if (gd.wasCanceled()) return false;
zoom_lev = (int) gd.getNextNumber();
min_overlap = gd.getNextNumber();
int [] wh = new int[2];
int [] origin = new int[2];
double [][] centers =new double [indices.length][];
double [][] dmulti = renderMultiDouble (
null, // double [][] ground_planes, // null - images, non-null altitudes. use new double[2][3] for old way alt
indices, // int [] indices, // null or which indices to use (normally just 2 for pairwise comparison)
bounds_to_indices, // boolean bounds_to_indices,
null, // affines, // double [][][] affines, // null or [indices.length][2][3]
null, // warp, // FineXYCorr warp,,
zoom_lev, // int zoom_level,
wh, // int [] wh,
origin, // int [] origin){ // maps[0] as a reference
centers); // double [][] centers)
double [] overlaps = getFracOverlaps(
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])){
num_overlaps_all++;
if (overlaps[i] >= min_overlap) {
num_overlaps_sel++;
int row = i / dmulti.length;
int col = i % dmulti.length;
intersects[row][col] = true;
}
}
int [] groups = new int [dmulti.length];
for (int i = 0; i < groups.length; i++) {
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]){
int g0 = groups[i];
int g1 = groups[j];
for (int k = 0; k < groups.length; k++) {
if (groups[k] == g1) {
groups[k] = g0;
}
}
}
}
HashSet<Integer> hs = new HashSet<Integer>();
for (int i:groups) {
hs.add(i);
}
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) {
ShowDoubleFloatArrays.showArrays(
overlaps,
"Overlaps");
}
return true;
}
public boolean processComboMap( public boolean processComboMap(
int debugLevel) { int debugLevel) {
int [] indices = getScenesSelection( int [] indices = getScenesSelection(
...@@ -4331,4 +4501,38 @@ public class OrthoMapsCollection implements Serializable{ ...@@ -4331,4 +4501,38 @@ public class OrthoMapsCollection implements Serializable{
} }
} }
public static double [] getFracOverlaps(
final double [][] dmulti) {
final double [] overlaps = new double [dmulti.length*dmulti.length];
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int nscene = ai.getAndIncrement(); nscene < (dmulti.length-1); nscene = ai.getAndIncrement()) {
double [] data0 = dmulti[nscene];
int num0 = 0;
for (int i = 0; i < data0.length; i++) if (!Double.isNaN(data0[i])) {
num0++;
}
for (int nscene1 = nscene+1; nscene1 < dmulti.length; nscene1++) {
double [] data1 = dmulti[nscene1];
int num1 = 0, num_intersect=0;
for (int i = 0; i < data0.length; i++) if (!Double.isNaN(data1[i])) {
num1++;
if (!Double.isNaN(data0[i])) {
num_intersect++;
}
}
overlaps[nscene * dmulti.length + nscene1] = (1.0 * num_intersect)/ Math.min(num0, num1);
}
}
}
};
}
ImageDtt.startAndJoin(threads);
return overlaps;
}
} }
...@@ -23,7 +23,19 @@ public class PairwiseOrthoMatch implements Serializable { ...@@ -23,7 +23,19 @@ public class PairwiseOrthoMatch implements Serializable {
this.jtj = jtj; this.jtj = jtj;
this.zoom_lev=zoom_lev; this.zoom_lev=zoom_lev;
} }
public PairwiseOrthoMatch clone() {
double [][] affine = {this.affine[0].clone(),this.affine[1].clone()};
double [][] jtj = new double [this.jtj.length][];
for (int i = 0; i < this.jtj.length; i++) {
jtj[i] = this.jtj[i].clone();
}
PairwiseOrthoMatch pom = new PairwiseOrthoMatch(
affine,
jtj,
this.rms,
this.zoom_lev);
return pom;
}
public PairwiseOrthoMatch getInverse(double [] rd) { public PairwiseOrthoMatch getInverse(double [] rd) {
double [][] affine = OrthoMap.invertAffine(getAffine()); double [][] affine = OrthoMap.invertAffine(getAffine());
PairwiseOrthoMatch inverted_match = new PairwiseOrthoMatch( PairwiseOrthoMatch inverted_match = new PairwiseOrthoMatch(
......
...@@ -107,6 +107,20 @@ public class IntersceneMatchParameters { ...@@ -107,6 +107,20 @@ public class IntersceneMatchParameters {
public double rln_sngl_rstr = 0.3; // minimal single-tile phase correlation maximums relative to max str 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_neib_rstr = 0.4; // minimal neighbors phase correlation maximums relative to max str
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_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 boolean ospir_ignore_rms = false; // ignore RMS worsening during spiral search
public int ospir_debug = 0; // Debug level during sppiral search
public double [] getImsMountATR() { public double [] getImsMountATR() {
return new double [] { return new double [] {
ims_mount_atr[0] * Math.PI/180, ims_mount_atr[0] * Math.PI/180,
...@@ -694,6 +708,20 @@ public class IntersceneMatchParameters { ...@@ -694,6 +708,20 @@ public class IntersceneMatchParameters {
"Minimal single-tile phase correlation maximums relative to maximal strength."); "Minimal single-tile phase correlation maximums relative to maximal strength.");
gd.addNumericField("Minimal relative strength (neighbors)", this.rln_neib_rstr, 5,8,"", gd.addNumericField("Minimal relative strength (neighbors)", this.rln_neib_rstr, 5,8,"",
"Minimal neighbors phase correlation maximums relative to maximal strength."); "Minimal neighbors phase correlation maximums relative to maximal strength.");
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("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.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.addTab ("Scene Series", "Processing series of scenes and multi-series sets"); gd.addTab ("Scene Series", "Processing series of scenes and multi-series sets");
gd.addMessage ("Build series options"); gd.addMessage ("Build series options");
...@@ -1535,6 +1563,17 @@ public class IntersceneMatchParameters { ...@@ -1535,6 +1563,17 @@ public class IntersceneMatchParameters {
this.rln_sngl_rstr = gd.getNextNumber(); this.rln_sngl_rstr = gd.getNextNumber();
this.rln_neib_rstr = gd.getNextNumber(); this.rln_neib_rstr = gd.getNextNumber();
this.ospir_existing = gd.getNextBoolean();
this.ospir_invert = gd.getNextBoolean();
this.ospir_step = gd.getNextNumber();
this.ospir_range = 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_ignore_rms = gd.getNextBoolean();
this.ospir_debug = (int) gd.getNextNumber();
this.center_reference = gd.getNextBoolean(); this.center_reference = gd.getNextBoolean();
this.overlap_sequences = gd.getNextBoolean(); this.overlap_sequences = gd.getNextBoolean();
this.reset_photometric = gd.getNextBoolean(); this.reset_photometric = gd.getNextBoolean();
...@@ -1994,7 +2033,18 @@ public class IntersceneMatchParameters { ...@@ -1994,7 +2033,18 @@ public class IntersceneMatchParameters {
properties.setProperty(prefix+"rln_cent_radius", this.rln_cent_radius +""); // double 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_n_recenter", this.rln_n_recenter+""); // int
properties.setProperty(prefix+"rln_sngl_rstr", this.rln_sngl_rstr +""); // double 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+"rln_neib_rstr", this.rln_neib_rstr +"");
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_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_ignore_rms", this.ospir_ignore_rms +""); // boolean
properties.setProperty(prefix+"ospir_debug", this.ospir_debug +""); // int
properties.setProperty(prefix+"center_reference", this.center_reference + ""); // boolean properties.setProperty(prefix+"center_reference", this.center_reference + ""); // boolean
properties.setProperty(prefix+"overlap_sequences", this.overlap_sequences + ""); // boolean properties.setProperty(prefix+"overlap_sequences", this.overlap_sequences + ""); // boolean
...@@ -2420,6 +2470,17 @@ public class IntersceneMatchParameters { ...@@ -2420,6 +2470,17 @@ 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_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+"rln_neib_rstr")!=null) this.rln_neib_rstr=Double.parseDouble(properties.getProperty(prefix+"rln_neib_rstr"));
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_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_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+"center_reference")!=null) this.center_reference=Boolean.parseBoolean(properties.getProperty(prefix+"center_reference")); 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+"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")); if (properties.getProperty(prefix+"reset_photometric")!=null) this.reset_photometric=Boolean.parseBoolean(properties.getProperty(prefix+"reset_photometric"));
...@@ -2873,6 +2934,16 @@ public class IntersceneMatchParameters { ...@@ -2873,6 +2934,16 @@ public class IntersceneMatchParameters {
imp.rln_sngl_rstr = this.rln_sngl_rstr; imp.rln_sngl_rstr = this.rln_sngl_rstr;
imp.rln_neib_rstr = this.rln_neib_rstr; imp.rln_neib_rstr = this.rln_neib_rstr;
imp.ospir_existing = this.ospir_existing;
imp.ospir_step = this.ospir_step;
imp.ospir_range = this.ospir_range;
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_ignore_rms = this.ospir_ignore_rms;
imp.ospir_debug = this.ospir_debug;
imp.center_reference = this.center_reference; imp.center_reference = this.center_reference;
imp.overlap_sequences = this.overlap_sequences; imp.overlap_sequences = this.overlap_sequences;
imp.reset_photometric = this.reset_photometric; 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