Loading src/main/java/com/elphel/imagej/orthomosaic/ComboMatch.java +14 −8 Original line number Original line Diff line number Diff line Loading @@ -104,7 +104,7 @@ public class ComboMatch { "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_19_sep13_25-50-75-100m", "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_19_sep13_25-50-75-100m", "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75", "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75", "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-50m"}; "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-50m"}; int default_list_choice = 0; // files_lists_paths.length-1; // String files_list_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75.list"; // String files_list_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75.list"; // String orthoMapsCollection_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75.data"; // String orthoMapsCollection_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75.data"; //maps_nov3_50-75 //maps_nov3_50-75 Loading Loading @@ -148,10 +148,11 @@ public class ComboMatch { boolean use_saved_collection = true; // false; boolean use_saved_collection = true; // false; boolean save_collection = true; boolean save_collection = true; boolean process_correlation = true; // use false to save new version of data boolean process_correlation = true; // use false to save new version of data int num_tries_fit = 10; boolean update_match = true; // use false to save new version of data boolean update_match = true; // use false to save new version of data boolean render_match = true; boolean render_match = false; // true; boolean pattern_match = false; boolean pattern_match = true; // false; boolean bounds_to_indices = true; boolean bounds_to_indices = true; int temp_mode = 1; int temp_mode = 1; Loading @@ -166,10 +167,13 @@ public class ComboMatch { GenericJTabbedDialog gd = new GenericJTabbedDialog("Set image pair",1200,900); GenericJTabbedDialog gd = new GenericJTabbedDialog("Set image pair",1200,900); gd.addChoice ("Files list/data path (w/o extension):", files_lists_paths, files_lists_paths[files_lists_paths.length-1]); gd.addChoice ("Files list/data path (w/o extension):", files_lists_paths, files_lists_paths[default_list_choice]); gd.addCheckbox ("Use saved maps collection", use_saved_collection, "If false - use files list."); gd.addCheckbox ("Use saved maps collection", use_saved_collection, "If false - use files list."); gd.addCheckbox ("Save maps collection", save_collection, "Save maps collection to be able to restore."); gd.addCheckbox ("Save maps collection", save_collection, "Save maps collection to be able to restore."); gd.addCheckbox ("Process correlations", process_correlation, "false to skip to just regenerate new save file."); gd.addCheckbox ("Process correlations", process_correlation, "false to skip to just regenerate new save file."); // int num_tries_fit = 10; gd.addNumericField("Num tries fit", num_tries_fit, 0,4,"", "Try matching images this number of times"); gd.addCheckbox ("Update match if calculated", update_match, "Will update correlation match for a pair if found."); gd.addCheckbox ("Update match if calculated", update_match, "Will update correlation match for a pair if found."); gd.addCheckbox ("Render match", render_match, "Render a pair of matched images."); gd.addCheckbox ("Render match", render_match, "Render a pair of matched images."); Loading Loading @@ -215,6 +219,7 @@ public class ComboMatch { use_saved_collection = gd.getNextBoolean(); use_saved_collection = gd.getNextBoolean(); save_collection = gd.getNextBoolean(); save_collection = gd.getNextBoolean(); process_correlation= gd.getNextBoolean(); process_correlation= gd.getNextBoolean(); num_tries_fit = (int) gd.getNextNumber(); update_match= gd.getNextBoolean(); update_match= gd.getNextBoolean(); render_match= gd.getNextBoolean(); render_match= gd.getNextBoolean(); Loading Loading @@ -728,10 +733,10 @@ public class ComboMatch { double [][][] affines = {affine0,affine1}; double [][][] affines = {affine0,affine1}; int [] zooms = {initial_zoom, min_zoom_lev, 1000,1000}; // make automatic int [] zooms = {initial_zoom, min_zoom_lev, 1000,1000}; // make automatic double scale = 2.0; // scale vectors when warping; double scale = 2.0; // scale vectors when warping; int num_tries = 5; // int num_tries = 5; // make configurable if (!process_correlation) { if (!process_correlation) { zooms = new int[] {min_zoom_lev, 1000}; zooms = new int[] {min_zoom_lev, 1000}; num_tries = 0; num_tries_fit = 0; update_match = false; update_match = false; } } debugLevel = 0; debugLevel = 0; Loading @@ -750,7 +755,7 @@ public class ComboMatch { frac_remove, // double frac_remove, // = 0.25 frac_remove, // double frac_remove, // = 0.25 metric_error, // double metric_error, metric_error, // double metric_error, ignore_prev_rms, // boolean ignore_prev_rms, ignore_prev_rms, // boolean ignore_prev_rms, num_tries, // = 5int num_tries, // = 5 num_tries_fit, // = 5int num_tries, // = 5 true, // boolean calc_warp, true, // boolean calc_warp, batch_mode, // boolean batch_mode, batch_mode, // boolean batch_mode, gpu_pair, // String [] gpu_spair, gpu_pair, // String [] gpu_spair, Loading Loading @@ -814,6 +819,7 @@ public class ComboMatch { // TODO Auto-generated catch block // TODO Auto-generated catch block e.printStackTrace(); e.printStackTrace(); } } System.out.println("Saved data to "+ orthoMapsCollection_path); } } return true; return true; } } Loading Loading @@ -942,7 +948,7 @@ adjusted affines[1] for a pair: 1694564291_293695/1694564778_589341 ImageDtt.THREADS_MAX, // int threadsMax, ImageDtt.THREADS_MAX, // int threadsMax, debugLevel); // int debug_level) debugLevel); // int debug_level) } } if (debugLevel > 0) { if (debugLevel > 1) { String [] dbg_titles = {"x-raw","x_filled","y-raw","y_filled"}; String [] dbg_titles = {"x-raw","x_filled","y-raw","y_filled"}; double [][] dbg_img = {vf[0], vf_filled[0],vf[1], vf_filled[1]}; double [][] dbg_img = {vf[0], vf_filled[0],vf[1], vf_filled[1]}; ShowDoubleFloatArrays.showArrays( ShowDoubleFloatArrays.showArrays( Loading src/main/java/com/elphel/imagej/orthomosaic/ItemMatch.java 0 → 100644 +97 −0 Original line number Original line Diff line number Diff line package com.elphel.imagej.orthomosaic; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; public class ItemMatch { public double [] combo_pxy; // combo image {pixel_x, pixel_y} public double [] lla; // add later public HashMap<String, ItemPatternMatch> pattern_matches = new HashMap<String, ItemPatternMatch>(); public ItemMatch (double [] combo_pxy) { this.combo_pxy = combo_pxy; } public double [] getXY() { return combo_pxy; } public int [] getIntXY() { return new int [] {(int) Math.round(combo_pxy[0]), (int) Math.round(combo_pxy[1])}; } public void addPatternMatches( GroundObjectPattern groundObjectPattern, double [] matches, int best_sub) { String pattern_path = groundObjectPattern.getPatternPath(); ItemPatternMatch match = pattern_matches.get(pattern_path); if (match == null) { match = new ItemPatternMatch(groundObjectPattern); pattern_matches.put(pattern_path, match); } match.setMatches(matches); match.setBestSub(best_sub); } public ItemPatternMatch getPatternMatch(GroundObjectPattern groundObjectPattern) { return getPatternMatch(groundObjectPattern.getPatternPath()); } public ItemPatternMatch getPatternMatch(String pattern_path) { return pattern_matches.get(pattern_path); } public double [] getMatchValues(String pattern_path) { ItemPatternMatch match = pattern_matches.get(pattern_path); if (match == null) { System.out.println("No matches found for pattern "+pattern_path); return null; } return match.getMatches(); } public double [] getMatchValues(GroundObjectPattern groundObjectPattern) { return getMatchValues(groundObjectPattern.getPatternPath()); } public double getMatchValue(String pattern_path, int indx) { ItemPatternMatch match = pattern_matches.get(pattern_path); if (match == null) { System.out.println("No matches found for pattern "+pattern_path); return Double.NaN; } return match.getMatch(indx); } public double getMatchValue(GroundObjectPattern groundObjectPattern, int indx) { return getMatchValue(groundObjectPattern.getPatternPath(), indx); } public static ArrayList<Integer> sortByMatch( ArrayList<ItemMatch> match_list, GroundObjectPattern groundObjectPattern, int indx){ String pattern_path = groundObjectPattern.getPatternPath(); return sortByMatch(match_list, pattern_path, indx); } public static ArrayList<Integer> sortByMatch( ArrayList<ItemMatch> match_list, String pattern_path, int indx){ ArrayList<Integer> result_list= new ArrayList<Integer>(match_list.size()); for (int i = 0; i < match_list.size(); i++) { result_list.add(i); } Collections.sort(result_list, new Comparator<Integer>() { @Override public int compare(Integer lhs, Integer rhs) { double rhsd = match_list.get(rhs).getMatchValue(pattern_path, indx); double lhsd = match_list.get(lhs).getMatchValue(pattern_path, indx); return (rhsd > lhsd) ? 1 : (rhsd < lhsd) ? -1 : 0; // recreasing } }); return result_list; } } src/main/java/com/elphel/imagej/orthomosaic/ItemPatternMatch.java 0 → 100644 +52 −0 Original line number Original line Diff line number Diff line package com.elphel.imagej.orthomosaic; import java.util.Arrays; public class ItemPatternMatch { // public String pattern_path; public double [] sub_matches; // array of sub-pattern match qualities. [0] - is usually combo, [1] - full pattern, [2+] - obscured public int best_sub = -1; // best subpattern public GroundObjectPattern groundObjectPattern; public ItemPatternMatch(GroundObjectPattern groundObjectPattern) { this.groundObjectPattern = groundObjectPattern; } public void setMatches(double [] matches) { sub_matches = matches; } public void setMatch(int indx, double match_value) { if (sub_matches == null) { sub_matches = new double [indx+1]; Arrays.fill(sub_matches, Double.NaN); } else if (sub_matches.length <= indx) { double [] old_matches = sub_matches; sub_matches = new double [indx+1]; Arrays.fill(sub_matches, Double.NaN); System.arraycopy(old_matches, 0, sub_matches, 0, old_matches.length); } sub_matches[indx] = match_value; } public void setBestSub(int sub) { best_sub = sub; } public int getBestSub() { return best_sub; } public double getMatch(int indx) { if ((sub_matches == null) || (sub_matches.length <= indx)) { return Double.NaN; } return sub_matches[indx]; } public double [] getMatches() { return sub_matches; } public GroundObjectPattern getGroundObjectPattern() { return groundObjectPattern; } } src/main/java/com/elphel/imagej/orthomosaic/ObjectLocation.java +236 −0 Original line number Original line Diff line number Diff line Loading @@ -10,6 +10,8 @@ import com.elphel.imagej.common.ShowDoubleFloatArrays; import com.elphel.imagej.tileprocessor.Correlation2d; import com.elphel.imagej.tileprocessor.Correlation2d; import com.elphel.imagej.tileprocessor.TileNeibs; import com.elphel.imagej.tileprocessor.TileNeibs; import Jama.EigenvalueDecomposition; import Jama.Matrix; import ij.ImagePlus; import ij.ImagePlus; import ij.gui.PointRoi; import ij.gui.PointRoi; Loading Loading @@ -80,6 +82,240 @@ public class ObjectLocation { return dcrop; return dcrop; } } public static double [] extractObjectImage( double [] center_xy, int size, double [] src_img, int width) { int height = src_img.length / width; double [] dcrop = new double [size*size]; Arrays.fill(dcrop, Double.NaN); int hsize = size/2; int [] xy = {(int) Math.round(center_xy[0]), (int) Math.round(center_xy[1])}; boolean has_NaN=false; for (int y = 0; y < size; y++) { int src_y = y - hsize + xy[1]; if ((src_y >= 0) && (y < height)) { for (int x = 0; x < size; x++) { int src_x = x - hsize + xy[0]; if ((src_x >= 0) && (x < width)) { double d = src_img[src_x + src_y * width]; dcrop[x + size * y] = d; has_NaN |= Double.isNaN(d); } else { has_NaN=true; } } } else { has_NaN=true; } } if (has_NaN) { TileNeibs tn = new TileNeibs(size,size); OrthoMap.fillNaNs( dcrop, // double [] data, tn, // TileNeibs tn, 3); // int min_neibs) } return dcrop; } /** * Search for maximum within specified radius from the center or specified offset from the center * @param data square data * @param cent_xy offset from the square center to search * @param radius search for maximum within this radius * @param frac_max fraction of maximum to measure area * @return {maximum, area, dist, x, y}. No interpolation yet, each returned value is integer. * Returns null if no local maximum within area. x,y are offsets from the (provided) center */ public static double [] getMaxLocAreaNaN( double [] data, // square data double [] cent_xy, // if null, use center of the square double radius, // search for maximum within this radius double frac_max, int debugLevel) { double [] rslt = getMaxLocArea( data, // square data cent_xy, // if null, use center of the square radius, // search for maximum within this radius frac_max, debugLevel); if (rslt != null) { return rslt; } else { return new double[] {Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN}; } } public static double [] getMaxLocArea( double [] data, // square data double [] cent_xy, // if null, use center of the square double radius, // search for maximum within this radius double frac_max, int debugLevel) { boolean debug = debugLevel > 1; int size = (int) Math.sqrt(data.length); if (cent_xy == null) { cent_xy = new double [] {size/2, size/2}; } double maxr2 = radius * radius; double best_d = 0; int best_indx = -1; int min_x = Math.max(1, (int) Math.floor(cent_xy[0]-radius)); int min_y = Math.max(1, (int) Math.floor(cent_xy[1]-radius)); int max_x = Math.min(size-2, (int) Math.ceil(cent_xy[0]+radius)); int max_y = Math.min(size-2, (int) Math.ceil(cent_xy[1]+radius)); for (int y = min_y; y <= max_y; y++) { // do not search on very edges double dy = (y-cent_xy[1]); double y2 = dy*dy; if (y2 < maxr2) { for (int x = min_x; x <= max_x; x++) { double dx = x - cent_xy[0]; double r2 = y2 + dx*dx; if (r2 < maxr2) { int indx = y * size + x; double d = data[indx]; if (d > best_d) { best_indx = indx; best_d = d; } } } } } // is it local max? if ((data[best_indx - 1] > best_d) || (data[best_indx + 1] > best_d) || (data[best_indx - size] > best_d) || (data[best_indx + size] > best_d)) { return null; // on the edge, not a local max } boolean [] above_thresh = new boolean [data.length]; double thresh = best_d * frac_max; for (int i = 0; i < data.length; i++) { above_thresh[i] = data[i] > thresh; } int [] clusters = (new TileNeibs(size,size)).enumerateClusters( above_thresh, // boolean [] tiles, null, // int [] num_clusters, false); // boolean ordered) int center_cluster = clusters[best_indx]; double xc = best_indx % size - cent_xy[0]; double yc = best_indx / size - cent_xy[1]; double s0=0, sx=0, sy=0, sx2 = 0, sy2=0, sxy = 0; for (int i = 0; i < clusters.length; i++) { if (clusters[i] == center_cluster) { double y = i / size - (yc + cent_xy[1]); //(yc + cent_xy[1]) - absolute, from (0,0) double x = i % size - (xc + cent_xy[0]); double w = data[i]-thresh; s0 += w; sx += w * x; sy += w * y; sx2 += w * x * x; sy2 += w * y * y; sxy += w * x * y; } } double cxx = sx2 - sx * sx / s0, cyy= sy2 - sy * sy / s0, cxy = sxy - sx * sy / s0; /* * sum(Mi*(Xi-avg(X))^2) = SX2 - SX^2/S0 * sum(Mi*(Yi-avg(Y))^2) = SY2 - SY^2/S0 * sum(Mi*(Xi-avg(X)*(Yi-avg(Y))) = SXY - SX*SY / S0 */ Matrix covar = new Matrix(new double[][] {{cxx, cxy},{cxy,cyy}}); double [] cent_offs = {sx/s0 + xc,sy/s0 + yc}; EigenvalueDecomposition eig = covar.eig(); double [] eigval = {eig.getD().get(0, 0),eig.getD().get(1, 1)}; Arrays.sort(eigval); // ascending double elong = Math.sqrt(eigval[1]/eigval[0]); double eff_rad = Math.sqrt(Math.sqrt(eigval[1]*eigval[0])); double dist = Math.sqrt(cent_offs[0]*cent_offs[0] + cent_offs[1]*cent_offs[1]); if (debug) { System.out.println("\ncenter offset ["+(sx/s0)+","+(sy/s0)+"] , from center: ["+cent_offs[0]+","+cent_offs[1]+"]"); System.out.println("Covariance matrix:"); covar.print(8, 6); System.out.println("eig.getV()"); eig.getV().print(8, 6); System.out.println("eig.getD()"); eig.getD().print(8, 6); System.out.println(String.format("best_d=%7.5f, rad=%6.3f, elong=%6.3f, dist=%6.3f, dx=%6.3f, dy=%6.3f", best_d, eff_rad, elong, dist, cent_offs[0], cent_offs[1])); } return new double [] {best_d, eff_rad, elong, dist, cent_offs[0], cent_offs[1]} ; } public static double [] getMaxLocAreaInt( double [] data, // square data double [] cent_xy, // if null, use center of the square double radius, // search for maximum within this radius double frac_max) { int size = (int) Math.sqrt(data.length); if (cent_xy == null) { cent_xy = new double [] {size/2, size/2}; } double maxr2 = radius * radius; double best_d = 0; int best_indx = -1; int min_x = Math.max(1, (int) Math.floor(cent_xy[0]-radius)); int min_y = Math.max(1, (int) Math.floor(cent_xy[1]-radius)); int max_x = Math.min(size-2, (int) Math.ceil(cent_xy[0]+radius)); int max_y = Math.min(size-2, (int) Math.ceil(cent_xy[1]+radius)); for (int y = min_y; y <= max_y; y++) { // do not search on very edges double dy = (y-cent_xy[1]); double y2 = dy*dy; if (y2 < maxr2) { for (int x = min_x; x <= max_x; x++) { double dx = x - cent_xy[0]; double r2 = y2 + dx*dx; if (r2 < maxr2) { int indx = y * size + x; double d = data[indx]; if (d > best_d) { best_indx = indx; best_d = d; } } } } } // is it local max? if ((data[best_indx - 1] > best_d) || (data[best_indx + 1] > best_d) || (data[best_indx - size] > best_d) || (data[best_indx + size] > best_d)) { return null; // on the edge, not a local max } boolean [] above_thresh = new boolean [data.length]; double thresh = best_d * frac_max; for (int i = 0; i < data.length; i++) { above_thresh[i] = data[i] > thresh; } int [] clusters = (new TileNeibs(size,size)).enumerateClusters( above_thresh, // boolean [] tiles, null, // int [] num_clusters, false); // boolean ordered) int center_cluster = clusters[best_indx]; double area_max = 0.0; for (int i = 0; i < clusters.length; i++) { if (clusters[i] == center_cluster) { area_max += 1; } } double x_max = best_indx % size - cent_xy[0]; double y_max = best_indx / size - cent_xy[1]; double dist = Math.sqrt(y_max * y_max + x_max * x_max); return new double [] {best_d, area_max, dist, x_max, y_max} ; } public static double [] getPatternCenter( public static double [] getPatternCenter( double [] data_in, double [] data_in, Loading Loading
src/main/java/com/elphel/imagej/orthomosaic/ComboMatch.java +14 −8 Original line number Original line Diff line number Diff line Loading @@ -104,7 +104,7 @@ public class ComboMatch { "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_19_sep13_25-50-75-100m", "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_19_sep13_25-50-75-100m", "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75", "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75", "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-50m"}; "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_sep12-50m"}; int default_list_choice = 0; // files_lists_paths.length-1; // String files_list_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75.list"; // String files_list_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75.list"; // String orthoMapsCollection_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75.data"; // String orthoMapsCollection_path = "/media/elphel/SSD3-4GB/lwir16-proc/ortho_videos/maps_nov3_50-75.data"; //maps_nov3_50-75 //maps_nov3_50-75 Loading Loading @@ -148,10 +148,11 @@ public class ComboMatch { boolean use_saved_collection = true; // false; boolean use_saved_collection = true; // false; boolean save_collection = true; boolean save_collection = true; boolean process_correlation = true; // use false to save new version of data boolean process_correlation = true; // use false to save new version of data int num_tries_fit = 10; boolean update_match = true; // use false to save new version of data boolean update_match = true; // use false to save new version of data boolean render_match = true; boolean render_match = false; // true; boolean pattern_match = false; boolean pattern_match = true; // false; boolean bounds_to_indices = true; boolean bounds_to_indices = true; int temp_mode = 1; int temp_mode = 1; Loading @@ -166,10 +167,13 @@ public class ComboMatch { GenericJTabbedDialog gd = new GenericJTabbedDialog("Set image pair",1200,900); GenericJTabbedDialog gd = new GenericJTabbedDialog("Set image pair",1200,900); gd.addChoice ("Files list/data path (w/o extension):", files_lists_paths, files_lists_paths[files_lists_paths.length-1]); gd.addChoice ("Files list/data path (w/o extension):", files_lists_paths, files_lists_paths[default_list_choice]); gd.addCheckbox ("Use saved maps collection", use_saved_collection, "If false - use files list."); gd.addCheckbox ("Use saved maps collection", use_saved_collection, "If false - use files list."); gd.addCheckbox ("Save maps collection", save_collection, "Save maps collection to be able to restore."); gd.addCheckbox ("Save maps collection", save_collection, "Save maps collection to be able to restore."); gd.addCheckbox ("Process correlations", process_correlation, "false to skip to just regenerate new save file."); gd.addCheckbox ("Process correlations", process_correlation, "false to skip to just regenerate new save file."); // int num_tries_fit = 10; gd.addNumericField("Num tries fit", num_tries_fit, 0,4,"", "Try matching images this number of times"); gd.addCheckbox ("Update match if calculated", update_match, "Will update correlation match for a pair if found."); gd.addCheckbox ("Update match if calculated", update_match, "Will update correlation match for a pair if found."); gd.addCheckbox ("Render match", render_match, "Render a pair of matched images."); gd.addCheckbox ("Render match", render_match, "Render a pair of matched images."); Loading Loading @@ -215,6 +219,7 @@ public class ComboMatch { use_saved_collection = gd.getNextBoolean(); use_saved_collection = gd.getNextBoolean(); save_collection = gd.getNextBoolean(); save_collection = gd.getNextBoolean(); process_correlation= gd.getNextBoolean(); process_correlation= gd.getNextBoolean(); num_tries_fit = (int) gd.getNextNumber(); update_match= gd.getNextBoolean(); update_match= gd.getNextBoolean(); render_match= gd.getNextBoolean(); render_match= gd.getNextBoolean(); Loading Loading @@ -728,10 +733,10 @@ public class ComboMatch { double [][][] affines = {affine0,affine1}; double [][][] affines = {affine0,affine1}; int [] zooms = {initial_zoom, min_zoom_lev, 1000,1000}; // make automatic int [] zooms = {initial_zoom, min_zoom_lev, 1000,1000}; // make automatic double scale = 2.0; // scale vectors when warping; double scale = 2.0; // scale vectors when warping; int num_tries = 5; // int num_tries = 5; // make configurable if (!process_correlation) { if (!process_correlation) { zooms = new int[] {min_zoom_lev, 1000}; zooms = new int[] {min_zoom_lev, 1000}; num_tries = 0; num_tries_fit = 0; update_match = false; update_match = false; } } debugLevel = 0; debugLevel = 0; Loading @@ -750,7 +755,7 @@ public class ComboMatch { frac_remove, // double frac_remove, // = 0.25 frac_remove, // double frac_remove, // = 0.25 metric_error, // double metric_error, metric_error, // double metric_error, ignore_prev_rms, // boolean ignore_prev_rms, ignore_prev_rms, // boolean ignore_prev_rms, num_tries, // = 5int num_tries, // = 5 num_tries_fit, // = 5int num_tries, // = 5 true, // boolean calc_warp, true, // boolean calc_warp, batch_mode, // boolean batch_mode, batch_mode, // boolean batch_mode, gpu_pair, // String [] gpu_spair, gpu_pair, // String [] gpu_spair, Loading Loading @@ -814,6 +819,7 @@ public class ComboMatch { // TODO Auto-generated catch block // TODO Auto-generated catch block e.printStackTrace(); e.printStackTrace(); } } System.out.println("Saved data to "+ orthoMapsCollection_path); } } return true; return true; } } Loading Loading @@ -942,7 +948,7 @@ adjusted affines[1] for a pair: 1694564291_293695/1694564778_589341 ImageDtt.THREADS_MAX, // int threadsMax, ImageDtt.THREADS_MAX, // int threadsMax, debugLevel); // int debug_level) debugLevel); // int debug_level) } } if (debugLevel > 0) { if (debugLevel > 1) { String [] dbg_titles = {"x-raw","x_filled","y-raw","y_filled"}; String [] dbg_titles = {"x-raw","x_filled","y-raw","y_filled"}; double [][] dbg_img = {vf[0], vf_filled[0],vf[1], vf_filled[1]}; double [][] dbg_img = {vf[0], vf_filled[0],vf[1], vf_filled[1]}; ShowDoubleFloatArrays.showArrays( ShowDoubleFloatArrays.showArrays( Loading
src/main/java/com/elphel/imagej/orthomosaic/ItemMatch.java 0 → 100644 +97 −0 Original line number Original line Diff line number Diff line package com.elphel.imagej.orthomosaic; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; public class ItemMatch { public double [] combo_pxy; // combo image {pixel_x, pixel_y} public double [] lla; // add later public HashMap<String, ItemPatternMatch> pattern_matches = new HashMap<String, ItemPatternMatch>(); public ItemMatch (double [] combo_pxy) { this.combo_pxy = combo_pxy; } public double [] getXY() { return combo_pxy; } public int [] getIntXY() { return new int [] {(int) Math.round(combo_pxy[0]), (int) Math.round(combo_pxy[1])}; } public void addPatternMatches( GroundObjectPattern groundObjectPattern, double [] matches, int best_sub) { String pattern_path = groundObjectPattern.getPatternPath(); ItemPatternMatch match = pattern_matches.get(pattern_path); if (match == null) { match = new ItemPatternMatch(groundObjectPattern); pattern_matches.put(pattern_path, match); } match.setMatches(matches); match.setBestSub(best_sub); } public ItemPatternMatch getPatternMatch(GroundObjectPattern groundObjectPattern) { return getPatternMatch(groundObjectPattern.getPatternPath()); } public ItemPatternMatch getPatternMatch(String pattern_path) { return pattern_matches.get(pattern_path); } public double [] getMatchValues(String pattern_path) { ItemPatternMatch match = pattern_matches.get(pattern_path); if (match == null) { System.out.println("No matches found for pattern "+pattern_path); return null; } return match.getMatches(); } public double [] getMatchValues(GroundObjectPattern groundObjectPattern) { return getMatchValues(groundObjectPattern.getPatternPath()); } public double getMatchValue(String pattern_path, int indx) { ItemPatternMatch match = pattern_matches.get(pattern_path); if (match == null) { System.out.println("No matches found for pattern "+pattern_path); return Double.NaN; } return match.getMatch(indx); } public double getMatchValue(GroundObjectPattern groundObjectPattern, int indx) { return getMatchValue(groundObjectPattern.getPatternPath(), indx); } public static ArrayList<Integer> sortByMatch( ArrayList<ItemMatch> match_list, GroundObjectPattern groundObjectPattern, int indx){ String pattern_path = groundObjectPattern.getPatternPath(); return sortByMatch(match_list, pattern_path, indx); } public static ArrayList<Integer> sortByMatch( ArrayList<ItemMatch> match_list, String pattern_path, int indx){ ArrayList<Integer> result_list= new ArrayList<Integer>(match_list.size()); for (int i = 0; i < match_list.size(); i++) { result_list.add(i); } Collections.sort(result_list, new Comparator<Integer>() { @Override public int compare(Integer lhs, Integer rhs) { double rhsd = match_list.get(rhs).getMatchValue(pattern_path, indx); double lhsd = match_list.get(lhs).getMatchValue(pattern_path, indx); return (rhsd > lhsd) ? 1 : (rhsd < lhsd) ? -1 : 0; // recreasing } }); return result_list; } }
src/main/java/com/elphel/imagej/orthomosaic/ItemPatternMatch.java 0 → 100644 +52 −0 Original line number Original line Diff line number Diff line package com.elphel.imagej.orthomosaic; import java.util.Arrays; public class ItemPatternMatch { // public String pattern_path; public double [] sub_matches; // array of sub-pattern match qualities. [0] - is usually combo, [1] - full pattern, [2+] - obscured public int best_sub = -1; // best subpattern public GroundObjectPattern groundObjectPattern; public ItemPatternMatch(GroundObjectPattern groundObjectPattern) { this.groundObjectPattern = groundObjectPattern; } public void setMatches(double [] matches) { sub_matches = matches; } public void setMatch(int indx, double match_value) { if (sub_matches == null) { sub_matches = new double [indx+1]; Arrays.fill(sub_matches, Double.NaN); } else if (sub_matches.length <= indx) { double [] old_matches = sub_matches; sub_matches = new double [indx+1]; Arrays.fill(sub_matches, Double.NaN); System.arraycopy(old_matches, 0, sub_matches, 0, old_matches.length); } sub_matches[indx] = match_value; } public void setBestSub(int sub) { best_sub = sub; } public int getBestSub() { return best_sub; } public double getMatch(int indx) { if ((sub_matches == null) || (sub_matches.length <= indx)) { return Double.NaN; } return sub_matches[indx]; } public double [] getMatches() { return sub_matches; } public GroundObjectPattern getGroundObjectPattern() { return groundObjectPattern; } }
src/main/java/com/elphel/imagej/orthomosaic/ObjectLocation.java +236 −0 Original line number Original line Diff line number Diff line Loading @@ -10,6 +10,8 @@ import com.elphel.imagej.common.ShowDoubleFloatArrays; import com.elphel.imagej.tileprocessor.Correlation2d; import com.elphel.imagej.tileprocessor.Correlation2d; import com.elphel.imagej.tileprocessor.TileNeibs; import com.elphel.imagej.tileprocessor.TileNeibs; import Jama.EigenvalueDecomposition; import Jama.Matrix; import ij.ImagePlus; import ij.ImagePlus; import ij.gui.PointRoi; import ij.gui.PointRoi; Loading Loading @@ -80,6 +82,240 @@ public class ObjectLocation { return dcrop; return dcrop; } } public static double [] extractObjectImage( double [] center_xy, int size, double [] src_img, int width) { int height = src_img.length / width; double [] dcrop = new double [size*size]; Arrays.fill(dcrop, Double.NaN); int hsize = size/2; int [] xy = {(int) Math.round(center_xy[0]), (int) Math.round(center_xy[1])}; boolean has_NaN=false; for (int y = 0; y < size; y++) { int src_y = y - hsize + xy[1]; if ((src_y >= 0) && (y < height)) { for (int x = 0; x < size; x++) { int src_x = x - hsize + xy[0]; if ((src_x >= 0) && (x < width)) { double d = src_img[src_x + src_y * width]; dcrop[x + size * y] = d; has_NaN |= Double.isNaN(d); } else { has_NaN=true; } } } else { has_NaN=true; } } if (has_NaN) { TileNeibs tn = new TileNeibs(size,size); OrthoMap.fillNaNs( dcrop, // double [] data, tn, // TileNeibs tn, 3); // int min_neibs) } return dcrop; } /** * Search for maximum within specified radius from the center or specified offset from the center * @param data square data * @param cent_xy offset from the square center to search * @param radius search for maximum within this radius * @param frac_max fraction of maximum to measure area * @return {maximum, area, dist, x, y}. No interpolation yet, each returned value is integer. * Returns null if no local maximum within area. x,y are offsets from the (provided) center */ public static double [] getMaxLocAreaNaN( double [] data, // square data double [] cent_xy, // if null, use center of the square double radius, // search for maximum within this radius double frac_max, int debugLevel) { double [] rslt = getMaxLocArea( data, // square data cent_xy, // if null, use center of the square radius, // search for maximum within this radius frac_max, debugLevel); if (rslt != null) { return rslt; } else { return new double[] {Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN}; } } public static double [] getMaxLocArea( double [] data, // square data double [] cent_xy, // if null, use center of the square double radius, // search for maximum within this radius double frac_max, int debugLevel) { boolean debug = debugLevel > 1; int size = (int) Math.sqrt(data.length); if (cent_xy == null) { cent_xy = new double [] {size/2, size/2}; } double maxr2 = radius * radius; double best_d = 0; int best_indx = -1; int min_x = Math.max(1, (int) Math.floor(cent_xy[0]-radius)); int min_y = Math.max(1, (int) Math.floor(cent_xy[1]-radius)); int max_x = Math.min(size-2, (int) Math.ceil(cent_xy[0]+radius)); int max_y = Math.min(size-2, (int) Math.ceil(cent_xy[1]+radius)); for (int y = min_y; y <= max_y; y++) { // do not search on very edges double dy = (y-cent_xy[1]); double y2 = dy*dy; if (y2 < maxr2) { for (int x = min_x; x <= max_x; x++) { double dx = x - cent_xy[0]; double r2 = y2 + dx*dx; if (r2 < maxr2) { int indx = y * size + x; double d = data[indx]; if (d > best_d) { best_indx = indx; best_d = d; } } } } } // is it local max? if ((data[best_indx - 1] > best_d) || (data[best_indx + 1] > best_d) || (data[best_indx - size] > best_d) || (data[best_indx + size] > best_d)) { return null; // on the edge, not a local max } boolean [] above_thresh = new boolean [data.length]; double thresh = best_d * frac_max; for (int i = 0; i < data.length; i++) { above_thresh[i] = data[i] > thresh; } int [] clusters = (new TileNeibs(size,size)).enumerateClusters( above_thresh, // boolean [] tiles, null, // int [] num_clusters, false); // boolean ordered) int center_cluster = clusters[best_indx]; double xc = best_indx % size - cent_xy[0]; double yc = best_indx / size - cent_xy[1]; double s0=0, sx=0, sy=0, sx2 = 0, sy2=0, sxy = 0; for (int i = 0; i < clusters.length; i++) { if (clusters[i] == center_cluster) { double y = i / size - (yc + cent_xy[1]); //(yc + cent_xy[1]) - absolute, from (0,0) double x = i % size - (xc + cent_xy[0]); double w = data[i]-thresh; s0 += w; sx += w * x; sy += w * y; sx2 += w * x * x; sy2 += w * y * y; sxy += w * x * y; } } double cxx = sx2 - sx * sx / s0, cyy= sy2 - sy * sy / s0, cxy = sxy - sx * sy / s0; /* * sum(Mi*(Xi-avg(X))^2) = SX2 - SX^2/S0 * sum(Mi*(Yi-avg(Y))^2) = SY2 - SY^2/S0 * sum(Mi*(Xi-avg(X)*(Yi-avg(Y))) = SXY - SX*SY / S0 */ Matrix covar = new Matrix(new double[][] {{cxx, cxy},{cxy,cyy}}); double [] cent_offs = {sx/s0 + xc,sy/s0 + yc}; EigenvalueDecomposition eig = covar.eig(); double [] eigval = {eig.getD().get(0, 0),eig.getD().get(1, 1)}; Arrays.sort(eigval); // ascending double elong = Math.sqrt(eigval[1]/eigval[0]); double eff_rad = Math.sqrt(Math.sqrt(eigval[1]*eigval[0])); double dist = Math.sqrt(cent_offs[0]*cent_offs[0] + cent_offs[1]*cent_offs[1]); if (debug) { System.out.println("\ncenter offset ["+(sx/s0)+","+(sy/s0)+"] , from center: ["+cent_offs[0]+","+cent_offs[1]+"]"); System.out.println("Covariance matrix:"); covar.print(8, 6); System.out.println("eig.getV()"); eig.getV().print(8, 6); System.out.println("eig.getD()"); eig.getD().print(8, 6); System.out.println(String.format("best_d=%7.5f, rad=%6.3f, elong=%6.3f, dist=%6.3f, dx=%6.3f, dy=%6.3f", best_d, eff_rad, elong, dist, cent_offs[0], cent_offs[1])); } return new double [] {best_d, eff_rad, elong, dist, cent_offs[0], cent_offs[1]} ; } public static double [] getMaxLocAreaInt( double [] data, // square data double [] cent_xy, // if null, use center of the square double radius, // search for maximum within this radius double frac_max) { int size = (int) Math.sqrt(data.length); if (cent_xy == null) { cent_xy = new double [] {size/2, size/2}; } double maxr2 = radius * radius; double best_d = 0; int best_indx = -1; int min_x = Math.max(1, (int) Math.floor(cent_xy[0]-radius)); int min_y = Math.max(1, (int) Math.floor(cent_xy[1]-radius)); int max_x = Math.min(size-2, (int) Math.ceil(cent_xy[0]+radius)); int max_y = Math.min(size-2, (int) Math.ceil(cent_xy[1]+radius)); for (int y = min_y; y <= max_y; y++) { // do not search on very edges double dy = (y-cent_xy[1]); double y2 = dy*dy; if (y2 < maxr2) { for (int x = min_x; x <= max_x; x++) { double dx = x - cent_xy[0]; double r2 = y2 + dx*dx; if (r2 < maxr2) { int indx = y * size + x; double d = data[indx]; if (d > best_d) { best_indx = indx; best_d = d; } } } } } // is it local max? if ((data[best_indx - 1] > best_d) || (data[best_indx + 1] > best_d) || (data[best_indx - size] > best_d) || (data[best_indx + size] > best_d)) { return null; // on the edge, not a local max } boolean [] above_thresh = new boolean [data.length]; double thresh = best_d * frac_max; for (int i = 0; i < data.length; i++) { above_thresh[i] = data[i] > thresh; } int [] clusters = (new TileNeibs(size,size)).enumerateClusters( above_thresh, // boolean [] tiles, null, // int [] num_clusters, false); // boolean ordered) int center_cluster = clusters[best_indx]; double area_max = 0.0; for (int i = 0; i < clusters.length; i++) { if (clusters[i] == center_cluster) { area_max += 1; } } double x_max = best_indx % size - cent_xy[0]; double y_max = best_indx / size - cent_xy[1]; double dist = Math.sqrt(y_max * y_max + x_max * x_max); return new double [] {best_d, area_max, dist, x_max, y_max} ; } public static double [] getPatternCenter( public static double [] getPatternCenter( double [] data_in, double [] data_in, Loading