Commit 7ad0875e authored by Andrey Filippov's avatar Andrey Filippov

Matching altitudes

parent 1cc6d251
......@@ -114,6 +114,7 @@ public class DoubleGaussianBlur {
* @param extraLines Number of lines (parallel to the blurring direction)
* below and above the roi bounds that should be processed.
*/
// TODO: Make threaded!
public void blur1Direction(double [] pixels,
int width,
int height,
......
......@@ -11,6 +11,8 @@ import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
......@@ -27,6 +29,7 @@ import com.elphel.imagej.gpu.TpTask;
import com.elphel.imagej.ims.Imx5;
import com.elphel.imagej.readers.ElphelTiffReader;
import com.elphel.imagej.tileprocessor.ImageDtt;
import com.elphel.imagej.tileprocessor.IntersceneMatchParameters;
import com.elphel.imagej.tileprocessor.OpticalFlow;
import com.elphel.imagej.tileprocessor.QuadCLT;
import com.elphel.imagej.tileprocessor.TDCorrTile;
......@@ -166,10 +169,13 @@ public class ComboMatch {
boolean create_pairwise_affines = false;
boolean augment_pairwise_affines = false; // needs to repeat create_pairwise_matches to update overlaps
boolean equalize_overlaps = false;
boolean altitude_match_pairs = false;
boolean display_pairs = false;
boolean create_map = false;
boolean create_equalize = false;
boolean use_saved_collection = true; // false;
boolean save_collection = true;
boolean process_correlation = true; // use false to save new version of data
......@@ -187,6 +193,7 @@ public class ComboMatch {
boolean update_lla = false; // re-read file metadata
boolean update_kernel_patterns = false;
boolean update_bl_bc = false;
boolean fix_duplicates = 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;
......@@ -232,6 +239,8 @@ public class ComboMatch {
gd.addCheckbox ("Create pairwise affines", create_pairwise_affines, "Create affines for scene pairs.");
gd.addCheckbox ("Augment pairwise affines", augment_pairwise_affines, "Augment pairwise affines after building initial map and re-running create_pairwise_matches.");
gd.addCheckbox ("Equalize overlap pairs", equalize_overlaps, "Equalize intensities in overlaps.");
gd.addCheckbox ("Altitude match pairs", altitude_match_pairs, "Pairwise match scenes altitude.");
gd.addCheckbox ("Display pairs", display_pairs, "Display pairwise match data.");
gd.addCheckbox ("Create map", create_map, "Create combined map from pairwise matches.");
gd.addCheckbox ("Equalize intensities", create_equalize, "Create map intensities equalization from pairwise matches.");
gd.addNumericField("Remove fraction of worst matches", frac_remove, 3,7,"", "When fitting scenes remove this fraction of worst match.");
......@@ -241,6 +250,8 @@ public class ComboMatch {
}
gd.addCheckbox ("Update files metadata", update_lla, "Re-read files metadata (if it was modified)");
gd.addCheckbox ("Update kernels/patterns", update_kernel_patterns, "Re-read kernels and patterns from *.list file");
gd.addCheckbox ("Remove duplicate scenes", fix_duplicates, "Remove scenes with the same timestamp");
gd.addCheckbox ("Update BC/BL suffix", update_bl_bc,
"Change source filenames to use -BC for bicubic, -BL - for bilinear, or empty - for old bilinear files");
gd.addChoice("BL/BC suffix:",
......@@ -284,6 +295,8 @@ public class ComboMatch {
create_pairwise_affines = gd.getNextBoolean();
augment_pairwise_affines = gd.getNextBoolean();
equalize_overlaps = gd.getNextBoolean();
altitude_match_pairs = gd.getNextBoolean();
display_pairs = gd.getNextBoolean();
create_map = gd.getNextBoolean();
create_equalize = gd.getNextBoolean();
frac_remove = gd.getNextNumber();
......@@ -293,6 +306,7 @@ public class ComboMatch {
}
update_lla= gd.getNextBoolean();
update_kernel_patterns= gd.getNextBoolean();
fix_duplicates= gd.getNextBoolean();
update_bl_bc= gd.getNextBoolean();
suffix_bc_bl_indx = gd.getNextChoiceIndex();
log_append = gd.getNextBoolean();
......@@ -325,6 +339,9 @@ public class ComboMatch {
maps_collection.updateNumberScenes();
maps_collection.updateSfmGain();
}
if (fix_duplicates) {
removeDuplicateScenes (maps_collection);
}
if (update_bl_bc) {
boolean OK = updateBlBcFileNames(
......@@ -826,6 +843,19 @@ public class ComboMatch {
return ok; // Just exit, do not try other commands. if (!ok) return false;
}
if (altitude_match_pairs) {
boolean ok =maps_collection.altutudeMatchPairs(
clt_parameters, // CLTParameters clt_parameters,
orthoMapsCollection_savepath); // String orthoMapsCollection_path);
return ok; // Just exit, do not try other commands. if (!ok) return false;
}
if (display_pairs) {
boolean ok =maps_collection.displayScenePairs(
clt_parameters, // CLTParameters clt_parameters,
orthoMapsCollection_savepath); // String orthoMapsCollection_path);
return ok; // Just exit, do not try other commands. if (!ok) return false;
}
if (process_correlation || render_match || pattern_match || test_multi_lma) {
// int [] gpu_pair;
if (gpu_spair == null) {
......@@ -864,11 +894,15 @@ public class ComboMatch {
boolean flt_filt_zoom = clt_parameters.imp.flt_filt_zoom; // true;
int flt_min_zoom = clt_parameters.imp.flt_min_zoom; // -2;
int flt_max_zoom = clt_parameters.imp.flt_max_zoom; // 10;
double flt_min_sfm = clt_parameters.imp.flt_min_sfm; // 0.0;
double flt_max_sfm = clt_parameters.imp.flt_max_sfm; //1000.0;
int flt_alt = clt_parameters.imp.flt_alt; // 0;
boolean flt_show_names = clt_parameters.imp.flt_show_names; // true;
boolean flt_show_overlaps = clt_parameters.imp.flt_show_overlaps; // true;
boolean flt_show_rms = clt_parameters.imp.flt_show_rms; // true;
boolean flt_show_zoom = clt_parameters.imp.flt_show_zoom; // true;
boolean flt_show_alt = clt_parameters.imp.flt_show_alt; // true;
boolean flt_update_config = false;
String flt_extra_line = "--- select a single image ---";
GenericJTabbedDialog gdf = new GenericJTabbedDialog("Select pairs filter/display",800,500);
......@@ -882,11 +916,16 @@ public class ComboMatch {
gdf.addCheckbox ("Filter by zoom level" , flt_filt_zoom, "Filter by the zoom level used for matching.");
gdf.addNumericField("Minimal zoom", flt_min_zoom, 0,3,"","Minimal zoom level used for matching.");
gdf.addNumericField("Maximal zoom", flt_max_zoom, 0,3,"","Maximal zoom level used for matching.");
gdf.addNumericField("Minimal SfM gain", flt_min_sfm, 3,7,"","Minimal SfM gain of the minimum in the scene pair.");
gdf.addNumericField("Maximal SfM gain", flt_max_sfm, 3,7,"","Maximal SfM gain of the minimum in the scene pair.");
gdf. addChoice("Filter by pairwise ALT availability",IntersceneMatchParameters.FLT_ALT_MODES, IntersceneMatchParameters.FLT_ALT_MODES[flt_alt],
"Filter by pairwise ALT availability.");
gdf.addCheckbox ("Show scene names", flt_show_names, "Show scene full names (timestamps) in selection drop-down list.");
gdf.addCheckbox ("Show scene overlaps", flt_show_overlaps, "Show scene overlaps (in percents) in selection drop-down list.");
gdf.addCheckbox ("Show pairs RMSE", flt_show_rms, "Show scene match RMSE in selection drop-down list.");
gdf.addCheckbox ("Show zoom level", flt_show_zoom, "Show zoom level.");
gdf.addCheckbox ("Show ALT", flt_show_alt, "Show altitude data availability.");
gdf.addCheckbox ("Update configuration", flt_update_config, "Update matching configuration parameters to be saved as defaults.");
gdf.showDialog();
......@@ -900,12 +939,16 @@ public class ComboMatch {
flt_nan_rms = gdf.getNextBoolean();
flt_filt_zoom = gdf.getNextBoolean();
flt_min_zoom =(int) gdf.getNextNumber();
flt_max_zoom =(int) gdf.getNextNumber();
flt_max_zoom =(int) gdf.getNextNumber();
flt_min_sfm = gdf.getNextNumber();
flt_max_sfm = gdf.getNextNumber();
flt_alt = gdf.getNextChoiceIndex();
flt_show_names = gdf.getNextBoolean();
flt_show_overlaps = gdf.getNextBoolean();
flt_show_rms = gdf.getNextBoolean();
flt_show_zoom = gdf.getNextBoolean();
flt_show_alt = gdf.getNextBoolean();
flt_update_config = gdf.getNextBoolean();
if (flt_update_config) {
clt_parameters.imp.flt_list = flt_list;
......@@ -918,10 +961,14 @@ public class ComboMatch {
clt_parameters.imp.flt_filt_zoom = flt_filt_zoom;
clt_parameters.imp.flt_min_zoom = flt_min_zoom;
clt_parameters.imp.flt_max_zoom = flt_max_zoom;
clt_parameters.imp.flt_min_sfm = flt_min_sfm;
clt_parameters.imp.flt_max_sfm = flt_max_sfm;
clt_parameters.imp.flt_alt = flt_alt;
clt_parameters.imp.flt_show_names = flt_show_names;
clt_parameters.imp.flt_show_overlaps = flt_show_overlaps;
clt_parameters.imp.flt_show_rms = flt_show_rms;
clt_parameters.imp.flt_show_zoom = flt_show_zoom;
clt_parameters.imp.flt_show_alt = flt_show_alt;
}
if (flt_list) {
......@@ -935,7 +982,11 @@ public class ComboMatch {
flt_nan_rms, // boolean nan_rms)
flt_filt_zoom, // boolean filt_zoom,
flt_min_zoom, // int min_zoom,
flt_max_zoom); // int max_zoom)
flt_max_zoom, // int max_zoom)
flt_min_sfm, // double min_sfm,
flt_max_sfm, // double max_sfm,
flt_alt); // int flt_alt)
}
String [] choices_all = maps_collection.textPairs (
......@@ -944,6 +995,8 @@ public class ComboMatch {
flt_show_overlaps, // boolean show_overlap,
flt_show_rms, // boolean show_rms,
flt_show_zoom, // boolean show_zoom,
flt_show_alt, // boolean show_alt,
false, // boolean use_tab,
flt_extra_line); // String extra_line)
GenericJTabbedDialog gdc = new GenericJTabbedDialog("Select image pair",1200,100);
......@@ -1399,6 +1452,77 @@ public class ComboMatch {
return pairwiseOrthoMatch;
}
public static boolean removeDuplicateScenes(
OrthoMapsCollection maps_collection) {
String duplicate_name=null;
do {
HashSet<String> scene_names = new HashSet<String>();
duplicate_name=null;
for (int i = 0; i < maps_collection.ortho_maps.length; i++) {
String name = maps_collection.ortho_maps[i].getName();
if (scene_names.contains(name)) {
duplicate_name=name;
break;
}
scene_names.add(name);
}
if (duplicate_name != null) {
long latest_modified = -1;
int latest_index = -1;
ArrayList<Integer> duplicates = new ArrayList<Integer>();
for (int i = 0; i < maps_collection.ortho_maps.length; i++) {
if (maps_collection.ortho_maps[i].getName().equals(duplicate_name)) {
duplicates.add(i);
long mod_ts = (new File(maps_collection.ortho_maps[i].getPath())).lastModified();
if (mod_ts > latest_modified) {
latest_index = i;
}
}
}
if (duplicates.size()> 1) {
System.out.println("removeDuplicateScenes(): found " +duplicates.size()+ " duplicates for scene "+duplicate_name+":");
for (int i :duplicates) {
System.out.println(((i==latest_index)?" KEEP ":"REMOVE ")+maps_collection.ortho_maps[i].getPath());
}
// combine pairwise matches, remove self-paired
HashMap <String, PairwiseOrthoMatch> combo_pairwise_matches = new HashMap <String, PairwiseOrthoMatch>();
OrthoMap [] new_maps = new OrthoMap[maps_collection.ortho_maps.length-duplicates.size()+1];
int indx = 0;
int new_indx = -1;
for (int i = 0; i < maps_collection.ortho_maps.length; i++) {
if (duplicates.contains(i)) {
for (String key:maps_collection.ortho_maps[i].pairwise_matches.keySet()) {
if (!key.equals(duplicate_name) && !combo_pairwise_matches.containsKey(key)) {
combo_pairwise_matches.put(key, maps_collection.ortho_maps[i].pairwise_matches.get(key));
}
}
}
if (!duplicates.contains(i) || (i == latest_index)) {
if (i == latest_index) {
new_indx = indx;
}
new_maps[indx++] = maps_collection.ortho_maps[i];
}
}
maps_collection.ortho_maps = new_maps;
maps_collection.ortho_maps[new_indx].pairwise_matches = combo_pairwise_matches;
// remove pairs with (now) itself
} else {
System.out.println("removeDuplicateScenes(): BUG - number of duplicates = "+duplicates.size()+", aborting.");
return false;
}
// find latest file
}
} while (duplicate_name != null);
maps_collection.reindex();
return true;
}
public static boolean updateBlBcFileNames(
String suffix,
String before,
......
/**
** OrthoAltitudeMatch - Represent elevation maps matching for pair of scenes
**
** Copyright (C) 2024 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** PairwiseOrthoMatch.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.orthomosaic;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import com.elphel.imagej.calibration.CalibrationFileManagement;
import com.elphel.imagej.cameras.CLTParameters;
import ij.IJ;
public class OrthoAltitudeMatch {
public static boolean altutudeMatchPairs(
CLTParameters clt_parameters,
OrthoMapsCollection orthoMapsCollection,
int [][] available_pairs,
boolean alt_overwrite, // overwrite existing altitude match pairs
boolean alt_pairwise, // use pairwise affines if available
double alt_sigma, // 5.0; Reduce weight of the border tiles, Gaussian sigma in tiles to apply to weights.
double alt_abs_outliers, // = 3.0; // remove absolute outliers when fitting planes
double alt_outliers, // 0.05; Remove outliers when fitting planes, removed fraction.
int alt_refine, // 1; Refine altitude difference plane after removing outliers (0 - no outlier removal, 1 - remove outliers and refine once, ...)
double metric_error,
// log/save parameters
boolean save_each,
boolean log_append,
String log_path,
String orthoMapsCollection_path,
int debugLevel) {
int [] indices = orthoMapsCollection.getScenesFromPairs( // may be shorter, each element - absolute scene number used in pairs
available_pairs, // pairs_defined_abs,// int [][] pairs,
null); // int [] indices_in) // preselected indices or null
boolean updateStatus = true;
int [][] condensed_pairs = orthoMapsCollection.condensePairs (available_pairs, indices);
int alt_zoom_offs = -3; // altitude resolution is 3 steps lower than the images
if (indices.length < 2) {
System.out.println("too few scenes remain: "+indices.length);
return false;
}
OrthoMap[] ortho_maps = orthoMapsCollection.getMaps();
int min_zoom_all = ortho_maps[indices[0]].getOriginalZoomLevel();
int max_zoom_all = ortho_maps[indices[0]].getOriginalZoomLevel();
for (int i = 1; i < indices.length; i++) {
min_zoom_all = Math.min(min_zoom_all, ortho_maps[indices[i]].getOriginalZoomLevel());
max_zoom_all = Math.max(max_zoom_all, ortho_maps[indices[i]].getOriginalZoomLevel());
}
int alt_zoom_lev = Math.min(max_zoom_all+alt_zoom_offs, min_zoom_all);
double pix_size_meters = OrthoMap.getPixelSizeMeters(alt_zoom_lev);
if (debugLevel > 0 ) {
System.out.println("max_zoom="+max_zoom_all+ " min_zoom="+min_zoom_all+" alt_zoom_lev="+alt_zoom_lev);
}
int min_scene = 0; // use if needs to continue (during development)
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("number of scenes pairs\t"+ available_pairs.length+"\n");
sb.append("number of scenes\t"+ indices.length+"\n");
sb.append("minimal zoom level\t"+ min_zoom_all+"\n");
sb.append("maximal zoom level\t"+ max_zoom_all+"\n");
sb.append("altitude zoom level\t"+ alt_zoom_lev+"\n");
// sb.append(String.format("%4s\t%4s\t%17s\t%17s\t%6s\t%3s\t%6s\t%6s\t%7s\n",
// "scn1","scn2","timestamp1","timestamp2","ovrlp","zl","RMS-sp","RMSfin","fzl","removed"));
sb.append(String.format("%4s\t%4s\t%17s\t%17s\t%6s\t%3s\t%6s\t%7s\t%7s\t%7s\n",
"scn1","scn2","timestamp1","timestamp2","ovrlp","zl","RMS","tiltX","tiltY","offs"));
CalibrationFileManagement.saveStringToFile (
log_path, //String path,
sb.toString(), // data,
true); // boolean append)
if (debugLevel>-3) {
System.out.print(sb.toString());
}
}
// create altitude map
int [] wh = new int[2];
int [] origin = new int[2];
boolean show_centers = true;
boolean bounds_to_indices = true;
double [][] centers = show_centers? (new double [indices.length][]): null;
double [][] alt_multi = orthoMapsCollection.renderMultiDouble (
new double [indices.length][] , // 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, // double [][] equalize,
true, // boolean ignore_equalize,
null, // warp, // FineXYCorr warp,,
alt_zoom_lev, // int zoom_level,
wh, // int [] wh,
origin, // int [] origin){ // maps[0] as a reference
centers); // double [][] centers)
final int width = wh[0];
// final int height = wh[1];
// int num_pairs = 0; // available_pairs.length
// ArrayList<Point> failed_pairs = new ArrayList<Point>();
for (int npair = 0; npair < condensed_pairs.length; npair++) {
int [] cpair = condensed_pairs[npair]; // index alt_multi
int [] ipair = {indices[cpair[0]], indices[cpair[1]]};
if (updateStatus) {
IJ.showStatus("Processing pair "+npair+" of "+condensed_pairs.length+" "+ipair[0]+"->"+ipair[1]);
}
/*
PairwiseOrthoMatch pairwiseOrthoMatch = ortho_maps[ipair[0]].getMatch(ortho_maps[ipair[1]].getName(), true).clone(); // ?
double [][] daffine = null;
if (pairwiseOrthoMatch != null) {
double [] enuOffset = ortho_maps[ipair[1]].enuOffsetTo(ortho_maps[ipair[0]]);
double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image
daffine = pairwiseOrthoMatch.getAffine();
if ((daffine != null) && alt_pairwise) {
} else { // combine differential affine from individual
double [][] aff0 = ortho_maps[ipair[0]].getAffine();
double [][] aff1 = ortho_maps[ipair[1]].getAffine();
PairwiseOrthoMatch aff_match = new PairwiseOrthoMatch (
aff0, // double [][] affine0,
aff1, // double [][] affine1,
rd); // double [] rd);
daffine = aff_match.getAffine();
pairwiseOrthoMatch.setAffine(daffine);
}
} else {
System.out.println("BUG: Missing pair for ["+ipair[0]+", "+ipair[1]+"] ");
continue;
}
*/
PairwiseOrthoMatch pairwiseOrthoMatch = ortho_maps[ipair[0]].getMatch(ortho_maps[ipair[1]].getName(), true); // ?
if (pairwiseOrthoMatch == null) {
System.out.println("BUG: Missing pair for ["+ipair[0]+", "+ipair[1]+"] ");
continue;
}
if (ipair[0] < min_scene) {
System.out.println ("Skipping "+ipair[0]+":"+ipair[1]+" until "+min_scene);
continue;
}
if ((pairwiseOrthoMatch.getAltData() != null) && !alt_overwrite) {
System.out.println ("Skipping "+ipair[0]+":"+ipair[1]+" as it has alt_data defined and alt_overwrite == false .");
continue;
}
// pairwiseOrthoMatch is clone() - not anymore
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 [][] alt_slices = {alt_multi[cpair[0]], alt_multi[cpair[1]]};
Rectangle woi_overlap = OrthoMap.getDefinedBounds(
alt_slices, // final double [][] data_slices,
width); // final int width);
double [] diff_data = OrthoMap. subtractWoi(
alt_multi[cpair[0]], // final double [] data0,
alt_multi[cpair[1]], // final double [] data1,
width, // final int width,
woi_overlap); // Rectangle woi_in);
double [] xy0 = {0.5*woi_overlap.width, 0.5*woi_overlap.height};
double [] weight = null; // make it fading?
if (alt_sigma > 0) {
weight = OrthoMap.getBorderWeights(
diff_data, //final double [] data,
alt_sigma, // final double sigma,
woi_overlap.width); // int width)
}
boolean [] mask = null;
double [] alt_data5 = null;
int num_bins = 1000;
for (int ntry = 0; ntry <= alt_refine; ntry++) {
alt_data5 = OrthoMap.getPlane(
diff_data, // final double [] data,
mask, // final boolean [] mask,
weight, // final double [] weight,
woi_overlap.width, // final int width,
xy0); // final double [] xy0) {
if ((alt_outliers > 0) && (ntry < alt_refine)){ // not the last pass
mask = OrthoMap.removeRelativeLowHigh (
diff_data, // final double [] data,
null, // mask, // final boolean [] mask_in, // new mask for all data and latest plane
alt_abs_outliers, // final double abs_diff,
alt_outliers, // final double rel_frac,
alt_data5, // final double [] ground_plane, // tiltx,tilty, offs, x0(pix), y0(pix) or null
woi_overlap.width, // final int width, // only used with ground_plane != null;
num_bins); // final int num_bins)
} else {
break;
}
}
// double [] alt_data = new double[3];
// System.arraycopy(alt_data5, 0, alt_data, 0, alt_data.length);
double [] alt_data = {alt_data5[0]/pix_size_meters, alt_data5[1]/pix_size_meters,alt_data5[2]};
pairwiseOrthoMatch.setAltData(alt_data);
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\t%6.4f\t%7.4f\t%7.4f\t%7.3f\n",
ipair[0], ipair[1], ortho_maps[ipair[0]].getName(), ortho_maps[ipair[1]].getName(),
pairwiseOrthoMatch.overlap, pairwiseOrthoMatch.zoom_lev, pairwiseOrthoMatch.rms,
pairwiseOrthoMatch.alt_data[0],pairwiseOrthoMatch.alt_data[1],pairwiseOrthoMatch.alt_data[2]));
CalibrationFileManagement.saveStringToFile (
log_path, //String path,
sb.toString(), // data,
true); // boolean append)
}
//pix_size_meters
/*
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
double overlap_frac = pairwiseOrthoMatch.getOverlap(); // .pairsGraph.getOverlap(next_pair);
// unityAffine()
// use unityAffine() for 0; getaffine for second?
double [][] affine0 = OrthoMapsCollection.unityAffine(); // ortho_maps[ipair[0]].getAffine();
double [][] affine1 = daffine; // ortho_maps[ipair[1]].getAffine();
double [][][] affines = new double[][][] {affine0,affine1};
boolean success = true;
Point pair = new Point(ipair[0],ipair[1]);
double lores_rms = pairwiseOrthoMatch.getRMS();
// high-res
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};//
pull_skew, // double pull_skew, // ~rotation, = 0 fraction of the total weight == 1
pull_tilt, // double pull_tilt, // > 0
pull_scale, // double pull_scale, // = 0
debugLevel-4); // final int debugLevel)
*/
}
if (orthoMapsCollection_path != null) {
try {
orthoMapsCollection.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 @@ import java.util.concurrent.atomic.AtomicInteger;
import com.elphel.imagej.cameras.CLTParameters;
import com.elphel.imagej.common.DoubleFHT;
import com.elphel.imagej.common.DoubleGaussianBlur;
import com.elphel.imagej.common.GenericJTabbedDialog;
import com.elphel.imagej.common.PolynomialApproximation;
import com.elphel.imagej.common.ShowDoubleFloatArrays;
......@@ -3467,6 +3468,141 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{
ImageDtt.startAndJoin(threads);
return mask;
}
/**
* Remove specified fractions (0 <= (rlow+rhigh) <= 1.0) of too low and too high values
* @param data double [] input data
* @param mask_in optional (may be null) mask for the data array. Will be modified if provided.
* @param abs_dif high limit of the histogram (reasonably higher than useful range of the data[])
* @param rel_frac fraction of the highest data[] elements to remove
* @param ground_plane null or {tilt_x, tilt_y, offs, x0,y0}, where tilt_x and tilt_y are per pixel.
* x0, y0 are also in pixels (not meters)
* @param width - width of the data[] array. Only used if ground_plane != null
* @param num_bins number of the histogram bins
* @return boolean array of the remaining data elements. Input mask_in array (if not null) is modified too.
*/
public static boolean [] removeRelativeLowHigh (
final double [] data,
final boolean [] mask_in,
final double abs_diff,
final double rel_frac,
final double [] ground_plane, // tiltx,tilty, offs, x0(pix), y0(pix) or null
final int width, // only used with ground_plane != null;
final int num_bins) {
final boolean [] mask = (mask_in == null) ? new boolean [data.length] : mask_in;
if (mask_in == null) {
Arrays.fill(mask, true);
}
final Thread[] threads = ImageDtt.newThreadArray();
final AtomicInteger ai = new AtomicInteger(0);
final AtomicInteger ati = new AtomicInteger(0);
final double [][] hist2 = new double [threads.length][num_bins];
final double scale = num_bins/abs_diff;
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
int thread_num = ati.getAndIncrement();
for (int nPix = ai.getAndIncrement(); nPix < data.length; nPix = ai.getAndIncrement()) if (mask[nPix]){
double d = data[nPix];
if (!Double.isNaN(d)) {
if (ground_plane != null) {
double x = nPix % width - ground_plane[3];
double y = nPix/width - ground_plane[4];
double tilt_x = ground_plane[0];
double tilt_y = ground_plane[1];
double offs = ground_plane[2];
d -= x * tilt_x + y * tilt_y + offs;
}
int bin = Math.min(Math.max(((int) Math.round(Math.abs(d)*scale)), 0), num_bins-1);
hist2[thread_num][bin] += 1.0;
}
}
}
};
}
ImageDtt.startAndJoin(threads);
ai.set(0);
final double [] hist = new double [num_bins];
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
for (int bin = ai.getAndIncrement(); bin < num_bins; bin = ai.getAndIncrement()) {
for (int i = 0; i < hist2.length; i++) {
hist[bin]+= hist2[i][bin];
}
}
}
};
}
ImageDtt.startAndJoin(threads);
double sw = 0;
for (int bin = 0; bin < num_bins; bin++) {
sw += hist[bin];
}
// double trlow = sw * rlow;
double trhigh = sw * rel_frac; // rhigh;
double sh = 0, shp = 0;
double threshold_high = abs_diff; // abs_high;
for (int bin = num_bins-1; bin>=0; bin--) {
shp = sh;
sh += hist[bin];
if (sh > trhigh) {
double r = (sh-trhigh)/(sh-shp);
threshold_high = (bin + r)/scale; // abs_low + (bin + r)/scale;
break;
}
}
sh = 0;
shp = 0;
/*
double threshold_low = abs_low;
for (int bin = 0; bin < num_bins; bin++) {
shp = sh;
sh += hist[bin];