package com.elphel.imagej.orthomosaic; import java.awt.Point; import java.awt.Rectangle; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; 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.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import com.elphel.imagej.calibration.CalibrationFileManagement; import com.elphel.imagej.cameras.CLTParameters; import com.elphel.imagej.common.DoubleGaussianBlur; import com.elphel.imagej.common.GenericJTabbedDialog; import com.elphel.imagej.common.PolynomialApproximation; import com.elphel.imagej.common.ShowDoubleFloatArrays; import com.elphel.imagej.gpu.GPUTileProcessor; import com.elphel.imagej.gpu.TpTask; import com.elphel.imagej.tileprocessor.ImageDtt; import com.elphel.imagej.tileprocessor.IntersceneMatchParameters; import com.elphel.imagej.tileprocessor.OpticalFlow; import com.elphel.imagej.tileprocessor.TDCorrTile; import com.elphel.imagej.tileprocessor.TileNeibs; import ij.IJ; import ij.ImagePlus; import ij.ImageStack; import ij.Prefs; import ij.gui.PointRoi; import ij.io.FileSaver; import ij.process.FloatPolygon; import ij.text.TextWindow; public class OrthoMapsCollection implements Serializable{ private static final long serialVersionUID = 1L; public static final int MODE_IMAGE = 0; public static final int MODE_ALT = 1; public static final int MODE_MASK = 2; public static final int VERSION_PRE_ORIENT = 100; public static final int VERSION_POST_ORIENT = 101; public static final int VERSION_ORANGE = 102; public static int LATEST_VERSION = VERSION_ORANGE; // 100; // use when read from .list /// public transient int current_version = LATEST_VERSION; public static int CURRENT_VERSION = LATEST_VERSION; /* public static final int PAIR_NONE = 0; public static final int PAIR_DEFINED = 1; public static final int PAIR_UNDEFINED = 2; public static final int PAIR_FAILED = -1; public static final int HEUR_LAST_SEQ = 1; // use last of connected following this public static final int HEUR_DIV_LONG = 2; // divide long connected series public static final int HEUR_MIN_DIA = 4; // minimal diameter increase (or no increase) public static final int HEUR_SAME_DIA = 8; // minimal diameter increase (or no increase) */ public static final String [] KEY_DIRS= {"rootDirectory", // from EyesisCorrectionParameters "sourceDirectory","linkedModels","videoDirectory","x3dDirectory","resultsDirectory","scenesDirectory", "kernelsDirectory", "patternsDirectory"}; public static final String [] NAME_FH = {"full","half"}; public static final String [] NAME_MO = {"main","other"}; public OrthoMap [] ortho_maps; private HashMap<String,ModelRegex> model_regexes; private String current_model_regex=""; private ArrayList<AltitudeMismatchKernel> kernels; private ArrayList<GroundObjectPattern> patterns; // real transient: public transient long version = -1; private transient HashMap<Double,Integer> map_index; private transient HashMap<String,Integer> map_index_string; private void writeObject(ObjectOutputStream oos) throws IOException { // ? oos.defaultWriteObject(); oos.writeObject(AltitudeMismatchKernel.getKernelsDirectory()); // oos.writeObject(kernels); oos.writeObject(GroundObjectPattern.getPatternsDirectory()); // oos.writeObject(patterns); // oos.writeObject(path); // ortho_maps is not transient } // @SuppressWarnings("unchecked") private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); AltitudeMismatchKernel.setKernelsDirectory((String) ois.readObject()); // kernels = (ArrayList<AltitudeMismatchKernel>) ois.readObject(); GroundObjectPattern.setPatternsDirectory((String) ois.readObject()); // patterns = (ArrayList<GroundObjectPattern>) ois.readObject(); reindex(); // will recreate map_index, map_index_string // lla is not transient } public OrthoMapsCollection( String path, // lit file OrthoMapsCollection orthoMapsCollection) { // if null will create new, if not null - will update other if (path.endsWith(".list")) { // String[] scenes0 = new String[1]; kernels = new ArrayList<AltitudeMismatchKernel>(); patterns = new ArrayList<GroundObjectPattern>(); model_regexes = new HashMap<String,ModelRegex>(); String [] regex_use_p = new String[1]; String[] paths = getPathsAndScenesFromSourceList( path, null, // scenes0, kernels, // ArrayList<AltitudeMismatchKernel> kernels, patterns, // ArrayList<GroundObjectPattern> patterns); model_regexes, // ArrayList<ModelRegex> model_regexes, regex_use_p, // String [] regex_use) { null, // ArrayList<AffineImport> affine_import, null); // ArrayList<Affine2Import> affine2_import) { current_model_regex = regex_use_p[0]; paths = ModelRegex.removeDuplicatesSort(paths); String [] orig_paths = paths.clone(); boolean [] replaced = new boolean [paths.length]; if (current_model_regex != null) { ModelRegex regex = model_regexes.get(current_model_regex); int num_replaced = 0; int num_changed = 0; if (regex != null) { for (int i = 0; i < paths.length;i++) { String replacement = regex.replaceByFilter(paths[i]); if (replacement != null) { paths[i] = replacement; replaced[i] = true; num_replaced++; if (!replacement.equals(orig_paths[i])) { num_changed++; } } } System.out.println(String.format("Replacing with regex \"%s\" (%s)", regex.name, regex.regex)); System.out.println(String.format("Replaced %d files (of %d), changed %d.", num_replaced, paths.length,num_changed)); } else { System.out.println("Replacement regex \""+current_model_regex+"\" does not exist"); } } // String scenes_path = scenes0[0]; // not used! ArrayList<String> new_paths = new ArrayList<String>(); if (orthoMapsCollection != null) { // 1. update existing maps for (int i = 0; i < paths.length; i++) { String name = OrthoMap.getNameFromPath(paths[i]); int indx = orthoMapsCollection.getIndex(name); if (indx >= 0) { OrthoMap omap = orthoMapsCollection.ortho_maps[indx]; omap.orig_image = new FloatImageData(path); // always replace (nothing to preserve /// omap.dt = omap.orig_image.getDT(); // just in case? // see if alt image is already initialized if (omap.alt_image != null) { FloatImageData alt_image = OrthoMap.getAlt(path); if (alt_image != null) { omap.alt_image = alt_image; } else { System.out.println("New altitude file does not exist, keeping old one: "+omap.alt_image.path); } } } else { new_paths.add(paths[i]); } } // 2. add new maps (none will be removed) if (!new_paths.isEmpty()) { // need to create new maps, increase orthoMapsCollection.ortho_maps, reorder and reindex it int num_old_maps = orthoMapsCollection.ortho_maps.length; int num_new_maps = new_paths.size(); ortho_maps = new OrthoMap[num_old_maps+num_new_maps]; System.arraycopy( orthoMapsCollection.ortho_maps, 0, ortho_maps, 0, num_old_maps); int map_index = num_old_maps; for (String scene_path: new_paths) { ortho_maps[map_index] = new OrthoMap(scene_path); // , scene_path); ortho_maps[map_index++].setAffine(); } Arrays.sort(ortho_maps); // mixed old+new, previous scene numbers will no longer be valui orthoMapsCollection.ortho_maps = ortho_maps; orthoMapsCollection.reindex(); } else { System.out.println("No new scenes added"); } return; // Constructed this should not be used, only the old orthoMapsCollection } ortho_maps = new OrthoMap[paths.length]; for (int n = 0; n < ortho_maps.length; n++) { ortho_maps[n] = new OrthoMap(paths[n]); // , scene_path); ortho_maps[n].setAffine(); } Arrays.sort(ortho_maps); reindex(); } else { System.out.println("OrthoMapsCollection(): path should end with \".list\""); } } public OrthoMap getMap(String name) { int indx = getIndex(name); if (indx < 0) return null; return ortho_maps[indx]; } public static double [][] unityAffine() { return new double [][] {{1,0,0},{0,1,0}}; } public void updateFiles ( String path) { if (path.endsWith(".list")) { new OrthoMapsCollection(path, this); // will update this instead of creating a new instance } else { System.out.println("updateFiles(): path should end with \".list\""); } } // add update kernels, update patterns public void updateKernels ( String path) { if (path.endsWith(".list")) { kernels = new ArrayList<AltitudeMismatchKernel>(); getPathsAndScenesFromSourceList( path, null, kernels, // ArrayList<AltitudeMismatchKernel> kernels, null, // ArrayList<GroundObjectPattern> patterns); null, // ArrayList<ModelRegex> model_regexes, null, // String [] regex_use) { null, // ArrayList<AffineImport> affine_import, null); // ArrayList<Affine2Import> affine2_import) { } else { System.out.println("updateKernels(): path should end with \".list\""); } } public void updatePatterns ( String path) { if (path.endsWith(".list")) { patterns = new ArrayList<GroundObjectPattern>(); getPathsAndScenesFromSourceList( path, null, null, // ArrayList<AltitudeMismatchKernel> kernels, patterns, // ArrayList<GroundObjectPattern> patterns); null, // ArrayList<ModelRegex> model_regexes, null, // String [] regex_use) { null, // ArrayList<AffineImport> affine_import, null); // ArrayList<Affine2Import> affine2_import) { } else { System.out.println("updatePatterns(): path should end with \".list\""); } } static String getSceneDir(String scenes_path, String name) { File [] scene_dirs = (new File(scenes_path)).listFiles(); // may contain non-directories, will be filtered by filterScenes //scene_dirs[3].listFiles() Arrays.sort(scene_dirs); for (int i = scene_dirs.length-1; i>=0; i--) { if (scene_dirs[i].isDirectory()) { String dirname = scene_dirs[i].getName(); if (name.compareTo(dirname) >= 0) { // verify it has needed scene File [] segment = scene_dirs[i].listFiles(); for (int j = 0; j < segment.length; j++) { if (segment[j].getName().equals(name)) { return segment[j].toString(); } } } } } return null; } public int reindex() { map_index = new HashMap<Double,Integer>(); map_index_string = new HashMap<String,Integer>(); for (int i = 0; i < ortho_maps.length; i++) { map_index.put(ortho_maps[i].getTimeStamp(), i); map_index_string.put(ortho_maps[i].name,i); } // updateNumberScenes(); return ortho_maps.length; } OrthoMap [] getMaps() { return ortho_maps; } public String[] getNames() { String [] names = new String [ortho_maps.length]; for (int n = 0; n < ortho_maps.length; n++) { names[n] = ortho_maps[n].getName(); } return names; } public void getAllTemperatures() { for (int n = 0; n < ortho_maps.length; n++) { ortho_maps[n].getTemperature(); ortho_maps[n].getTemperatureAndTelemetry(); } } public void updateLLA() { // make automatic by saving/testing file modification stamp? for (int n = 0; n < ortho_maps.length; n++) { if (n == 530) { System.out.println("updateLLa(): n="+n); } ortho_maps[n].updateLLA(); IJ.showStatus("Updating LLA "+n+" of "+ortho_maps.length); IJ.showProgress(1.0*n/ortho_maps.length); } updateAGL(); } public void updateAGL() { for (int n = 0; n < ortho_maps.length; n++) { IJ.showStatus("Updating AGL "+n+" of "+ortho_maps.length); ortho_maps[n].agl = Double.NaN; ortho_maps[n].agl = ortho_maps[n].getAGL(); IJ.showProgress(1.0*n/ortho_maps.length); } } public void updateNumberScenes() { for (int n = 0; n < ortho_maps.length; n++) { ortho_maps[n].num_scenes = -1; ortho_maps[n].num_scenes = ortho_maps[n].getNumberScenes(); } } public void updateSfmGain() { for (int n = 0; n < ortho_maps.length; n++) { ortho_maps[n].sfm_gain = Double.NaN; ortho_maps[n].sfm_gain = ortho_maps[n].getSfmGain(); } } /* public static String[] getPathsFromSorceList( String files_list) { return getPathsAndScenesFromSourceList(files_list, null); } */ public static String[] getPathsAndScenesFromSourceList( String files_list, String[] scenes0, ArrayList<AltitudeMismatchKernel> kernels, ArrayList<GroundObjectPattern> patterns, HashMap<String,ModelRegex> model_regexes, String [] regex_use, ArrayList<AffineImport> affine_import, ArrayList<Affine2Import> affine2_import) { List<String> lines; List<String> rel_files = new ArrayList<String>(); Path seq_path = Paths.get(files_list); try { lines = Files.readAllLines(seq_path, StandardCharsets.UTF_8); // lines.stream().forEach(System.out::println); } catch (IOException e) { e.printStackTrace(); return null; } Path base_path = seq_path.getParent(); HashMap<String,String> dir_map = new HashMap<String,String>(); for (String line:lines){ if (line.split("#").length > 0) { String[] tokens = line.split("#")[0].trim().split("[\\s,;=]+"); if ((tokens.length > 2) && (tokens[0].toUpperCase().equals("SET"))) { parse_set: { for (String dir_name:KEY_DIRS) if (dir_name.equals(tokens[1])) { dir_map.put(dir_name,tokens[2]); System.out.println("Parsed SET: "+tokens[1]+" in line: "+line); break parse_set; } System.out.println("*********** Unknown SET: "+tokens[1]+" in line: "+line); } } else if ((tokens.length > 0) && (tokens[0].toUpperCase().equals("KERNEL"))) { if ((kernels != null) && (tokens.length > 4)) { kernels.add(new AltitudeMismatchKernel( tokens[1], Integer.parseInt(tokens[2]), Double.parseDouble(tokens[3]), Double.parseDouble(tokens[4]))); } } else if ((tokens.length > 0) && (tokens[0].toUpperCase().equals("PATTERN"))) { if ((patterns != null) && (tokens.length > 4)) { patterns.add(new GroundObjectPattern( tokens[1], Integer.parseInt(tokens[2]), tokens[3], LocalDateTime.parse(tokens[4]))); } } else if ((tokens.length > 0) && (tokens[0].toUpperCase().equals("REGEX"))) { if ((model_regexes != null) && (tokens.length > 4)) { model_regexes.put(tokens[1],new ModelRegex( tokens[1], // String name, tokens[2], // String regex, LocalDateTime.parse(tokens[3]), // LocalDateTime start_date, LocalDateTime.parse(tokens[4]))); // LocalDateTime end_date); } } else if ((tokens.length > 0) && (tokens[0].toUpperCase().equals("REGEX_USE"))) { if ((regex_use != null) && (tokens.length > 1)) { regex_use[0] = tokens[1]; } } else if ((tokens.length > 0) && AffineImport.matches(tokens)) { if (affine_import != null) { affine_import.add(new AffineImport(tokens)); } } else if ((tokens.length > 0) && Affine2Import.matches(tokens)) { if (affine2_import != null) { affine2_import.add(new Affine2Import(tokens)); } } else if ((tokens.length == 1) && (tokens[0].length() > 0)) { rel_files.add(tokens[0]); } } } if (dir_map.containsKey("rootDirectory")) { base_path=base_path.resolve(Paths.get(dir_map.get("rootDirectory"))); File base_dir = new File(base_path.toString()); } String sourceDirectory = base_path.toString(); // set sourceDirectory: if (dir_map.containsKey("sourceDirectory")) { sourceDirectory=(base_path.resolve(Paths.get(dir_map.get("sourceDirectory")))).toString(); } Path source_path = Paths.get(sourceDirectory); // File source_dir = new File(source_path.toString()); String [] paths = new String[rel_files.size()]; for (int i = 0; i < paths.length; i++) { paths[i] = (source_path.resolve(Paths.get(rel_files.get(i)))).toString(); } if (dir_map.containsKey("scenesDirectory")) { if (scenes0 != null) { scenes0[0]=(base_path.resolve(Paths.get(dir_map.get("scenesDirectory")))).toString(); } } String kernelsDirectory = base_path.toString(); // set sourceDirectory: if (dir_map.containsKey("kernelsDirectory")) { kernelsDirectory=(base_path.resolve(Paths.get(dir_map.get("kernelsDirectory")))).toString(); } AltitudeMismatchKernel.setKernelsDirectory(kernelsDirectory); String patternsDirectory = base_path.toString(); // set sourceDirectory: if (dir_map.containsKey("patternsDirectory")) { patternsDirectory=(base_path.resolve(Paths.get(dir_map.get("patternsDirectory")))).toString(); } GroundObjectPattern.setPatternsDirectory(patternsDirectory); return paths; } /** * Get rectified bounds of all provided ortho images relative to the origin (vertical * point) of the first one in pixels at the provided zoom level. Center of the first * image correspond to the {0,0} point from which the bounds are counted * @param zoom_level zoom level - 0 corresponds to 1pix=1cm scale, +1 - to 1pix = 0.5cm * @return {{min_x,max_x},{min_y, max_y}} relative to the origin of the first image */ public int [][] getBoundsPixels( int zoom_level, // maps[0] as a reference int [] indices) { double [][] bounds_meters = getBoundsMeters(indices); double pix_size = OrthoMap.getPixelSizeMeters (zoom_level); int [][] bounds_pix = new int[2][2]; for (int n = 0; n < bounds_pix.length; n++) { bounds_pix[n][0] = (int) Math.floor(bounds_meters[n][0]/pix_size); bounds_pix[n][1] = (int) Math.ceil (bounds_meters[n][1]/pix_size); } return bounds_pix; } public int [][] getBoundsPixels( int zoom_level, // maps[0] as a reference int [] indices, double [][][] affines) { double [][] bounds_meters = (affines == null) ? getBoundsMeters(indices):getBoundsMeters(indices,affines); double pix_size = OrthoMap.getPixelSizeMeters (zoom_level); int [][] bounds_pix = new int[2][2]; for (int n = 0; n < bounds_pix.length; n++) { bounds_pix[n][0] = (int) Math.floor(bounds_meters[n][0]/pix_size); bounds_pix[n][1] = (int) Math.ceil (bounds_meters[n][1]/pix_size); } return bounds_pix; } public int [][] getBoundsPixels( int zoom_level) { // maps[0] as a reference return getBoundsPixels(zoom_level, null); } //double [][][] affines /** * Get rectified bounds of all provided ortho images relative to the origin (vertical * point) of the first one in meters * @return {{min_x,max_x},{min_y,max_y}} bounds that include all provided maps * relative to the origin (vertical point) of the first image */ /* public double [][] getBoundsMeters(){ // maps[0] as a reference double [][] bounds = ortho_maps[0].getBoundsMeters(true); for (int nmap = 1; nmap < ortho_maps.length; nmap++) { double [][] bounds_other = ortho_maps[nmap].getBoundsMeters(true); double [] enuOffset = ortho_maps[0].enuOffsetTo(ortho_maps[nmap]); double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image for (int n = 0; n < bounds.length; n++) { bounds[n][0] = Math.min(bounds[n][0],bounds_other[n][0]+ rd[n]); bounds[n][1] = Math.max(bounds[n][1],bounds_other[n][1]+ rd[n]); } } return bounds; } */ public double [][] getBoundsMeters(){ // maps[0] as a reference return getBoundsMeters(null); } /** * Get rectified bounds of all (if indices==null) or selected by indices[] * provided ortho images relative to the origin (vertical * point) of the first one in meters * @param indices - image indices to process or null to process all images * @return {{min_x,max_x},{min_y,max_y}} bounds that include all provided maps * relative to the origin (vertical point) of the first image */ public double [][] getBoundsMeters(int [] indices){ // maps[0] as a reference if (indices == null) { indices = new int [ortho_maps.length]; for (int i = 0; i < indices.length; i++) { indices[i] = i; } } double [][] bounds = ortho_maps[indices[0]].getBoundsMeters(true); for (int imap = 1; imap < indices.length; imap++) { int nmap = indices[imap]; double [][] bounds_other = ortho_maps[nmap].getBoundsMeters(true); double [] enuOffset = ortho_maps[indices[0]].enuOffsetTo(ortho_maps[nmap]); double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image for (int n = 0; n < bounds.length; n++) { bounds[n][0] = Math.min(bounds[n][0],bounds_other[n][0]+ rd[n]); bounds[n][1] = Math.max(bounds[n][1],bounds_other[n][1]+ rd[n]); } } return bounds; } public double [][] getBoundsMeters( int [] indices, double [][][] affines){ // maps[0] as a reference if (indices == null) { indices = new int [ortho_maps.length]; for (int i = 0; i < indices.length; i++) { indices[i] = i; } } if (affines == null) { affines = new double [ortho_maps.length][][]; for (int i = 0; i < affines.length; i++) { affines[i] = ortho_maps[indices[i]].affine; } } double [][] bounds = ortho_maps[indices[0]].getBoundsMeters(true,affines[0]); for (int imap = 1; imap < indices.length; imap++) { int nmap = indices[imap]; double [][] bounds_other = ortho_maps[nmap].getBoundsMeters(true,affines[imap]); double [] enuOffset = ortho_maps[indices[0]].enuOffsetTo(ortho_maps[nmap]); double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image for (int n = 0; n < bounds.length; n++) { bounds[n][0] = Math.min(bounds[n][0],bounds_other[n][0]+ rd[n]); bounds[n][1] = Math.max(bounds[n][1],bounds_other[n][1]+ rd[n]); } } return bounds; } /** * Get rectified overlap bounds of two provided ortho images relative to the origin (vertical * point) of the first one in meters. Use specified affine transforms, not saved with the orto map * @param ref_index - index of the first (reference) map * @param other_index - index of the second (other) map * @param ref_affine - specified affine transform of the reference image (referenced to its vertical_point) * @param other_affine - specified affine transform of the other image (referenced to its vertical_point) * @return {{min_x,max_x},{min_y,max_y}} bounds that include an overlap of the two provided maps * relative to the origin (vertical point) of the first image. * Returns null if there is no overlap */ private double [][] getOverlapMeters( int ref_index, int other_index, double [][] ref_affine, double [][] other_affine){ double [][] bounds = ortho_maps[ref_index].getBoundsMeters(true,ref_affine); double [][] bounds_other = ortho_maps[other_index].getBoundsMeters(true,other_affine); double [] enuOffset = ortho_maps[ref_index].enuOffsetTo(ortho_maps[other_index]); double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image for (int n = 0; n < bounds.length; n++) { bounds[n][0] = Math.max(bounds[n][0],bounds_other[n][0]+ rd[n]); bounds[n][1] = Math.min(bounds[n][1],bounds_other[n][1]+ rd[n]); if (bounds[n][0] > bounds[n][1]) { return null; } } return bounds; } /** * Use object coordinates (now metric) on the composite image to * determine corresponding pixel coordinates of the source images * @param objects ArrayList of objects with the scene name and xy coordinates * in the composite image. Will fill in pixel coordinates of the objects * @param zoom_level zoom level of the composite image */ public void reverseRender( ArrayList<ObjectLocation> objects, int zoom_level, int [] indices){ // null or selected image indices int [][] bounds = getBoundsPixels( // should be for rectified, {-bounds[0][0], -bounds[0][1]} - exact center zoom_level, indices); int width = bounds[0][1] - bounds[0][0]; // bounds[x][0] - negative int height = bounds[1][1] - bounds[1][0]; /* int [] indices = new int [ortho_maps.length]; //maybe will get rid of for (int i = 0; i < indices.length; i++) { indices[i] = i; } */ final int reference_index = (indices == null)?0:indices[0]; for (ObjectLocation ol: objects) { int nmap =getIndex(ol.getName()); final double scale = 1.0/OrthoMap.getPixelSizeMeters(zoom_level); final double src_scale = 1.0/OrthoMap.getPixelSizeMeters(ortho_maps[nmap].getImage().getZoomLevel()); // pix per meter double [][] mbounds = ortho_maps[nmap].getBoundsMeters(true); // keep original bounds double [] enu_offset = ortho_maps[reference_index].enuOffsetTo(ortho_maps[nmap]); double [] scaled_out_center = { // xy center to apply affine to -bounds[0][0] + scale * enu_offset[0], -bounds[1][0] - scale * enu_offset[1]}; double [] metric_center = { // back to meters to match objects scaled_out_center[0]/scale, scaled_out_center[1]/scale}; double dX = ol.getMetric()[0]-metric_center[0]; // matching dX, dY in renderMulti() double dY = ol.getMetric()[1]-metric_center[1]; // final int [][] obounds = new int [2][2]; // output (rectified, combined) image bounds, relative to thje top-left // for (int n = 0; n< 2; n++) { // obounds[n][0] = (int) Math.floor(scaled_out_center[n] + scale*mbounds[n][0]); // obounds[n][1] = (int) Math.ceil (scaled_out_center[n] + scale*mbounds[n][1]); // } // Output window size (keep original affine - OK if will not exactly fit) // final int ownd_width = obounds[0][1] - obounds[0][0]; // final int ownd_height = obounds[1][1] - obounds[1][0]; // final int ownd_len = ownd_width * ownd_height; double [][] src_bounds=ortho_maps[nmap].getBoundsMeters (true); // using original affines final double [] src_center = {-src_bounds[0][0],-src_bounds[1][0]}; // x,y center offset in the source image final double [][] affine = ortho_maps[nmap].affine; // only here use provided // final int src_width = ortho_maps[nmap].getImageData().width; // final int src_height = ortho_maps[nmap].getImageData().height; // final float [] src_img = ortho_maps[nmap].getImageData().data; // FIXME: will not use double [] xy_src = { // pixels of the source image src_scale * (affine[0][0]*dX + affine[0][1]*dY + affine[0][2] + src_center[0]), src_scale * (affine[1][0]*dX + affine[1][1]*dY + affine[1][2] + src_center[1])}; ol.setPixels(xy_src); } } public ImagePlus renderMulti ( String title, int eq_mode, // 0 - ignore equalization, 1 - use stored equalization, 2 - calculate equalization int [] indices, // null or which indices to use (normally just 2 for pairwise comparison) boolean bounds_to_indices, int temp_mode, // 0 - do nothing, 1 - equalize average,2 - try to correct double [][][] affines, // null or [indices.length][2][3] FineXYCorr warp, // use for a single pair only boolean show_centers, int zoom_level, int [] origin){ boolean show_2d_histogram = true; // false; int num_images = (indices != null)? indices.length : ortho_maps.length; int [] wh = new int[2]; double [][] centers = new double [(indices !=null)? indices.length: ortho_maps.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, affines, // affines, // double [][][] affines, // null or [indices.length][2][3] null, // double [][] equalize, (eq_mode != 1), // boolean ignore_equalize, null, // warp, // FineXYCorr warp,, zoom_level, // int zoom_level, wh, // int [] wh, origin, // int [] origin){ // maps[0] as a reference centers); // double [][] centers) if ((eq_mode == 2) && (num_images == 2)) { boolean [] mask = null; if (show_2d_histogram) { mask = new boolean [dmulti[0].length]; // int hist_min = -140,hist_max=135; // 700,200 // int hist_min = -100,hist_max=100; // 700,200 int hist_min = -350, hist_max=350; // 700,200 int hist_width= hist_max-hist_min; double [] hist = new double [hist_width*hist_width]; for (int i = 0; i < dmulti[0].length; i++) if (!Double.isNaN(dmulti[0][i]) && !Double.isNaN(dmulti[1][i])){ int x = (int) Math.round(dmulti[0][i]) - hist_min; int y = (int) Math.round(dmulti[1][i]) - hist_min; if ((x>=0) && (y>=0) && (x < hist_width) && (y < hist_width)) { int indx = y*hist_width+x; hist[indx] += 1.0; mask[i] = true; } } ShowDoubleFloatArrays.showArrays( hist, hist_width, hist_width, title+"hist_"+hist_min+"_"+hist_max); } double [] regression = PolynomialApproximation.getOrthoRegression( dmulti[0], // final double [] data_x, dmulti[1], // final double [] data_y, mask); // final boolean [] mask) double [] inv_regression = PolynomialApproximation.invertRegression(regression); double [] regression10 = PolynomialApproximation.getOrthoRegression( dmulti[1], // final double [] data_x, dmulti[0], // final double [] data_y, mask); // final boolean [] mask) /* double [] dmulti1 = dmulti[1].clone(); double [] regression00 = PolynomialApproximation.getOrthoRegression( dmulti[0], // final double [] data_x, dmulti[0], // final double [] data_y, mask); // final boolean [] mask) double [] regression11 = PolynomialApproximation.getOrthoRegression( dmulti[1], // final double [] data_x, dmulti[1], // final double [] data_y, mask); // final boolean [] mask) */ PolynomialApproximation.applyRegression( dmulti[1], // dmulti1, // final double [] data, // clone by caller inv_regression); // final double [] regression) { /* double [] regression01 = PolynomialApproximation.getOrthoRegression( dmulti[0], // final double [] data_x, dmulti1, // final double [] data_y, mask); // final boolean [] mask) double [] dmulti12 = dmulti[1].clone(); for (int i = 0; i < dmulti12.length; i++) { dmulti12[i] = 2.0*dmulti12[i]+10; } double [] regression012 = PolynomialApproximation.getOrthoRegression( dmulti[0], // final double [] data_x, dmulti12, // final double [] data_y, mask); // final boolean [] mask) double [] dmulti13 = dmulti[1].clone(); for (int i = 0; i < dmulti13.length; i++) { dmulti13[i] = dmulti13[i]+10; } double [] regression013 = PolynomialApproximation.getOrthoRegression( dmulti[0], // final double [] data_x, dmulti13, // final double [] data_y, mask); // final boolean [] mask) double [] dmulti14 = dmulti[1].clone(); for (int i = 0; i < dmulti14.length; i++) { dmulti14[i] = 2*dmulti14[i]; } double [] regression014 = PolynomialApproximation.getOrthoRegression( dmulti[0], // final double [] data_x, dmulti14, // final double [] data_y, mask); // final boolean [] mask) double [] regression_single = getYXRegression( dmulti[0], // final double [] data_x, dmulti[1]); // final double [] data_y, */ System.out.println(); } String [] map_names = new String[num_images + (((num_images==2))? 1 : 0)]; // float [][] extra_multi = new float [map_names.length][]; double [][] extra_multi = new double [map_names.length][]; for (int n = 0; n <num_images; n++) { int mapn= (indices != null)? indices[n] : n; map_names[n] = ortho_maps[mapn].getName()+"_"+ortho_maps[mapn].getLocalDateTime().toString().replace("T","_")+"_UTC"; map_names[n] += String.format(" raw=%7.1f", ortho_maps[mapn].getTemperature()); extra_multi[n] = dmulti[n]; } if (map_names.length > num_images) { // inefficient way, only for display // correct(offset) pixel values relative to multi[0] int map0= (indices != null)? indices[0] : 0; for (int n = 1; n < num_images; n++) { //temp_mode, // 0 - do nothing, 1 - equalize average,2 - try to correct int mapn= (indices != null)? indices[n] : n; double offs = 0; switch (temp_mode) { case 1: offs = ortho_maps[map0].getAveragePixel()-ortho_maps[mapn].getAveragePixel(); break; case 2: offs = ortho_maps[mapn].getTemperature()-ortho_maps[mapn].getAveragePixel() -(ortho_maps[map0].getTemperature()-ortho_maps[map0].getAveragePixel()); break; } // float foffs= (float) offs; for (int i = 0; i < extra_multi[n].length; i++) if (!Double.isNaN(extra_multi[n][i])) { extra_multi[n][i]+= offs; } } map_names[num_images] = "Diff 0-1"; extra_multi[num_images] = new double[extra_multi[0].length]; for (int i = 0; i < extra_multi[num_images].length; i++) { extra_multi[num_images][i] = extra_multi[1][i]-extra_multi[0][i]; } } ImageStack stack; stack = ShowDoubleFloatArrays.makeStack( extra_multi, wh[0], wh[1], map_names, false); ImagePlus imp = new ImagePlus(title, stack); if (show_centers) { PointRoi roi = new PointRoi(); for (int i = 0; i < centers.length; i++) { roi.addPoint(centers[i][0],centers[i][1], i+1); } roi.setOptions("label"); imp.setRoi(roi); } imp.show(); // debugging return imp; } public static double [] getYXRegression( final double [] data_x, final double [] data_y) { double s0=0,sx=0,sx2=0,sy = 0, sxy=0; for (int i = 0; i < data_x.length; i++) { double x = data_x[i]; double y = 2*data_x[i]; if (!Double.isNaN(x) && !Double.isNaN(y)) { s0+= 1.0; sx+= x; sx2+=x*x; sy+= y; sxy += x*y; } } double d = (s0*sx2-sx*sx); double a = (sxy*s0-sy*sx)/d; double b = (sy*sx2-sx*sxy)/d; return new double [] {a,b}; } /** * Rectify and render multiple images (as slices) matching vert_meters to * their (vert_meters points) provided IMS coordinates * @param maps array of the ortho images * @param zoom_level zoom level (0 - 1pix=1cm, 1 - 1pix=0.5cm, -1 - 1pix=2cm * @param wh null or int [2], will return {width, height} * @param origin - null or double [2], will return {x0, y0} in pixels * @param centers - null or double[maps.length][] - will return image centers coordinates * @return */ public float [][] renderMulti ( int mode, // 0 - regular image, 1 - altitudes, 2 - black/white mask int [] indices, // null or which indices to use (normally just 2 for pairwise comparison) boolean bounds_to_indices, double [][][] affines, // null or [indices.length][2][3] FineXYCorr warp, int zoom_level, int [] wh, int [] origin, // maps[0] as a reference double [][] centers){ final boolean use_alt = mode == MODE_ALT; final int dbg_x=2783, dbg_y=-5228; int [][] bounds; if (affines == null) { bounds = getBoundsPixels( // should be for rectified, {-bounds[0][0], -bounds[0][1]} - exact center zoom_level, bounds_to_indices? indices: null); } else { bounds = getBoundsPixels( // should be for rectified, {-bounds[0][0], -bounds[0][1]} - exact center zoom_level, bounds_to_indices? indices: null, affines); } int width = bounds[0][1] - bounds[0][0]; // bounds[x][0] - negative int height = bounds[1][1] - bounds[1][0]; if (wh != null) { wh[0] = width; wh[1] = height; } if (origin != null) { origin[0] = -bounds[0][0]; origin[1] = -bounds[1][0]; } if (indices == null) { indices = new int [ortho_maps.length]; for (int i = 0; i < indices.length; i++) { indices[i] = i; } } final float [][] fpixels = new float [indices.length][width * height]; for (int indx = 0; indx < indices.length; indx++) { //:indices) { // = 0; nmap< ortho_maps.length; nmap++) { final int findx= indx; final int nmap = indices[indx]; // nmap; final double [][] affine = (affines !=null) ? affines[indx]: ortho_maps[nmap].affine; // only here use provided Arrays.fill(fpixels[findx], Float.NaN); final double scale = 1.0/OrthoMap.getPixelSizeMeters(zoom_level); // final int orig_zoom_level = getOriginalZoomLevel() final int orig_zoom_level = use_alt? ortho_maps[nmap].getAlt().getZoomLevel(): ortho_maps[nmap].getImage().getZoomLevel(); final double src_scale = 1.0/OrthoMap.getPixelSizeMeters(orig_zoom_level); // pix per meter // double [][] mbounds = ortho_maps[nmap].getBoundsMeters(true); // keep original bounds double [][] mbounds = ortho_maps[nmap].getBoundsMeters(true,affine); // keep original bounds double [] enu_offset = ortho_maps[indices[0]].enuOffsetTo(ortho_maps[nmap]); final double [] scaled_out_center = { // xy center to apply affine to in output pixels -bounds[0][0] + scale * enu_offset[0], -bounds[1][0] - scale * enu_offset[1]}; if (centers != null) { centers[findx] = scaled_out_center; } final int [][] obounds = new int [2][2]; // output (rectified, combined) image bounds, relative to the top-left for (int n = 0; n< 2; n++) { // output pixels obounds[n][0] = (int) Math.floor(scaled_out_center[n] + scale*mbounds[n][0]); obounds[n][1] = (int) Math.ceil (scaled_out_center[n] + scale*mbounds[n][1]); } // Output window size (keep original affine - OK if will not exactly fit) final int ownd_width = obounds[0][1] - obounds[0][0]; final int ownd_height = obounds[1][1] - obounds[1][0]; final int ownd_len = ownd_width * ownd_height; // double [][] src_bounds=ortho_maps[nmap].getBoundsMeters (true); // using original affines double [][] src_bounds=ortho_maps[nmap].getBoundsMeters (false,affine); // using provided affines final double [] src_center = {-src_bounds[0][0],-src_bounds[1][0]}; // x,y center offset in the source image final int src_width = use_alt? ortho_maps[nmap].getAltData().width: ortho_maps[nmap].getImageData().width; final int src_height = use_alt? ortho_maps[nmap].getAltData().height : ortho_maps[nmap].getImageData().height; if ((indx==0) && (warp != null)) { // set center from the first image warp.setRender( zoom_level, // int zoom_lev, scaled_out_center[0], // double px0, // in render pixels scaled_out_center[1]); // // double py0); } final float [] src_img = use_alt? ortho_maps[nmap].getAltData().readFData() : ortho_maps[nmap].getImageData().readFData(); final Rectangle warp_woi =((indx==1) && (warp != null))? warp.getRenderWOI():null; final Thread[] threads = ImageDtt.newThreadArray(); final AtomicInteger ai = new AtomicInteger(0); /* boolean tilt_alt = (ground_planes != null) && (ground_planes[indx] != null); final float [] src_img = use_alt? (tilt_alt? ortho_maps[nmap].getAltData().data.clone(): ortho_maps[nmap].getAltData().data) : ortho_maps[nmap].getImageData().data; if (tilt_alt) { // apply tilt to the source altitudes double [] vert_meters = ortho_maps[nmap].getVertMeters(); // altitude at top-left pixel; final double top_left_alt = ground_planes[indx][2] - vert_meters[0]*ground_planes[indx][0] - vert_meters[1]*ground_planes[indx][1]; final double [] tilt_XY = {ground_planes[indx][0]/src_scale, ground_planes[indx][1]/src_scale}; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nPix = ai.getAndIncrement(); nPix < src_img.length; nPix = ai.getAndIncrement()) { int py = nPix / src_width; int px = nPix % src_width; src_img[nPix] -= (top_left_alt + px * tilt_XY[0] + py * tilt_XY[1]); } } }; } ImageDtt.startAndJoin(threads); ai.set(0); } */ for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nPix = ai.getAndIncrement(); nPix < ownd_len; nPix = ai.getAndIncrement()) { int opX = nPix % ownd_width + obounds[0][0]; // absolute output pX, pY int opY = nPix / ownd_width + obounds[1][0]; double dX = (opX - scaled_out_center[0]) /scale; // in meters double dY = (opY - scaled_out_center[1]) /scale; double [] xy_src = { // pixels of the source image src_scale * (affine[0][0]*dX + affine[0][1]*dY + affine[0][2] + src_center[0]), src_scale * (affine[1][0]*dX + affine[1][1]*dY + affine[1][2] + src_center[1])}; // limit to the source image if ((((int) opX)==dbg_x) && (((int) opY)==dbg_y)) { System.out.println("opX="+opX+", opy="+opY); } if ((warp_woi != null) && (warp_woi.contains(opX,opY))) { double [] dxy = warp.getWarp(opX,opY); xy_src[0] += dxy[0]; xy_src[1] += dxy[1]; } if ((xy_src[0] >= 0) && (xy_src[0] < (src_width-1)) && (xy_src[1] >= 0) && (xy_src[1] < (src_height-1))) { int [] ixy_src = {(int) Math.floor(xy_src[0]), (int)Math.floor(xy_src[1]) }; double [] kxy = {xy_src[0]-ixy_src[0], xy_src[1]-ixy_src[1]}; int indx00 = ixy_src[0] + ixy_src[1] * src_width; double d00 = src_img[indx00]; if (!Double.isNaN(d00)) { double d01 = src_img[indx00 + 1]; double d10 = src_img[indx00 + src_width]; double d11 = src_img[indx00 + src_width + 1]; double d = d00*(1.0 - kxy[0])*(1.0 - kxy[1])+ d01* kxy[0] *(1.0 - kxy[1])+ d10*(1.0 - kxy[0])* kxy[1]+ d11* kxy[0] * kxy[1]; fpixels[findx][opX + opY*width] = (float) d; } } } } }; } ImageDtt.startAndJoin(threads); } return fpixels; } public double [][] renderMultiDouble ( // should work with just a single image (indice.length ==1)? double [][] ground_planes, // null - images, non-null altitudes. use new double[2][] for old way alt int [] indices, // null or which indices to use (normally just 2 for pairwise comparison) boolean bounds_to_indices, double [][][] affines, // null or [indices.length][2][3] double [][] equalize_in, boolean ignore_equalize, FineXYCorr warp, int zoom_level, int [] wh, int [] origin, // maps[0] as a reference double [][] centers){ boolean updateStatus = true; boolean use_alt = ground_planes != null; final int dbg_x=707, dbg_y=-615; int [][] bounds; if (affines == null) { bounds = getBoundsPixels( // should be for rectified, {-bounds[0][0], -bounds[0][1]} - exact center zoom_level, bounds_to_indices? indices: null); } else { bounds = getBoundsPixels( // should be for rectified, {-bounds[0][0], -bounds[0][1]} - exact center zoom_level, bounds_to_indices? indices: null, affines); } int width = bounds[0][1] - bounds[0][0]; // bounds[x][0] - negative int height = bounds[1][1] - bounds[1][0]; if (wh != null) { wh[0] = width; wh[1] = height; } if (origin != null) { origin[0] = -bounds[0][0]; origin[1] = -bounds[1][0]; } if (indices == null) { indices = new int [ortho_maps.length]; for (int i = 0; i < indices.length; i++) { indices[i] = i; } } final double [][] dpixels = new double [indices.length][width * height]; for (int indx = 0; indx < indices.length; indx++) { //:indices) { // = 0; nmap< ortho_maps.length; nmap++) { if (updateStatus) IJ.showStatus("Preparing slice "+indx+" of "+indices.length); final int findx= indx; final int nmap = indices[indx]; // nmap; final double [][] affine = (affines !=null) ? affines[indx]: ortho_maps[nmap].affine; // only here use provided final double [] equalize = ignore_equalize ? null:((equalize_in != null) ? equalize_in[indx] : ortho_maps[nmap].equalize); Arrays.fill(dpixels[findx], Float.NaN); final double scale = 1.0/OrthoMap.getPixelSizeMeters(zoom_level); final int orig_zoom_level = use_alt? ortho_maps[nmap].getAlt().getZoomLevel(): ortho_maps[nmap].getImage().getZoomLevel(); final double src_scale = 1.0/OrthoMap.getPixelSizeMeters(orig_zoom_level); // pix per meter // final double src_scale = 1.0/OrthoMap.getPixelSizeMeters(ortho_maps[nmap].orig_zoom_level); // pix per meter // metric bounds of the rectified image relative to its origin /// double [][] mbounds = ortho_maps[nmap].getBoundsMeters(true); // keep original bounds double [][] mbounds = ortho_maps[nmap].getBoundsMeters(true,affine); // keep original bounds double [] enu_offset = ortho_maps[indices[0]].enuOffsetTo(ortho_maps[nmap]); final double [] scaled_out_center = { // xy center to apply affine to in output pixels -bounds[0][0] + scale * enu_offset[0], -bounds[1][0] - scale * enu_offset[1]}; if (centers != null) { centers[findx] = scaled_out_center; } final int [][] obounds = new int [2][2]; // output (rectified, combined) image bounds, relative to the top-left for (int n = 0; n< 2; n++) { // output pixels obounds[n][0] = (int) Math.floor(scaled_out_center[n] + scale*mbounds[n][0]); obounds[n][1] = (int) Math.ceil (scaled_out_center[n] + scale*mbounds[n][1]); } // Output window size (keep original affine - OK if will not exactly fit) final int ownd_width = obounds[0][1] - obounds[0][0]; final int ownd_height = obounds[1][1] - obounds[1][0]; final int ownd_len = ownd_width * ownd_height; /// final double [][] affine = (affines !=null) ? affines[indx]: ortho_maps[nmap].affine; // only here use provided /// double [][] src_bounds=ortho_maps[nmap].getBoundsMeters (true); // using original affines double [][] src_bounds=ortho_maps[nmap].getBoundsMeters (false,affine); // using provided affines final double [] src_center = {-src_bounds[0][0],-src_bounds[1][0]}; // x,y center offset in the source image final int src_width = use_alt? ortho_maps[nmap].getAltData().width: ortho_maps[nmap].getImageData().width; final int src_height = use_alt? ortho_maps[nmap].getAltData().height : ortho_maps[nmap].getImageData().height; // final float [] src_img = use_alt? ortho_maps[nmap].getAltData().data : ortho_maps[nmap].getImageData().data; if ((indx==0) && (warp != null)) { // set center from the first image warp.setRender( zoom_level, // int zoom_lev, scaled_out_center[0], // double px0, // in render pixels scaled_out_center[1]); // // double py0); } boolean tilt_alt = (ground_planes != null) && (ground_planes[indx] != null); final float [] src_img = use_alt? (tilt_alt? ortho_maps[nmap].getAltData().readFData().clone(): ortho_maps[nmap].getAltData().readFData()) : ortho_maps[nmap].getImageData().readFData(); final Rectangle warp_woi =((indx==1) && (warp != null))? warp.getRenderWOI():null; final Thread[] threads = ImageDtt.newThreadArray(); final AtomicInteger ai = new AtomicInteger(0); if (tilt_alt) { // apply tilt to the source altitudes double [] vert_meters = ortho_maps[nmap].getVertMeters(); // altitude at top-left pixel; final double top_left_alt = ground_planes[indx][2] - vert_meters[0]*ground_planes[indx][0] - vert_meters[1]*ground_planes[indx][1]; final double [] tilt_XY = {ground_planes[indx][0]/src_scale, ground_planes[indx][1]/src_scale}; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nPix = ai.getAndIncrement(); nPix < src_img.length; nPix = ai.getAndIncrement()) { int py = nPix / src_width; int px = nPix % src_width; src_img[nPix] -= (top_left_alt + px * tilt_XY[0] + py * tilt_XY[1]); } } }; } ImageDtt.startAndJoin(threads); ai.set(0); } for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nPix = ai.getAndIncrement(); nPix < ownd_len; nPix = ai.getAndIncrement()) { int opX = nPix % ownd_width + obounds[0][0]; // absolute output pX, pY int opY = nPix / ownd_width + obounds[1][0]; double dX = (opX - scaled_out_center[0]) /scale; // in meters double dY = (opY - scaled_out_center[1]) /scale; // in meters, Y-down double [] xy_src = { // pixels of the source image src_scale * (affine[0][0]*dX + affine[0][1]*dY + affine[0][2] + src_center[0]), src_scale * (affine[1][0]*dX + affine[1][1]*dY + affine[1][2] + src_center[1])}; // limit to the source image if ((((int) opX)==dbg_x) && (((int) opY)==dbg_y)) { System.out.println("opX="+opX+", opy="+opY); } if ((warp_woi != null) && (warp_woi.contains(opX,opY))) { double [] dxy = warp.getWarp(opX,opY); xy_src[0] += dxy[0]; xy_src[1] += dxy[1]; } if ((xy_src[0] >= 0) && (xy_src[0] < (src_width-1)) && (xy_src[1] >= 0) && (xy_src[1] < (src_height-1))) { int [] ixy_src = {(int) Math.floor(xy_src[0]), (int)Math.floor(xy_src[1]) }; double [] kxy = {xy_src[0]-ixy_src[0], xy_src[1]-ixy_src[1]}; int indx00 = ixy_src[0] + ixy_src[1] * src_width; double d00 = src_img[indx00]; if (!Double.isNaN(d00)) { double d01 = src_img[indx00 + 1]; double d10 = src_img[indx00 + src_width]; double d11 = src_img[indx00 + src_width + 1]; double d = d00*(1.0 - kxy[0])*(1.0 - kxy[1])+ d01* kxy[0] *(1.0 - kxy[1])+ d10*(1.0 - kxy[0])* kxy[1]+ d11* kxy[0] * kxy[1]; if (equalize != null) { d = equalize[0] * d + equalize[1]; } dpixels[findx][opX + opY*width] = d; } } } } }; } ImageDtt.startAndJoin(threads); } return dpixels; } public int getIndex(String sindx) { Integer indx = map_index_string.get(sindx); if (indx == null) { return -1; } return indx; } /* public float [][] getPaddedPairGPU( String [] spair, int zoom_lev){ int [] pair = new int [spair.length]; for (int i = 0; i < pair.length; i++) { pair[i] = getIndex(spair[i]); } return getPaddedPairGPU( pair, // int [] pair, zoom_lev); // int zoom_lev, } private float [][] getPaddedPairGPU( int [] pair, int zoom_lev){ float [][] gpu_pair_img = new float [2][]; for (int n = 0; n < pair.length; n++) { gpu_pair_img[n] = ortho_maps[pair[n]].getPaddedGPU ( zoom_lev); // int zoom_level, } return gpu_pair_img; } */ public PairwiseOrthoMatch SpiralMatch ( CLTParameters clt_parameters, double frac_remove, // = 0.25 double metric_error, boolean pmtch_use_affine, double max_std, // maximal standard deviation to limit center area double min_std_rad, // minimal radius of the central area (if less - fail) double rad_fraction, 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, double pix_step, double pix_range, double good_rms, double max_rms, int num_tries, // = 5 int min_overlap, // = 5 boolean ignore_rms, double [] max_rms_iter, // = {1.0, 0.6};// double overlap, double pull_skew, // ~rotation, = 0 fraction of the total weight == 1 double pull_tilt, // > 0 double pull_scale, // = 0 int debugLevel){ double [][] affine1 = new double [][] {affines_init[1][0].clone(),affines_init[1][1].clone()}; double [][][] affines = new double [][][] {affines_init[0],affine1}; boolean show_vf = false; boolean batch_mode = true; double [][] ground_planes = null; // null or double[2] - will return ground planes: 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 nabs = (int) Math.ceil(pix_range/pix_step); PairwiseOrthoMatch pairwiseOrthoMatch = new PairwiseOrthoMatch( affines[1], // double [][] affine, null, // double [][] jtj, Double.NaN, // double rms, zoom_lev, // int zoom_lev); overlap); // double overlap); // double max_std = 0.5; // maximal standard deviation to limit center area // double min_std_rad = 2.0; // minimal radius of the central area (if less - fail) int best_nx = -1, best_ny = -1; PairwiseOrthoMatch best_pom = null; while ((Math.abs(nx) <= nabs) && (Math.abs(ny) <= nabs)) { for (int i = 0; i < affines_init.length; i++) { System.arraycopy(affines_init[1][i], 0, affine1[i], 0, affine1[i].length); } affine1[0][2] += pix_step * pix_size * nx; affine1[1][2] += pix_step * pix_size * ny; pairwiseOrthoMatch.rms = Double.NaN; correlateOrthoPair( clt_parameters, // CLTParameters clt_parameters, pairwiseOrthoMatch, //PairwiseOrthoMatch pairwiseOrthoMatch, // will return statistics min_overlap, // 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, // double metric_error, ignore_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, gpu_pair, // String [] gpu_spair, affines, // double [][][] affines, // on top of GPS offsets null, // woi, // Rectangle woi, 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};// 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) pairwiseOrthoMatch.setAffine(affines[1]); // modified by correlateOrthoPair if (debugLevel > -4) { 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)){ ny++; } else if ((nx <= ny) && (nx > -ny)){ nx--; } else if ((ny <= -nx) && (ny > nx)){ ny--; } else { nx++; } } if (pairwiseOrthoMatch.rms < good_rms) { // immediately OK best_pom.ok = true; if (debugLevel > -4) { System.out.println("SpiralMatch(): best RMSE="+best_pom.rms+" < "+good_rms+ " for nx = "+best_nx+", ny="+best_ny+", using it."); } return best_pom; // 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."); } best_pom.ok = true; return best_pom; } // delegating too large rms to the caller (it will have best_pom.ok==false if ((debugLevel > -4) && ((best_pom == null) || Double.isNaN(best_pom.rms))){ 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 best_pom; // null; // pairwiseOrthoMatch.affine will have adjusted affine[1] } public FineXYCorr correlateOrthoPair( CLTParameters clt_parameters, PairwiseOrthoMatch pairwiseOrthoMatch, // will return statistics, may be null if not needed int min_overlap, double max_std, // maximal standard deviation to limit center area double min_std_rad, // minimal radius of the central area (if less - fail) double frac_remove, // = 0.25 double metric_error, boolean ignore_prev_rms, int num_tries, // = 5 boolean calc_warp, boolean batch_mode, int [] gpu_pair, double [][][] affines, // here in meters, relative to vertical points Rectangle woi, 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};// double pull_skew, // ~rotation, = 0 fraction of the total weight == 1 double pull_tilt, // > 0 double pull_scale, // = 0 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) { woi = new Rectangle(); } int min_tiles_overlap = min_overlap/GPUTileProcessor.DTT_SIZE/GPUTileProcessor.DTT_SIZE; if (pairwiseOrthoMatch != null) { pairwiseOrthoMatch.rms = Double.NaN; // mark as failed } boolean show_gpu_img = true; // (debugLevel > 1); boolean show_tile_centers = false; // true; // (debugLevel > 1); if (!batch_mode) { debugLevel = 0; } else { show_gpu_img = false; // (debugLevel > 1); show_tile_centers = false; // true; // (debugLevel > 1); } boolean show_vector_field = show_vf || (!batch_mode && (debugLevel>-1)); // true; double [][] bounds_overlap_meters = getOverlapMeters( gpu_pair[0], // int ref_index, gpu_pair[1], // int other_index) affines[0], // double [][] ref_affine, affines[1]); // double [][] other_affine 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; } 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)); } // convert to pixels,shift top-left to [0,0] (remember offsets, limit w,h, // change to pixels last, remember TL in meters? // keep center where it was // {bounds_overlap_meters[0][0],bounds_overlap_meters[1][0], double [] enuOffset = ortho_maps[gpu_pair[0]].enuOffsetTo(ortho_maps[gpu_pair[1]]); double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image double [][] tlo_rect_metric = new double [2][2]; // top-left of overlap referenced to it's own vertical point (subtract!) tlo_rect_metric[0][0] = bounds_overlap_meters[0][0]; // relative to ref vert_meters tlo_rect_metric[0][1] = bounds_overlap_meters[1][0]; // vert_meters tlo_rect_metric[1][0] = bounds_overlap_meters[0][0] - rd[0]; // relative to other vert_meters tlo_rect_metric[1][1] = bounds_overlap_meters[1][0] - rd[1]; double [][] tlo_src_metric = new double[tlo_rect_metric.length][2]; // relative to it's own vert_meters for (int n=0; n <tlo_src_metric.length; n++) { for (int i = 0; i < 2; i++) { // subtracting tl_rect_metric[n] (-1) tlo_src_metric[n][i] = tlo_rect_metric[n][0] * affines[n][i][0] + tlo_rect_metric[n][1] * affines[n][i][1] + affines[n][i][2]; } } /// referenced to top-left pixel of the gpu image double [][] tlo_source_pixel = new double[tlo_src_metric.length][2]; for (int n=0; n <tlo_source_pixel.length; n++) { for (int i = 0; i < 2; i++) { tlo_source_pixel[n][i] = (tlo_src_metric[n][i] + ortho_maps[gpu_pair[n]].getVertMeters()[i])/pix_size; } } double [][][] affines_gpu = new double [affines.length][2][3]; // relative to top left corners, in pixels float [][] gpu_pair_img = new float [2][]; for (int n = 0; n < gpu_pair.length; n++) { for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { affines_gpu[n][i][j] = affines[n][i][j]; } affines_gpu[n][i][2] = tlo_source_pixel[n][i]; } gpu_pair_img[n] = ortho_maps[gpu_pair[n]].getPaddedGPU (zoom_lev); // int zoom_level, } boolean invert_second = (debugLevel > 1000); if (debugLevel > 10) { return null; } if (invert_second) { for (int i = 0; i < gpu_pair_img[1].length; i++) { gpu_pair_img[1][i]= -gpu_pair_img[1][i]; } } if (show_gpu_img) { String [] dbg_titles = {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_img", dbg_titles); } woi.width = overlap_wh_pixel[0]; woi.height = overlap_wh_pixel[1]; // = new Rectangle(0, 0, overlap_wh_pixel[0], overlap_wh_pixel[1]); if (woi.width * woi.height < min_overlap) { if (debugLevel > -4) { System.out.println("correlateOrthoPair(): too small overlap: "+ woi.width+" * "+ woi.height+" = "+(woi.width * woi.height)+" < "+min_overlap+""); } return null; } if (woi.width > OrthoMap.gpu_width) { if (debugLevel > -3) { System.out.println("correlateOrthoPair() correlation woi.width="+woi.width+" > gpu_width="+OrthoMap.gpu_width+". Truncating."); } woi.width = OrthoMap.gpu_width; } if (woi.height > OrthoMap.gpu_height) { if (debugLevel > -3) { System.out.println("correlateOrthoPair() correlation woi.height="+woi.height+" > gpu_height="+OrthoMap.gpu_height+". Truncating."); } 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; int tilesY = gpu_height/GPUTileProcessor.DTT_SIZE; Rectangle tile_woi = scaleRectangle (woi, GPUTileProcessor.DTT_SIZE); // uses fixed_size gpu image size TpTask [][] tp_tasks = new TpTask [2][]; // int num_tries = 5; double [] prev_rms = {Double.NaN,Double.NaN}; double rel_improve = 1E-3; double [][] elevations = new double [tp_tasks.length][]; double [][] ground_planes_metric = new double [tp_tasks.length][]; boolean [][] ground_planes_masks = new boolean[2][]; double initial_above = 3; // m double initial_below = 5; //3; // m int num_refine = 3; double frac_above = 0.5; // 0.3; double frac_below = 0.01; // 0.1; double [][] jtj = null; double [] rms = {Double.NaN, Double.NaN}; double [] rms_pure = {Double.NaN, 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) { System.out.println("correlateOrthoPair(): ntry="+ntry); } } String dbg_suffix = batch_mode? null: String.format("_%02d", ntry); vector_field = ComboMatch.rectilinearVectorField(//rectilinearCorrelate_TD( // scene0/scene1 clt_parameters, // final CLTParameters clt_parameters, gpu_pair_img, // final float [][] fpixels, // to check for empty gpu_width, // final int img_width, woi, // Rectangle woi, // if null, use full GPU window affines_gpu, // final double [][][] affine, // [2][2][3] affine coefficients to translate common to 2 images tp_tasks, // TpTask [][] tp_tasks_o, batch_mode, // final boolean batch_mode, dbg_suffix, // final String dbg_suffix, // for image_names debugLevel); // final int debugLevel); if (tp_tasks[0].length < min_tiles_overlap) { if (pairwiseOrthoMatch != null) { pairwiseOrthoMatch.rms = Double.NaN; // mark as failed } if (debugLevel > -4) { System.out.println("correlateOrthoPair(): too small non-null overlap: "+ tp_tasks[0].length +" < "+min_tiles_overlap+""); } return null; } // get elevations // double ease_nosfm = 3.0; int zoom_lev_tiles = zoom_lev-3; ground_planes_weight = null; for (int num_elevations = 0; num_elevations < 1; num_elevations++) { for (int n = 0; n < gpu_pair.length; n++) { 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, tilesY, // final int tilesY, debugLevel); // final int debugLevel) String debug_title = (show_vector_field && (ntry==0))? (ortho_maps[gpu_pair[n]].getName()+"_plane"+dbg_suffix):null; ground_planes_metric[n] = ortho_maps[gpu_pair[n]].getPlaneMeters ( zoom_lev_tiles, // int zoom_lev, // of the data (3 levels down for tiles) elevations[n], // double [] elev, tilesX, // int width, initial_above, // double initial_above, // from average initial_below, // double initial_below, // from average, // positive value num_refine, // int num_refine, frac_above, // double frac_above, frac_below, // double frac_below, null, // double [][] tile_plane_elev, // null or double[2][] will return per-tile elevations debug_title); // String debug_title) ground_planes_masks[n] = ortho_maps[gpu_pair[n]].removeHighElevationMismatch ( zoom_lev_tiles, // int zoom_lev, // of the data (3 levels down for tiles) elevations[n], // double [] elev, tilesX, // int width, ground_planes_metric[n], // double [] plane_metric, // tiltx,tilty, offs - in meters 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; for (int i = 0; i < ground_planes_weight.length; i++) { if (ground_planes_masks[0][i] && ground_planes_masks[1][i]) { num_left++; ground_planes_weight[i] = 1.0; } } if (debugLevel > -3) { System.out.println("correlateOrthoPair(): left "+num_left+" tiles (was "+tp_tasks[0].length+" before filtering). metric_error="+metric_error); System.out.println(); } 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; } if (num_left < min_tiles_overlap) { if (pairwiseOrthoMatch != null) { pairwiseOrthoMatch.rms = Double.NaN; // mark as failed } if (debugLevel > -4) { System.out.println("correlateOrthoPair(): too small non-null, filtered by elevations overlap: "+ num_left +" < "+min_tiles_overlap+""); } return null; } } double max_err= 7.0; int num_bins = 1000; boolean ignore_strength = false; //double frac_remove = 0.15; double [][] vf_error2 = new double [vector_field.length][]; // may be able to skip [0] double [][][] vector_field_bkp = show_vector_field? new double[vector_field.length][][]:null; if (vector_field_bkp != null) { for (int i = 0; i < vector_field_bkp.length; i++) { vector_field_bkp[i] = vector_field[i].clone(); } } for (int nvf_mode = 0; nvf_mode < vector_field.length; nvf_mode++) { vf_error2[nvf_mode] = getVectorFieldError2( vector_field[nvf_mode], // double [][] vector_field, tilesX, // int width, // tilesX tile_woi); // Rectangle twoi); // in tiles double threshold = filterVectorField( vector_field[nvf_mode], // final double [][] vector_field, vf_error2[nvf_mode], // final double [] vf_error2, frac_remove, // final double frac_remove, ignore_strength, // final boolean ignore_strength, num_bins, // final int num_bins, 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."); } } // TODO: use crop, keep (0,0) (woi.x+woi.width, woi.y+woi.height if (vector_field_bkp != null) { // show_vector_field) { int dbg_width = tile_woi.x+tile_woi.width; int dbg_height = tile_woi.y+tile_woi.height; double [][][][] vf_all = {vector_field_bkp,vector_field}; double [][] dbg_vf = new double [8 * vector_field.length+5][dbg_width * dbg_height]; String [] dbg_titles = new String[dbg_vf.length]; String [] prefix= {"single","single_filtered","neibs","neibs_filtered"}; for (int n = 0; n < 2*vector_field.length; n++) { dbg_titles [4*n+0] = prefix[n]+"-vx"; dbg_titles [4*n+1] = prefix[n]+"-vy"; dbg_titles [4*n+2] = prefix[n]+"-str"; dbg_titles [4*n+3] = prefix[n]+"-err"; } dbg_titles [8 * vector_field.length + 0] = "mask-elev"; dbg_titles [8 * vector_field.length + 1] = "elev-0"; dbg_titles [8 * vector_field.length + 2] = "elev-1"; dbg_titles [8 * vector_field.length + 3] = "mask-0"; dbg_titles [8 * vector_field.length + 4] = "mask-1"; for (int i = 0; i < dbg_vf.length; i++) { Arrays.fill(dbg_vf[i], Double.NaN); } for (int l = 0; l<dbg_vf[0].length; l++) { // local tile int lx = l % dbg_width; int ly = l / dbg_width; int t = lx + ly * tilesX; // full width for (int n = 0; n < vector_field.length; n++) { for (int m = 0; m < vf_all.length; m++) { if (vf_all[m][n][t] != null) { for (int k = 0; k < 3; k++) { dbg_vf[m*4 + n*8 + k][l] = vf_all[m][n][t][k]; } // will automatically apply filtered dbg_vf[m*4 + n*8 + 3][l] = Math.sqrt(vf_error2[n][t]); } } } dbg_vf [8 * vector_field.length + 0][l] = ground_planes_weight[t]; dbg_vf [8 * vector_field.length + 1][l] = elevations[0][t]; dbg_vf [8 * vector_field.length + 2][l] = elevations[1][t]; dbg_vf [8 * vector_field.length + 3][l] = ground_planes_masks[0][t]?1.0:0.0; dbg_vf [8 * vector_field.length + 4][l] = ground_planes_masks[1][t]?1.0:0.0; } ShowDoubleFloatArrays.showArrays( dbg_vf, dbg_width, dbg_height, true, "vector_field"+dbg_suffix, dbg_titles); } // may use tl_rect_metric to remap to the original image 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(); } if (show_tile_centers){ double [][] dbg_img = new double [6][tile_centers.length]; String [] dbg_titles = {"cX","cY","px0","py0", "px1","py1"}; for (int i = 0; i< dbg_img.length;i++) Arrays.fill(dbg_img[i], Double.NaN); for (int t = 0; t < tp_tasks[0].length; t++) { TpTask task0 = tp_tasks[0][t]; TpTask task1 = tp_tasks[1][t]; int ti = task0.getTileY() * tilesX + task0.getTileX(); dbg_img[0][ti] = task0.getDoubleCenterXY()[0]; // same for task0, task1 dbg_img[1][ti] = task1.getDoubleCenterXY()[1]; dbg_img[2][ti] = task0.getXY()[0][0]; dbg_img[3][ti] = task0.getXY()[0][1]; dbg_img[4][ti] = task1.getXY()[0][0]; dbg_img[5][ti] = task1.getXY()[0][1]; } // getXY() ShowDoubleFloatArrays.showArrays( dbg_img, tilesX, tile_centers.length/tilesX, true, "tile_centers"+dbg_suffix, dbg_titles); } if (!batch_mode) { if (debugLevel>-3) { System.out.println("correlateOrthoPair(): before LMA, ntry="+ntry); } } boolean origin_center = true; // false - old mode orthoPairLMA = new OrthoPairLMA(origin_center); // vector_field[1] - neighbors double lambda = 0.1; double lambda_scale_good = 0.5; double lambda_scale_bad = 8.0; double lambda_max = 100; double rms_diff = 0.001; int num_iter = 20; boolean last_run = false; int min_good_tiles = min_tiles_overlap/2; // show_lma_dbg String dbg_lma_prefix = show_lma_dbg? ("LMA_"+gpu_pair[0]+"-"+gpu_pair[1]+"_ntry"+ntry+"_"):null; int num_good_tiles = orthoPairLMA.prepareLMA( // will always calculate relative affine, starting with unity 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) 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, affines_gpu[1], // double [][] src_affine, 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); // final int debug_level) 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 (debugLevel>-3) { System.out.println("correlateOrthoPair(): center_radius="+ orthoPairLMA.getCenterRadius()+" < "+ 1); } return null; } if (num_good_tiles < min_good_tiles) { if (debugLevel>-4) { System.out.println("correlateOrthoPair(): num_good_tiles="+ num_good_tiles+ " < "+min_good_tiles); } if (pairwiseOrthoMatch != null) { pairwiseOrthoMatch.rms = Double.NaN; // mark as failed } return null; } int lma_rslt = orthoPairLMA.runLma( // <0 - failed, >=0 iteration number (1 - immediately) lambda, // double lambda, // 0.1 lambda_scale_good, // double lambda_scale_good,// 0.5 lambda_scale_bad, // double lambda_scale_bad, // 8.0 lambda_max, // double lambda_max, // 100 rms_diff, // double rms_diff, // 0.001 num_iter, // int num_iter, // 20 last_run, // boolean last_run, dbg_lma_prefix, // String dbg_prefix, debugLevel); // int debug_level) if (debugLevel > -3) { System.out.println("LMA result = "+lma_rslt); } if (lma_rslt < 0) { System.out.println("LMA failed, result="+lma_rslt); return null; } rms = orthoPairLMA.getRms(); if (debugLevel > -3) { System.out.println("RMS= "+rms[0]+" ("+orthoPairLMA.getInitialRms()[0]+ "), RMS pure="+rms[1]+" ("+orthoPairLMA.getInitialRms()[1]+")"); } if (max_rms_iter != null) { int max_rms_index = Math.min(max_rms_iter.length-1, ntry); if (rms[0] > max_rms_iter[max_rms_index]) { if (debugLevel > -3) { System.out.println("RMS= "+rms[0]+" > max_rms_iter["+ max_rms_index+"] = "+max_rms_iter[max_rms_index]); } return null; } } if (rms[0] > prev_rms[0]) { if (debugLevel > -3) { if ((rms[0]-prev_rms[0])/prev_rms[0] < rel_improve) { System.out.println("LMA RMSE worsened, but less than improvement threshold: new "+rms[0]+" ("+ orthoPairLMA.getInitialRms()[0]+"), prev="+prev_rms[0]); } else { System.out.println("LMA RMSE worsened: new "+rms[0]+" ("+ orthoPairLMA.getInitialRms()[0]+"), prev="+prev_rms[0]); } } if (ignore_prev_rms) { if (debugLevel > -3) { 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 { 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 ((rms[0] <= prev_rms[0]) && ((prev_rms[0] - rms[0])/prev_rms[0] < rel_improve)) { if (debugLevel > -2) { System.out.println("LMA relative RMSE improvement = "+((prev_rms[0] - rms[0])/prev_rms[0])+" < "+rel_improve+", exiting."); } if (ignore_prev_rms) { if (debugLevel > -3) { 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 { 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 ++; 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.clone(); 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) { System.out.println("correlateOrthoPair(): center_radius="+ last_center_radius+" < "+ min_std_rad); } return null; } } if (pairwiseOrthoMatch != null) { pairwiseOrthoMatch.jtj = jtj; pairwiseOrthoMatch.rms= rms[1]; // pure rms pairwiseOrthoMatch.zoom_lev = zoom_lev; } double [] tlo_src_metric_other = new double[2]; // check affines[][][] here - for (int i = 0; i < 2; i++) { tlo_src_metric_other[i] = affines_gpu[1][i][2] * pix_size - ortho_maps[gpu_pair[1]].getVertMeters()[i]; for (int j = 0; j < 2; j++) { affines[1][i][j] = affines_gpu[1][i][j]; } } for (int i = 0; i < 2; i++) { affines[1][i][2] = tlo_src_metric_other[i] - tlo_rect_metric[1][0] * affines[1][i][0] - tlo_rect_metric[1][1] * affines[1][i][1]; } if (debugLevel>-3) { System.out.println("correlateOrthoPair(): adjusted affines[1]"); 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]+"]]"); } // modify affines[1] if ((debugLevel > 1) && (num_tries>0)) {// show result here 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); } if (calc_warp) { double scale = 2.0; String dbg_suffix = batch_mode? null: "_warp"; // return only neighbors vector field double [][] vf = ComboMatch.rectilinearVectorField(//rectilinearCorrelate_TD( // scene0/scene1 clt_parameters, // final CLTParameters clt_parameters, gpu_pair_img, // final float [][] fpixels, // to check for empty gpu_width, // final int img_width, woi, // Rectangle woi, // if null, use full GPU window affines_gpu, // final double [][][] affine, // [2][2][3] affine coefficients to translate common to 2 images tp_tasks, // TpTask [][] tp_tasks_o, batch_mode, // final boolean batch_mode, dbg_suffix, // final String dbg_suffix, debugLevel)[1]; // final int debugLevel); double [][] tile_elevs = null; if (ground_planes != null) { for (int n = 0; n < ground_planes.length; n++) { ground_planes[n] = getGroundPlane( gpu_pair[n], // int scene_num, tp_tasks[n], // TpTask [] tp_tasks, zoom_lev, // int zoom_lev, tilesX, // int tilesX, tilesY, // int tilesY, debugLevel); // int debugLevel) } } if (show_vf && (num_tries == 0) && (debugLevel > 1)) { // not needed now tile_elevs = new double [tp_tasks.length][]; for (int n = 0; n < tile_elevs.length; n++) { String debug_title = (show_vector_field && (debugLevel>0))? (ortho_maps[gpu_pair[n]].getName()+"_plane"+dbg_suffix):null; tile_elevs[n] = getTileElevations( gpu_pair[n], // int scene_num, tp_tasks[n], // TpTask [] tp_tasks, zoom_lev, // int zoom_lev, tilesX, // int tilesX, tilesY, // int tilesY, tile_woi, // Rectangle tile_woi, // only width, height are used top-left corners are the same debug_title, // String debug_title, debugLevel); // int debugLevel) } String [] dbg_titles = new String[tile_elevs.length]; String dbg_title = "tile_elevations"; for (int i =0; i < tile_elevs.length; i++) { dbg_titles[i] = ortho_maps[gpu_pair[i]].getName(); dbg_title += "-"+ortho_maps[gpu_pair[i]].getName(); } ShowDoubleFloatArrays.showArrays( tile_elevs, tile_woi.width, tile_woi.height, true, dbg_title , dbg_titles); System.out.println(); } double [][] vf_interpolated = interpolateVectorField( vf, // final double [][] vector_field, // sparse {vx,vy,strength} tp_tasks[1], // final TpTask [] tp_tasks, // to remove no-overlap tiles tilesX, // final int gpu_tilesX, // 512 tile_woi, // final Rectangle tile_woi, // only width, height are used top-left corners are the same scale, // final double scale, 1); // final int debugLevel); // vector_filed_filled - use 2 double [] tl_metric = {bounds_overlap_meters[0][0],bounds_overlap_meters[1][0]}; FineXYCorr warp = new FineXYCorr( zoom_lev, // int zoom_lev, tile_woi.width, // int width, tl_metric, // double [] tl_metric,// relative to the reference image vertical point, normally negative vf_interpolated); // double [][] vf); return warp; } return null; } public double [] getTileElevations( int scene_num, TpTask [] tp_tasks, int zoom_lev, int tilesX, int tilesY, final Rectangle tile_woi, // only width, height are used top-left corners are the same String debug_title, int debugLevel) { double initial_above = 3; // m double initial_below = 3; // m int num_refine = 3; double frac_above = 0.3; double frac_below = 0.1; int zoom_lev_tiles = zoom_lev-3; double [] elevations = ortho_maps[scene_num].getTileElevations( zoom_lev, // final int zoom_level, tp_tasks, // final TpTask [] tp_tasks, tilesX, // final int tilesX, tilesY, // final int tilesY, debugLevel); // final int debugLevel) double [] tile_plane_elev = new double [elevations.length]; double [] ground_planes_metric = ortho_maps[scene_num].getPlaneMeters ( zoom_lev_tiles, // int zoom_lev, // of the data (3 levels down for tiles) elevations, // double [] elev, tilesX, // int width, initial_above, // double initial_above, // from average initial_below, // double initial_below, // from average, // positive value num_refine, // int num_refine, frac_above, // double frac_above, frac_below, // double frac_below, tile_plane_elev, // double [][] tile_plane_elev, // null or double[2][] will return per-tile elevations debug_title); // String debug_title) // tile_woi, // double [] elev_crop = new double[tile_woi.width*tile_woi.height]; for (int row = 0; row < tile_woi.height; row++) { System.arraycopy( tile_plane_elev, row * tilesX, elev_crop, row*tile_woi.width, tile_woi.width); } return elev_crop; } /** * Get planar approximation of the ground * @param scene_num scene index * @param tp_tasks * @param tp_tasks array of non-empty tiles to be processed in the GPU. Integer tx and ty reference * tile on a tile grid (8x8 pix), center X,Y for the only "sensor" 0 - float coordinates * corresponding to the current GPU zoom level * @param zoom_lev zoom level of the GPU image, 0 is 1pix=1cm, -1 is 1pix=2cm * @param tilesX * @param tilesY * @param debugLevel * @return plane parameters - all metric, relative to the vertical point: * {tilt_x (m/m), tilt_y (m/m), offset (elevation at vertical point in meters) */ public double [] getGroundPlane( int scene_num, TpTask [] tp_tasks, int zoom_lev, int tilesX, int tilesY, int debugLevel) { double initial_above = 3; // m double initial_below = 3; // m int num_refine = 3; double frac_above = 0.3; double frac_below = 0.1; int zoom_lev_tiles = zoom_lev-3; double [] elevations = ortho_maps[scene_num].getTileElevations( zoom_lev, // final int zoom_level, tp_tasks, // final TpTask [] tp_tasks, tilesX, // final int tilesX, tilesY, // final int tilesY, debugLevel); // final int debugLevel) double [] ground_plane_metric = ortho_maps[scene_num].getPlaneMeters ( zoom_lev_tiles, // int zoom_lev, // of the data (3 levels down for tiles) elevations, // double [] elev, tilesX, // int width, initial_above, // double initial_above, // from average initial_below, // double initial_below, // from average, // positive value num_refine, // int num_refine, frac_above, // double frac_above, frac_below, // double frac_below, null, // double [][] tile_plane_elev, // null or double[2][] will return per-tile elevations null); // String debug_title) // tile_woi, // return ground_plane_metric; } /** * Project object offset to along and perpendicular to the scenes offset * @param object_offset object center pixel x and y offset between images (y - down) * @param scene_offset pixel offset between images * @return A pair of offsets: parallel (to scene offset) and perpendicular. Negative parallel * offsets corresponds to objects above ground */ public static double [] projectOffsetOnVector( double [] object_offset, double [] scene_offset) { double l1 = Math.sqrt(scene_offset[0]*scene_offset[0] + scene_offset[1]*scene_offset[1]); if (!(l1 > 0.00001)) { double [] offs_rot = new double [2]; offs_rot[0] = ( object_offset[0]*scene_offset[0] + object_offset[1]*scene_offset[1]) / l1; offs_rot[1] = (-object_offset[0]*scene_offset[1] + object_offset[1]*scene_offset[0]) / l1; return offs_rot; } return object_offset; } /** * Estimate object height if its offset between image is caused by its elevation * @param object_offset object center pixel x and y offset between images (y - down) * @param scene_offset pixel offset between images * @param zoom_level images zoom level * @param agl scene (second) altitude above ground in meters * @return Estimated object elevation above ground in meters */ public static double offsetToElevation( double [] object_offset, double [] scene_offset, int zoom_level, double agl) { double l1 = Math.sqrt(scene_offset[0]*scene_offset[0] + scene_offset[1]*scene_offset[1]); double offs_par = projectOffsetOnVector(object_offset, scene_offset)[0]; // *OrthoMap.getPixelSizeMeters(zoom_level); return -agl * offs_par / l1 ; } /** * Calculate (negative) pixel offset parallel to the scene offset corresponding to object height * @param height * @param scene_offset * @param zoom_level * @param agl * @return */ public static double elevationToParallelOffset( double height, double [] scene_offset, int zoom_level, double agl) { double l1 = Math.sqrt(scene_offset[0]*scene_offset[0] + scene_offset[1]*scene_offset[1]); return -height/agl * l1 ; } public static Rectangle scaleRectangle( Rectangle woi, int tile_size){ int min_tx = woi.x/tile_size; int min_ty = woi.y/tile_size; int max_tx1 = (woi.x + woi.width + (tile_size -1)) / tile_size; int max_ty1 = (woi.y + woi.height + (tile_size -1)) / tile_size; return new Rectangle(min_tx,min_ty,max_tx1 - min_tx,max_ty1 - min_ty); } public static double [] getVectorFieldError2( double [][] vector_field, int width, // tilesX Rectangle twoi) { // in tiles final Rectangle woi = (twoi != null)? twoi: (new Rectangle (0,0,width,vector_field.length/width)); final int num_tiles = woi.width*woi.height; final int tlTile = width * woi.y + woi.x; final double [] vf_error2 = new double [vector_field.length]; // Arrays.fill(vf_error2, Double.NaN); 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 nTile = ai.getAndIncrement(); nTile < num_tiles; nTile = ai.getAndIncrement()) { int itileX= nTile%woi.width; int itileY= nTile/woi.width; int tile = tlTile + itileY * width + itileX; if (vector_field[tile] != null) { double [] xys = vector_field[tile]; vf_error2[tile] += xys[0]*xys[0]+xys[1]*xys[1]; } } } }; } ImageDtt.startAndJoin(threads); return vf_error2; } public static double filterVectorField( final double [][] vector_field, final double [] vf_error2, final double frac_remove, final boolean ignore_strength, final int num_bins, final double max_err, final int width, // tilesX final Rectangle twoi) { // in tiles final Rectangle woi = (twoi != null)? twoi: (new Rectangle (0,0,width,vector_field.length/width)); final int STRENGTH_INDEX=2; final double max_err2 = max_err*max_err; final double scale = num_bins/max_err2; final int num_tiles = woi.width*woi.height; final int tlTile = width * woi.y + woi.x; 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]; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { int thread_num = ati.getAndIncrement(); for (int nTile = ai.getAndIncrement(); nTile < num_tiles; nTile = ai.getAndIncrement()) { int itileX= nTile%woi.width; int itileY= nTile/woi.width; int tile = tlTile + itileY * width + itileX; if (!Double.isNaN(vf_error2[tile]) && (vector_field[tile] != null)){ // one test is enough int bin = (int) Math.round(vf_error2[tile]*scale); if (bin >= num_bins) { bin= num_bins - 1; } hist2[thread_num][bin] += ignore_strength? 1.0 : vector_field[tile][STRENGTH_INDEX]; } } } }; } 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 aremove = sw * frac_remove; double sh = 0, shp = 0; double t2 = max_err2; for (int bin = num_bins-1; bin>=0; bin--) { shp = sh; sh += hist[bin]; if (sh > aremove) { double r = (sh-aremove)/(sh-shp); t2 = (bin + r)/scale; break; } } final double threshold2 = t2; // remove bad tiles ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nTile = ai.getAndIncrement(); nTile < num_tiles; nTile = ai.getAndIncrement()) { int itileX= nTile%woi.width; int itileY= nTile/woi.width; int tile = tlTile + itileY * width + itileX; if ((vector_field[tile] != null) && !(vf_error2[tile] <= threshold2)){ vector_field[tile]= null; } } } }; } ImageDtt.startAndJoin(threads); return Math.sqrt(threshold2); } /** * Start with this when reading from saved data * @param path * @return * @throws IOException * @throws ClassNotFoundException */ public static OrthoMapsCollection readOrthoMapsCollection ( String path) throws IOException, ClassNotFoundException { // try reading current_version, if fails - restart without it (for older formats) FileInputStream fileInputStream = new FileInputStream(path); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); int current_version = (int) objectInputStream.readObject(); // reads OrthoMapsCollection OrthoMapsCollection.CURRENT_VERSION = current_version; // trying here 08.29.2024 - before orthoMapsCollection exists System.out.println("readOrthoMapsCollection(): got current_version="+current_version); OrthoMapsCollection orthoMapsCollection = (OrthoMapsCollection) objectInputStream.readObject(); objectInputStream.close(); System.out.println("readOrthoMapsCollection(): got orthoMapsCollection, current_version="+current_version); return orthoMapsCollection; } public void writeOrthoMapsCollection (String path) throws IOException { FileOutputStream fileOutputStream = new FileOutputStream(path); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(LATEST_VERSION); // current_version); objectOutputStream.writeObject(this); objectOutputStream.flush(); objectOutputStream.close(); } public static double [][] interpolateVectorField( final double [][] vector_field, // sparse {vx,vy,strength} final TpTask [] tp_tasks, // to remove no-overlap tiles final int gpu_tilesX, // 512 final Rectangle tile_woi, // only width, height are used top-left corners are the same final double scale, final int debugLevel) { final int tiles = tile_woi.width*tile_woi.height; final double [][] vf = new double[2][tile_woi.width*tile_woi.height]; for (int i = 0; i < vf.length; i++) { Arrays.fill(vf[i], Double.NaN); } final boolean [] mask = (tp_tasks==null)? null: new boolean [tiles]; 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 ipix = ai.getAndIncrement(); ipix < tiles; ipix = ai.getAndIncrement()) { int y = ipix / tile_woi.width; int x = ipix % tile_woi.width; double [] v = vector_field[y*gpu_tilesX+x]; if (v != null) { for (int i = 0; i < vf.length; i++) { vf[i][ipix] = scale*v[i]; } } } } }; } ImageDtt.startAndJoin(threads); if (mask != null) { ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int iTile = ai.getAndIncrement(); iTile < tp_tasks.length; iTile = ai.getAndIncrement()) { TpTask task = tp_tasks[iTile]; int[] txy = task.getTileXY(); if ((txy[0] < tile_woi.width) && (txy[1] < tile_woi.height)) { mask[txy[0] + txy[1] * tile_woi.width] = true; } } } }; } ImageDtt.startAndJoin(threads); } // now fill NaNs in each of vf[0], vf[1] double [][] vf_filled = new double[vf.length][]; int num_passes = 100; double max_diff = 1E-4; for (int i = 0; i < vf.length; i++) { vf_filled[i] = OpticalFlow.fillGapsDouble( vf[i], // double [] data, mask, // boolean [] mask_in, // do not process if false (may be null) tile_woi.width, // int width, 0, // int max_grow, num_passes, // int num_passes, max_diff, // double max_diff, ImageDtt.THREADS_MAX, // int threadsMax, debugLevel-1); // int debug_level) } if (debugLevel > 0) { String [] dbg_titles = {"x-raw","x_filled","y-raw","y_filled"}; double [][] dbg_img = {vf[0], vf_filled[0],vf[1], vf_filled[1]}; ShowDoubleFloatArrays.showArrays( dbg_img, tile_woi.width, dbg_img[0].length / tile_woi.width, true, "vector_field_filled", dbg_titles); } double [][] vf_out = new double [tiles][]; ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int ipix = ai.getAndIncrement(); ipix < tiles; ipix = ai.getAndIncrement()) { if (!Double.isNaN(vf_filled[0][ipix]) && !Double.isNaN(vf_filled[1][ipix])) { vf_out[ipix] = new double[] {vf_filled[0][ipix],vf_filled[1][ipix]}; } } } }; } ImageDtt.startAndJoin(threads); return vf_out; } public ImagePlus patternMatchDualWrap ( int [] indices, // null or which indices to use (normally just 2 for pairwise comparison) double [][][] affines, // null or [indices.length][2][3] FineXYCorr warp, double [][] ground_planes) { // null or) { // use for a single pair only //indices //getAGL() OrthoMapsParameters omp = new OrthoMapsParameters(); // will set defaults double scene0_agl = ortho_maps[indices[0]].getAGL(); // Overwrite some defaults based on AGL if (scene0_agl <= ((50+75)/2)) { // add versions for morning/morning or morning/evening omp.setDefaults50(); } else if (scene0_agl < ((75+100)/2)) { // 75m defaults omp.setDefaults75(); } else { // 100m defaults omp.setDefaults100(); } omp.setupDialog( indices, ortho_maps); return patternMatchDual ( indices, // int [] indices, // null or which indices to use (normally just 2 for pairwise comparison) affines, // double [][][] affines, // null or [indices.length][2][3] warp, // FineXYCorr warp); ground_planes, // double [][] ground_planes, omp);//OrthoMapsParameters omp ); } public ImagePlus patternMatchDual ( int [] indices, // null or which indices to use (normally just 2 for pairwise comparison) double [][][] affines, // null or [indices.length][2][3] FineXYCorr warp, // use for a single pair only double [][] ground_planes, OrthoMapsParameters omp) { boolean sort_by_best = true; // false - as was, by selected pattern double ease_first_best = 0.9; // before recenter String basename = OrthoMapsParameters.getBaseName( ortho_maps, // OrthoMap [] ortho_maps, indices); // int [] indices) int debugLevel = omp.debugLevel; // sample around + and - 128/spectrum_sample[0] // from (128/spectrum_sample[0])/spectrum_sample[1] // to (128/spectrum_sample[0])*spectrum_sample[1]. // Calculate average of FFT power spectrum on both sides, in both directions and get ratio double [] spectrum_sample = {14, 1.5, 1.0}; double search_radius_recenter = 3; // how far to look for updated correlation peak after recenter if (warp != null) { warp.scale_warp = omp.scale_warp; } int last_scene = indices.length-1; int min_zoom_lev = ortho_maps[indices[0]].getOriginalZoomLevel(); int max_zoom_lev = ortho_maps[indices[0]].getOriginalZoomLevel(); for (int i = 0; i < indices.length; i++) { min_zoom_lev = Math.min(min_zoom_lev, ortho_maps[indices[i]].getOriginalZoomLevel()); max_zoom_lev = Math.max(max_zoom_lev, ortho_maps[indices[i]].getOriginalZoomLevel()); } int zoom_level = min_zoom_lev; 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) true, // boolean bounds_to_indices, affines, // double [][][] affines, // null or [indices.length][2][3] null, // double [][] equalize, false, // boolean ignore_equalize, warp, // FineXYCorr warp,, zoom_level, // int zoom_level, wh, // int [] wh, origin, // int [] origin){ // maps[0] as a reference centers); // double [][] centers) double [][] dalt = null; if (ground_planes != null) { dalt = renderMultiDouble ( ground_planes, // 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) true, // boolean bounds_to_indices, affines, // double [][][] affines, // null or [indices.length][2][3] null, // double [][] equalize, true, // boolean ignore_equalize, warp, // FineXYCorr warp,, zoom_level, // int zoom_level, wh, // int [] wh, origin, // int [] origin){ // maps[0] as a reference null); // centers); // double [][] centers) // already set with dmulti } int width = wh[0]; int height = wh[1]; String [] dbg_titles = new String[dmulti.length]; for (int i = 0; i < dbg_titles.length; i++) { dbg_titles[i] = ""+i; } ImagePlus img_first = ShowDoubleFloatArrays.makeArrays( dmulti, width, height, basename, // ortho_maps[indices[0]].getName(), dbg_titles); showSaveImagePlus( img_first, // ImagePlus imp, omp.show_images, // boolean show, omp.save_images, // boolean save, omp.save_dir, // String save_dir, debugLevel); // int debugLevel) if (dalt != null) { ImagePlus img_alt = ShowDoubleFloatArrays.makeArrays( dalt, width, height, basename + "-alt", // ortho_maps[indices[0]].getName()+"-alt", dbg_titles); showSaveImagePlus( img_alt, // ImagePlus imp, omp.show_images, // boolean show, omp.save_images, // boolean save, omp.save_dir, // String save_dir, debugLevel); // int debugLevel) } double [][] src_marks = null; double [][][] patterns_all = new double [indices.length][][]; double [][] kernels_all = new double [indices.length][]; GroundObjectPattern [] gops = new GroundObjectPattern[indices.length]; AltitudeMismatchKernel[] ak = new AltitudeMismatchKernel[indices.length]; int [] pattern_sizes = new int[indices.length]; String [][] pattern_labels = new String[indices.length][]; double [][][] corrs_out = new double[indices.length][][]; double [][][] corr_patterns = new double[indices.length][][]; int [][][] icorr_patterns= new int[indices.length][][]; // will only be used for main ! int [] zoomout = new int[indices.length]; double [][] bestcorr = new double[indices.length][]; double [][] fullcorr = new double[indices.length][]; int [][] bestpatt = new int [indices.length][]; double [] min_corrs_full = new double[indices.length]; ArrayList <ItemMatch> matches_list = new ArrayList <ItemMatch>(); double [] adv_radii = new double[indices.length]; int [] corr_radius = new int [indices.length]; for (int scene_num = 0; scene_num < indices.length; scene_num++) { // will probably just use first one, second - derivative gops[scene_num] = GroundObjectPattern.getPattern( omp.object_type, // String object_type, ortho_maps[indices[scene_num]].getLocalDateTime(), // LocalDateTime utcDateTime, patterns); // ArrayList<GroundObjectPattern> patterns) if (gops[scene_num] == null) { System.out.println("Failed to find a pattern for object \""+omp.object_type+"\", aborting operation."); return null; } zoomout[scene_num] = 1; for (int i = zoom_level; i < gops[scene_num].getZoomLevel();i++) { zoomout[scene_num] *= 2; } adv_radii[scene_num] = omp.adv_radius/zoomout[scene_num]; corr_radius[scene_num] = (int) (Math.sqrt(0.5)* adv_radii[scene_num]) -1 ; ImagePlus imp_pattern = new ImagePlus(gops[scene_num].getPatternPath()); pattern_sizes[scene_num] = imp_pattern.getWidth(); if (pattern_sizes[scene_num] == 0) { System.out.println("testPatternCorrelate(): pattern \""+gops[scene_num].getPatternPath()+"\" is not found."); return null; } ImageStack stack_pattern = imp_pattern.getStack(); int nSlices = stack_pattern.getSize(); patterns_all[scene_num] = new double[nSlices][]; pattern_labels[scene_num] = new String[nSlices]; for (int k = 0; k < patterns_all[scene_num].length; k++) { pattern_labels[scene_num][k]=stack_pattern.getShortSliceLabel(k+1); float [] fpixels_pattern = (float[]) stack_pattern.getPixels(k+1); patterns_all[scene_num][k]=new double[fpixels_pattern.length]; for (int i = 0; i < fpixels_pattern.length; i++) { patterns_all[scene_num][k][i] = fpixels_pattern[i]; } } ak[scene_num] = AltitudeMismatchKernel.getKernel( gops[scene_num].getZoomLevel(), // zoom_level, // int zoom_level, gops[scene_num].getAGL(), // double agl_from, ortho_maps[indices[scene_num]].getAGL(), // double agl_to, kernels); // ArrayList<AltitudeMismatchKernel> kernels) if (ak[scene_num] != null) { String kernel_path = ak[scene_num].getKernelPath(); ImagePlus imp_kernel = new ImagePlus(kernel_path); if (imp_kernel.getWidth() > 0) { float [] kernel_pixels = (float[]) imp_kernel.getProcessor().getPixels(); kernels_all[scene_num] = new double[kernel_pixels.length]; for (int i = 0; i < kernel_pixels.length; i++) { kernels_all[scene_num][i] = kernel_pixels[i]; } } } corrs_out[scene_num] = new double[patterns_all[scene_num].length][]; corr_patterns[scene_num] = new double[patterns_all[scene_num].length][]; for (int n = 0; n < patterns_all[scene_num].length; n++) { if (omp.convolve_after) { corr_patterns[scene_num][n] = OrthoMap.patternZoomCropPad( patterns_all[scene_num][n], // double [] pattern, pattern_sizes[scene_num], // int pattern_size, omp.corr_size, // int size, zoomout[scene_num], // int zoomout, false); // out_normalize); // boolean normalize) if (kernels_all[scene_num] != null) { corr_patterns[scene_num][n] = OrthoMap.convolveWithKernel( corr_patterns[scene_num][n], // final double [] data, kernels_all[scene_num], // final double [] kernel, omp.corr_size); // final int width) } } else { double [] pattern = patterns_all[scene_num][n].clone(); if (kernels_all[scene_num] != null) { pattern = OrthoMap.convolveWithKernel( pattern, // final double [] data, kernels_all[scene_num], // final double [] kernel, pattern_sizes[scene_num]); // final int width) } corr_patterns[scene_num][n] = OrthoMap.patternZoomCropPad( pattern, // double [] pattern, pattern_sizes[scene_num], // int pattern_size, omp.corr_size, // int size, zoomout[scene_num], // int zoomout, false); // out_normalize); // boolean normalize) } } if (scene_num == 0) { boolean debug = false; // debugLevel > 0; // 1; icorr_patterns[scene_num] = OrthoMap.getIntPatterns( corr_patterns[scene_num], // double [][] patterns, omp.abs_edge_frac, // double edge_frac, // 0.15 omp.abs_oversize, // double oversize, debug); // boolean debug); } // so far contrast is only for the main scene if ((debugLevel > -1) && (omp.show_images || omp.save_images) && (scene_num == 0)) { String [] pattern_titles = new String[corr_patterns[scene_num].length]; for (int i = 0; i < pattern_titles.length; i++) { pattern_titles[i] = "patt-"+i; } ImagePlus imp = ShowDoubleFloatArrays.makeArrays( corr_patterns[scene_num], omp.corr_size, omp.corr_size, "pattern-"+scene_num+"_"+omp.corr_size+"x"+omp.corr_size+(omp.convolve_after?"_convolved_after":"_convolved_before"), pattern_titles); showSaveImagePlus( imp, // ImagePlus imp, omp.show_images, // boolean show, omp.save_images, // boolean save, omp.save_dir, // String save_dir, debugLevel); // int debugLevel) double [][] dbg_img = new double [icorr_patterns[scene_num].length][icorr_patterns[scene_num][0].length]; for (int n = 0; n < dbg_img.length; n++) { for (int i= 0; i < dbg_img[n].length; i++) if (icorr_patterns[scene_num][n][i] != 0){ switch (icorr_patterns[scene_num][n][i]) { case 1: dbg_img[n][i] = 1.0; break; case 2: dbg_img[n][i] = -1.0; break; case 3: dbg_img[n][i] = -2.0; break; } } } ImagePlus imp1 = ShowDoubleFloatArrays.makeArrays( dbg_img, omp.corr_size, omp.corr_size, "integer_patterns", pattern_titles); showSaveImagePlus( imp1, // ImagePlus imp, omp.show_images, // boolean show, omp.save_images, // boolean save, omp.save_dir, // String save_dir, debugLevel); // int debugLevel) } } // Splitting - above done for both scenes, below - just for the main one int scene_num = 0; double hi_lo_freq = 0.0; double [][] hi_freq_arr = null; int [] hi_freq_wh = new int[2]; { // Just testing for now int shrink_sel = 4; double spectrum_sigma = 2.0; int blank_xy = (int) Math.round(spectrum_sample[1]); double [][] hi_freq = OrthoMap.getHiFreqCirc( dmulti[scene_num], // final double [] data, width, // final int width, omp.corr_size, // final int size, // power of 2, such as 64 spectrum_sample[0], // final double center_period,// center frequency is size/center_period spectrum_sample[1], // final double range_period, // ~1.5 - from center/range to center*range blank_xy, // final int blank_xy, // hi_freq_wh, // final int [] wh, // result size debugLevel); // final int debugLevel); hi_freq_arr = new double [4][hi_freq.length]; for (int i = 0; i < hi_freq_arr.length; i++) { Arrays.fill(hi_freq_arr[i], Double.NaN); } for (int i = 0; i < hi_freq.length;i++) if (hi_freq[i] != null) { hi_freq_arr[2][i] = hi_freq[i][0]; hi_freq_arr[3][i] = hi_freq[i][1]; hi_freq_arr[1][i] = hi_freq[i][1]/hi_freq[i][0]; } hi_freq_arr[0] = hi_freq_arr[1].clone(); TileNeibs tn = new TileNeibs(hi_freq_wh[0],hi_freq_wh[1]); OrthoMap.fillNaNs( hi_freq_arr[0], // double [] data, tn, // TileNeibs tn, 3); // int min_neibs) (new DoubleGaussianBlur()).blurDouble( hi_freq_arr[0], // double[] pixels, hi_freq_wh[0], // int width, hi_freq_wh[1], // int height, spectrum_sigma, // double sigmaX, spectrum_sigma, // double sigmaY, 0.01); // double accuracy); boolean [] mask = new boolean[hi_freq.length]; for (int i = 0; i < mask.length;i++) { mask[i] = hi_freq[i] != null; } tn.shrinkSelection( shrink_sel, // final int shrink, // grow tile selection by 1 over non-background tiles 1: 4 directions, 2 - 8 directions, 3 - 8 by 1, 4 by 1 more mask, // final boolean [] tiles, null); // final boolean [] prohibit) double swd = 0.0, sw = 0.0; for (int i = 0; i < mask.length;i++) if (mask[i]){ swd += hi_freq[i][1]/hi_freq[i][0]; sw+= 1.0; } hi_lo_freq = swd/sw; String [] hi_freq_titles = {"blur","ratio","low","high"}; ImagePlus img_hi_freq = ShowDoubleFloatArrays.makeArrays( hi_freq_arr, hi_freq_wh[0], hi_freq_wh[1], // indices[scene_num]+"-"+ortho_maps[indices[scene_num]].getName()+"-HIGH_FREQ", basename+"-HIGH_FREQ", // hi_freq_titles); showSaveImagePlus( img_hi_freq, // ImagePlus imp, omp.show_images, // boolean show, omp.save_images, // boolean save, omp.save_dir, // String save_dir, debugLevel); // int debugLevel) System.out.println("Average hi_lo_freq="+hi_lo_freq); System.out.println(); } corrs_out[scene_num] = correlateAllPatterns( dmulti[scene_num], // double [] data, width, // int width, omp.corr_size, // int corr_size, corr_patterns[scene_num], // double [][] patterns, false, // boolean convolve, omp.phaseCoeff[scene_num], // double phaseCoeff, omp.lpf_sigma[scene_num], //double lpf_sigma, src_marks, // double [][] src_marks, ortho_maps[indices[scene_num]].getName(), // String prefix, // ortho_maps[indices[scene_num]].getName() omp.save_dir, // String save_dir, omp.show_images, // boolean show, omp.save_images, // boolean save, debugLevel); // int debugLevel) bestcorr[scene_num] = new double [dmulti[scene_num].length]; fullcorr[scene_num] = new double [dmulti[scene_num].length]; bestpatt[scene_num] = new int [bestcorr[scene_num].length]; min_corrs_full[scene_num] = omp.min_corrs[scene_num] * omp.min_corr_full_rel; // TODO: scale adv_radius, corr_radius ArrayList<Point> plist =OrthoMap.combineDirCorrs ( corrs_out[scene_num], // final double [][] corrs, width, // final int width, fullcorr[scene_num], // final double [] fullcorr_in, bestcorr[scene_num], // final double [] bestcorr_in bestpatt[scene_num], // final int [] bestpatt_in, ease_first_best*omp.min_corrs[scene_num], // final double min_corr, min_corrs_full[scene_num], // final double min_corr_full, omp.full_preference, // final double full_preference, omp.max_min_ratio, // final double max_min_ratio, omp.combine_full[0], // final boolean combine_full, // multiply by normalized full pattern correlation maximum adv_radii[scene_num], // final double adv_radius, corr_radius[scene_num]); // final int corr_radius) for (Point p : plist) { double [] matches = new double [corrs_out[scene_num].length + 1]; double [] match_xy = {p.x%width, p.x/width}; // here int values int best_patt = p.y + 1; matches[0] = bestcorr[scene_num][p.x]; for (int i = 1; i < matches.length; i++) { matches[i] = corrs_out[scene_num][i-1][p.x]; } ItemMatch item_match = new ItemMatch ( indices.length, match_xy); item_match.addPatternMatches( gops[scene_num], // GroundObjectPattern groundObjectPattern, matches, // double [] matches, best_patt); // int best_sub); matches_list.add(item_match); } /// int sort_pattern_index = 0; // used in other places, not just for sorting int sort_pattern_mode = sort_by_best? -1 : 0; // -1 - best match, regardles off best pointer 0; //combo. 1 - full pattern ArrayList<Integer> match_sort = ItemMatch.sortByMatch( matches_list, // ArrayList<ItemMatch> match_list, gops[scene_num], false, // boolean keep_removed,// GroundObjectPattern groundObjectPattern, sort_pattern_mode); // int indx) // Print all results String log_lines = "\n====="+ (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime())+ "=====\n"); log_lines += "Match candidates ("+match_sort.size()+ ") that exceed minimal correlation strength of "+omp.min_corrs[scene_num]+"\n"; System.out.println("Match candidates ("+match_sort.size()+ ") that exceed minimal correlation strength of "+omp.min_corrs[scene_num]); // remove non-overlapped from the list int num_non_overlap = 0; for (int i = match_sort.size()-1; i >=0; i--) { int indx =match_sort.get(i); ItemMatch match = matches_list.get(indx); int [] icenter_xy = match.getIntXY(); for (int sn = 1; sn < indices.length; sn++) { if (Double.isNaN(dmulti[sn][icenter_xy[0] + icenter_xy[1] * width])) { match.remove(); // mark as removed match_sort.remove(i); // remove from index list log_lines += "Removed object at x="+icenter_xy[0]+", y="+icenter_xy[1]+ " as it is not in the scenes overlap area\n"; if (debugLevel > -1) { System.out.println("Removed object at x="+icenter_xy[0]+", y="+icenter_xy[1]+ " as it is not in the scenes overlap area"); } num_non_overlap++; break; } } } // near the edge - check both images for (int i = match_sort.size()-1; i >=0; i--) { int indx =match_sort.get(i); ItemMatch match = matches_list.get(indx); int [] icenter_xy = match.getIntXY(); boolean has_NaN = true; seach_NaN: { int half_size=omp.corr_size/2; if ((icenter_xy[0] < half_size) || (icenter_xy[1] < half_size) || (icenter_xy[0] > (width - half_size)) || (icenter_xy[1] > (height - half_size))) { break seach_NaN; } for (int y = (icenter_xy[1] - half_size); y < (icenter_xy[1] + half_size); y++) { int indx0 = y * width; for (int dy =- half_size; dy < half_size; dy++) { for (int x = (icenter_xy[0] - half_size); x < (icenter_xy[0] + half_size); x++) { int indx1 = indx0 + x; for (int sn = 0; sn < indices.length; sn++) { if (Double.isNaN(dmulti[sn][indx1])) { break seach_NaN; } } } } } has_NaN = false; } if (has_NaN) { match.remove(); // mark as removed match_sort.remove(i); // remove from index list log_lines += "Removed object at x="+icenter_xy[0]+", y="+icenter_xy[1]+ " as it is near the edge of one of the images\n"; if (debugLevel > -1) { System.out.println("Removed object at x="+icenter_xy[0]+", y="+icenter_xy[1]+ " as it is near the edge of one of the images"); } num_non_overlap++; } } if (num_non_overlap > 0) { log_lines += "Removed "+num_non_overlap+" objects outside of the scenes overlap, "+ match_sort.size()+" remain\n"; System.out.println("Removed "+num_non_overlap+" objects outside of the scenes overlap, "+ match_sort.size()+" remain"); } if (omp.append_log) { // starting log with a date/time separator String log_path = omp.save_dir+basename+OrthoMapsParameters.LOG_SUFFIX; CalibrationFileManagement.saveStringToFile ( log_path, //String path, log_lines, // data, true); // boolean append) } double [] scene_xy_offset = {centers[last_scene][0]-centers[0][0],centers[last_scene][1]-centers[0][1]}; double scene_agl = gops[last_scene].getAGL(); // second scene agl omp.generateReport( // only goes to log null, // String log_line, // should end with \n or be null false, // boolean show, // ands with .show_images false, // boolean save, // ands with .save_images true, // boolean gen_results, false, // boolean gen_parameters, ortho_maps, // OrthoMap [] ortho_maps, new int [] {indices[0]}, // int [] indices, match_sort, // ArrayList<Integer> match_sort, matches_list, // ArrayList <ItemMatch> matches_list, zoom_level, // int zoom_level, scene_xy_offset, // double [] scene_xy_offset, scene_agl, // double scene_agl, corrs_out[scene_num].length, // int num_patterns, // corrs_out[scene_num].length origin, // int [] origin, centers, // double [][] centers, gops[scene_num], // GroundObjectPattern gop, hi_lo_freq, // double hi_lo_freq, hi_freq_wh, // int [] hi_freq_wh, hi_freq_arr); // double [][] hi_freq_arr, if (debugLevel > 0) { omp.printReport( true, // boolean gen_results, false, // boolean gen_parameters, ortho_maps, // OrthoMap [] ortho_maps, new int [] {indices[0]}, // int [] indices, match_sort, // ArrayList<Integer> match_sort, matches_list, // ArrayList <ItemMatch> matches_list, zoom_level, // int zoom_level, scene_xy_offset, // double [] scene_xy_offset, scene_agl, // double scene_agl, corrs_out[scene_num].length, // int num_patterns, // corrs_out[scene_num].length origin, // int [] origin, centers, // double [][] centers, gops[scene_num], // GroundObjectPattern gop, hi_lo_freq, // double hi_lo_freq, hi_freq_wh, // int [] hi_freq_wh, hi_freq_arr); // double [][] hi_freq_arr, } PointRoi roi = new PointRoi(); roi.setOptions("nolabel"); // label"); for (int mn=0; mn < match_sort.size(); mn++) { roi.addPoint(omp.extr_size/2,omp.extr_size/2,mn+1); // ,1); } // Extract main scene areas around candidates ItemMatch.setExtractedObjects( scene_num, // int scene_num, omp.extr_size, // int extr_size, gops[scene_num], //GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, dmulti[scene_num], // double[] data, width, // int width, debugLevel); // int debugLevel) // calculate absolute contrasts for all patterns, update best_sub modes: 0 - keep, 1 - keep type, 2 keep type and if half +/-1, // 3 - free, 4 - force round objects. This may change best subpattern and should be reflected by filters. Maybe even reorder candidates? if (omp.corr_centered) { // re-evaluate correlations centered, recreate and reorder list log_lines = ""; for (int mn=0; mn < match_sort.size(); mn++) { int indx =match_sort.get(mn); ItemMatch match = matches_list.get(indx); double [] center_xy = match.getXY(); // correlate each extracted object with all patterns, save to corrs_out_centered // get correlation for the pattern[0] and re-center if needed // CorrelationPeakStats[] filter_data = new CorrelationPeakStats[2]; double [] corrs_centered = OrthoMap.correlateWithPattern( match.extracted_nodc[scene_num], // extr_data[mn], // final double [] data, omp.corr_size, // final int width, omp.corr_size, // final int psize, // power of 2, such as 64 corr_patterns[scene_num][0], // final double [] pattern, // [psize*psize] false, // final boolean convolve, // convolve, not correlate omp.phaseCoeff[scene_num], // final double phaseCoeff, omp.lpf_sigma[scene_num], // final double lpf_sigma, // 0 - do not filter debugLevel); // final int debugLevel) { match.setCorrFull( scene_num, // int scene_num, corr_patterns[scene_num].length, // int num_patt, corrs_centered); // double [] corr_data) match.filter_data[scene_num][0] = new CorrelationPeakStats( // null pointer match.getCorr(scene_num,0), // corrs_centered, // double [] data, // square data null, // double [] cent_xy, // if null, use center of the square search_radius_recenter, // search_radius, // double radius, // search for maximum within this radius. Should radius be 0 here? omp.filt_frac_max, // double frac_max) 0, // filt_other_rad, // double other_radius, debugLevel); double [] matches = new double [corr_patterns[scene_num].length + 1]; int best_indx = 0; if (Double.isNaN(match.filter_data[scene_num][0].best_d)) { log_lines += "Could not find recentered maximum for ["+center_xy[0]+"/"+center_xy[1]+ "], removing this peak.\n"; if (debugLevel > -4) { System.out.println("Could not find recentered maximum for ["+center_xy[0]+"/"+center_xy[1]+ "], removing this peak."); } // will add fake with all 0 } else { // double [] cent_offs = filter_data[0].cent_offs; double [] cent_offs = match.filter_data[scene_num][0].cent_offs; int [] old_xy = new int [] {(int) Math.round(center_xy[0]), (int) Math.round(center_xy[1])}; center_xy[0]+=cent_offs[0]; center_xy[1]+=cent_offs[1]; int [] new_xy = new int [] {(int) Math.round(center_xy[0]), (int) Math.round(center_xy[1])}; match.setComboPXY(center_xy); // update center if ((new_xy[0] != old_xy[0]) || (new_xy[1] != old_xy[1])) { log_lines += "Centered object position shifted from ["+old_xy[0]+"/"+old_xy[1]+ "], to ["+new_xy[0]+"/"+new_xy[1]+"], rebuilding extracts.\n"; if (debugLevel > -4) { System.out.println("Centered object position shifted from ["+old_xy[0]+"/"+old_xy[1]+ "], to ["+new_xy[0]+"/"+new_xy[1]+"], rebuilding extracts."); } match.extractObject( dmulti[scene_num], // double [] data, width, // int width, scene_num, // int scene_num, omp.extr_size); // int extr_size); corrs_centered = OrthoMap.correlateWithPattern( match.extracted_nodc[scene_num], // final double [] data, omp.corr_size, // final int width, omp.corr_size, // final int psize, // power of 2, such as 64 corr_patterns[scene_num][0], // final double [] pattern, // [psize*psize] false, // final boolean convolve, // convolve, not correlate omp.phaseCoeff[scene_num], // final double phaseCoeff, omp.lpf_sigma[scene_num], // final double lpf_sigma, // 0 - do not filter debugLevel); // final int debugLevel) { match.setCorr ( scene_num, // int scene_num, 0, // int patt_index, corrs_centered); // double [] corr_data) } // calculate correlations for the rest of patterns for (int patt_indx = 1; patt_indx < corr_patterns[scene_num].length; patt_indx++) { corrs_centered = OrthoMap.correlateWithPattern( match.extracted_nodc[scene_num], // final double [] data, omp.corr_size, // final int width, omp.corr_size, // final int psize, // power of 2, such as 64 corr_patterns[scene_num][patt_indx], // final double [] pattern, // [psize*psize] false, // final boolean convolve, // convolve, not correlate omp.phaseCoeff[scene_num], // final double phaseCoeff, omp.lpf_sigma[scene_num], // final double lpf_sigma, // 0 - do not filter debugLevel); // final int debugLevel) { match.setCorr ( scene_num, // int scene_num, patt_indx, // int patt_index, corrs_centered); // double [] corr_data) } double [][] corrs_all = match.getCorrs( scene_num); // int scene_num); int cent_indx = (omp.corr_size+1)*omp.corr_size/2; double best_v = corrs_all[1][cent_indx]; double worst_v = best_v; double full_v = corrs_all[0][cent_indx]; best_indx=1; for (int patt_indx = 2; patt_indx < corrs_all.length; patt_indx++) { if (corrs_all[patt_indx][cent_indx] > best_v) { best_v=corrs_all[patt_indx][cent_indx]; best_indx = patt_indx; } if (corrs_all[patt_indx][cent_indx] < worst_v) { worst_v = corrs_all[patt_indx][cent_indx]; } } if (full_v * omp.full_preference > best_v) { best_indx = 0; } else { if ((worst_v > 0) && (best_v/worst_v < omp.max_min_ratio)) { best_indx = 0; } } matches[0] = corrs_all[best_indx][cent_indx]; for (int i = 1; i < matches.length; i++) { matches[i] = corrs_all[i-1][cent_indx]; } } // select best pattern similar to combineDirCorrs(), using center pixel // add to a new list (and then replace main list and reorder?) //matches_list_centered match.addPatternMatches( // will update existing match too gops[scene_num], // GroundObjectPattern groundObjectPattern, matches, // double [] matches, best_indx + 1); // int best_sub); } // for (int mn=0; mn < match_sort.size(); mn++) { // some matches are already marked as removed match_sort = ItemMatch.sortByMatch( matches_list, // ArrayList<ItemMatch> match_list, gops[scene_num], // GroundObjectPattern groundObjectPattern, false, // boolean keep_removed,// GroundObjectPattern groundObjectPattern, sort_pattern_mode); // int indx) // Print all results log_lines += "Centered match candidates ("+match_sort.size()+")\n"; System.out.println("Centered match candidates ("+match_sort.size()+ ")"); // +min_corrs[scene_num]); if (omp.append_log) { // starting log with a date/time separator String log_path = omp.save_dir+basename+OrthoMapsParameters.LOG_SUFFIX; CalibrationFileManagement.saveStringToFile ( log_path, //String path, log_lines, // data, true); // boolean append) } // print updated list omp.generateReport( // only goes to log null, // String log_line, // should end with \n or be null false, // boolean show, // ands with .show_images false, // boolean save, // ands with .save_images true, // boolean gen_results, false, // boolean gen_parameters, ortho_maps, // OrthoMap [] ortho_maps, new int [] {indices[0]}, // int [] indices, match_sort, // ArrayList<Integer> match_sort, matches_list, // ArrayList <ItemMatch> matches_list, zoom_level, // int zoom_level, scene_xy_offset, // double [] scene_xy_offset, scene_agl, // double scene_agl, corrs_out[scene_num].length, // int num_patterns, // corrs_out[scene_num].length origin, // int [] origin, centers, // double [][] centers, gops[scene_num], // GroundObjectPattern gop, hi_lo_freq, // double hi_lo_freq, hi_freq_wh, // int [] hi_freq_wh, hi_freq_arr); // double [][] hi_freq_arr, if (debugLevel > 0) { omp.printReport( true, // boolean gen_results, false, // boolean gen_parameters, ortho_maps, // OrthoMap [] ortho_maps, new int [] {indices[0]}, // int [] indices, match_sort, // ArrayList<Integer> match_sort, matches_list, // ArrayList <ItemMatch> matches_list, zoom_level, // int zoom_level, scene_xy_offset, // double [] scene_xy_offset, scene_agl, // double scene_agl, corrs_out[scene_num].length, // int num_patterns, // corrs_out[scene_num].length origin, // int [] origin, centers, // double [][] centers, gops[scene_num], // GroundObjectPattern gop, hi_lo_freq, // double hi_lo_freq, hi_freq_wh, // int [] hi_freq_wh, hi_freq_arr); // double [][] hi_freq_arr, } } else { // reuse already calculated correlations for (int mn=0; mn < match_sort.size(); mn++) { int indx =match_sort.get(mn); ItemMatch match = matches_list.get(indx); match.extractCorrs( // copy all 9 layers corrs_out[scene_num], // double [][] corr_data, width, // int width, scene_num, // int scene_num, omp.extr_size); // int extr_size) } } setAbsoluteContrasts( // move later than corr_centered omp, // OrthoMapsParameters omp gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, scene_num, // int scene_num, icorr_patterns[scene_num], // int [][] ipatterns, debugLevel); // int debugLevel for (int mn=0; mn < match_sort.size(); mn++) { int indx =match_sort.get(mn); ItemMatch match = matches_list.get(indx); // should be already updated by setAbsoluteContrasts(); int best_patt = match.getPatternMatch(gops[scene_num]).getBestSub() - 1; // double [] center_xy = match.getXY(); match.filter_data[scene_num][0] = new CorrelationPeakStats( // null pointer match.getCorr(scene_num,0),// corrs_centered, // double [] data, // square data null, // double [] cent_xy, // if null, use center of the square omp.search_radius, // search_radius, // double radius, // search for maximum within this radius. Should radius be 0 here? omp.filt_frac_max, // double frac_max) omp.filt_other_rad, // filt_other_rad, // double other_radius, debugLevel); match.setCorrHalf( scene_num, // int scene_num, best_patt, // int best_patt, omp.combine_full[0]); // boolean combine_full) { match.filter_data[scene_num][1] = new CorrelationPeakStats( match.getCorrHalf(scene_num), // double [] data, // square data null, // double [] cent_xy, // if null, use center of the square omp.search_radius, // double radius, // search for maximum within this radius. Should radius be 0 here? omp.filt_frac_max, // double frac_max) omp.filt_other_rad, // double other_radius, debugLevel); // final int debugLevel) { } boolean show_centers = true; if (indices.length == 1) { // for a single-scene show before filtering by the first(only) scene for (int sn = 0; sn < indices.length; sn++) { String prefix = ortho_maps[indices[sn]].getName()+"-prefilter1"; String suffix0 = "_pc"+omp.phaseCoeff[sn]+"_lpf"+omp.lpf_sigma[sn]; for (int mode = 0; mode < 4; mode++) { // all 4 image types String suffix = (mode <2)? "":suffix0; if ((sn == 0) || (mode == 0)) { ItemMatch.getImageExtracts( prefix, // String prefix, // include PC, filter here suffix, // String suffix, // w/o .tiff omp.show_images, // boolean show, omp.save_images, // boolean save, false, // boolean show_removed, omp.save_dir, // String save_dir, sn, // int scene_num, omp.remove_dc, // boolean nodc, mode, // int mode, // 0 - extract, 1 - masked, 2 - corr full, 3 - corr half show_centers, // boolean show_centers, omp.extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, debugLevel); // int debugLevel) } } } } if (!omp.filt_atonce) { // first filter using scene 1 only boolean[] filt_main_other = new boolean [indices.length]; //{true,false}; filt_main_other[0] = true; int num_removed = filterCandidates( omp, // OrthoMapsParameters omp, basename, // String basename, filt_main_other, // new boolean [] filt_main_other, gops[scene_num], // GroundObjectPattern gop, zoom_level, // int zoom_level, scene_agl, // double scene_agl, scene_xy_offset, // double [] scene_xy_offset, //second scene vertical projection offset from the first scene matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, debugLevel); // int debugLevel ) // if (!filt_keep_pre && (removed.length > match_sort.size())) { // decimate data arrays // removed = new boolean [match_sort.size()]; // } } if (indices.length > 1) { // for a multi-scene show after filtering by the first scene // here extract for the second scene for (int sn = 1; sn < indices.length; sn++) { ItemMatch.setExtractedObjects( sn, // int scene_num, omp.extr_size, // int extr_size, gops[sn], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, dmulti[sn], // double[] data, width, // int width, debugLevel); // int debugLevel) } for (int sn = 0; sn < indices.length; sn++) { String prefix = ortho_maps[indices[sn]].getName()+"-postfilter1"; String suffix0 = "_pc"+omp.phaseCoeff[sn]+"_lpf"+omp.lpf_sigma[sn]; for (int mode = 0; mode < 4; mode++) { // all 4 image types String suffix = (mode <2)? "":suffix0; if ((sn == 0) || (mode == 0)) { ItemMatch.getImageExtracts( prefix, // String prefix, // include PC, filter here suffix, // String suffix, // w/o .tiff omp.show_images, // boolean show, omp.save_images, // boolean save, false, // boolean show_removed, omp.save_dir, // String save_dir, sn, // int scene_num, omp.remove_dc, // boolean nodc, mode, // int mode, // 0 - extract, 1 - masked, 2 - corr full, 3 - corr half show_centers, // boolean show_centers, omp.extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, debugLevel); // int debugLevel) } } } } // display extracted correlations for the main scene: if (indices.length > 1) { // print updated table after some scenes may be removed by the filter omp.generateReport( // only goes to log "\nAfter first scene filtering:\n", // String log_line, // should end with \n or be null false, // boolean show, // ands with .show_images false, // boolean save, // ands with .save_images true, // boolean gen_results, false, // boolean gen_parameters, ortho_maps, // OrthoMap [] ortho_maps, new int [] {indices[0]}, // int [] indices, match_sort, // ArrayList<Integer> match_sort, matches_list, // ArrayList <ItemMatch> matches_list, zoom_level, // int zoom_level, scene_xy_offset, // double [] scene_xy_offset, scene_agl, // double scene_agl, corrs_out[scene_num].length, // int num_patterns, // corrs_out[scene_num].length origin, // int [] origin, centers, // double [][] centers, gops[scene_num], // GroundObjectPattern gop, hi_lo_freq, // double hi_lo_freq, hi_freq_wh, // int [] hi_freq_wh, hi_freq_arr); // double [][] hi_freq_arr, if (debugLevel > 0) { System.out.println("\nAfter first scene filtering:"); omp.printReport( true, // boolean gen_results, false, // boolean gen_parameters, ortho_maps, // OrthoMap [] ortho_maps, new int [] {indices[0]}, // int [] indices, match_sort, // ArrayList<Integer> match_sort, matches_list, // ArrayList <ItemMatch> matches_list, zoom_level, // int zoom_level, scene_xy_offset, // double [] scene_xy_offset, scene_agl, // double scene_agl, corrs_out[scene_num].length, // int num_patterns, // corrs_out[scene_num].length origin, // int [] origin, centers, // double [][] centers, gops[scene_num], // GroundObjectPattern gop, hi_lo_freq, // double hi_lo_freq, hi_freq_wh, // int [] hi_freq_wh, hi_freq_arr); // double [][] hi_freq_arr, } } // Correlate second scene only for selected fragments for (int scene_other = 1; scene_other < indices.length; scene_other++) { // normally just 1 for (int mn=0; mn < match_sort.size(); mn++) { int indx =match_sort.get(mn); ItemMatch match = matches_list.get(indx); int best_patt = match.getPatternMatch(gops[scene_num]).getBestSub() - 1; // correlate extracted squares of the other scene with full pattern double [] corr_full = OrthoMap.correlateWithPattern( match.extracted_nodc[scene_other], // extr_data[mn], // final double [] data, omp.corr_size, // final int width, omp.corr_size, // final int psize, // power of 2, such as 64 corr_patterns[scene_other][0], // final double [] pattern, // [psize*psize] false, // final boolean convolve, // convolve, not correlate omp.phaseCoeff[scene_other], // final double phaseCoeff, omp.lpf_sigma[scene_other], // final double lpf_sigma, // 0 - do not filter debugLevel); // final int debugLevel) { // set [9] array of correlations and copy full pattern ([0]) data. Other (1-9) will not be used // for scene_other match.setCorrFull( scene_other, // int scene_num, corr_patterns[scene_other].length, // int num_patt, corr_full); // double [] corr_data) match.filter_data[scene_other][0] = new CorrelationPeakStats( // null pointer match.getCorr(scene_other,0), // double [] data, // square data null, // double [] cent_xy, // if null, use center of the square omp.search_radius, // search_radius, // double radius, // search for maximum within this radius. Should radius be 0 here? omp.filt_frac_max, // double frac_max) omp.filt_other_rad, // double other_radius, debugLevel); if (best_patt > 0) { // not full match - for full match it will copy full correlation double [] corr_half = OrthoMap.correlateWithPattern( match.extracted_nodc[scene_other], // extr_data[mn], // final double [] data, omp.corr_size, // final int width, omp.corr_size, // final int psize, // power of 2, such as 64 corr_patterns[scene_other][best_patt], // final double [] pattern, // [psize*psize] false, // final boolean convolve, // convolve, not correlate omp.phaseCoeff[scene_other], // final double phaseCoeff, omp.lpf_sigma[scene_other], // final double lpf_sigma, // 0 - do not filter debugLevel); // final int debugLevel) { // setting only one correlation of 8 halves match.setCorr ( scene_other, // int scene_num, best_patt, // int patt_index, corr_half); // double [] corr_data) } // Do it in any case, if best_patt==0 it will copy, optionally limit by 0 and square full correlation match.setCorrHalf( // D scene_other, // int scene_num, best_patt, // int best_patt, omp.combine_full[1]); // boolean combine_full) { // set statistics for half-correlations of the second scene // before it was just a copy of full, regardless of combine_full[1] match.filter_data[scene_other][1] = new CorrelationPeakStats( match.getCorrHalf(scene_other), // double [] data, // square data null, // double [] cent_xy, // if null, use center of the square omp.search_radius, // double radius, // search for maximum within this radius. Should radius be 0 here? omp.filt_frac_max, // double frac_max) omp.filt_other_rad, // double other_radius, debugLevel); // final int debugLevel) { } // show correlations for the second scene String prefix = ortho_maps[indices[scene_num]].getName()+"-postfilter1"; String suffix0 = "_pc"+omp.phaseCoeff[scene_other]+"_lpf"+omp.lpf_sigma[scene_other]; for (int mode = 2; mode < 4; mode++) { // all 2 correlation types (full and half) String suffix = (mode < 2)? "":suffix0; ItemMatch.getImageExtracts( prefix, // String prefix, // include PC, filter here suffix, // String suffix, // w/o .tiff omp.show_images, // boolean show, omp.save_images, // boolean save, false, // boolean show_removed, omp.save_dir, // String save_dir, scene_other, // int scene_num, omp.remove_dc, // boolean nodc, mode, // int mode, // 0 - extract, 1 - masked, 2 - corr full, 3 - corr half show_centers, // boolean show_centers, omp.extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, debugLevel); // int debugLevel) } } // final filter boolean[] filt_main_other = new boolean [indices.length]; //{true,false}; Arrays.fill(filt_main_other, true); /// int num_removed = filterCandidates( // will filter again if already omp, // OrthoMapsParameters omp, basename, // String basename, filt_main_other, // new boolean [] filt_main_other, gops[scene_num], // GroundObjectPattern gop, zoom_level, // int zoom_level, scene_agl, // double scene_agl, scene_xy_offset, // double [] scene_xy_offset, //second scene vertical projection offset from the first scene matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, debugLevel); // int debugLevel ) if (!omp.filt_keep) { // show final filtered images again - just extracts (2) and all correlations (4) for (int sn = 0; sn < indices.length; sn++) { String prefix = ortho_maps[indices[sn]].getName()+"-final"; String suffix0 = "_pc"+omp.phaseCoeff[sn]+"_lpf"+omp.lpf_sigma[sn]; for (int mode = 0; mode < 4; mode++) { // all 4 image types String suffix = (mode <2)? "":suffix0; if ((sn == 0) || (mode != 1)) { // masked - for main scene only ItemMatch.getImageExtracts( prefix, // String prefix, // include PC, filter here suffix, // String suffix, // w/o .tiff omp.show_images, // boolean show, omp.save_images, // boolean save, false, // boolean show_removed, omp.save_dir, // String save_dir, sn, // int scene_num, omp.remove_dc, // boolean nodc, mode, // int mode, // 0 - extract, 1 - masked, 2 - corr full, 3 - corr half show_centers, // boolean show_centers, omp.extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, debugLevel); // int debugLevel) } } } } omp.generateReport( // final null, // String log_line, // should end with \n or be null true, // boolean show, // ands with .show_images true, // boolean save, // ands with .save_images true, // boolean gen_results, true, // boolean gen_parameters, ortho_maps, // OrthoMap [] ortho_maps, indices, // int [] indices, match_sort, // ArrayList<Integer> match_sort, matches_list, // ArrayList <ItemMatch> matches_list, zoom_level, // int zoom_level, scene_xy_offset, // double [] scene_xy_offset, scene_agl, // double scene_agl, corrs_out[scene_num].length, // int num_patterns, // corrs_out[scene_num].length origin, // int [] origin, centers, // double [][] centers, gops[scene_num], // GroundObjectPattern gop, hi_lo_freq, // double hi_lo_freq, hi_freq_wh, // int [] hi_freq_wh, hi_freq_arr); // double [][] hi_freq_arr, if (debugLevel > -3) { omp.printReport( true, // boolean gen_results, true, // boolean gen_parameters, ortho_maps, // OrthoMap [] ortho_maps, indices, // int [] indices, match_sort, // ArrayList<Integer> match_sort, matches_list, // ArrayList <ItemMatch> matches_list, zoom_level, // int zoom_level, scene_xy_offset, // double [] scene_xy_offset, scene_agl, // double scene_agl, corrs_out[scene_num].length, // int num_patterns, // corrs_out[scene_num].length origin, // int [] origin, centers, // double [][] centers, gops[scene_num], // GroundObjectPattern gop, hi_lo_freq, // double hi_lo_freq, hi_freq_wh, // int [] hi_freq_wh, hi_freq_arr); // double [][] hi_freq_arr, } ImagePlus img_final = null; { // Create image with marked mines PointRoi final_roi = new PointRoi(); final_roi.setOptions("label"); for (int i = 0; i < match_sort.size(); i++) { int indx =match_sort.get(i); ItemMatch match = matches_list.get(indx); // double [] match_values = match.getMatchValues(gops[scene_num]); double [] center_xy = match.getXY(); final_roi.addPoint(center_xy[0], center_xy[1]); // ,1); } String [] final_titles = new String[indices.length]; double [][] final_map = new double [indices.length][]; for (int i = 0; i < final_map.length; i++) { final_titles[i] = ortho_maps[indices[i]].getName(); final_map[i] = dmulti[i]; } String final_title =indices[0]+""; for (int i = 1; i < final_map.length; i++) { final_title +="-"+indices[i]; } final_title += "_"+final_titles[0]; for (int i = 1; i < final_map.length; i++) { final_title +="-"+final_titles[i]; } final_title += "-MARKED_"+omp.object_type+"_"+match_sort.size()+"_OBJECTS"; img_final = ShowDoubleFloatArrays.makeArrays( final_map, width, height, final_title, final_titles); img_final.setRoi(final_roi); showSaveImagePlus( img_final, // ImagePlus imp, omp.show_final_image, // boolean show, omp.save_images, // boolean save, omp.save_dir, // String save_dir, debugLevel); // int debugLevel) } System.out.println("patternMatchDual(): all correlations DONE"); return img_final; } public static double[][] correlateAllPatterns( double [] data, int width, int corr_size, double [][] patterns, boolean convolve, double phaseCoeff, double lpf_sigma, double [][] src_marks, String prefix, // ortho_maps[indices[scene_num]].getName(). null - do not show String save_dir, boolean show, boolean save, int debugLevel) { double [][] corrs_out = new double [patterns.length][]; for (int n = 0; n < patterns.length; n++) { corrs_out[n]= OrthoMap.correlateWithPattern( data, // final double [] data, width, // final int width, corr_size, // final int psize, // power of 2, such as 64 patterns[n], // final double [] pattern, // [psize*psize] convolve, // final boolean convolve, // convolve, not correlate phaseCoeff, // final double phaseCoeff, lpf_sigma, // final double lpf_sigma, // 0 - do not filter debugLevel); // final int debugLevel) { } if ((prefix != null) && (show || save)) { String [] patt_titles = new String[corrs_out.length]; for (int i = 0; i < patt_titles.length; i++) { patt_titles[i] = "patt_"+i; } int height = data.length/width; ImagePlus imp_corr = ShowDoubleFloatArrays.makeArrays( corrs_out, width, height, prefix+"-PATTERN_CORRS_PC"+phaseCoeff+"_LPF"+lpf_sigma, patt_titles); // test_titles, if (src_marks != null) { PointRoi roi = new PointRoi(); roi.setOptions("label"); for (int i = 0; i < src_marks.length; i++) { roi.addPoint(src_marks[i][0],src_marks[i][1]); // ,1); } imp_corr.setRoi(roi); } showSaveImagePlus( imp_corr, // ImagePlus imp, show, // boolean show, save, // boolean save, save_dir, // String save_dir, debugLevel); // int debugLevel) // imp_corr.show(); } return corrs_out; } /** * Returns boolean array of original length * @param filt_keep * @param filt_main_other * @param min_corrs * @param min_corr_full_rel * @param filt_max_radius * @param filt_elongation * @param filt_dist * @param filt_height * @param filter_data * @param scene_xy_offset * @param matches_list * @param match_sort * @param debugLevel * @return */ public static int filterCandidates( OrthoMapsParameters omp, String basename, boolean [] filt_main_other, // length== number of scenes GroundObjectPattern gop, int zoom_level, double scene_agl, double [] scene_xy_offset, //second scene vertical projection offset from the first scene ArrayList <ItemMatch> matches_list, ArrayList<Integer> match_sort, int debugLevel ) { // >0 - print StringBuffer sb = new StringBuffer(); int num_scenes = filt_main_other.length; int num_removed = 0; double [][] min_corrs_other = { {omp.min_corrs[0]*omp.min_corr_full_rel, omp.min_corrs[0]}, {omp.min_corrs[1]*omp.min_corr_full_rel, omp.min_corrs[1]}}; double offset_from_height = elevationToParallelOffset( // will be negative omp.filt_height, // double height, scene_xy_offset, // double [] scene_offset, zoom_level, // int zoom_level, scene_agl) ;// double agl) for (int mn=0; mn < match_sort.size(); mn++){ ItemMatch match = matches_list.get(match_sort.get(mn)); if (match.isRemoved()) { continue; // already removed } CorrelationPeakStats[][] filter_data = match.filter_data; int [] ixy = match.getIntXY(); String name = ixy[0]+"/"+ixy[1]; double abs_contrast = match.getAbsoluteContrast(); double roundness = match.getRoundness(gop); int best_patt= match.getPatternMatch(gop).getBestSub(); // 1-based double best_value = match.getMatchBestValue(gop); boolean is_partial = best_patt != 1; // only apply to combined full/half correlations, just full may have nearby maximums near the same size double [] strength_full_half_ratio = new double[num_scenes]; for (int scene_num = 0; scene_num < num_scenes; scene_num++) if (filt_main_other[scene_num] && (filter_data[scene_num] != null)){ strength_full_half_ratio[scene_num] = filter_data[scene_num][0].best_d / filter_data[scene_num][1].best_d; } // filt_abs_easepart double min_abs_contrast = omp.filt_abs_contrast; if (is_partial) { min_abs_contrast *= omp.filt_abs_easepart; } if (abs_contrast < min_abs_contrast) { sb.append(name+": filtered out by "+NAME_MO[0]+" scene because its absolute contrast = "+ abs_contrast+ " < "+min_abs_contrast+(is_partial?(". Eased for partial from "+omp.filt_abs_contrast):"")); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } if (!is_partial && !(roundness >= omp.filt_roundness[0])) { // NaN - also bad sb.append(name+": filtered out by "+NAME_MO[0]+" scene because it is full/round, and its roundness = "+ roundness+ " < "+omp.filt_roundness[0]); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } // only apply to full patterns, because partial can be filtered without it if (!is_partial && !(best_value >= omp.filt_best[0])) { // NaN - also bad sb.append(name+": filtered out by "+NAME_MO[0]+ " scene because it is full-pattern and its best correlation value = "+ best_value+ " < "+omp.filt_best[0]); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } for (int hf = 0; hf < 2; hf++) { for (int scene_num = 0; scene_num < num_scenes; scene_num++) if (filt_main_other[scene_num]){ if (Double.isNaN(filter_data[scene_num][hf].best_d)) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+"/"+NAME_FH[hf]+ " as correlation maximum is not found"); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); continue; } double eff_rad = filter_data[scene_num][hf].eff_rad; double elong = filter_data[scene_num][hf].elong; double corr_val = filter_data[scene_num][hf].best_d; double dist = filter_data[scene_num][hf].dist; double near_max_frac = filter_data[scene_num][hf].max_other; double [] second_xy_offsets = filter_data[scene_num][hf].cent_offs; // calculate projection double [] scnd_xy_proj = projectOffsetOnVector( second_xy_offsets, // double [] object_offset, scene_xy_offset); // double [] scene_offset) if (corr_val < min_corrs_other[scene_num][hf]) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+" correlation maximum value for "+ NAME_FH[hf]+ " = "+corr_val+" < "+ min_corrs_other[scene_num][hf]); sb.append("\n"); if (scene_num == 0) { sb.append( "For the main scene it could happen if the sub_pattern index changed after"+ " the absolute contrast evaluation."); sb.append("\n"); } if (!match.isRemoved()) num_removed++; match.remove(); } if (is_partial && (hf > 0) && (corr_val < omp.min_corr_half_rel * omp.min_corrs[scene_num])) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+ " because it is partial and correlation maximum value for "+ NAME_FH[hf]+ " = "+corr_val+" < "+ omp.min_corr_half_rel*min_corrs_other[scene_num][hf]+ " (increased by "+omp.min_corr_half_rel+" from base "+omp.min_corrs[scene_num]+")."); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } if (scene_num > 0) { // other scene // {best_d, eff_rad, elong, dist, cent_offs[0], cent_offs[1]} ; if (dist > omp.filt_dist[0]) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+" distance from the main scene peak "+ NAME_FH[hf]+ " = "+dist+" > "+omp.filt_dist[0]); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } // distance for main only or both? if (Math.abs(scnd_xy_proj[1]) > omp.filt_dist[1]) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+" distance from the main scene peak perpendicular to the scene offset vector "+ NAME_FH[hf]+ " = "+scnd_xy_proj[1]+", abs() > "+omp.filt_dist[1]); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } // offset_from_height if (scnd_xy_proj[0] > omp.filt_dist[1]) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+ " distance from the main scene peak parallel to the scene offset vector "+ NAME_FH[hf]+ " = "+scnd_xy_proj[0]+" > "+omp.filt_dist[1]+ " it could happen if the object is deep below average ground surface."); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } if (scnd_xy_proj[0] < (-omp.filt_dist[1]+offset_from_height)) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+ " distance from the main scene peak parallel to the scene offset vector "+ NAME_FH[hf]+ " = "+scnd_xy_proj[0]+" < "+(-omp.filt_dist[1]+offset_from_height)+ ".\n This limit accounts for objects above ground up to "+omp.filt_height+" meters ("+ offset_from_height+" pix."); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } } if (eff_rad > omp.filt_max_radius[scene_num][hf]) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+" scene radius for "+ NAME_FH[hf]+ " pattern: "+eff_rad+" > "+ omp.filt_max_radius[scene_num][hf]); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } if (elong > omp.filt_elongation[scene_num][hf]) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+" scene peak cros-section elongation for "+ NAME_FH[hf]+ " pattern: "+elong+" > "+ omp.filt_elongation[scene_num][hf]); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } if (is_partial && (omp.filt_other_frac != null) && (hf > 0) && (near_max_frac > omp.filt_other_frac[scene_num])) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+ " because it matches partial pattern ("+best_patt+") and combined full/partial correlation has a near peak "+ near_max_frac+" > "+ omp.filt_other_frac[scene_num]+" of the used peak."); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } if (hf==0) { if ( strength_full_half_ratio[scene_num] < omp.filt_full_half_frac[scene_num]) { sb.append(name+": filtered out by "+NAME_MO[scene_num]+ " because its correlation with the full patern strengh ratio to the partial pattern one = "+ strength_full_half_ratio[scene_num]+" < "+ omp.filt_full_half_frac[scene_num]+" (minimum allowed)."); sb.append("\n"); if (!match.isRemoved()) num_removed++; match.remove(); } } } } } StringBuffer sb1 = new StringBuffer(); if (! omp.filt_keep) { if (num_removed > 0) { for (int mn=match_sort.size()-1; mn >=0; mn--) { ItemMatch match = matches_list.get(match_sort.get(mn)); if (match.isRemoved()) { match_sort.remove(mn); } } sb1.append("Filtered out "+num_removed+ " scenes by the filter.\n"); } else { sb1.append("No scenes were filtered out by the filter\n"); } sb1.append(match_sort.size()+" candidate(s) remain\n"); } if (debugLevel > 0) { System.out.println(sb.toString()); } if (debugLevel > -3) { System.out.println(sb1.toString()); } sb.append(sb1); if (omp.append_log && (omp.save_dir!= null)) { String log_path = omp.save_dir+basename+OrthoMapsParameters.LOG_SUFFIX; CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) } return num_removed; } /** * Set absolute contrasts for each object, optionally changing sub-pattern index. Absolute contrast * is a difference between average (w/o outliers) data value in zone1 (center area of the pattern) and * the ring around it (zone2). * On return the matches_list data is modified - absolute contrasts are set, and best_pattern may be * updated. * Gets extracted_objects square arrays for each of the object, so the center of objects is in the * center of the square from ItemMatch instance from the list. Calculated (for debug) masked image is * also saved there. * * @param force_round overwrite mode with 4 - force pattern index to full (round) one * @param mode Update of the best sub-pattern index: * 0 - keep current * 1 - keep type (full/half) and allow half direction to change by -1,0, or +1 * 2 - keep type (full/half) * 3 - no restrictions - find the best sub-pattern index from scratch, * 4 - force round (full pattern) * @param neg_better center lower (colder) than around * @param outliers_frac fraction of outliers to remove while calculating averages * @param obscure_warm allow obscurant (that makes half-pattern from a full one only to be warmer * than the object (defined below fraction between the object center region and peripheral * ring. For hot objects obscurant should be colder than such fraction. * @param obscure_frac fraction between center average and peripheral ring to compare when obscure_warm * is set. * @param gop GroundObjectPattern instance used * @param matches_list list of pattern matches * @param match_sort sorted list of indices in pattern matches list, so the first is the best match. * @param scene_num - 0 for main, 1 - "other" * @param ipatterns integer square patterns with the same dimensions as the extracted_objects. Value * 0 - do not use this pixel, 1 - Zone1 (inside the pattern), 2 - Zone2 - reference ring around * the pattern. * @param debugLevel debug level. >0 - display images, >-4 - print. * @return true */ public static boolean setAbsoluteContrasts( OrthoMapsParameters omp, /* boolean force_round, int mode, // 0 - keep, 1 keep type and if half +/-1, 2 - keep type, 3 - any boolean neg_better, // more negative is difference - stronger result double outliers_frac, boolean obscure_warm, // = true; // obscured can only be by warmer objects double obscure_frac, // 0.25; // obscured threshold between center and outer */ GroundObjectPattern gop, ArrayList <ItemMatch> matches_list, ArrayList<Integer> match_sort, int scene_num, int [][] ipatterns, int debugLevel) { boolean debug = debugLevel > 1; int mode = omp.abs_mode; for (int mn=0; mn < match_sort.size(); mn++) { if (debugLevel > 1) { System.out.println("setAbsoluteContrasts(): Calculating absolute contrast for match #"+mn); } int indx =match_sort.get(mn); ItemMatch match = matches_list.get(indx); boolean [] try_sub = new boolean [ipatterns.length]; int best_patt= match.getPatternMatch(gop).getBestSub() - 1; // 0-based int num_halves = ipatterns.length - 1; // == 8 if (omp.abs_force_round) { mode = 4; } switch (mode) { case 0: try_sub[best_patt] = true; break; // only one case 1: // keep type and +-1 for direction if (best_patt == 0) { try_sub[0] = true; } else { for (int offs = -1; offs <= 1 ; offs++) { int i = best_patt+offs; if (i < 1) { i += num_halves; } else if (i > num_halves) { i -= num_halves; } try_sub[i] = true; } } break; case 2: // keep type if (best_patt == 0) { try_sub[0] = true; } else { for (int i = 1; i < try_sub.length; i++) { try_sub[i] = true; } } break; case 3: // any for (int i = 0; i < try_sub.length; i++) { try_sub[i] = true; } break; case 4: try_sub[0] = true; break; // only full pattern } double [][] dir_contrasts = new double [try_sub.length][]; int best_index = -1; for (int i = 0; i < try_sub.length; i++) if ((i==0) || try_sub[i]) { dir_contrasts[i]= OrthoMap.getAbsoluteContrast( match.extracted_objects[scene_num], // extracted_objects[mn], // double [] data, ipatterns[i], // int [] ipattern, omp.abs_outliers_frac, // double outliers_frac, omp.abs_obscure_frac, // double obscure_frac, // 0.25; // obscured threshold between center and outer null, debug); // boolean debug) if (omp.abs_invert) { dir_contrasts[i][0] *= -1; } boolean bad_obscurant = omp.abs_obscure_warm && (dir_contrasts[i][1] < 0); if (try_sub[i] && (dir_contrasts[i][0] > 0)) { if (bad_obscurant) { if (debugLevel > -4) { System.out.println("setAbsoluteContrasts(): removing pattern_sub="+i +", match object="+mn+" because of the bad obscurant (cold for cold object)"); } } else { best_index = i; } } } if (best_index < 0) { best_index = 0; if (debugLevel > -3) { System.out.println("setAbsoluteContrasts(): no candidates found (probably after removing bad obscurants) #" +mn+", giving a chance as full pattern. Was (1 for full):"+ (best_patt+1)+", new is "+(best_index + 1)); } } if (best_index != best_patt) { if (debugLevel > -3) { System.out.println("setAbsoluteContrasts(): updating best pattern index for match #" +mn+". Was (1 for full):"+(best_patt+1)+", new is "+(best_index + 1)); } match.getPatternMatch(gop).setBestSub(best_index + 1); } match.setAbsoluteContrast(dir_contrasts[best_index][0]); // the higher the better if (debugLevel > -3) { System.out.println("setAbsoluteContrasts(): Set absolute contrast for match #"+mn+" = "+ dir_contrasts[best_index][0]+ ", over threshold="+dir_contrasts[best_index][1]+"."); } match.extracted_masked[scene_num] = new double [match.extracted_objects[scene_num].length]; OrthoMap.getAbsoluteContrast( // calculate and set extracted_masked match.extracted_objects[scene_num], // double [] data, ipatterns[best_index], // int [] ipattern, omp.abs_outliers_frac, // double outliers_frac, omp.abs_obscure_frac, // double obscure_frac, // 0.25; // obscured threshold between center and outer match.extracted_masked[scene_num], debug); // boolean debug) } return true; } public String selectOneScene( int num_scene, 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 the "+fs[num_scene]+" image from the list ",1200,100); 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; int scene_number= gd.getNextChoiceIndex(); 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(int [] indices) { if (indices == null) { indices = new int [ortho_maps.length]; for (int indx = 0; indx < ortho_maps.length; indx++) { indices[indx] = indx; } } String [] lines = new String [indices.length]; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss.SS zzz");// may be VV instead of zzz int dbg_indx = -530; for (int i = 0; i < indices.length; i++) { int indx = indices[i]; if (indx == dbg_indx) { System.out.println("indx="+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[i]=String.format( "%3d %17s %26s %6.2f",indx, name, sdt, agl); } return lines; } public int [] getScenesSelection( CLTParameters clt_parameters, String purpose) { // " to generate stats", " to generate images" boolean filter_scenes = true; boolean select_scenes = true; double flt_min_sfm = clt_parameters.imp.flt_min_sfm; double flt_max_sfm = clt_parameters.imp.flt_max_sfm; // int flt_alt = clt_parameters.imp.flt_alt; int flt_orient = clt_parameters.imp.flt_orient; boolean flt_update_config = false; GenericJTabbedDialog gd = new GenericJTabbedDialog("Select scenes filter",800,400); gd.addCheckbox ("Filter scenes", filter_scenes, "Filter scenes."); gd.addCheckbox ("Select scenes", select_scenes, "Select scenes manually."); gd.addNumericField("Minimal SfM gain", flt_min_sfm, 3,7,"","Minimal SfM gain of the scenes."); gd.addNumericField("Maximal SfM gain", flt_max_sfm, 3,7,"","Maximal SfM gain of the scenes."); // gd. addChoice("Filter by pairwise ALT availability",IntersceneMatchParameters.FLT_ALT_MODES, IntersceneMatchParameters.FLT_ALT_MODES[flt_alt],"Filter by pairwise ALT availability."); gd. addChoice("Filter by orientation availability",IntersceneMatchParameters.FLT_ORIENT_MODES, IntersceneMatchParameters.FLT_ORIENT_MODES[flt_orient],"Filter by the scene orientation availability."); gd.addCheckbox ("Update configuration", flt_update_config, "Update matching configuration parameters to be saved as defaults."); gd.showDialog(); if (gd.wasCanceled()) return null; filter_scenes = gd.getNextBoolean(); select_scenes = gd.getNextBoolean(); flt_min_sfm = gd.getNextNumber(); flt_max_sfm = gd.getNextNumber(); // flt_alt = gd.getNextChoiceIndex(); flt_orient = gd.getNextChoiceIndex(); flt_update_config = gd.getNextBoolean(); if (flt_update_config) { 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_orient = flt_orient; } int [] indices = new int [ortho_maps.length]; for (int indx = 0; indx < ortho_maps.length; indx++) { indices[indx] = indx; } if (filter_scenes) { boolean [] selection = new boolean [ortho_maps.length]; int num_selected = 0; for (int indx = 0; indx < ortho_maps.length; indx++) { if ((ortho_maps[indx].getSfmGain()<flt_min_sfm) || (ortho_maps[indx].getSfmGain()>flt_max_sfm)) { selection[indx]=false; continue; } if ((ortho_maps[indx].getQOrinet()==null) && (flt_orient == 1)) { selection[indx]=false; continue; } if ((ortho_maps[indx].getQOrinet()!=null) && (flt_orient == 2)) { selection[indx]=false; continue; } selection[indx]=true; num_selected++; } int [] selected_indices = new int [num_selected]; int indx = 0; for (int i = 0; i < ortho_maps.length; i++) if (selection[i]){ selected_indices[indx++] = i; } indices = selected_indices; } if (select_scenes) { String [] lines = getScenesList(indices); boolean [] selection = new boolean [indices.length]; gd = new GenericJTabbedDialog("Select scenes",1200,1000); gd.addCheckbox ("Select all", false, "Select all scenes listed"); for (int i = 0; i < lines.length; i++) { gd.addCheckbox (lines[i], false, "Select scene number "+indices[i]); } gd.showDialog(); if (gd.wasCanceled()) return null; boolean sel_all = gd.getNextBoolean(); int num_selected = 0; for (int i = 0; i < lines.length; i++) { selection[i] = gd.getNextBoolean(); if (selection[i]) num_selected++; } if (sel_all) { Arrays.fill(selection, true); } else { int [] sel_indices = new int [num_selected]; int indx=0; for (int i = 0; i < lines.length; i++) if (selection[i]) { sel_indices[indx++] = indices[i]; } indices = sel_indices; } } return indices; } public int [] getScenesSelection( boolean [] pre_selects, String purpose) { // " to generate stats", " to generate images" String [] lines = getScenesList(null); 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]) { Arrays.fill(selection, true); } if (pre_selects[1]) { for (int i:sel_50) { selection[i] = true; } } if (pre_selects[2]) { for (int i:sel_75) { selection[i] = true; } } if (pre_selects[3]) { for (int i:sel_100) { 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[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; 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; for (int i = 0; i < selection.length; i++) { selection[i] = gd.getNextBoolean(); if (selection[i]) { num_sel++; } } int [] indices = new int [num_sel]; int indx=0; for (int i = 0; i < selection.length; i++) if (selection[i]) { indices[indx++] = i; } return indices; } public boolean equalizeIntersectedPairs( CLTParameters clt_parameters, String orthoMapsCollection_path) { boolean use_inv = false; int [] indices = getScenesSelection( clt_parameters, // CLTParameters clt_parameters, " to build a equalize intensities"); // String purpose) PairwiseOrthoMatch [][] matches = new PairwiseOrthoMatch[indices.length][indices.length]; ArrayList<Point> pairs_list = new ArrayList<Point>(); int num_new = 0; for (int i = 0; i < indices.length-1; i++) { int scene0 = indices[i]; for (int j = i+1; j < indices.length; j++){ int scene1 = indices[j]; PairwiseOrthoMatch match = ortho_maps[scene0].getMatch(ortho_maps[scene1].getName()); PairwiseOrthoMatch inv_match = use_inv? ortho_maps[scene1].getMatch(ortho_maps[scene0].getName()):null; if ((match != null) || (inv_match != null)){ if (match == null) { double [] enuOffset = ortho_maps[scene0].enuOffsetTo(ortho_maps[scene1]); double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image match = inv_match.getInverse(rd); } if (inv_match == null) { double [] enuOffset = ortho_maps[scene1].enuOffsetTo(ortho_maps[scene0]); double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image inv_match = match.getInverse(rd); } } if (match != null) { if (!match.isSetEqualize2to1()) { num_new++; } matches[i][j] = match; matches[j][i] = inv_match; pairs_list.add(new Point(i,j)); // only once? } } } boolean skip_exist = clt_parameters.imp.pwise_skip_exist; // 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; // GenericJTabbedDialog gd = new GenericJTabbedDialog("Pairwise Match Parameters",1200,1000); gd.addMessage("Number of scenes - "+indices.length+ ", number of pairs - "+pairs_list.size()+ ", 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 ("Save state after each pair", 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.showDialog(); if (gd.wasCanceled()) return false; skip_exist = gd.getNextBoolean(); save_each = gd.getNextBoolean(); log_append = gd.getNextBoolean(); log_path = gd.getNextString(); debugLevel = (int) gd.getNextNumber(); 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"+ indices.length+"\n"); sb.append("num_pairs\t"+ pairs_list.size()+"\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%6s\t%8s\t%4s\n", "scn1","scn2","timestamp1","timestamp2","ovrlp","zl","a","b","old")); CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) if (debugLevel>-3) { System.out.print(sb.toString()); } } for (Point p:pairs_list) { StringBuffer sb = new StringBuffer(); PairwiseOrthoMatch match = matches[p.x][p.y]; int scene0 = indices[p.x]; int scene1 = indices[p.y]; int [] indices_pair = {scene0,scene1}; if (skip_exist && match.isSetEqualize2to1()) { sb.append(String.format("%4d\t%4d\t%s\t%s\t%6.4f\t%3d\t%6.4f\t%8.4f\tOLD\n", scene0, scene1, ortho_maps[scene0].getName(), ortho_maps[scene1].getName(), match.overlap, match.zoom_lev, match.equalize1to0[0],match.equalize1to0[1])); } else { double [][][] affines = {{{1,0,0},{0,1,0}},match.getAffine()}; 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_pair, // int [] indices, // null or which indices to use (normally just 2 for pairwise comparison) true, // boolean bounds_to_indices, affines, // affines, // double [][][] affines, // null or [indices.length][2][3] null, // double [][] equalize, true, // boolean ignore_equalize, null, // warp, // FineXYCorr warp,, match.zoom_lev, // int zoom_level, wh, // int [] wh, origin, // int [] origin){ // maps[0] as a reference centers); // double [][] centers) double [] regression = PolynomialApproximation.getOrthoRegression( dmulti[0], // final double [] data_x, dmulti[1], // final double [] data_y, null); // final boolean [] mask) double [] inv_regression = PolynomialApproximation.invertRegression(regression); PolynomialApproximation.applyRegression( dmulti[1], // dmulti1, // final double [] data, // clone by caller inv_regression); // final double [] regression) { match.setEqualize2to1(inv_regression); 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); } } sb.append(String.format("%4d\t%4d\t%s\t%s\t%6.4f\t%3d\t%6.4f\t%8.4f\tNEW\n", scene0, scene1, ortho_maps[scene0].getName(), ortho_maps[scene1].getName(), match.overlap, match.zoom_lev, match.equalize1to0[0],match.equalize1to0[1])); } if (log_append && (log_path != null)) { // assuming directory exists CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) } if (debugLevel>-3) { System.out.print(sb.toString()); } } 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); } } StringBuffer sb = new StringBuffer(); sb.append("\nSUMMARY\n"); sb.append("processed\t"+(skip_exist?num_new:pairs_list.size()) +"\n"); sb.append("new\t"+ num_new+"\n"); sb.append("skipped\t"+(skip_exist?(pairs_list.size()-num_new):0)+"\n"); if (debugLevel > -3) { System.out.print(sb.toString()); } if (log_append && (log_path != null)) { // assuming directory exists CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) if (debugLevel > -4) { System.out.println("Appended log file "+ log_path); } } return true; } public boolean getOverlapPairs( CLTParameters clt_parameters, String orthoMapsCollection_path) { int [] indices = getScenesSelection( clt_parameters, // CLTParameters clt_parameters, " to find intersects"); // String purpose) if (indices == null) { return false; } int zoom_lev = clt_parameters.imp.pwise_zoom; // -5; double min_overlap_frac = clt_parameters.imp.pwise_overlap; // 0.25; boolean ignore_affines = false; boolean from_scratch = false; boolean bounds_to_indices = true; 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_frac, 3,7,"", "Minimal overlap as a fraction of the smaller image."); gd.addCheckbox ("Ignore known affines", ignore_affines, "Ignore previously calculated scenes' affines."); gd.addCheckbox ("Start from scratch", from_scratch, "Remove all pairwise affines (false - keep them)."); 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_frac = gd.getNextNumber(); ignore_affines = gd.getNextBoolean(); from_scratch = gd.getNextBoolean(); debugLevel= (int) gd.getNextNumber(); int [] wh = new int[2]; int [] origin = new int[2]; double [][] centers =new double [indices.length][]; double [][][] affines = null; if (ignore_affines) { affines = new double [indices.length][2][3]; for (int i = 0; i < affines.length; i++) { affines[i][0][0] = 1.0; affines[i][1][1] = 1.0; } } for (int i = 0; i < indices.length; i++) { // public void unsetMatches(boolean undefined_only) { ortho_maps[indices[i]].unsetMatches(!from_scratch); } 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, affines, // affines, // double [][][] affines, // null or [indices.length][2][3] null, // double [][] equalize, true, // boolean ignore_equalize, 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 < dmulti.length-1; i++) { for (int j = i+1; j < dmulti.length; j++) { int indx = i*dmulti.length+j; double overlap = overlaps[indx]; if (overlap > 0) { num_overlaps_all++; if (overlap > min_overlap_frac) { String name2 = ortho_maps[indices[j]].getName(); PairwiseOrthoMatch match = ortho_maps[indices[i]].getMatch(name2); if (match == null) { match = new PairwiseOrthoMatch ( null, // double [][] affine, new double [6][6], // double [][] jtj, Double.NaN, // double rms, zoom_lev, // int zoom_lev, overlap); // double overlap) ortho_maps[indices[i]].setMatch(name2, match); } else { match.setOverlap(overlap); // just update overlap } num_overlaps_sel++; intersects[i][j] = 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 (intersects[i][j] && (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 > -4) { ShowDoubleFloatArrays.showArrays( overlaps, "Overlaps"); } 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; } public boolean getIntersectedPairs( CLTParameters clt_parameters, String orthoMapsCollection_path) { int [] indices = getScenesSelection( clt_parameters, // CLTParameters clt_parameters, " to find intersects"); // String purpose) if (indices == null) { return false; } 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; 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_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_frac = gd.getNextNumber(); debugLevel= (int) 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, // double [][] equalize, true, // boolean ignore_equalize, 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 (overlaps[i] > 0){ num_overlaps_all++; if (overlaps[i] >= min_overlap_frac) { 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 (intersects[i][j] && (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 > -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, 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 boolean pmtch_use_affine = clt_parameters.imp.pmtch_use_affine; double max_std = clt_parameters.imp.pmtch_max_std;// 1.5; // maximal standard deviation to limit center area double min_std_rad = clt_parameters.imp.pmtch_min_std_rad;// 2.0; // minimal radius of the central area (if less - fail) boolean ignore_prev_rms = clt_parameters.imp.pmtch_ignore_rms;// true; 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; double pull_skew = clt_parameters.imp.pmtch_pull_skew; // ~rotation, = 0 fraction of the total weight == 1 double pull_tilt = clt_parameters.imp.pmtch_pull_tilt; // > 0 double pull_scale = clt_parameters.imp.pmtch_pull_scale; // = 0 int min_scene = 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.addCheckbox ("Use scenes' affine", pmtch_use_affine, "Use known scenes' affine matrices, false - start from scratch (unity) ones."); gd.addNumericField("Central area standard deviation", max_std, 3,7,"", "Central area limit by the standard deviation."); gd.addNumericField("Central area minimal radius", min_std_rad, 3,7,"tile", "Minimal radius of the central area after all LMA passes."); gd.addCheckbox ("Ignore previous RMSE", ignore_prev_rms, "Do not exit full fitting cycles if the RMSE worsened/not improved."); 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.addNumericField("Pull skew (rotation)", pull_skew, 3,7,"", "Prevent pairwise match from rotation."); gd.addNumericField("Pull tilt", pull_tilt, 3,7,"", "Prevent pairwise match from tilt."); gd.addNumericField("Pull scale", pull_scale, 3,7,"", "Prevent pairwise match from scaling."); gd.addNumericField("Start scene (skip all earlier)", min_scene, 0,3,"","To be able to continue skipping some."); // 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(); pmtch_use_affine= gd.getNextBoolean(); 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(); pull_skew = gd.getNextNumber(); pull_tilt = gd.getNextNumber(); pull_scale = gd.getNextNumber(); min_scene = (int) 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, pmtch_use_affine, // boolean pmtch_use_affine, max_std, // double max_std, min_std_rad, // double min_std_rad, ignore_prev_rms, // boolean ignore_prev_rms, 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, min_scene, // int min_scene, 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); // 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, boolean pmtch_use_affine, 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 min_scene, double pull_skew, // ~rotation, = 0 fraction of the total weight == 1 double pull_tilt, // > 0 double pull_scale, // = 0 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","fzl","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]}; if (ipair[0] < min_scene) { System.out.println ("Skipping "+ipair[0]+":"+ipair[1]+" until "+min_scene); continue; } 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 && (direct_match.overlap > 0)) { // do not skip old pairs - refine them 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\t%3d\n", ipair[0], ipair[1], ortho_maps[ipair[0]].getName(), ortho_maps[ipair[1]].getName(), overlaps[overlap_indx], initial_zoom, direct_match.rms, direct_match.zoom_lev)); CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) } continue; } PairwiseOrthoMatch pairwiseOrthoMatch = null; // unityAffine() double [][] affine0 = pmtch_use_affine?ortho_maps[ipair[0]].getAffine():unityAffine(); // {{1,0,0},{0,1,0}}; // will always stay the same double [][] affine1 = pmtch_use_affine?ortho_maps[ipair[0]].getAffine():unityAffine(); // {{1,0,0},{0,1,0}}; // here (manual mode) start from the center, may use prediction in auto double [][][] affines = new double[][][] {affine0,affine1}; double spiral_rms = Double.NaN; if (use_direct) { 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, pmtch_use_affine, // boolean pmtch_use_affine, max_std, // double max_std, // maximal standard deviation to limit center area min_std_rad, // double min_std_rad, // minimal radius of the central area (if less - fail) rad_fraction, // double rad_fraction, 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};// overlaps[overlap_indx], // double overlap, 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 spiral_debug); // int debugLevel) // late failure if ((pairwiseOrthoMatch != null) && !Double.isNaN(pairwiseOrthoMatch.rms) && !pairwiseOrthoMatch.ok) { // 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"; String str_failed = delete_failed? "%4d\t%4d\t%s\t%s\t%6.4f\t%3d\t%3d\t%3d\t%6.4f\tREMOVED\n": "%4d\t%4d\t%s\t%s\t%6.4f\t%3d\t%3d\t%3d\t%6.4f\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, pairwiseOrthoMatch.nxy[0],pairwiseOrthoMatch.nxy[1],pairwiseOrthoMatch.rms)); CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) } } if ((pairwiseOrthoMatch == null) || !pairwiseOrthoMatch.ok) { if (delete_failed) { ortho_maps[ipair[0]].unsetMatch(ortho_maps[ipair[1]].getName()); } if ((pairwiseOrthoMatch != null) && Double.isNaN(pairwiseOrthoMatch.rms)) { 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); pairwiseOrthoMatch = null; 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};// 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) 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\t\tREMOVED\n":"\tFAILED\n"; failed_pairs.add(pair); sb.append(String.format(str_failed)); if (debugLevel > -4) System.out.print("Final adjustment (1)"+str_failed); } else { pairwiseOrthoMatch.overlap = overlaps[overlap_indx]; // needed here if refining old/manual w/o overlap sb.append(String.format("\t%6.4f\t%3d\n",pairwiseOrthoMatch.rms,pairwiseOrthoMatch.zoom_lev)); if (debugLevel > -4) System.out.println("Final adjustment (2) RMSE="+pairwiseOrthoMatch.rms+ ", overlap = "+pairwiseOrthoMatch.overlap); 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; } /** * Add new scene pairs from the already adjusted scenes * @param clt_parameters * @param orthoMapsCollection_path * @return */ public boolean augmentPairwiseAffines( CLTParameters clt_parameters, String orthoMapsCollection_path) { // Create list of all pairs (after recreating all overlaps with updated affines) ArrayList<Point> pairs_list = new ArrayList<Point>(); for (OrthoMap map : ortho_maps) { for (String other_name: map.pairwise_matches.keySet()) { pairs_list.add(new Point( getIndex(map.getName()), 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 } }); // convert ArrayList<Point> to array int[][] 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; available_pairs[i][1] = pairs_list.get(i).y; } boolean flt_undef_only = false; // clt_parameters.imp.flt_undef_only; // false; double flt_min_overlap = clt_parameters.imp.flt_min_overlap; // 0.0; double flt_max_overlap = clt_parameters.imp.flt_max_overlap; // 1.0; 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; // double flt_min_rms = 0; // clt_parameters.imp.flt_min_rms; // 0.0; // double flt_max_rms = 2.0; // clt_parameters.imp.flt_max_rms; // 2.0; double flt_min_rms = clt_parameters.imp.flt_min_rms; // 0.0; double flt_max_rms = clt_parameters.imp.flt_max_rms; // 2.0; boolean flt_nan_rms = true; // clt_parameters.imp.flt_nan_rms; // false; boolean flt_show_names = true; // clt_parameters.imp.flt_show_names; // true; boolean flt_show_overlaps = true; // clt_parameters.imp.flt_show_overlaps; // true; boolean flt_show_rms = true; // clt_parameters.imp.flt_show_rms; // true; boolean flt_show_zoom = true; // clt_parameters.imp.flt_show_zoom; // true; boolean flt_show_alt = true; // clt_parameters.imp.flt_show_alt; // true; //Initial spiral search for image matching boolean ospir_augment = clt_parameters.imp.ospir_augment; // true double max_rms = clt_parameters.imp.ospir_max_rms; // 0.35; // double max_rms_refine = clt_parameters.imp.pwise_max_rms; // 0.35; // int min_overlap_tiles = clt_parameters.imp.ospir_overlap; // 3000; // do not try to match if there is too small overlap (scaled pixels) double double_threshold = clt_parameters.imp.ospir_double; // increase resolution if too small overlap in tiles 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 lores_ignore_rms = clt_parameters.imp.ospir_ignore_rms; // false //Final pairwise scenes matching double frac_remove = clt_parameters.imp.pmtch_frac_remove;// 0.15; double metric_err = clt_parameters.imp.pmtch_metric_err;// 0.05; // 0.02;// 2 cm // boolean pmtch_use_affine = clt_parameters.imp.pmtch_use_affine; double max_std = clt_parameters.imp.pmtch_max_std;// 1.5; // maximal standard deviation to limit center area double min_std_rad = clt_parameters.imp.pmtch_min_std_rad;// 2.0; // minimal radius of the central area (if less - fail) boolean ignore_prev_rms = clt_parameters.imp.pmtch_ignore_rms;// true; 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; int min_scene = 0; double pull_skew = clt_parameters.imp.pmtch_pull_skew; // ~rotation, = 0 fraction of the total weight == 1 double pull_tilt = clt_parameters.imp.pmtch_pull_tilt; // > 0 double pull_scale = clt_parameters.imp.pmtch_pull_scale; // = 0 // log/save parameters 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; // boolean flt_update_config = false; GenericJTabbedDialog gdf = new GenericJTabbedDialog("Select pairs filter/display",800,1200); gdf.addMessage("Filter pairs parameters"); gdf.addNumericField("Minimal scene overlap (0..1)",flt_min_overlap, 3,7,"", "Minimal overlap of the scenes to keep (0-no overlap, 1.0 - smaller scene is inside the parger one."); gdf.addNumericField("Maximal scene overlap (0..1)",flt_max_overlap, 3,7,"", "Maximal overlap of the scenes to keep (0-no overlap, 1.0 - smaller scene is inside the parger one."); 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.addMessage("Normally, NaN RMS should be selected for new pairs. Other settings to re-adjust previously adjusted ones!!!"); gdf.addNumericField("Minimal RMSE", flt_min_rms, 3,7,"", "Minimal LMA RMSE of the scene pair."); gdf.addNumericField("Maximal RMSE", flt_max_rms, 3,7,"", "Maximal LMA RMSE of the scene pair."); gdf.addCheckbox ("NaN RMS (failed match)", flt_nan_rms, "Keep only failed matches with RMSE=NaN."); gdf. addChoice("Filter by pairwise ALT availability",IntersceneMatchParameters.FLT_ALT_MODES, IntersceneMatchParameters.FLT_ALT_MODES[flt_alt],"Filter by pairwise ALT availability."); gdf.addMessage("Low-resolution match parameters"); gdf.addCheckbox ("Use low-res in augmentation", ospir_augment, "Use low-res matching during augmenting (false - skip, go to high-res)."); gdf.addNumericField("Good RMSE, low-res", max_rms, 3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels, during spiral."); gdf.addNumericField("Good RMSE, final", max_rms_refine,3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels, during refine."); gdf.addNumericField("Minimal overlap", min_overlap_tiles, 0,4,"scaled pix ^ 2","Minimal overlap area in square scaled pixels."); gdf.addNumericField("Mitigate small overlap", double_threshold, 3,7,"","For small overlaps increase zoom by 1 and range - twice."); gdf.addNumericField("LMA iterations", num_iter_lma, 0,2,"", "Number of LMA iterations during spiral search."); gdf.addNumericField("RMSE at first iteration", max_rms_iter[0], 3,7,"scaled pix","Maximal RMSE at first iteration."); gdf.addNumericField("RMSE at second iteration", max_rms_iter[1], 3,7,"scaled pix","Maximal RMSE at second iteration."); gdf.addCheckbox ("Ignore worsening low-res RMSE",lores_ignore_rms, "Ignore worsening/not improving RMSE low-res matching."); gdf.addMessage ("Final (high-res) pairwise scenes matching"); gdf.addNumericField("Remove fraction of worst matches", frac_remove, 3,7,"", "When fitting scenes remove this fraction of worst match tiles."); gdf.addNumericField("Maximal metric error", metric_err, 3,7,"m", "Maximal tolerable fitting error caused by elevation variations."); // gdf.addCheckbox ("Use scenes' affine", pmtch_use_affine, "Use known scenes' affine matrices, false - start from scratch (unity) ones."); gdf.addNumericField("Central area standard deviation", max_std, 3,7,"", "Central area limit by the standard deviation."); gdf.addNumericField("Central area minimal radius", min_std_rad, 3,7,"tile", "Minimal radius of the central area after all LMA passes."); gdf.addCheckbox ("Ignore previous RMSE", ignore_prev_rms, "Do not exit full fitting cycles if the RMSE worsened/not improved."); gdf.addNumericField("Number of fitting iterations", num_tries, 0,3,"","number of full fittng iterations."); gdf.addNumericField("Central area radius as fraction", rad_fraction, 3,7,"", "Central area radius as fraction of half minimal WOI dimension."); gdf.addNumericField("Maximal central area radius", max_tile_rad, 3,7,"tiles", "Absolute limit to the center area radius (eases bad peripheral matching)."); gdf.addNumericField("Central area minimal fill", fill_fraction, 3,7,"", "Central area minimal fill for all but the last iteration."); gdf.addNumericField("Central area minimal fill final", fill_fraction_final, 3,7,"", "Central area minimal fill for the last iteration."); gdf.addNumericField("Relax metric error for no-SfM", ease_nosfm, 3,7,"", "Relax metric error for no-SfM scenes (sfm_gain==0)."); gdf.addNumericField("Pull skew (rotation)", pull_skew, 3,7,"", "Prevent pairwise match from rotation."); gdf.addNumericField("Pull tilt", pull_tilt, 3,7,"", "Prevent pairwise match from tilt."); gdf.addNumericField("Pull scale", pull_scale, 3,7,"", "Prevent pairwise match from scaling."); gdf.addMessage("Log and Save, and Debug parameters"); gdf.addCheckbox ("Save state after each match", save_each, "Update state file after each match generation to mitigate possible crashes."); gdf.addCheckbox ("Write log file", log_append, "Enable writing log file with matching results."); gdf.addStringField ("Log file full path", log_path, 150, "Path of the log file to be appended."); gdf.addNumericField("Debug level", debugLevel, 0,3,"","Debug level during Spiral search."); gdf.addNumericField("Start scene (skip all earlier)", min_scene, 0,3,"","To be able to continue skipping some."); gdf.addCheckbox ("Update configuration", flt_update_config, "Update matching configuration parameters to be saved as defaults."); gdf.showDialog(); if (gdf.wasCanceled()) return false; flt_min_overlap = gdf.getNextNumber(); flt_max_overlap = gdf.getNextNumber(); flt_filt_zoom = gdf.getNextBoolean(); flt_min_zoom = (int) gdf.getNextNumber(); flt_max_zoom = (int) gdf.getNextNumber(); flt_min_sfm = gdf.getNextNumber(); flt_max_sfm = gdf.getNextNumber(); flt_min_rms = gdf.getNextNumber(); flt_max_rms = gdf.getNextNumber(); flt_nan_rms = gdf.getNextBoolean(); flt_alt = gdf.getNextChoiceIndex(); ospir_augment = gdf.getNextBoolean(); max_rms = gdf.getNextNumber(); max_rms_refine = gdf.getNextNumber(); min_overlap_tiles = (int) gdf.getNextNumber(); double_threshold = gdf.getNextNumber(); num_iter_lma = (int) gdf.getNextNumber(); max_rms_iter[0] = gdf.getNextNumber(); max_rms_iter[1] = gdf.getNextNumber(); lores_ignore_rms = gdf.getNextBoolean(); frac_remove = gdf.getNextNumber(); metric_err = gdf.getNextNumber(); // pmtch_use_affine= gdf.getNextBoolean(); max_std = gdf.getNextNumber(); min_std_rad = gdf.getNextNumber(); ignore_prev_rms = gdf.getNextBoolean(); num_tries = (int) gdf.getNextNumber(); rad_fraction = gdf.getNextNumber(); max_tile_rad = gdf.getNextNumber(); fill_fraction = gdf.getNextNumber(); fill_fraction_final= gdf.getNextNumber(); ease_nosfm = gdf.getNextNumber(); pull_skew = gdf.getNextNumber(); pull_tilt = gdf.getNextNumber(); pull_scale = gdf.getNextNumber(); save_each = gdf.getNextBoolean(); log_append = gdf.getNextBoolean(); log_path = gdf.getNextString(); debugLevel = (int) gdf.getNextNumber(); min_scene = (int) gdf.getNextNumber(); flt_update_config = gdf.getNextBoolean(); if (flt_update_config) { clt_parameters.imp.flt_min_overlap = flt_min_overlap; clt_parameters.imp.flt_max_overlap = flt_max_overlap; 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.ospir_augment = ospir_augment; clt_parameters.imp.ospir_max_rms = max_rms; clt_parameters.imp.pwise_max_rms = max_rms_refine; clt_parameters.imp.ospir_overlap = min_overlap_tiles; clt_parameters.imp.ospir_double = double_threshold; clt_parameters.imp.ospir_num_iter = num_iter_lma; clt_parameters.imp.ospir_rms_iter = max_rms_iter; clt_parameters.imp.ospir_ignore_rms = lores_ignore_rms; clt_parameters.imp.pmtch_frac_remove = frac_remove; clt_parameters.imp.pmtch_metric_err = metric_err; // clt_parameters.imp.pmtch_use_affine = pmtch_use_affine; clt_parameters.imp.pmtch_max_std = max_std; clt_parameters.imp.pmtch_min_std_rad = min_std_rad; clt_parameters.imp.pmtch_ignore_rms = ignore_prev_rms; clt_parameters.imp.pmtch_num_iter = num_tries; clt_parameters.imp.pmtch_cent_rad = rad_fraction; clt_parameters.imp.pmtch_max_cent_rad = max_tile_rad; clt_parameters.imp.pmtch_cent_fill = fill_fraction; clt_parameters.imp.pmtch_cent_final = fill_fraction_final; clt_parameters.imp.pmtch_ease_nosfm = ease_nosfm; clt_parameters.imp.pmtch_pull_skew = pull_skew; clt_parameters.imp.pmtch_pull_tilt = pull_tilt; clt_parameters.imp.pmtch_pull_scale = pull_scale; clt_parameters.imp.pwise_save_each = save_each; clt_parameters.imp.pwise_log_append = log_append; clt_parameters.imp.pwise_log_path = log_path; clt_parameters.imp.pwise_debug = debugLevel; } available_pairs = filterPairs( available_pairs, // int [][] plist_in, flt_undef_only, // boolean undef_only, flt_min_overlap, // double min_overlap, flt_max_overlap, // double max_overlap, flt_min_rms, // double min_rms, flt_max_rms, // double max_rms, 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_min_sfm, // double min_sfm, flt_max_sfm, // double max_sfm, flt_alt); // int flt_alt) String [] choices_all = textPairs ( available_pairs, // int [][] plist, flt_show_names, // boolean show_names, flt_show_overlaps, // boolean show_overlap, flt_show_rms, // boolean show_rms, flt_show_zoom, // boolean show_zoom, flt_show_alt, // boolean show_alt, true, // boolean use_tab, null); // String extra_line) if (debugLevel > 0) { System.out.println("Selected "+available_pairs.length+" scene pairs for matching"); for (int i = 0; i < available_pairs.length; i++) { System.out.println(String.format("%4d:%s",i,choices_all[i])); } } if (available_pairs.length == 0) { return false; } return augmentPairwiseAffines( clt_parameters, // CLTParameters clt_parameters, available_pairs, // int [][] available_pairs, //Initial spiral search for image matching ospir_augment, // boolean ospir_augment, max_rms, // double max_rms, max_rms_refine, // double max_rms_refine, min_overlap_tiles, // int min_overlap_tiles, double_threshold, // double double_threshold, num_iter_lma, // int num_iter_lma, max_rms_iter, // double [] max_rms_iter, lores_ignore_rms, // boolean lores_ignore_rms //Final pairwise scenes matching frac_remove, // double frac_remove, metric_err, // double metric_error, // pmtch_use_affine, // boolean pmtch_use_affine, max_std, // double max_std, min_std_rad, // double min_std_rad, ignore_prev_rms, // boolean ignore_prev_rms, 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, min_scene, // int min_scene, pull_skew, // double pull_skew, pull_tilt, // double pull_tilt, pull_scale, // double pull_scale, // log/save parameters save_each, // boolean save_each, log_append, // boolean log_append, log_path, // String log_path, orthoMapsCollection_path, // String orthoMapsCollection_path debugLevel); // int debugLevel) } public boolean generatePairwiseAffines( CLTParameters clt_parameters, String orthoMapsCollection_path) { int [] indices = getScenesSelection( clt_parameters, // CLTParameters clt_parameters, " to find intersects"); // String purpose) if ((indices == null) || (indices.length < 2)) { System.out.println ("generatePairwiseAffines(): indices[] is null or too short, exiting"); return false; } int [] groups = new int [indices.length]; for (int i = 0; i < indices.length; i++) { groups[i] = i; } int num_defined = 0; int num_undefined = 0; boolean single_pair = indices.length==2; for (int i = 0; i < indices.length-1; i++) { for (int j = i+1; j < indices.length; j++){ String name2 = ortho_maps[indices[j]].getName(); PairwiseOrthoMatch match = ortho_maps[indices[i]].getMatch(name2,true); if (match != null) { if (match.isDefined() && !single_pair) { num_defined++; } else { num_undefined++; } } } } HashSet<Integer> hs = new HashSet<Integer>(); for (int i:groups) { hs.add(i); } boolean dry_run = false; 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; // double min_overlap_frac = clt_parameters.imp.pwise_overlap; // 0.25; //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; // double max_rms_refine = clt_parameters.imp.pwise_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 boolean pmtch_use_affine = clt_parameters.imp.pmtch_use_affine; double max_std = clt_parameters.imp.pmtch_max_std;// 1.5; // maximal standard deviation to limit center area double min_std_rad = clt_parameters.imp.pmtch_min_std_rad;// 2.0; // minimal radius of the central area (if less - fail) boolean ignore_prev_rms = clt_parameters.imp.pmtch_ignore_rms;// true; 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; double pull_skew = clt_parameters.imp.pmtch_pull_skew; // ~rotation, = 0 fraction of the total weight == 1 double pull_tilt = clt_parameters.imp.pmtch_pull_tilt; // > 0 double pull_scale = clt_parameters.imp.pmtch_pull_scale; // = 0 boolean use_multi = true; int heur = 15; int min_scene = 0; GenericJTabbedDialog gd = new GenericJTabbedDialog("Pairwise Match Parameters",1200,1100); gd.addMessage("Number of scenes - "+indices.length+ ", number of defined pairs - "+num_defined+ ", number of undefined pairs - "+num_undefined); // +", number of disconnected groups - "+hs.size()); // gd.addCheckbox ("Dry run", dry_run, "Create pairs, do not adjust."); /// 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.addNumericField("Minimal overlap fraction", min_overlap_frac, 3,7,"", "Minimal overlap fraction."); 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, spiral", max_rms, 3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels, during spiral."); gd.addNumericField("Satisfactory RMSE, final", max_rms_refine,3,7,"scaled pix", "Maximal RMSE to consider match, in scaled pixels, during refine."); 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.addCheckbox ("Use scenes' affine", pmtch_use_affine, "Use known scenes' affine matrices, false - start from scratch (unity) ones."); gd.addNumericField("Central area standard deviation", max_std, 3,7,"", "Central area limit by the standard deviation."); gd.addNumericField("Central area minimal radius", min_std_rad, 3,7,"tile", "Minimal radius of the central area after all LMA passes."); gd.addCheckbox ("Ignore previous RMSE", ignore_prev_rms, "Do not exit full fitting cycles if the RMSE worsened/not improved."); 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.addNumericField("Pull skew (rotation)", pull_skew, 3,7,"", "Prevent pairwise match from rotation."); gd.addNumericField("Pull tilt", pull_tilt, 3,7,"", "Prevent pairwise match from tilt."); gd.addNumericField("Pull scale", pull_scale, 3,7,"", "Prevent pairwise match from scaling."); gd.addNumericField("Start scene (skip all earlier)", min_scene, 0,3,"","To be able to continue skipping some."); gd.addNumericField("Heuristics bitmap", heur, 0,3,"","Bitmap of modes to suggest the next pair."); gd.addCheckbox ("Use multiple threads", use_multi, "Use multiple threads (may be disabled in debug mode)."); // gd.showDialog(); if (gd.wasCanceled()) return false; dry_run = gd.getNextBoolean(); /// 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(); min_overlap_frac = gd.getNextNumber(); search_step = gd.getNextNumber(); search_range = gd.getNextNumber(); double_threshold = gd.getNextNumber(); good_rms = gd.getNextNumber(); max_rms = gd.getNextNumber(); max_rms_refine = 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(); pmtch_use_affine= gd.getNextBoolean(); 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(); pull_skew = gd.getNextNumber(); pull_tilt = gd.getNextNumber(); pull_scale = gd.getNextNumber(); min_scene = (int) gd.getNextNumber(); heur = (int) gd.getNextNumber(); use_multi = gd.getNextBoolean(); double max_rmse_reuse= max_rms_refine; int [][] pairs_defined = single_pair?(new int[0][]):filterPairs( indices, // int [] indices_in, min_overlap_frac, // double min_overlap_frac, max_rmse_reuse, // double max_rmse, true, // boolean max_resolution, null); // int [][] remove_pairs int [][] pairs_undefined = filterPairs( indices, // int [] indices_in, min_overlap_frac, // double min_overlap_frac, 0, // max_rmse_reuse, // double max_rmse, false, // boolean max_resolution, pairs_defined); // int [][] remove_pairs /* PairsGraph pairsGraph = new PairsGraph( this, // OrthoMapsCollection orthoMapsCollection, indices, // int [] indices, min_overlap_frac, // double min_overlap_frac, max_rmse_reuse, // double max_rmse_reuse, use_multi, // boolean multi, debugLevel); // int debugLevel); */ PairsGraph pairsGraph = new PairsGraph( this, // OrthoMapsCollection orthoMapsCollection, pairs_undefined, // int [][] undefined_pairs, pairs_defined, // int [][] defined_pairs, use_multi, // boolean multi, debugLevel); // int debugLevel); if (dry_run) { return pairsGraph.dryRun( heur, // int heur, debugLevel); // int debugLevel) } else { 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"+ indices.length+"\n"); sb.append("num_defined\t"+ num_defined+"\n"); sb.append("num_undefined\t"+ num_undefined+"\n"); sb.append("num_groups\t"+ pairsGraph.getNumberOfGroups()+"\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","fzl","removed")); CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) if (debugLevel>-3) { System.out.print(sb.toString()); } } return generatePairwiseAffines( clt_parameters, // CLTParameters clt_parameters, pairsGraph, // PairsGraph pairsGraph, heur, // int heur, 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, max_rms_refine, // double max_rms_refine, 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, pmtch_use_affine, // boolean pmtch_use_affine, max_std, // double max_std, min_std_rad, // double min_std_rad, ignore_prev_rms, // boolean ignore_prev_rms, 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, min_scene, // int min_scene, 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); // int debugLevel } } public boolean augmentPairwiseAffines( CLTParameters clt_parameters, int [][] available_pairs, //Initial spiral search for image matching boolean ospir_augment, double max_rms, double max_rms_refine, int min_overlap_tiles, double double_threshold, int num_iter_lma, double [] max_rms_iter, boolean lores_ignore_rms, //Final pairwise scenes matching 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, int min_scene, double pull_skew, double pull_tilt, double pull_scale, // log/save parameters boolean save_each, boolean log_append, String log_path, String orthoMapsCollection_path, int debugLevel) { boolean batch_mode = true; boolean show_vf = false; boolean use_degrees = true; double [][] ground_planes = null; // null or double[2] - will return ground planes: if (log_append && (log_path != null)) { // assuming directory exists String svd_title=SingularValueDecomposition.titleString(use_degrees); 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(String.format("%4s\t%4s\t%17s\t%17s\t%6s\t%3s\t%6s\t%6s\t%7s\t%7s\t%s\n", "scn1","scn2","timestamp1","timestamp2","ovrlp","zl","RMS-sp","RMSfin","fzl","removed",svd_title)); CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) if (debugLevel>-3) { System.out.print(sb.toString()); } } int num_pairs = 0; // available_pairs.length ArrayList<Point> failed_pairs = new ArrayList<Point>(); for (int npair = 0; npair < available_pairs.length; npair++) { int [] ipair = available_pairs[npair]; PairwiseOrthoMatch pairwiseOrthoMatch = ortho_maps[ipair[0]].getMatch(ortho_maps[ipair[1]].getName(), true); double [][] daffine = null; if (pairwiseOrthoMatch != null) { double [][] aff0 = ortho_maps[ipair[0]].getAffine(); double [][] aff1 = ortho_maps[ipair[1]].getAffine(); double [] enuOffset = ortho_maps[ipair[1]].enuOffsetTo(ortho_maps[ipair[0]]); double [] rd = {enuOffset[0], -enuOffset[1]}; // {right,down} of the image 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; } if (ipair[0] < min_scene) { System.out.println ("Skipping "+ipair[0]+":"+ipair[1]+" until "+min_scene); continue; } // only do low-res if ospir_augment // boolean direct = ipair[0] < ipair[1]; // always? 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 double overlap_frac = pairwiseOrthoMatch.getOverlap(); // .pairsGraph.getOverlap(next_pair); double overlap_frac_mod = overlap_frac; while (overlap_frac_mod < double_threshold) { overlap_frac_mod *= 2; initial_zoom += 1; } // unityAffine() // use unityAffine() for 0; getaffine for second? double [][] affine0 = 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]); if (ospir_augment) { correlateOrthoPair( clt_parameters, // CLTParameters clt_parameters, pairwiseOrthoMatch, // PairwiseOrthoMatch pairwiseOrthoMatch, // will return statistics min_overlap_tiles, // 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, // metric_error, // double metric_error, lores_ignore_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, // int [] gpu_pair, affines, // double [][][] affines, // on top of GPS offsets null, // woi, // Rectangle woi, initial_zoom, // 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};// 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) pairwiseOrthoMatch.setAffine(affines[1]); // modified by correlateOrthoPair ALREADY SET if (debugLevel > -4) { System.out.println(String.format("Low-res Match(): %3d-%3d RMSE=%8.6f", ipair[0], ipair[1], pairwiseOrthoMatch.rms)); // if NaN - provide reason } if (pairwiseOrthoMatch.rms < max_rms) { pairwiseOrthoMatch.ok = true; } success &= pairwiseOrthoMatch.ok; // && !Double.isNaN(pairwiseOrthoMatch.rms) if (!success) { String str_failed = "%4d\t%4d\t%s\t%s\t%6.4f\t%3d\t%6.4f\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(), overlap_frac, initial_zoom, // pairwiseOrthoMatch.nxy[0],pairwiseOrthoMatch.nxy[1], pairwiseOrthoMatch.rms)); CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) } failed_pairs.add(pair); continue; } } // if (ospir_augment) { // PairwiseOrthoMatch pairwiseOrthoMatch_lores = pairwiseOrthoMatch.clone(); 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) boolean failed_refine = Double.isNaN(pairwiseOrthoMatch.rms) || (pairwiseOrthoMatch.rms > max_rms_refine); // 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(), overlap_frac, initial_zoom)); sb.append(String.format("\t%6.4f", // pairwiseOrthoMatch.nxy[0],pairwiseOrthoMatch.nxy[1], lores_rms)); if (failed_refine) { String str_failed = String.format("\t%6.4f\tFAILED\n",pairwiseOrthoMatch.rms); failed_pairs.add(pair); sb.append(String.format(str_failed)); if (debugLevel > -4) System.out.print("Final adjustment (3)"+str_failed); } else { pairwiseOrthoMatch.overlap = overlap_frac; // needed here if refining old/manual w/o overlap SingularValueDecomposition svd = SingularValueDecomposition.singularValueDecompose(pairwiseOrthoMatch.getAffine()); String ssvd = svd.toString(use_degrees, 1); sb.append(String.format("\t%6.4f\t%3d\t \t%s\n",pairwiseOrthoMatch.rms,pairwiseOrthoMatch.zoom_lev, ssvd)); if (debugLevel > -4) System.out.println("Final adjustment (4) RMSE="+pairwiseOrthoMatch.rms+ ", overlap = "+pairwiseOrthoMatch.overlap); 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) } success &= !failed_refine; if (success) { num_pairs++; } } // for (int npair = 0; npair < available_pairs.length; npair++) { 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("\nSUMMARY\n"); sb.append("new\t"+ num_pairs+"\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]}; int [] ipair = {failed_pairs.get(i).x, 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; } public boolean generatePairwiseAffines( CLTParameters clt_parameters, PairsGraph pairsGraph, int heur, 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, double max_rms_refine, int min_overlap, int num_iter_lma, boolean ignore_rms, int spiral_debug, double frac_remove, double metric_error, boolean pmtch_use_affine, 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 min_scene, double pull_skew, // ~rotation, = 0 fraction of the total weight == 1 double pull_tilt, // > 0 double pull_scale, // = 0 int debugLevel) { int [] indices = pairsGraph.getIndices(); boolean batch_mode = true; // int num_scenes = indices.length; int num_pairs = 0; ArrayList<Point> failed_pairs = new ArrayList<Point>(); int ok_dist = 1; int [] start_ij = {0,0}; int [] next_pair; while (!pairsGraph.isSingleConnected()) { next_pair = pairsGraph.suggestPairSingle( // single-connected, no cycles // add some parameters heur, // int heur, ok_dist, // int ok_dist) start_ij, // int [] start_ij) // = new int [indices.length]; pairsGraph.useMultiThreaded(), // multi, debugLevel); if (next_pair == null) { System.out.println("No pairs suggested, isSingleConnected()="+pairsGraph.isSingleConnected()); break; } if (next_pair[2] > PairsGraph.HEUR_LAST_SEQ) { heur &= ~PairsGraph.HEUR_LAST_SEQ; } if (next_pair[2] > PairsGraph.HEUR_DIV_LONG) { heur &= ~PairsGraph.HEUR_DIV_LONG; } if (next_pair.length > 3) { ok_dist = next_pair[3]; start_ij[0] = next_pair[0]; start_ij[1] = next_pair[1]; if (debugLevel > 0) { System.out.println(String.format("%3d: %3d-%3d %2d (%3d)", num_pairs,next_pair[0], next_pair[1],next_pair[2],next_pair[3])); } } else { if (debugLevel > 0) { System.out.println(String.format("%3d: %3d-%3d %2d", num_pairs,next_pair[0], next_pair[1],next_pair[2])); } } int [] ipair = {indices[next_pair[0]], indices[next_pair[1]]}; if (ipair[0] < min_scene) { System.out.println ("Skipping "+ipair[0]+":"+ipair[1]+" until "+min_scene); continue; } boolean direct = ipair[0] < ipair[1]; // always? 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 double overlap_frac = pairsGraph.getOverlap(next_pair); double overlap_frac_mod = overlap_frac; double search_range = search_range_in; while (overlap_frac_mod < double_threshold) { overlap_frac_mod *= 2; initial_zoom += 1; search_range *= 2; } // unityAffine() double [][] affine0 = pmtch_use_affine?ortho_maps[ipair[0]].getAffine():unityAffine(); // {{1,0,0},{0,1,0}}; // will always stay the same double [][] affine1 = pmtch_use_affine?ortho_maps[ipair[1]].getAffine():unityAffine(); // {{1,0,0},{0,1,0}}; // here (manual mode) start from the center, may use prediction in auto double [][][] affines = new double[][][] {affine0,affine1}; double spiral_rms = Double.NaN; // now always unity, but after partial adjustment may be non-unity // TODO: Not yet tested with intermediate adjustment. PairwiseOrthoMatch pairwiseOrthoMatch = SpiralMatch ( clt_parameters, // CLTParameters clt_parameters, frac_remove, // double frac_remove, // = 0.25 metric_error_adj,// double metric_error, pmtch_use_affine, // boolean pmtch_use_affine, max_std, // double max_std, // maximal standard deviation to limit center area min_std_rad, // double min_std_rad, // minimal radius of the central area (if less - fail) rad_fraction, // double rad_fraction, 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};// overlap_frac, // double overlap, // OR IS IT overlap_frac_mod ? 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 spiral_debug); // int debugLevel) // late failure if ((pairwiseOrthoMatch != null) && !Double.isNaN(pairwiseOrthoMatch.rms) && !pairwiseOrthoMatch.ok) { String str_failed = "%4d\t%4d\t%s\t%s\t%6.4f\t%3d\t%3d\t%3d\t%6.4f\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(), overlap_frac, initial_zoom, pairwiseOrthoMatch.nxy[0],pairwiseOrthoMatch.nxy[1],pairwiseOrthoMatch.rms)); CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) } } Point pair = new Point(next_pair[0],next_pair[1]); boolean success = (pairwiseOrthoMatch != null) && pairwiseOrthoMatch.ok; // && !Double.isNaN(pairwiseOrthoMatch.rms) if (!success) { // not yet add to pairsGraph until verified with high-res if ((pairwiseOrthoMatch == null) || Double.isNaN(pairwiseOrthoMatch.rms)) { // too high RMS already reported String str_failed = "%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(), overlap_frac, initial_zoom)); CalibrationFileManagement.saveStringToFile ( log_path, //String path, sb.toString(), // data, true); // boolean append) } } failed_pairs.add(pair); } else { // success! 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};// 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) boolean failed_refine = Double.isNaN(pairwiseOrthoMatch.rms) || (pairwiseOrthoMatch.rms > max_rms_refine); // 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(), overlap_frac, initial_zoom)); sb.append(String.format("\t%3d\t%3d\t%6.4f", pairwiseOrthoMatch.nxy[0],pairwiseOrthoMatch.nxy[1],spiral_rms)); if (failed_refine) { String str_failed = String.format("\t%6.4f\tFAILED\n",pairwiseOrthoMatch.rms); failed_pairs.add(pair); sb.append(String.format(str_failed)); if (debugLevel > -4) System.out.print("Final adjustment (3)"+str_failed); } else { pairwiseOrthoMatch.overlap = overlap_frac; // needed here if refining old/manual w/o overlap sb.append(String.format("\t%6.4f\t%3d\n",pairwiseOrthoMatch.rms,pairwiseOrthoMatch.zoom_lev)); if (debugLevel > -4) System.out.println("Final adjustment (4) RMSE="+pairwiseOrthoMatch.rms+ ", overlap = "+pairwiseOrthoMatch.overlap); 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) } success &= !failed_refine; } pairsGraph.recordPair( next_pair, // int [] pair, success, // boolean success, pairsGraph.useMultiThreaded()); if (success) { num_pairs++; } } // next pair if (log_append && (log_path != null)) { // assuming directory exists StringBuffer sb = new StringBuffer(); sb.append("\nSUMMARY\n"); sb.append("new\t"+ num_pairs+"\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; } public boolean altutudeMatchPairs( CLTParameters clt_parameters, String orthoMapsCollection_path) { //OrientationSceneLMA.testGetPairErrQuaternion (); // Create list of all pairs (after recreating all overlaps with updated affines) ArrayList<Point> pairs_list = new ArrayList<Point>(); boolean dbg00 = false; for (OrthoMap map : ortho_maps) { // if (getIndex(map.getName()) == 87) { // System.out.println("index="+getIndex(map.getName())); // } for (String other_name: map.pairwise_matches.keySet()) { pairs_list.add(new Point( getIndex(map.getName()), getIndex(other_name))); if (dbg00) { System.out.println(getIndex(map.getName())+"-"+getIndex(other_name)+" "+map.getName()+"-"+other_name); if(getIndex(map.getName()) == getIndex(other_name)) { System.out.println(); } } } } // 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 } }); // convert ArrayList<Point> to array int[][] 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; available_pairs[i][1] = pairs_list.get(i).y; } boolean flt_undef_only = false; // clt_parameters.imp.flt_undef_only; // false; double flt_min_overlap = clt_parameters.imp.flt_min_overlap; // 0.0; double flt_max_overlap = clt_parameters.imp.flt_max_overlap; // 1.0; double flt_min_rms = clt_parameters.imp.flt_min_rms; // 0.0; double flt_max_rms = clt_parameters.imp.flt_max_rms; // 2.0; boolean flt_nan_rms = clt_parameters.imp.flt_nan_rms; // false; 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 = true; // clt_parameters.imp.flt_show_names; // true; boolean flt_show_overlaps = true; // clt_parameters.imp.flt_show_overlaps; // true; boolean flt_show_rms = true; // clt_parameters.imp.flt_show_rms; // true; boolean flt_show_zoom = true; // clt_parameters.imp.flt_show_zoom; // true; boolean flt_show_alt = true; // clt_parameters.imp.flt_show_alt; // true; boolean alt_overwrite = clt_parameters.imp.alt_overwrite; // false; // overwrite existing altitude match pairs boolean alt_pairwise = clt_parameters.imp.alt_pairwise; // false; // overwrite existing altitude match pairs double alt_sigma = clt_parameters.imp.alt_sigma; // 5.0; Reduce weight of the border tiles, Gaussian sigma in tiles to apply to weights. double alt_abs_outliers = clt_parameters.imp.alt_abs_outliers; // 3.0; // remove absolute outliers when fitting planes double alt_outliers = clt_parameters.imp.alt_outliers; // 0.05; Remove outliers when fitting planes, removed fraction. int alt_refine = clt_parameters.imp.alt_refine; // 1; Refine altitude difference plane after removing outliers (0 - no outlier removal, 1 - remove outliers and refine once, ...) double metric_err = clt_parameters.imp.pmtch_metric_err;// 0.05; // 0.02;// 2 cm double weight_rot = clt_parameters.imp.alt_weight_rot; // >0 weight of pairs errors in qn3 double weight_tilt = clt_parameters.imp.alt_weight_tilt; // >0 weight of pairs errors in qn1, qn2 double weight_scale = clt_parameters.imp.alt_weight_scale; // >0 weight in pairs scale-1.0 errors double pull = clt_parameters.imp.alt_pull; // 0 <= pull <1 - fraction of all RMS contributors double pull_rots = clt_parameters.imp.alt_pull_rots; // >=0 weight of sum of rotations, may be 0, normalized by pull value double pull_tilts = clt_parameters.imp.alt_pull_tilts; // >=0 weights of sum of qn1 and qn2 of scenes, normalized by pull value double pull_scales = clt_parameters.imp.alt_pull_scales; // >=0 weights of scales of scenes, normalized by pull value // log/save parameters 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; // boolean flt_update_config = false; boolean select_pairs = false; GenericJTabbedDialog gdf = new GenericJTabbedDialog("Select pairs filter/display",800,1200); gdf.addMessage("Filter pairs parameters"); gdf.addNumericField("Minimal scene overlap (0..1)",flt_min_overlap, 3,7,"", "Minimal overlap of the scenes to keep (0-no overlap, 1.0 - smaller scene is inside the parger one."); gdf.addNumericField("Maximal scene overlap (0..1)",flt_max_overlap, 3,7,"", "Maximal overlap of the scenes to keep (0-no overlap, 1.0 - smaller scene is inside the parger one."); 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.addNumericField("Minimal RMSE", flt_min_rms, 3,7,"", "Minimal LMA RMSE of the scene pair."); gdf.addNumericField("Maximal RMSE", flt_max_rms, 3,7,"", "Maximal LMA RMSE of the scene pair."); gdf.addCheckbox ("NaN RMS (failed match)", flt_nan_rms, "Keep only failed matches with RMSE=NaN."); gdf.addChoice("Filter by pairwise ALT availability",IntersceneMatchParameters.FLT_ALT_MODES, IntersceneMatchParameters.FLT_ALT_MODES[flt_alt],"Filter by pairwise ALT availability."); gdf.addMessage("Alt match parameters"); gdf.addCheckbox ("Overwrite existing alt_data", alt_overwrite, "Overwrite already defined altitude match pairs."); gdf.addCheckbox ("Use pairwise affines", alt_pairwise, "Use pairwise affines if available (false - always recalculate from individual)."); gdf.addNumericField("Border sigma", alt_sigma, 3,7,"tiles", "Reduce weight of the border tiles, Gaussian sigma in tiles to apply to weights."); gdf.addNumericField("Absolute outliers offset", alt_abs_outliers, 3,7,"m","Remove absolute outliers when fitting planes."); gdf.addNumericField("Fraction of ouliers", alt_outliers, 3,7,"", "Remove outliers when fitting planes, removed fraction."); gdf.addNumericField("Number of alt plane refines", alt_refine, 0,3,"", "Refine altitude difference plane after removing outliers (0 - no outlier removal, 1 - remove outliers and refine once, ...)"); gdf.addMessage("Log and Save, and Debug parameters"); gdf.addCheckbox ("Save state after each match", save_each, "Update state file after each match generation to mitigate possible crashes."); gdf.addCheckbox ("Write log file", log_append, "Enable writing log file with matching results."); gdf.addStringField ("Log file full path", log_path, 150, "Path of the log file to be appended."); gdf.addNumericField("Debug level", debugLevel, 0,3,"","Debug level during Spiral search."); gdf.addCheckbox ("Update configuration", flt_update_config, "Update matching configuration parameters to be saved as defaults."); gdf.addCheckbox ("Select filtered pairs", select_pairs, "Manually select from the filtered pairs."); gdf.showDialog(); if (gdf.wasCanceled()) return false; flt_min_overlap = gdf.getNextNumber(); flt_max_overlap = gdf.getNextNumber(); flt_filt_zoom = gdf.getNextBoolean(); flt_min_zoom = (int) gdf.getNextNumber(); flt_max_zoom = (int) gdf.getNextNumber(); flt_min_sfm = gdf.getNextNumber(); flt_max_sfm = gdf.getNextNumber(); flt_min_rms = gdf.getNextNumber(); flt_max_rms = gdf.getNextNumber(); flt_nan_rms = gdf.getNextBoolean(); flt_alt = gdf.getNextChoiceIndex(); alt_overwrite = gdf.getNextBoolean(); alt_pairwise = gdf.getNextBoolean(); alt_sigma = gdf.getNextNumber(); alt_abs_outliers = gdf.getNextNumber(); alt_outliers = gdf.getNextNumber(); alt_refine = (int) gdf.getNextNumber(); save_each = gdf.getNextBoolean(); log_append = gdf.getNextBoolean(); log_path = gdf.getNextString(); debugLevel = (int) gdf.getNextNumber(); flt_update_config = gdf.getNextBoolean(); select_pairs = gdf.getNextBoolean(); if (flt_update_config) { clt_parameters.imp.flt_min_overlap = flt_min_overlap; clt_parameters.imp.flt_max_overlap = flt_max_overlap; 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_min_rms = flt_min_rms; clt_parameters.imp.flt_max_rms = flt_max_rms; clt_parameters.imp.flt_nan_rms = flt_nan_rms; clt_parameters.imp.flt_alt = flt_alt; clt_parameters.imp.alt_overwrite = alt_overwrite; clt_parameters.imp.alt_pairwise = alt_pairwise; clt_parameters.imp.alt_sigma = alt_sigma; clt_parameters.imp.alt_abs_outliers = alt_abs_outliers; clt_parameters.imp.alt_outliers = alt_outliers; clt_parameters.imp.alt_refine = alt_refine; clt_parameters.imp.pwise_save_each = save_each; clt_parameters.imp.pwise_log_append = log_append; clt_parameters.imp.pwise_log_path = log_path; clt_parameters.imp.pwise_debug = debugLevel; } available_pairs = filterPairs( available_pairs, // int [][] plist_in, flt_undef_only, // boolean undef_only, flt_min_overlap, // double min_overlap, flt_max_overlap, // double max_overlap, flt_min_rms, // double min_rms, flt_max_rms, // double max_rms, 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_min_sfm, // double min_sfm, flt_max_sfm, // double max_sfm, flt_alt); // int flt_alt)\ if (select_pairs) { String [] choices_all = textPairs ( available_pairs, // int [][] plist, flt_show_names, // boolean show_names, 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, null); // flt_extra_line); // String extra_line) GenericJTabbedDialog gdc = new GenericJTabbedDialog("Select image pairs",1200,1000); boolean [] bselected_pairs = new boolean [choices_all.length]; for (int i = 0; i < choices_all.length; i++) { gdc.addCheckbox (i+": "+choices_all[i], bselected_pairs[i], "Select this scene pair for processing."); } gdc.showDialog(); if (gdc.wasCanceled()) return false; int num_pairs = 0; for (int i = 0; i < choices_all.length; i++) { bselected_pairs[i] = gdc.getNextBoolean(); if (bselected_pairs[i]) num_pairs++; } int [][] selected_pairs = new int[num_pairs][]; int indx = 0; for (int i = 0; i < bselected_pairs.length; i++) if (bselected_pairs[i]){ selected_pairs[indx++]= available_pairs[i]; } available_pairs = selected_pairs; } String [] choices_all = textPairs ( available_pairs, // int [][] plist, flt_show_names, // boolean show_names, flt_show_overlaps, // boolean show_overlap, flt_show_rms, // boolean show_rms, flt_show_zoom, // boolean show_zoom, flt_show_alt, // boolean show_alt, true, // boolean use_tab, null); // String extra_line) if (debugLevel > 0) { System.out.println("Selected "+available_pairs.length+" scene pairs for matching"); for (int i = 0; i < available_pairs.length; i++) { System.out.println(String.format("%4d:%s",i,choices_all[i])); } } if (available_pairs.length == 0) { return false; } //// return OrthoAltitudeMatch.altutudeMatchPairs( clt_parameters, // CLTParameters clt_parameters, this, // OrthoMapsCollection orthoMapsCollection, available_pairs, // int [][] available_pairs, select_pairs || alt_overwrite, // boolean alt_overwrite, // overwrite existing altitude match pairs alt_pairwise, // boolean alt_pairwise, // use pairwise affines if available alt_sigma, // double alt_sigma, // 5.0; Reduce weight of the border tiles, Gaussian sigma in tiles to apply to weights. alt_abs_outliers, //double alt_abs_outliers, // = 3.0; // remove absolute outliers when fitting planes alt_outliers, // double alt_outliers, // 0.05; Remove outliers when fitting planes, removed fraction. alt_refine, // int alt_refine, // 1; Refine altitude difference plane after removing outliers (0 - no outlier removal, 1 - remove outliers and refine once, ...) metric_err, // double metric_error, weight_rot, // double weight_rot, // >0 weight of pairs errors in qn3 weight_tilt, // double weight_tilt, // >0 weight of pairs errors in qn1, qn2 weight_scale, // double weight_scale, // >0 weight in pairs scale-1.0 errors pull, // double pull, // 0 <= pull <1 - fraction of all RMS contributors pull_rots, // double pull_rots, // >=0 weight of sum of rotations, may be 0, normalized by pull value pull_tilts, // double pull_tilts, // >=0 weights of sum of qn1 and qn2 of scenes, normalized by pull value pull_scales, // double pull_scales, // >=0 weights of scales of scenes, normalized by pull value // log/save parameters save_each, // boolean save_each, log_append, // boolean log_append, log_path, // String log_path, orthoMapsCollection_path, // String orthoMapsCollection_path, debugLevel); // int debugLevel) } public boolean displayScenePairs( CLTParameters clt_parameters, String orthoMapsCollection_path) { // Create list of all pairs (after recreating all overlaps with updated affines) ArrayList<Point> pairs_list = new ArrayList<Point>(); boolean dbg00=false; for (OrthoMap map : ortho_maps) { if (getIndex(map.getName()) == 87) { System.out.println("index="+getIndex(map.getName())); } for (String other_name: map.pairwise_matches.keySet()) { pairs_list.add(new Point( getIndex(map.getName()), getIndex(other_name))); if (dbg00) { System.out.println(getIndex(map.getName())+"-"+getIndex(other_name)+" "+map.getName()+"-"+other_name); if(getIndex(map.getName()) == getIndex(other_name)) { System.out.println(); } } } } // 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 } }); // convert ArrayList<Point> to array int[][] 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; available_pairs[i][1] = pairs_list.get(i).y; } boolean flt_undef_only = false; // clt_parameters.imp.flt_undef_only; // false; double flt_min_overlap = clt_parameters.imp.flt_min_overlap; // 0.0; double flt_max_overlap = clt_parameters.imp.flt_max_overlap; // 1.0; double flt_min_rms = clt_parameters.imp.flt_min_rms; // 0.0; double flt_max_rms = clt_parameters.imp.flt_max_rms; // 2.0; boolean flt_nan_rms = clt_parameters.imp.flt_nan_rms; // false; 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 = true; // clt_parameters.imp.flt_show_names; // true; boolean flt_show_overlaps = true; // clt_parameters.imp.flt_show_overlaps; // true; boolean flt_show_rms = true; // clt_parameters.imp.flt_show_rms; // true; boolean flt_show_zoom = true; // clt_parameters.imp.flt_show_zoom; // true; boolean flt_show_alt = true; // clt_parameters.imp.flt_show_alt; // true; boolean flt_show_svd = true; boolean flt_show_degrees = true; boolean alt_overwrite = clt_parameters.imp.alt_overwrite; // false; // overwrite existing altitude match pairs boolean alt_pairwise = clt_parameters.imp.alt_pairwise; // false; // overwrite existing altitude match pairs double metric_err = clt_parameters.imp.pmtch_metric_err;// 0.05; // 0.02;// 2 cm // log/save parameters 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; // boolean flt_update_config = false; GenericJTabbedDialog gdf = new GenericJTabbedDialog("Select pairs filter/display",800,1200); gdf.addMessage("Filter pairs parameters"); gdf.addNumericField("Minimal scene overlap (0..1)",flt_min_overlap, 3,7,"", "Minimal overlap of the scenes to keep (0-no overlap, 1.0 - smaller scene is inside the parger one."); gdf.addNumericField("Maximal scene overlap (0..1)",flt_max_overlap, 3,7,"", "Maximal overlap of the scenes to keep (0-no overlap, 1.0 - smaller scene is inside the parger one."); 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.addNumericField("Minimal RMSE", flt_min_rms, 3,7,"", "Minimal LMA RMSE of the scene pair."); gdf.addNumericField("Maximal RMSE", flt_max_rms, 3,7,"", "Maximal LMA RMSE of the scene pair."); gdf.addCheckbox ("NaN RMS (failed match)", flt_nan_rms, "Keep only failed matches with RMSE=NaN."); gdf.addChoice("Filter by pairwise ALT availability",IntersceneMatchParameters.FLT_ALT_MODES, IntersceneMatchParameters.FLT_ALT_MODES[flt_alt],"Filter by pairwise ALT availability."); gdf.addMessage("Alt match parameters"); gdf.addCheckbox ("Overwrite existing alt_data", alt_overwrite, "Overwrite already defined altitude match pairs."); gdf.addCheckbox ("Use pairwise affines", alt_pairwise, "Use pairwise affines if available (false - always recalculate from individual)."); gdf.addMessage("Log and Save, and Debug parameters"); gdf.addCheckbox ("Save state after each match", save_each, "Update state file after each match generation to mitigate possible crashes."); gdf.addCheckbox ("Write log file", log_append, "Enable writing log file with matching results."); gdf.addStringField ("Log file full path", log_path, 150, "Path of the log file to be appended."); gdf.addNumericField("Debug level", debugLevel, 0,3,"","Debug level during Spiral search."); gdf.addCheckbox ("Update configuration", flt_update_config, "Update matching configuration parameters to be saved as defaults."); gdf.showDialog(); if (gdf.wasCanceled()) return false; flt_min_overlap = gdf.getNextNumber(); flt_max_overlap = gdf.getNextNumber(); flt_filt_zoom = gdf.getNextBoolean(); flt_min_zoom = (int) gdf.getNextNumber(); flt_max_zoom = (int) gdf.getNextNumber(); flt_min_sfm = gdf.getNextNumber(); flt_max_sfm = gdf.getNextNumber(); flt_min_rms = gdf.getNextNumber(); flt_max_rms = gdf.getNextNumber(); flt_nan_rms = gdf.getNextBoolean(); flt_alt = gdf.getNextChoiceIndex(); alt_overwrite = gdf.getNextBoolean(); alt_pairwise = gdf.getNextBoolean(); save_each = gdf.getNextBoolean(); log_append = gdf.getNextBoolean(); log_path = gdf.getNextString(); debugLevel = (int) gdf.getNextNumber(); flt_update_config = gdf.getNextBoolean(); if (flt_update_config) { clt_parameters.imp.flt_min_overlap = flt_min_overlap; clt_parameters.imp.flt_max_overlap = flt_max_overlap; 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_min_rms = flt_min_rms; clt_parameters.imp.flt_max_rms = flt_max_rms; clt_parameters.imp.flt_nan_rms = flt_nan_rms; clt_parameters.imp.flt_alt = flt_alt; clt_parameters.imp.alt_overwrite = alt_overwrite; clt_parameters.imp.alt_pairwise = alt_pairwise; clt_parameters.imp.pwise_save_each = save_each; clt_parameters.imp.pwise_log_append = log_append; clt_parameters.imp.pwise_log_path = log_path; clt_parameters.imp.pwise_debug = debugLevel; } available_pairs = filterPairs( available_pairs, // int [][] plist_in, flt_undef_only, // boolean undef_only, flt_min_overlap, // double min_overlap, flt_max_overlap, // double max_overlap, flt_min_rms, // double min_rms, flt_max_rms, // double max_rms, 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_min_sfm, // double min_sfm, flt_max_sfm, // double max_sfm, flt_alt); // int flt_alt) boolean use_tab=true; boolean flt_show_sfm=flt_show_alt; String [] choices_all = textPairs ( available_pairs, // int [][] plist, flt_show_names, // boolean show_names, flt_show_overlaps, // boolean show_overlap, flt_show_rms, // boolean show_rms, flt_show_zoom, // boolean show_zoom, flt_show_alt, // boolean show_alt, use_tab, // boolean use_tab, null, // String extra_line) flt_show_svd, // boolean show_svd, flt_show_degrees); // boolean use_degrees); { System.out.println("Selected\t"+available_pairs.length+"\tscene pairs for matching"); if (use_tab) { System.out.print(String.format("#\t%3s\t%3s","i0","i1")); if (flt_show_names) System.out.print(String.format("\t%17s\t%17s","timestamp 0","timestamp 1")); if (flt_show_overlaps) System.out.print(String.format("\t%5s", "olap%")); if (flt_show_rms) System.out.print(String.format("\t%5s", "RMSE")); if (flt_show_zoom) System.out.print(String.format("\t%4s", "Zoom")); if (flt_show_alt) System.out.print(String.format("\t%7s\t%7s\t%7s", "tiltX","tiltY","offs")); if (flt_show_sfm) System.out.print(String.format("\t%5s\t%5s", "SfM0","SfM1")); if (flt_show_svd) { String svd_title=SingularValueDecomposition.titleString(flt_show_degrees); System.out.print("\t"+ svd_title); } System.out.println(); } for (int i = 0; i < available_pairs.length; i++) { // System.out.println(String.format("%4d:%s",i,choices_all[i])); System.out.println(String.format(use_tab?"%4d\t%s":"%4d:%s",i,choices_all[i])); } } if (available_pairs.length == 0) { return false; } //// return true; } public boolean processComboMap( CLTParameters clt_parameters, int debugLevel) { int [] indices = getScenesSelection( clt_parameters, // CLTParameters clt_parameters, " to process/display"); // String purpose) if (indices == null) { return false; } boolean more = true; while (more) { // generate multiple images/stats for the same selection more = processComboSelection( clt_parameters, // CLTParameters clt_parameters, indices, debugLevel); } return true; } public boolean processComboSelection( CLTParameters clt_parameters, int [] indices, int debugLevel) { boolean show_map_stats = false; boolean show_combo_map = false; // true; // false; // true; boolean show_alt_map = false; boolean show_combo_mask = false; // generate image mas (where it is defined) boolean show_frames = false; int frame_shrink = 4; int frame_grow = 4; double frame_blur = 1.0; double frame_cut_frac = 0.01; int zoom_lev = -2; // 0; // +1 - zoom in twice, -1 - zoom out twice int margins = 0; // 10; boolean show_centers = true; boolean bounds_to_indices = true; boolean merge_layers = false; // instead of individuals boolean ignore_affines = false; boolean ignore_equalize = false; boolean use_orientations = true; boolean keep_window = true; double scale_tilt = 1.0; // String save_top_dir = "/media/elphel/NVME/lwir16-proc/ortho_videos/debug/sept12-13/pattern_match/"; // String sub_dir = "combo_maps"; String save_top_dir = clt_parameters.imp.patt_save_top; String sub_dir = clt_parameters.imp.patt_save_subdir; boolean show_images = true; boolean save_images = true; GenericJTabbedDialog gd = new GenericJTabbedDialog("Combo map/stats generation",1200,1000); gd.addCheckbox ("Show statistics for ortho images", show_map_stats, "Generate and show statistics for ortho maps."); gd.addCheckbox ("Show image map", show_combo_map, "Generate composite map of images."); gd.addCheckbox ("Show altitudes", show_alt_map, "Generate composite process altitude maps."); gd.addCheckbox ("Show image masks", show_combo_mask, "Generate composite binary image."); gd.addCheckbox ("Show image frames", show_frames, "Generate image borders."); gd.addNumericField("Shrink inner frames", frame_shrink, 0,4,"pix","Shrink inner of the frames from the actual image borders."); gd.addNumericField("Grow outer frames", frame_grow, 0,4,"pix","Grow outer of the frames from the actual image borders."); gd.addNumericField("Blur frames", frame_blur, 3,7,"pix","Blur frames for anti-aliasing."); gd.addNumericField("Cut threshold", frame_cut_frac, 3,7,"","After blurring, replace lower values by NaN."); gd.addNumericField("Zoom level", zoom_lev, 0,4,"", "Zoom level: +1 - zoom in twice, -1 - zoom out twice"); gd.addNumericField("Margins", margins, 0,4,"", "Add margins around images"); gd.addCheckbox ("Show transformation centers",show_centers, "Mark verticals from the UAS on the ground."); gd.addCheckbox ("Bounds to selected images", bounds_to_indices, "Set combo image bounds to selected images only. False - all images."); gd.addCheckbox ("Merge layers", merge_layers, "Generate composite binary image."); gd.addCheckbox ("Ignore affines", ignore_affines, "Ignore available affines, use unity."); gd.addCheckbox ("Ignore equalization", ignore_equalize, "Ignore available intensity equalization, use unity."); gd.addCheckbox ("Use scene orientations", use_orientations, "Do not resize window to accomodate transformed image."); gd.addCheckbox ("Keep sorce window", keep_window, "Use scenes scales/orientations."); gd.addNumericField("Scale tilt", scale_tilt, 3,7,"pix","Blur frames for anti-aliasing."); gd.addStringField ("Pattern match save directory", save_top_dir, 120, "Top directory to save combo maps"); gd.addStringField ("Save subdirectory", sub_dir, 80, "Subdirectory for versions of the same scene/pair of scenes"); gd.addCheckbox ("Show generated images", show_images, "Display generated images."); gd.addCheckbox ("Save generated images", save_images, "Save generated image to the location defined by above.."); gd.showDialog(); if (gd.wasCanceled()) return false; show_map_stats = gd.getNextBoolean(); show_combo_map = gd.getNextBoolean(); show_alt_map = gd.getNextBoolean(); show_combo_mask = gd.getNextBoolean(); show_frames = gd.getNextBoolean(); frame_shrink = (int) gd.getNextNumber(); frame_grow = (int) gd.getNextNumber(); frame_blur = gd.getNextNumber(); frame_cut_frac = gd.getNextNumber(); zoom_lev = (int) gd.getNextNumber(); margins = (int) gd.getNextNumber(); show_centers = gd.getNextBoolean(); bounds_to_indices = gd.getNextBoolean(); merge_layers = gd.getNextBoolean(); ignore_affines = gd.getNextBoolean(); ignore_equalize = gd.getNextBoolean(); use_orientations = gd.getNextBoolean(); keep_window = gd.getNextBoolean(); scale_tilt = gd.getNextNumber(); save_top_dir= gd.getNextString(); sub_dir= gd.getNextString(); show_images= gd.getNextBoolean(); save_images= gd.getNextBoolean(); String save_dir = null; if (save_images) { if (!save_top_dir.endsWith(Prefs.getFileSeparator())) { save_top_dir+= Prefs.getFileSeparator(); } if (indices.length == 1) { save_dir=String.format("%s%03d-%s", save_top_dir,indices[0],ortho_maps[indices[0]].getName()); } else if (indices.length == 2) { save_dir=String.format("%s%03d-%03d_%s-%s", save_top_dir,indices[0],indices[1],ortho_maps[indices[0]].getName(),ortho_maps[indices[1]].getName()); } save_dir=save_top_dir; if (!sub_dir.equals("")) { save_dir+= Prefs.getFileSeparator()+sub_dir; } // create if it does not exist File fsave_dir = new File(save_dir); fsave_dir.mkdirs(); save_dir += Prefs.getFileSeparator(); } if (show_map_stats) { String time_zone_name = "Europe/Kyiv"; String [] stats = getScenesStats(indices, time_zone_name); // int [] indices) TextWindow tw = new TextWindow("Ortho_images_stats", stats[0], stats[1], 1250,1000); if (save_images && (save_dir != null)) { String path = save_dir+"ortho_images_stats.csv"; tw.getTextPanel().saveAs(path); if (debugLevel > -4) { System.out.println("Saved stats to "+path); } } } double pix_m = OrthoMap.getPixelSizeMeters (zoom_lev); if (use_orientations) { System.out.println("using orientations"); for (int nscene=0; nscene<indices.length; nscene++) { int indx = indices[nscene]; System.out.println("Scene="+indx+", agl="+ortho_maps[indx].getAGL()); double [] qorient = ortho_maps[indx].getQOrinet(); // QuatUtils.invert(ortho_maps[indx].getQOrinet()); if (qorient == null) { qorient = new double [] {1,0,0,0}; } final int src_width = ortho_maps[indx].getImageData().width; // assuming same image size for alt and texture final int src_height = ortho_maps[indx].getImageData().height; // assuming same image size for alt and texture final double [] image_data = ortho_maps[indx].getImageData().getDData(); final double [] alt_data = ortho_maps[indx].getAltData().getDData(); double pix_size_meters = OrthoMap.getPixelSizeMeters(ortho_maps[indx].getOriginalZoomLevel()); //ortho_maps[indx] int hwidth = src_width/2; int hheight = src_height/2; double [] xyz_center = {hwidth, hheight, alt_data[hheight*src_width + hwidth]/ pix_size_meters}; // in "pixels" Rectangle owoi = new Rectangle(); int stride = 4; // >2, 3 - OK double rfz = 0.3; // w= 1/(x^2+y^2+rfz^2) double [][] rotation_map_notilt = rotateAltCenter( qorient, // final double [] quat_in, xyz_center, // final double [] xyz_center, alt_data, // final double [] alt_in, src_width, // final int width, pix_size_meters, // final double pix_size_meters, owoi, // final Rectangle owoi, // should not be null - will provide offset and new size stride, // final int stride, // >=2, use 4? rfz, // final double rfz, // 0.3 w= 1/(x^2+y^2+rfz^2) keep_window, // final boolean keep_window, 0, // scale_tilt, // final double scale_tilt, debugLevel); // final int debugLevel) double [] image_out_notilt = applySceneRotation( image_data, // final double [] texture_in, src_width, // final int width_in, rotation_map_notilt, // final double [][] rotation_map, debugLevel); // final int debugLevel) double [] alt_out_notilt = getRotatedAlt( rotation_map_notilt);// final double [][] rotation_map) double [][] rotation_map = rotateAltCenter( qorient, // final double [] quat_in, xyz_center, // final double [] xyz_center, alt_data, // final double [] alt_in, src_width, // final int width, pix_size_meters, // final double pix_size_meters, owoi, // final Rectangle owoi, // should not be null - will provide offset and new size stride, // final int stride, // >=2, use 4? rfz, // final double rfz, // 0.3 w= 1/(x^2+y^2+rfz^2) keep_window, // final boolean keep_window, scale_tilt, // final double scale_tilt, debugLevel); // final int debugLevel) final double [] xy_center_out = {xyz_center[0]-owoi.x, xyz_center[1]-owoi.y}; // do outside by caller - new rotation center double [] image_out = applySceneRotation( image_data, // final double [] texture_in, src_width, // final int width_in, rotation_map, // final double [][] rotation_map, debugLevel); // final int debugLevel) double [] alt_out = getRotatedAlt( rotation_map);// final double [][] rotation_map) double [] image_data_padded = debugPadInToOut( image_data, // final double [] texture_in, src_width, // final int width_in, owoi); // final Rectangle woi) double [] alt_data_padded = debugPadInToOut( alt_data, // final double [] texture_in, src_width, // final int width_in, owoi); // final Rectangle woi) String [] titles= {"img_src","img_rot","img_out","alt_src","alt_rot","alt_out"}; double [][] dbg_img = {image_data_padded,image_out_notilt,image_out,alt_data_padded, alt_out_notilt, alt_out}; String title = String.format("%03d-%s", indx, ortho_maps[indx].getName()+"-ROTATION_DBG"); ImageStack stack = ShowDoubleFloatArrays.makeStack( dbg_img, owoi.width, owoi.height, titles, false); ImagePlus imp = new ImagePlus(title, stack); if (show_centers) { PointRoi roi = new PointRoi(); roi.addPoint(xy_center_out[0], xy_center_out[1], 0+1); roi.setOptions("label"); imp.setRoi(roi); } imp.show(); // debugging } } if (show_combo_map) { int [] wh = new int[2]; int [] origin = new int[2]; double [][] centers = show_centers? (new double [indices.length][]): null; double [][][] affines = null; if (ignore_affines) { affines = new double [indices.length][2][3]; for (int i = 0; i < indices.length; i++) { affines[i][0][0] = 1; affines[i][1][1] = 1; } } 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, affines, // null, // affines, // double [][][] affines, // null or [indices.length][2][3] null, // double [][] equalize, ignore_equalize, // true, // boolean ignore_equalize, null, // warp, // FineXYCorr warp,, zoom_lev, // int zoom_level, wh, // int [] wh, origin, // int [] origin){ // maps[0] as a reference centers); // double [][] centers) if (margins > 0) { addMargins( dmulti, // double [][] dmulti, wh, // int width, margins); // int margins) } showSaveMap( indices, // int [] indices, dmulti, // double [][] dmulti, wh[0], // int width, centers, // int [][] centers, merge_layers, // boolean merge_layers, false, // boolean use_max, show_images, // boolean show, save_images, // boolean save, save_dir, // String save_dir, "_"+indices.length+"_MAP-"+pix_m, // String suffix debugLevel); // int debugLevel) } if (show_alt_map) { int [] wh = new int[2]; int [] origin = new int[2]; double [][] centers = show_centers? (new double [indices.length][]): null; double [][] dmulti = 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,, zoom_lev, // int zoom_level, wh, // int [] wh, origin, // int [] origin){ // maps[0] as a reference centers); // double [][] centers) if (margins > 0) { addMargins( dmulti, // double [][] dmulti, wh, // int width, margins); // int margins) } showSaveMap( indices, // int [] indices, dmulti, // double [][] dmulti, wh[0], // int width, centers, // int [][] centers, merge_layers, // boolean merge_layers, false, // boolean use_max, show_images, // boolean show, save_images, // boolean save, save_dir, // String save_dir, "_"+indices.length+"_ALT-"+pix_m, // String suffix debugLevel); // int debugLevel) } if (show_combo_mask) { int [] wh = new int[2]; int [] origin = new int[2]; double [][] centers = show_centers? (new double [indices.length][]): null; 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, // double [][] equalize, true, // boolean ignore_equalize, null, // warp, // FineXYCorr warp,, zoom_lev, // int zoom_level, wh, // int [] wh, origin, // int [] origin){ // maps[0] as a reference centers); // double [][] centers) if (margins > 0) { addMargins( dmulti, // double [][] dmulti, wh, // int width, margins); // int margins) } for (int n = 0; n < dmulti.length; n++) { for (int i = 0; i < dmulti[n].length; i++) { dmulti[n][i] = Double.isNaN(dmulti[n][i])? 0:1; } } showSaveMap( indices, // int [] indices, dmulti, // double [][] dmulti, wh[0], // int width, centers, // int [][] centers, merge_layers, // boolean merge_layers, true, // boolean use_max, show_images, // boolean show, save_images, // boolean save, save_dir, // String save_dir, "_"+indices.length+"_MASK-"+pix_m, // String suffix debugLevel); // int debugLevel) } if (show_frames) { int [] wh = new int[2]; int [] origin = new int[2]; double [][] centers = show_centers? (new double [indices.length][]): null; 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, // double [][] equalize, true, // boolean ignore_equalize, null, // warp, // FineXYCorr warp,, zoom_lev, // int zoom_level, wh, // int [] wh, origin, // int [] origin){ // maps[0] as a reference centers); // double [][] centers) if (margins > 0) { addMargins( dmulti, // double [][] dmulti, wh, // int width, margins); // int margins) } double [][] dframes = new double [dmulti.length][]; for (int n = 0; n < dmulti.length; n++) { dframes[n] = createBorderByNaN( dmulti[n], // double [] data, wh[0], // int width, frame_shrink, // int frame_shrink, frame_grow, // int frame_grow, frame_blur, // double frame_blur, frame_cut_frac); // double cut_frac) // for (int i = 0; i < dmulti[n].length; i++) { // dmulti[n][i] = Double.isNaN(dmulti[n][i])? 0:1; // } } showSaveMap( indices, // int [] indices, dframes, // double [][] dmulti, wh[0], // int width, centers, // int [][] centers, merge_layers, // boolean merge_layers, true, // boolean use_max, show_images, // boolean show, save_images, // boolean save, save_dir, // String save_dir, "_"+indices.length+"_FRAME-"+pix_m, // String suffix debugLevel); // int debugLevel) } /* show_frames = gd.getNextBoolean(); frame_shrink = (int) gd.getNextNumber(); frame_grow = (int) gd.getNextNumber(); frame_blur = gd.getNextNumber(); */ //showScenesStats //show_combo_mask return true; } public static void addMargins( double [][] dmulti, int [] wh, int margins){ int width = wh[0]; int height = dmulti[0].length/width; int widthm = width+2*margins; int heightm = height + 2* margins; int indx0m = (widthm + 1) * margins; for (int n = 0; n < dmulti.length; n++) { double [] d = new double[widthm*heightm]; Arrays.fill(d, Double.NaN); for (int row = 0; row < height; row++) { System.arraycopy( dmulti[n], row*width, d, indx0m + widthm * row, width); } dmulti[n] = d; } wh[0] = widthm; wh[1] = heightm; } public static double [] createBorderByNaN( double [] data, int width, int frame_shrink, int frame_grow, double frame_blur, double cut_frac) { double [] dframe =new double [data.length]; int height = data.length/width; final TileNeibs tnSurface = new TileNeibs(width, height); boolean [] mask_in = new boolean[data.length]; for (int i = 0; i < data.length; i++) { mask_in[i] = !Double.isNaN(data[i]); } boolean [] mask_out = mask_in.clone(); tnSurface.shrinkSelection( frame_shrink, // shrink, mask_in, // tiles, null); // prohibit); tnSurface.growSelection( frame_grow, // grow, mask_out, // tiles, null); // prohibit); if (frame_blur > 0) { for (int i = 0; i < dframe.length; i++) { dframe[i] = (mask_out[i] && !mask_in[i]) ? 1.0 : 0.0; } // DoubleGaussianBlur gb = new DoubleGaussianBlur(); (new DoubleGaussianBlur()).blurDouble( dframe, width, height, frame_blur, frame_blur, 0.01); double max = 0.0; for (int i = 0; i < dframe.length; i++) { max = Math.max(max, dframe[i]); } double threshold = max*cut_frac; for (int i = 0; i < dframe.length; i++) { if (dframe[i] < threshold) { dframe[i] = Double.NaN; } } } else { for (int i = 0; i < dframe.length; i++) { dframe[i] = (mask_out[i] && !mask_in[i]) ? 1.0 : Double.NaN; } } return dframe; } public void showSaveMap( int [] indices, double [][] dmulti, int width, double [][] centers, boolean merge_layers, boolean use_max, boolean show, boolean save, String save_dir, String suffix, // "_"+indices.length+"_MAP" int debugLevel) { boolean show_centers = centers != null; if (merge_layers) { dmulti = new double [][] {mergeLayers(dmulti,use_max)}; } String [] titles = new String[dmulti.length]; for (int i = 0; i < titles.length; i++) { titles[i] = String.format("%03d-%s", indices[i], ortho_maps[indices[i]].getName()); } ImagePlus imp = ShowDoubleFloatArrays.makeArrays( dmulti, width, dmulti[0].length/width, ortho_maps[indices[0]].getName()+(merge_layers? "_MERGED":"")+suffix+".tiff", titles); if (show_centers) { PointRoi roi = new PointRoi(); for (int i = 0; i < centers.length; i++) { roi.addPoint(centers[i][0],centers[i][1], 1+(merge_layers?0:i)); } roi.setOptions("label"); imp.setRoi(roi); } showSaveImagePlus( imp, // ImagePlus imp, show, // boolean show, save, // boolean save, save_dir, // String save_dir, debugLevel); // int debugLevel) } public static double [] mergeLayers( double [][] layers, boolean use_max) { // false - use last if ((layers == null) || (layers.length == 0)){ return null; } double [] merged = layers[0].clone(); for (int n = 1; n < layers.length; n++) { if (use_max) { for (int i = 0; i < merged.length; i++) if (!Double.isNaN(layers[n][i])) { if (Double.isNaN(merged[i])) { merged[i] = layers[n][i]; } else { merged[i] = Math.max(layers[n][i],merged[i]); } } } else { for (int i = 0; i < merged.length; i++) if (!Double.isNaN(layers[n][i])) { merged[i] = layers[n][i]; } } } return merged; } /* public boolean showScenesStats() { String time_zone_name = "Europe/Kyiv"; int [] indices = getScenesSelection( null, // boolean select_all, " to generate stats"); // String purpose) if (indices == null) { return false; } String [] stats = getScenesStats(indices, time_zone_name); // int [] indices) new TextWindow("Ortho_images_stats", stats[0], stats[1], 1250,1000); return true; } */ public String[] getScenesStats(int [] indices, String time_zone_name) { if (indices == null) { indices = new int [ortho_maps.length]; for (int i = 0; i < indices.length; i++) { indices[i] = i; } } boolean y_down_ccw = true; String [] stats = new String[2]; // header, body StringBuffer sb = new StringBuffer(); // String head_fmt = "%3s\t%17s\t%26s\t%13s\t%13s\t%7s\t%6s\t%7s\t%6s\t%6s\t%6s\t%6s\n"; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss.SS zzz");// may be VV instead of zzz // stats [0] = String.format( "%3s\t%17s\t%26s\t%11s\t%11s\t%7s\t"+ "%6s\t%7s\t%6s\t%6s\t%6s\t%6s\t"+ "%6s\t%6s\t"+ "%7s\t%7s\t%7s\t%7s\t%7s\t%7s\t"+ "%7s\t%7s\t%7s\t%7s", "#", "timestamp", "date/time", "latitude", "longitude", "GND-ASL", "AGL","pix(cm)", "width", "height","vert_x","vert_y", "scenes", "sfm", "scale","tilt","beta","rot","offs-X","offs-Y", "a00","a01","a10","a11"); for (int indx:indices) { 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(time_zone_name)); String sdt = zonedDateTime.format(formatter); String name = map.getName(); double [] lla = map.getLLA(); double agl = map.getAGL(); double pix_size_cm = 100*map.getOrigPixMeters(); int width = map.getWidth(); int height = map.getHeight(); int [] vert_pix = map.getVertPixels(); double sfm_gain = map.getSfmGain(); int scenes = map.getNumberScenes(); double [][] affine = map.getAffine(); SingularValueDecomposition svd_st =SingularValueDecomposition.singularValueDecomposeScaleTiltGamma( affine, y_down_ccw); // boolean y_down_ccw) // double [] svd_st = OrthoMap.singularValueDecomposeScaleTilt( // affine, // y_down_ccw); // boolean y_down_ccw) // svd_st[0] - now gamma - short axis relative to map sb.append(String.format( "%3d\t%17s\t%26s\t%11.6f\t%11.6f\t%7.2f\t"+ "%6.2f\t%7.4f\t%6d\t%6d\t%6d\t%6d\t"+ "%6d\t%6.2f"+ "\t%7.4f\t%7.4f\t%7.4f\t%7.4f\t%7.3f\t%7.3f"+ "\t%7.4f\t%7.4f\t%7.4f\t%7.4f\n", indx, name, sdt, lla[0], lla[1], lla[2] - agl,// ground level agl, pix_size_cm, width, height, vert_pix[0], vert_pix[1], scenes, sfm_gain, svd_st.scale,svd_st.ratio,svd_st.beta,svd_st.rot, affine[0][2], affine[1][2], affine[0][0], affine[0][1], affine[1][0], affine[1][1])); } stats[1] = sb.toString(); return stats; } public static void showSaveImagePlus( ImagePlus imp, boolean show, boolean save, String save_dir, int debugLevel) { if (save && (save_dir != null)) { if (!save_dir.endsWith(Prefs.getFileSeparator())) { save_dir += Prefs.getFileSeparator(); } String save_path = save_dir+imp.getTitle(); if (!save_path.endsWith(".tiff")) { save_path+=".tiff"; } FileSaver imp_fs = new FileSaver(imp); imp_fs.saveAsTiff(save_path); if (debugLevel > -4) { System.out.println("Saved "+save_path); } } if (show) { imp.show(); } } 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; } public int [][] filterPairs( int [] indices_in, double min_overlap_frac, double max_rmse, boolean max_resolution, // require defined and full resolution int [][] remove_pairs) { if (indices_in == null) { indices_in = new int [ortho_maps.length]; for (int i = 0; i < indices_in.length; i++) { indices_in[i] = i; } } boolean [] used_scenes = new boolean [ortho_maps.length]; for (int i:indices_in) { used_scenes[i] = true; } HashSet <Point> donotuse_set = new HashSet<Point>(); if (remove_pairs != null) { for (int [] p: remove_pairs) { donotuse_set.add(new Point(p[0],p[1])); } } ArrayList<Point> pairs_list = new ArrayList<Point>(); for (int scene0:indices_in) { Set<String> match_names = ortho_maps[scene0].pairwise_matches.keySet(); ArrayList<String> names_list = new ArrayList<String>(); names_list.addAll(match_names); Collections.sort(names_list); for (String scene1_name:names_list) { PairwiseOrthoMatch match = ortho_maps[scene0].getMatch(scene1_name, !max_resolution); // may be not defined only if (match != null) { int scene1 = getIndex(scene1_name); if ( used_scenes[scene1] && (!max_resolution || match.isDefined()) && (match.getOverlap() >= min_overlap_frac) && ((max_rmse <= 0) || (match.getRMS() <= max_rmse)) ){ if (max_resolution) { int min_zoom = Math.min(ortho_maps[scene0].getOriginalZoomLevel(), ortho_maps[scene1].getOriginalZoomLevel()); if (match.getZoomLevel() < min_zoom) { continue; } } Point p = new Point(scene0, scene1); if (remove_pairs != null) { if (donotuse_set.contains(p)) { continue; } } System.out.println(String.format("%5d: %3d-%3d, %7.4f, %7.4f", pairs_list.size(), scene0, scene1, match.getOverlap(), match.getRMS())); pairs_list.add(p); } } } } int [][] pairs = new int [pairs_list.size()][2]; for (int i = 0; i < pairs.length; i++) { Point p = pairs_list.get(i); pairs[i][0] = p.x; pairs[i][1] = p.y; } return pairs; } public int [] getScenesFromPairs( int [][] pairs, int [] indices_in) { // preselected indices or null boolean [] used_scenes = new boolean [ortho_maps.length]; if (indices_in != null) { for (int i:indices_in) { used_scenes[i] = true; } } for (int [] p:pairs) { used_scenes[p[0]] = true; used_scenes[p[1]] = true; } int indx = 0; for (int i = 0; i < used_scenes.length; i++) if (used_scenes[i]){ indx++; } int [] indices = new int [indx]; indx = 0; for (int i = 0; i < used_scenes.length; i++) if (used_scenes[i]){ indices[indx++] = i; } return indices; } /** * Re-index pairs to exclude scenes not present in indices[] * @param pairs - array of start,end absolute scene numbers * @param indices - ascending array of used scenes (or null - all scenes) * @return re-indexed pairs, indices exclude scenes that are not in indices[] */ public int [][] condensePairs( int [][] pairs_all, int [] indices) { if (indices == null) { return pairs_all; } int [] reindex = new int [ortho_maps.length]; int [][] pairs = new int [pairs_all.length][2]; Arrays.fill(reindex, -1); for (int i = 0; i < indices.length; i++) { reindex[indices[i]] = i; } for (int i = 0; i < pairs.length; i++) { pairs[i][0] = reindex[pairs_all[i][0]]; pairs[i][1] = reindex[pairs_all[i][1]]; } return pairs; } /** * Filter list of image pairs by overlap and RMSE * @param list_in array of image pairs indices * @param undef_only only keep undefined pairs (no affine transform), ignore RMSE parameters * @param min_overlap minimal pair overlap * @param max_overlap maximal pair overlap * @param min_rms minimal RMSE * @param max_rms maximal RMSE * @param nan_rms use only NaN RMSE pairs * @param filt_zoom filter by zoom * @param min_zoom minimal zoom (including) * @param max_zoom maximal zoom (including) * @param min_sfm minimal SfM gain of the minimum in the scene pair * @param max_sfm maximal SfM gain of the minimum in the scene pair * @param filt_alt filter by alt data availability (0 - no filter, 1 - with alt only, 2 - without ALT only) * @return filtered array of pair indices */ public int [][] filterPairs( int [][] list_in, boolean undef_only, double min_overlap, double max_overlap, double min_rms, double max_rms, boolean nan_rms, boolean filt_zoom, int min_zoom, int max_zoom, double min_sfm, double max_sfm, int filt_alt){ ArrayList<Point> plist = new ArrayList<Point>(); int num_all = 0; int num_def = 0; for (int [] ipair: list_in) { Point pair = new Point(ipair[0],ipair[1]); PairwiseOrthoMatch pairwiseOrthoMatch = ortho_maps[pair.x].getMatch( ortho_maps[pair.y].getName(), undef_only || nan_rms); double sfm_gain = Math.min(ortho_maps[pair.x].getSfmGain(), ortho_maps[pair.y].getSfmGain()); if (pairwiseOrthoMatch != null) { num_all++; boolean defined = pairwiseOrthoMatch.isDefined(); if (defined) { num_def++; } if (filt_alt != 0) { // has alt data if (pairwiseOrthoMatch.alt_data != null) { if (filt_alt == 2) { continue; // skip if no-ALT only } } else { // does not have alt data if (filt_alt == 1) { continue; // skip if ALT only } } } if ((sfm_gain < min_sfm) || (sfm_gain > max_sfm)) { continue; } if (undef_only) { if (!defined) { plist.add(pair); } } else { if ((pairwiseOrthoMatch.overlap >= min_overlap) && (pairwiseOrthoMatch.overlap <= max_overlap)) { if (nan_rms) { if (Double.isNaN(pairwiseOrthoMatch.rms)) { plist.add(pair); } } else if ((pairwiseOrthoMatch.rms >= min_rms) && (pairwiseOrthoMatch.rms <= max_rms)) { if (!filt_zoom || ((pairwiseOrthoMatch.zoom_lev >= min_zoom) && (pairwiseOrthoMatch.zoom_lev <= max_zoom))) { plist.add(pair); } } } } } else { // System.out.println("pair=["+pair.x+","+pair.y+"] "+ // ortho_maps[pair.x].getName()+"->"+ortho_maps[pair.y].getName()); } } System.out.println("num_all = "+num_all+", num_def="+num_def+", remained="+plist.size()); int [][] pairs = new int [plist.size()][2]; for (int i = 0; i < pairs.length; i++) { pairs[i][0] = plist.get(i).x; pairs[i][1] = plist.get(i).y; } return pairs; } /** * Create text representation of the image pairs for drop-down selection lists * @param plist ArrayList<Point> as pairs of scenes indices * @param show_names show full scene names (timestamps) * @param show_overlap show scene overlap in percents * @param show_rms show pairs RMSE * @param show_zoom show pairs zoom level * @param show_alt show altitude data * @param extra_line null or an extra string to be added as a last element * @return Array of strings to be shown in a drop-down list */ public String [] textPairs ( int [][] plist, boolean show_names, boolean show_overlap, boolean show_rms, boolean show_zoom, boolean show_alt, boolean use_tab, String extra_line) { return textPairs ( plist, // int [][] plist, show_names, // boolean show_names, show_overlap, // boolean show_overlap, show_rms, // boolean show_rms, show_zoom, // boolean show_zoom, show_alt, // boolean show_alt, use_tab, // boolean use_tab, extra_line, // String extra_line, false, // boolean show_svd, false); // boolean use_degrees); } public String [] textPairs ( int [][] plist, boolean show_names, boolean show_overlap, boolean show_rms, boolean show_zoom, boolean show_alt, boolean use_tab, String extra_line, boolean show_svd, boolean use_degrees) { boolean show_sfm = show_alt; // TODO: show SfM of each scene String [] text_pairs = new String [plist.length+((extra_line !=null)? 1 : 0)]; for (int i = 0; i < plist.length; i++) { int [] pair = plist[i]; double [] sfm_gain = {ortho_maps[pair[0]].getSfmGain(), ortho_maps[pair[1]].getSfmGain()}; PairwiseOrthoMatch pairwiseOrthoMatch = ortho_maps[pair[0]].getMatch( ortho_maps[pair[1]].getName(), true); // include undefined // if (pairwiseOrthoMatch== null) { // System.out.println("pair=["+pair[0]+","+pair[1]+"] "+ // ortho_maps[pair[0]].getName()+"->"+ortho_maps[pair[1]].getName()); // } text_pairs[i] = String.format(use_tab?"%3d\t%3d":"%3d -> %3d", pair[0], pair[1]); if (show_names) text_pairs[i] += String.format(use_tab?"\t%17s\t%17s":" (%17s -> %17s)", ortho_maps[pair[0]].getName(),ortho_maps[pair[1]].getName()); if (pairwiseOrthoMatch != null) { if (show_overlap) text_pairs[i] += String.format(use_tab?"\t%5.1f%%":" %5.1f%%", 100*pairwiseOrthoMatch.overlap); if (show_rms) text_pairs[i] += String.format(use_tab?"\t%5.3f":" %5.3f", pairwiseOrthoMatch.rms); if (show_zoom) text_pairs[i] += String.format(use_tab?"\t%4d":" Z%3d", pairwiseOrthoMatch.zoom_lev); if (show_alt){ if (pairwiseOrthoMatch.alt_data != null) { text_pairs[i] += String.format(use_tab?"\t%7.4f\t%7.4f\t%7.3f":" A %7.4f %7.4f %7.3f", pairwiseOrthoMatch.alt_data[0],pairwiseOrthoMatch.alt_data[1],pairwiseOrthoMatch.alt_data[2]); //" ALT"; } else if (use_tab) { text_pairs[i] += String.format("\t%7s\t%7s\t%7s","---","---","---"); } } if (show_sfm) text_pairs[i] += String.format(use_tab?"\t%5.1f\t%5.1f":" S %5.1f %5.1f", sfm_gain[0],sfm_gain[1]); if (show_svd && use_tab && (pairwiseOrthoMatch.getAffine()!=null)) { // now only for tab SingularValueDecomposition svd = SingularValueDecomposition.singularValueDecompose(pairwiseOrthoMatch.getAffine()); String ssvd = svd.toString(use_degrees, 1); text_pairs[i] += "\t"+ssvd; } } } if (extra_line !=null) { text_pairs[plist.length] = extra_line; } return text_pairs; } public static double [][] rotateAltCenter( final double [] quat_in, final double [] xyz_center, final double [] alt_in, final int width, final double pix_size_meters, final Rectangle owoi, // should not be null - will provide offset and new size final int stride, // >=2, use 4? final double rfz, // 0.3 w= 1/(x^2+y^2+rfz^2) final boolean keep_window, final double scale_tilt, final int debugLevel) { final double discard_frac = 0.1; // discard interpolation if it is extrapolation more than this final int len_in = alt_in.length; final int height = len_in/width; final double r2fz = rfz*rfz; final double x0=xyz_center[0], y0=xyz_center[1],z0=xyz_center[2]; // xyz_center[2] now in "pixels" final double scale = QuatUtils.norm(quat_in); System.out.println("scale = "+scale+", NOT USED - set to k=1.0"); final double k = 1.0; final double [] quat = QuatUtils.normalize(quat_in); quat[2]*= -1; // y is down quat[3]*= -1; // rotation does not match tilts quat[1]*=scale_tilt; quat[2]*=scale_tilt; QuatUtils.normalizeInPlace(quat); final double [][] xyz1 = new double [alt_in.length][]; final Thread[] threads = ImageDtt.newThreadArray(); final double [][][] xy_mn_mx_thread= new double [threads.length][2][2]; final double [] pos_neg_inf = {Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}; final double [][] xy_mn_mx= {pos_neg_inf.clone(),pos_neg_inf.clone()}; for (int nthread=0; nthread < threads.length; nthread++) { xy_mn_mx_thread[nthread] = new double [][] {pos_neg_inf.clone(),pos_neg_inf.clone()}; } // Arrays.fill(xy_mn_mx_thread,xy_mn_mx); final AtomicInteger ai = new AtomicInteger(0); final AtomicInteger ati = new AtomicInteger(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { int nthread = ati.getAndIncrement(); double [] xyz = new double[3]; double [] xyz_out = new double[3]; for (int ipix = ai.getAndIncrement(); ipix < alt_in.length; ipix = ai.getAndIncrement()) if (!Double.isNaN(alt_in[ipix])){ xyz[0] = ipix % width - x0; xyz[1] = ipix/ width - y0; xyz[2] = alt_in[ipix]/ pix_size_meters - z0 ; // meters -> pix QuatUtils.applyTo( k, // final double k, // scale quat, // final double [] q, xyz, // final double [] in, xyz_out); // final double [] out) xyz1[ipix]= new double[3]; xyz1[ipix][0]=xyz_out[0]+x0; xyz1[ipix][1]=xyz_out[1]+y0; xyz1[ipix][2]=xyz_out[2]+z0; xy_mn_mx_thread[nthread][0][0] = Math.min(xy_mn_mx_thread[nthread][0][0], xyz1[ipix][0]); xy_mn_mx_thread[nthread][0][1] = Math.max(xy_mn_mx_thread[nthread][0][1], xyz1[ipix][0]); xy_mn_mx_thread[nthread][1][0] = Math.min(xy_mn_mx_thread[nthread][1][0], xyz1[ipix][1]); xy_mn_mx_thread[nthread][1][1] = Math.max(xy_mn_mx_thread[nthread][1][1], xyz1[ipix][1]); } } }; } ImageDtt.startAndJoin(threads); for (int i = 0; i < xy_mn_mx_thread.length; i++) { xy_mn_mx[0][0]=Math.min(xy_mn_mx[0][0], xy_mn_mx_thread[i][0][0]); xy_mn_mx[0][1]=Math.max(xy_mn_mx[0][1], xy_mn_mx_thread[i][0][1]); xy_mn_mx[1][0]=Math.min(xy_mn_mx[1][0], xy_mn_mx_thread[i][1][0]); xy_mn_mx[1][1]=Math.max(xy_mn_mx[1][1], xy_mn_mx_thread[i][1][1]); } int x_min = keep_window ? 0: ((int) Math.floor(xy_mn_mx[0][0])-1); int y_min = keep_window ? 0: ((int) Math.floor(xy_mn_mx[1][0])-1); int x_max = keep_window ? width: ((int) Math.ceil(xy_mn_mx[0][1]) +1); int y_max = keep_window ? height: ((int) Math.ceil(xy_mn_mx[1][1]) +1); //final Rectangle owoi.x = x_min; owoi.y = y_min; owoi.width= x_max-x_min; owoi.height=y_max-y_min; final int olen = owoi.width*owoi.height; final double [][] alt_out = new double [olen][]; // from rectified to alt_in, each element - {x,y,z} final double [] xyz_center_out = {xyz_center[0]-x_min, xyz_center[1]-y_min}; // do outside by caller - new rotation center // shift x,y in xyz1 to match new window ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int ipix = ai.getAndIncrement(); ipix < alt_in.length; ipix = ai.getAndIncrement()) if (xyz1[ipix] != null){ xyz1[ipix][0] -= owoi.x; xyz1[ipix][1] -= owoi.y; } } }; } ImageDtt.startAndJoin(threads); final double [] weights = new double [owoi.width*owoi.height]; // when mixing same result from multiple sources final int stride_w = (width -1) / stride + 1; final int stride_h = (height -1) / stride + 1; final int stride_l = stride_w * stride_h; final int [] neib_oindices = { 0, 1, owoi.width, owoi.width+1}; // output indices, Z-order final int [] neib_in_indices = { 0, 1, width, width+1}; // input indices, Z-order final int [][] neib2_indices = { // Z-shape {-width-1, -width, -1, 0}, // top-left (each also in Z-order) {-width, -width+1, 0, 1}, // top-right (each also in Z-order) { -1, 0, width-1, width }, // bottom-left (each also in Z-order) { 0, 1, width, width+1} // bottom-right (each also in Z-order) }; for (int offs_y = 0; offs_y < stride; offs_y++) { for (int offs_x = 0; offs_x < stride; offs_x++) { final int foffs_y = offs_y, foffs_x=offs_x; ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { double [] vh = new double[2], vv=new double[2], v=new double[2], o=new double[2]; double [] xyz = new double[3]; double [][] oxyz4 = new double[4][]; double [][] ixyz4 = new double[4][3]; for (int ipix = ai.getAndIncrement(); ipix < stride_l; ipix = ai.getAndIncrement()) { int pix_y = (ipix/stride_w)*stride + foffs_y; int pix_x = (ipix%stride_w)*stride + foffs_x; if ((pix_x < width) && (pix_y < height)) { int pix = pix_y * width + pix_x; double [] xyz1p = xyz1[pix]; // int x1 = (int) xyz1p[0]; // int y1 = (int) xyz1p[1]; if (xyz1p != null) { int x1 = (int) Math.floor(xyz1p[0]); int y1 = (int) Math.floor(xyz1p[1]); int x2 = (int) Math.ceil(xyz1p[0]); int y2 = (int) Math.ceil(xyz1p[1]); // accumulate 4 corners around the fractional int [][] xy_dir= {{x1,y1},{x2,y1},{x1,y2},{x2,y2}}; for (int dir = 0; dir < 4; dir++) { int [] indices_in = neib2_indices[dir]; // input indices (presumably) around output // make sure all defined and surround an output node boolean defined4 = true; // double [][] oxyz4 = new double[4][]; // double [][] ixyz4 = new double[4][]; for (int i = 0; i < 4; i++) { int indx= pix+indices_in[i]; if ((indx < 0) || (indx >= len_in) || (xyz1[indx] == null)) { defined4 = false; break; } oxyz4[i] = xyz1[indx]; ixyz4[i][0]= indx%width; ixyz4[i][1]= indx/width; ixyz4[i][2]= xyz1[indx][2]; // Z same as in in oxyz4 } if (!defined4) { continue; } int oindx = neib_oindices[dir]+ (y1*owoi.width + x1); if ((oindx < 0) || (oindx >= olen)) { continue; } o[0] = oindx % owoi.width; // output integer coordinates that should be inside oxyz4 o[1] = oindx / owoi.width; // average horizontal and vertical vectors for (int i = 0; i < 2; i++) { vh[i] = (oxyz4[1][i]-oxyz4[0][i]+oxyz4[3][i]-oxyz4[2][i])/2; vv[i] = (oxyz4[2][i]-oxyz4[0][i]+oxyz4[3][i]-oxyz4[1][i])/2; v[i]= o[i] - oxyz4[0][i]; } // find ax, ay for bilinear interpolation double lvh = Math.sqrt(vh[0]*vh[0]+vh[1]*vh[1]); double lvv = Math.sqrt(vv[0]*vv[0]+vv[1]*vv[1]); double lv = Math.sqrt( v[0]* v[0]+ v[1]* v[1]); double ax = (v[0]*vh[0]+v[1]*vh[1])/lv/lvh; double ay = (v[0]*vv[0]+v[1]*vv[1])/lv/lvv; // discard interpolation if it is extrapolation by more than discard_frac if ((ax < -discard_frac) || (ay < -discard_frac) || (ax > 1 + discard_frac) || (ax > 1 + discard_frac)) { continue; } //xyz = new double[3]; for (int i = 0; i <3; i++) { xyz[i] = (1-ax)*(1-ay)*ixyz4[0][i]+ ( ax)*(1-ay)*ixyz4[1][i]+ (1-ax)*( ay)*ixyz4[2][i]+ ( ax)*( ay)*ixyz4[3][i]; } xyz[2] *= pix_size_meters; // pix-> meters double dax = (ax > 0.5) ? (1-ax) : ax; double day = (ay > 0.5) ? (1-ay) : ay; double w =r2fz / (dax*dax+day*day+r2fz); //weights[] if (alt_out[oindx] == null) { alt_out[oindx] = xyz.clone(); weights[oindx] = w; } else { double w_new = (weights[oindx]+ w); double w0 = weights[oindx]/w_new; double w1 = w /w_new; for (int i = 0; i < xyz.length; i++) { alt_out[oindx][i] = w0 * alt_out[oindx][i] + w1 * xyz[i]; } weights[oindx] = w_new; } } } } } } }; } ImageDtt.startAndJoin(threads); } } return alt_out; } public static double [] applySceneRotation( final double [] texture_in, final int width_in, final double [][] rotation_map, // final int width_out, final int debugLevel) { final int height_in = texture_in.length/width_in; final double [] texture_out = new double[rotation_map.length]; Arrays.fill(texture_out, Double.NaN); 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 opix = ai.getAndIncrement(); opix < rotation_map.length; opix = ai.getAndIncrement()) if (rotation_map[opix] != null){ // int x = opix % width_out; // int y = opix / width_out; double [] xyz = rotation_map[opix]; int ipx0 = (int) Math.floor(xyz[0]); int ipy0 = (int) Math.floor(xyz[1]); if ((ipx0 >= width_in) || (ipy0 >= height_in)) { continue; } int ipx1 = ipx0 + 1; if (ipx1 >= width_in) ipx1 = width_in-1; int ipy1 = ipy0 + 1; if (ipy1 >= height_in) ipy1 = height_in-1; if ((ipx1 < 0) || (ipy1 < 0)) { continue; } if (ipx0 < 0) ipx0 = ipx1; if (ipy0 < 0) ipy0 = ipy1; double fx = xyz[0] - ipx0; double fy = xyz[1] - ipy0; texture_out[opix] = (1-fx) * (1-fy) * texture_in[ipy0 * width_in + ipx0] + ( fx) * (1-fy) * texture_in[ipy0 * width_in + ipx1] + (1-fx) * ( fy) * texture_in[ipy1 * width_in + ipx0] + ( fx) * ( fy) * texture_in[ipy1 * width_in + ipx1]; } } }; } ImageDtt.startAndJoin(threads); return texture_out; } public static double [] getRotatedAlt( final double [][] rotation_map) { final double [] alt_out = new double[rotation_map.length]; Arrays.fill(alt_out, Double.NaN); 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 opix = ai.getAndIncrement(); opix < rotation_map.length; opix = ai.getAndIncrement()) if (rotation_map[opix] != null){ alt_out[opix] = rotation_map[opix][2]; } } }; } ImageDtt.startAndJoin(threads); return alt_out; } public static double [] debugPadInToOut( final double [] texture_in, final int width_in, final Rectangle woi) { final int height_in = texture_in.length/width_in; final double [] texture_out = new double[woi.width*woi.height]; Arrays.fill(texture_out, Double.NaN); 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 opix = ai.getAndIncrement(); opix < texture_out.length; opix = ai.getAndIncrement()) { int x_out = opix % woi.width; int y_out = opix / woi.width; int x_in = x_out+woi.x; int y_in = y_out+woi.y; if ((x_in >=0) && (y_in >= 0) && (x_in < width_in) && (y_in < height_in)) { texture_out[opix] = texture_in[y_in * width_in + x_in]; } } } }; } ImageDtt.startAndJoin(threads); return texture_out; } }