Loading src/main/java/com/elphel/imagej/orthomosaic/ItemMatch.java +18 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,24 @@ public class ItemMatch { return match.getMatch(indx); } /** * Return correlation value as the one of the pattern correlations pointed * by the best subpattern index. * @param groundObjectPattern * @return selected correlation value */ public double getMatchValue(GroundObjectPattern groundObjectPattern) { return getMatchValue(groundObjectPattern.getPatternPath()); } public double getMatchValue(String pattern_path) { int indx = getPatternMatch(pattern_path).getBestSub(); // -1; return getMatchValue(pattern_path,indx); } public double getMatchValue(GroundObjectPattern groundObjectPattern, int indx) { return getMatchValue(groundObjectPattern.getPatternPath(), indx); } Loading src/main/java/com/elphel/imagej/orthomosaic/OrthoMap.java +48 −12 Original line number Diff line number Diff line Loading @@ -2073,18 +2073,28 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ * @param ipattern square pattern array corresponding to data. 1 means * average (w/o outliers) and add, 2 - average and subtract * @param outliers_frac - fraction of outliers to remove while averaging * @param obscure_frac fraction between center average and peripheral ring to compare when obscure_warm * is set. * @param masked_data null or double[data.length] - will return a copy of data * with NaN for all unused for averaging * @param debug show debug images * @return difference between average in-pattern (1) and outside (2) average * values * @return a pair of : difference between average in-pattern (1) and outside (2) average * values, and difference between third zone and a threshold between averages of zone1 * and zone2. For full patterns (no zone3) this value is NaN. Positive values are good, * regardless the cold or warm center. For cold patterns (morning) positive means * that zone 3 minimum is warmer than fraction point between zone1 and zone2 (cold * object obscured by something warmer, for hot - that obscurant is colder. * */ public static double getAbsoluteContrast( public static double [] getAbsoluteContrast( double [] data, int [] ipattern, 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 double [] masked_data, // null or double [data.length] to return masked data boolean debug) { int num_segments = 3; if (debug) { int size = (int) Math.sqrt(data.length); ShowDoubleFloatArrays.showArrays( Loading @@ -2095,11 +2105,14 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ } ArrayList<ArrayList<Integer>> lists = new ArrayList<ArrayList<Integer>>(); for (int i = 0; i < 2; i++) { for (int i = 0; i < num_segments; i++) { lists.add(new ArrayList<Integer>()); } double [] swd = new double [2],sw = new double [2], avg= new double[2], fracw=new double[2]; for (int n = 0; n < swd.length; n++) { double [] swd = new double [num_segments], sw = new double [num_segments], avg= new double[num_segments], fracw=new double[num_segments]; for (int n = 0; n < num_segments; n++) { // swd.length; n++) { ArrayList<Integer> list = lists.get(n); int n1 = n+1; for (int i = 0; i < ipattern.length; i++) if (ipattern[i] == n1){ Loading @@ -2110,9 +2123,23 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ avg[n] = swd[n]/sw[n]; fracw[n] = (1.0-outliers_frac) * sw[n]; } double [] zone3_min_max = {Double.NaN, Double.NaN}; int zone3=3; for (int i:lists.get(zone3-1)) { if (Double.isNaN(zone3_min_max[0]) || (data[i] < zone3_min_max[0])) { zone3_min_max[0] = data[i]; } if (Double.isNaN(zone3_min_max[1]) || (data[i] > zone3_min_max[1])) { zone3_min_max[1] = data[i]; } } if (debug) { System.out.println("getAbsoluteContrast() before outliers: avg[0] ="+avg[0]+", avg[1]="+avg[1]+", avg[0]-avg[1]="+(avg[0]-avg[1])); } if (outliers_frac > 0) { for (int n = 0; n < swd.length; n++) { ArrayList<Integer> list = lists.get(n); Loading @@ -2124,7 +2151,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ return (rhsd > lhsd) ? -1 : (rhsd < lhsd) ? 1 : 0; } }); while (sw[n] >= fracw[n]) { while (!list.isEmpty() && (sw[n] >= fracw[n])) { int indx_first = list.get(0); int indx_last = list.get(list.size()-1); boolean remove_first = Math.abs(data[indx_first] - avg[n]) > Math.abs(data[indx_last] - avg[n]); Loading @@ -2137,6 +2164,11 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ } } } double threshold = obscure_frac * avg[0] + (1.0 - obscure_frac) * avg[1]; double over_thresh = (avg[0] < avg[1]) ? (zone3_min_max[0] - threshold) : (threshold - zone3_min_max[1]); if (masked_data != null) { Arrays.fill(masked_data, Double.NaN); for (int n = 0; n < lists.size(); n++) { Loading Loading @@ -2175,14 +2207,15 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ if (debug) { System.out.println("getAbsoluteContrast() after outliers: avg[0] ="+avg[0]+", avg[1]="+avg[1]+", avg[0]-avg[1]="+(avg[0]-avg[1])); } return avg[0]-avg[1]; return new double [] {avg[0]-avg[1], over_thresh}; } /** * From existing pattern (only for simple dark main scene) * create two zones to calculate average (w/o outliers) inside * the pattern and around it. Mark inner with 1, outer - 2, keep * other 0. Then later find 2 averages (removing outliers) and their * the pattern and around it. Mark inner with 1, outer - 2, * 3 - potential obscurant for partial patterns * keep other 0. Then later find 2 averages (removing outliers) and their * difference - absolute contrast. * @param patterns set of square patterns, first [0] for full pattern, * others - for half-patterns cut in different directions. Loading Loading @@ -2287,6 +2320,8 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ ( fys)*( fxs)*npatterns[n][sindx+size+1]; if (sdn/sd0 > 0.5) { ipatterns[n][indx] = ipatterns[0][indx]; // =2 } else { ipatterns[n][indx] = 3; // =2 } } } Loading @@ -2305,6 +2340,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ switch (ipatterns[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; } } } Loading Loading @@ -4297,7 +4333,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ final int dbg_pix3 = 1439211; // 2827822; final int dbg_x = 1445; final int dbg_y = 2338; final int dbg_tolerance = 10; final int dbg_tolerance = -10; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { Loading src/main/java/com/elphel/imagej/orthomosaic/OrthoMapsCollection.java +108 −31 Original line number Diff line number Diff line Loading @@ -1816,12 +1816,13 @@ public class OrthoMapsCollection implements Serializable{ phaseCoeff[0] = 0.9; // adv_radius = 20; // pix? } else { // 100m defaults min_corrs[0] = 0.009; min_corrs[0] = 0.0119; abs_edge_frac = 0.35; abs_oversize = 2.2; filt_abs_contrast = 60; filt_abs_easepart = 0.85; phaseCoeff[0] = 0.9; abs_force_round = true; // adv_radius = 20; // pix? } } Loading Loading @@ -2359,7 +2360,7 @@ public class OrthoMapsCollection implements Serializable{ System.out.print(String.format("%4d: %4d/%4d %8.5f %3d", i, icenter_xy[0],icenter_xy[1], match.getMatchValue(gops[scene_num],sort_pattern_index), match.getMatchValue(gops[scene_num]), // ,sort_pattern_index), match.getPatternMatch(gops[scene_num]).getBestSub())); // 1-based for (int j = 0; j < match_values.length; j++) if (j != sort_pattern_index) { System.out.print(String.format(" %8.5f",match_values[j])); Loading @@ -2384,12 +2385,47 @@ public class OrthoMapsCollection implements Serializable{ extr_corr[scene_num] = new double [match_sort.size()][]; // extracted_objects[scene_num].length][]; extracted_masked[scene_num]= new double [match_sort.size()][]; // extr_corr_half[scene_num] = new double [match_sort.size()][]; // extracted_objects[scene_num].length][]; /// best_patt_indx = new int [match_sort.size()]; // Extract main scene areas around candidates // extract main scene data, needed for the absolute contrast calculations, no display showExtractedImages( // scene_num == 0 here null, // ortho_maps[indices[scene_num]].getName()+"-prefilter", // String prefix scene_num, //int scene_num, //sort_pattern_index, // int sort_pattern_index, extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, extracted_objects, // double[][][] extracted_objects, // may be null - will not be generated null, // extract_display, // double[][][] extract_display, // may be null - will not be displayed dmulti, // double[][] dmulti, width, // int width, remove_dc, // boolean remove_dc, null, // imps_extracted, //ImagePlus[] imps_extracted, // may be null - will not be displayed 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? setAbsoluteContrasts( abs_force_round, // boolean abs_force_round, abs_mode, // int mode, // 0 - keep, 1 keep type and if half +/-1, 2 - keep type, 3 - any abs_invert, // boolean neg_better, // more negative is difference - stronger result abs_outliers_frac, // double outliers_frac, abs_obscure_warm, // boolean abs_obscure_warm, // = true; // obscured can only be by warmer objects abs_obscure_frac, // double abs_obscure_frac, // 0.25; // obscured threshold between center and outer gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, extracted_objects[scene_num], //double[][] extracted_objects, // for this scene icorr_patterns[scene_num], // int [][] ipatterns, extracted_masked[scene_num], // double [][] masked_src, // if not null, will return masked extracted objects 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; /// best_patt_indx[mn] = best_patt; double [] center_xy = match.getXY(); extr_corr[scene_num][mn] = ObjectLocation.extractObjectImage( center_xy, // double [] center_xy, Loading Loading @@ -2426,11 +2462,12 @@ public class OrthoMapsCollection implements Serializable{ frac_max, // double frac_max) filt_other_rad, // double other_radius, debugLevel); // final int debugLevel) { /* // extract main scene data, needed for the absolute contrast calculations, no display showExtractedImages( // scene_num == 0 here null, // ortho_maps[indices[scene_num]].getName()+"-prefilter", // String prefix scene_num, //int scene_num, sort_pattern_index, // int sort_pattern_index, //sort_pattern_index, // int sort_pattern_index, extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, Loading @@ -2442,12 +2479,17 @@ public class OrthoMapsCollection implements Serializable{ remove_dc, // boolean remove_dc, null, // imps_extracted, //ImagePlus[] imps_extracted, // may be null - will not be displayed 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 setAbsoluteContrasts( abs_force_round, // boolean abs_force_round, abs_mode, // int mode, // 0 - keep, 1 keep type and if half +/-1, 2 - keep type, 3 - any abs_invert, // boolean neg_better, // more negative is difference - stronger result abs_outliers_frac, // double outliers_frac, abs_obscure_warm, // boolean abs_obscure_warm, // = true; // obscured can only be by warmer objects abs_obscure_frac, // double abs_obscure_frac, // 0.25; // obscured threshold between center and outer gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, Loading @@ -2455,7 +2497,7 @@ public class OrthoMapsCollection implements Serializable{ icorr_patterns[scene_num], // int [][] ipatterns, extracted_masked[scene_num], // double [][] masked_src, // if not null, will return masked extracted objects debugLevel); // int debugLevel */ double [] scene_xy_offset = {centers[last_scene][0]-centers[0][0],centers[last_scene][1]-centers[0][1]}; boolean [] removed = null; double scene_agl = gops[last_scene].getAGL(); // second scene agl Loading @@ -2464,7 +2506,7 @@ public class OrthoMapsCollection implements Serializable{ showExtractedImages( ortho_maps[indices[scene_num]].getName()+"-prefilter1", // String prefix sn, //int scene_num, sort_pattern_index, // int sort_pattern_index, //sort_pattern_index, // int sort_pattern_index, extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, Loading Loading @@ -2553,7 +2595,7 @@ public class OrthoMapsCollection implements Serializable{ showExtractedImages( ortho_maps[indices[scene_num]].getName()+"-postfilter1", // String prefix sn, //int scene_num, sort_pattern_index, // int sort_pattern_index, //sort_pattern_index, // int sort_pattern_index, extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, Loading Loading @@ -2609,10 +2651,14 @@ public class OrthoMapsCollection implements Serializable{ ItemMatch match = matches_list.get(indx); double [] match_values = match.getMatchValues(gops[scene_num]); int [] icenter_xy = match.getIntXY(); // double best_val = match.getMatchValue(gops[scene_num],sort_pattern_index); // so it is updated after best subpattern could be changed in setAbsoluteContrasts() double best_val = match.getMatchValue(gops[scene_num]); //match_values[match.getPatternMatch(gops[scene_num]).getBestSub() -1]; System.out.print(String.format("%4d: %4d/%4d %8.5f %8.3f %3d", i, icenter_xy[0],icenter_xy[1], match.getMatchValue(gops[scene_num],sort_pattern_index), best_val, match.getAbsoluteContrast(), match.getPatternMatch(gops[scene_num]).getBestSub())); // 1-based for (int j = 0; j < match_values.length; j++) if (j != sort_pattern_index) { Loading Loading @@ -2748,7 +2794,7 @@ public class OrthoMapsCollection implements Serializable{ showExtractedImages( ortho_maps[indices[scene_num]].getName()+"-final", // String prefix sn, //int scene_num, sort_pattern_index, // int sort_pattern_index, //sort_pattern_index, // int sort_pattern_index, extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, Loading Loading @@ -3121,7 +3167,9 @@ public class OrthoMapsCollection implements Serializable{ int num_scenes = filt_main_other.length; boolean [] remove = new boolean [match_sort.size()]; int num_removed = 0; double [] min_corrs_other = {min_corrs[1]*min_corr_full_rel, min_corrs[1]}; double [][] min_corrs_other = { {min_corrs[0]*min_corr_full_rel, min_corrs[0]}, {min_corrs[1]*min_corr_full_rel, min_corrs[1]}}; double offset_from_height = elevationToParallelOffset( // will be negative filt_height, // double height, scene_xy_offset, // double [] scene_offset, Loading Loading @@ -3171,14 +3219,19 @@ public class OrthoMapsCollection implements Serializable{ double [] scnd_xy_proj = projectOffsetOnVector( second_xy_offsets, // double [] object_offset, scene_xy_offset); // double [] scene_offset) if (scene_num > 0) { // other scene // {best_d, eff_rad, elong, dist, cent_offs[0], cent_offs[1]} ; if (corr_val < min_corrs_other[hf]) { if (corr_val < min_corrs_other[scene_num][hf]) { System.out.println(name+": filtered out by "+NAME_MO[scene_num]+" correlation maximum value for "+ NAME_FH[hf]+ " = "+corr_val+" < "+ min_corrs_other[hf]); NAME_FH[hf]+ " = "+corr_val+" < "+ min_corrs_other[scene_num][hf]); if (scene_num == 0) { System.out.println( "For the main scene it could happen if the sub_pattern index changed after\n"+ "the absolute contrast evaluation."); } if (!remove[mn]) num_removed++; remove[mn] = true; } if (scene_num > 0) { // other scene // {best_d, eff_rad, elong, dist, cent_offs[0], cent_offs[1]} ; if (dist > filt_dist[0]) { System.out.println(name+": filtered out by "+NAME_MO[scene_num]+" distance from the main scene peak "+ NAME_FH[hf]+ " = "+dist+" > "+filt_dist[0]); Loading Loading @@ -3266,13 +3319,20 @@ public class OrthoMapsCollection implements Serializable{ * the ring around it (zone2). * On return the matches_list data is modified - absolute contrasts are set, and best_pattern may be * updated. * @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. * 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. Loading @@ -3286,9 +3346,12 @@ public class OrthoMapsCollection implements Serializable{ * @return true */ public static boolean setAbsoluteContrasts( 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, Loading @@ -3307,6 +3370,9 @@ public class OrthoMapsCollection implements Serializable{ 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 (force_round) { mode = 4; } switch (mode) { case 0: try_sub[best_patt] = true; break; // only one case 1: // keep type and +-1 for direction Loading Loading @@ -3338,27 +3404,35 @@ public class OrthoMapsCollection implements Serializable{ try_sub[i] = true; } break; case 4: try_sub[0] = true; break; // only full pattern } double [] dir_contrasts = new double [try_sub.length]; double [][] dir_contrasts = new double [try_sub.length][]; int best_index = -1; for (int i = 0; i < try_sub.length; i++) if (try_sub[i]) { dir_contrasts[i]= OrthoMap.getAbsoluteContrast( extracted_objects[mn], // double [] data, ipatterns[i], // int [] ipattern, outliers_frac, // double outliers_frac, obscure_frac, // double obscure_frac, // 0.25; // obscured threshold between center and outer null, debug); // boolean debug) if (neg_better) { dir_contrasts[i] *= -1; dir_contrasts[i][0] *= -1; } if (dir_contrasts[i] > 0) { if ((best_index < 0) || (dir_contrasts[i] > dir_contrasts[best_index])) { boolean bad_obscurant = obscure_warm && (dir_contrasts[i][1] < 0); if (!bad_obscurant && (dir_contrasts[i][0] > 0)) { if ((best_index < 0) || (dir_contrasts[i][0] > dir_contrasts[best_index][0])) { best_index = i; } } } if (best_index < 0) { best_index = best_patt; 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) { Loading @@ -3367,9 +3441,11 @@ public class OrthoMapsCollection implements Serializable{ } match.getPatternMatch(gop).setBestSub(best_index + 1); } match.setAbsoluteContrast(dir_contrasts[best_index]); // the higher the better 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]); System.out.println("setAbsoluteContrasts(): Set absolute contrast for match #"+mn+" = "+ dir_contrasts[best_index][0]+ ", over threshold="+dir_contrasts[best_index][1]+"."); } if (masked_src != null) { masked_src[mn] = new double [extracted_objects[mn].length]; Loading @@ -3377,6 +3453,7 @@ public class OrthoMapsCollection implements Serializable{ extracted_objects[mn], // double [] data, ipatterns[best_index], // int [] ipattern, outliers_frac, // double outliers_frac, obscure_frac, // double obscure_frac, // 0.25; // obscured threshold between center and outer masked_src[mn], debug); // boolean debug) } Loading @@ -3390,7 +3467,7 @@ public class OrthoMapsCollection implements Serializable{ public static void showExtractedImages( String prefix, int scene_num, int sort_pattern_index, // int sort_pattern_index, int extr_size, GroundObjectPattern gop, ArrayList <ItemMatch> matches_list, Loading Loading @@ -3452,7 +3529,7 @@ public class OrthoMapsCollection implements Serializable{ ItemMatch match = matches_list.get(indx); int [] ixy = match.getIntXY(); extr_titles[mn] = ixy[0]+"/"+ ixy[1]+":"+String.format("%7.5f", match.getMatchValue(gop,sort_pattern_index)); match.getMatchValue(gop)); // ,sort_pattern_index)); } imps_extracted[scene_num] = ShowDoubleFloatArrays.makeArrays( extract_display[scene_num], Loading Loading
src/main/java/com/elphel/imagej/orthomosaic/ItemMatch.java +18 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,24 @@ public class ItemMatch { return match.getMatch(indx); } /** * Return correlation value as the one of the pattern correlations pointed * by the best subpattern index. * @param groundObjectPattern * @return selected correlation value */ public double getMatchValue(GroundObjectPattern groundObjectPattern) { return getMatchValue(groundObjectPattern.getPatternPath()); } public double getMatchValue(String pattern_path) { int indx = getPatternMatch(pattern_path).getBestSub(); // -1; return getMatchValue(pattern_path,indx); } public double getMatchValue(GroundObjectPattern groundObjectPattern, int indx) { return getMatchValue(groundObjectPattern.getPatternPath(), indx); } Loading
src/main/java/com/elphel/imagej/orthomosaic/OrthoMap.java +48 −12 Original line number Diff line number Diff line Loading @@ -2073,18 +2073,28 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ * @param ipattern square pattern array corresponding to data. 1 means * average (w/o outliers) and add, 2 - average and subtract * @param outliers_frac - fraction of outliers to remove while averaging * @param obscure_frac fraction between center average and peripheral ring to compare when obscure_warm * is set. * @param masked_data null or double[data.length] - will return a copy of data * with NaN for all unused for averaging * @param debug show debug images * @return difference between average in-pattern (1) and outside (2) average * values * @return a pair of : difference between average in-pattern (1) and outside (2) average * values, and difference between third zone and a threshold between averages of zone1 * and zone2. For full patterns (no zone3) this value is NaN. Positive values are good, * regardless the cold or warm center. For cold patterns (morning) positive means * that zone 3 minimum is warmer than fraction point between zone1 and zone2 (cold * object obscured by something warmer, for hot - that obscurant is colder. * */ public static double getAbsoluteContrast( public static double [] getAbsoluteContrast( double [] data, int [] ipattern, 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 double [] masked_data, // null or double [data.length] to return masked data boolean debug) { int num_segments = 3; if (debug) { int size = (int) Math.sqrt(data.length); ShowDoubleFloatArrays.showArrays( Loading @@ -2095,11 +2105,14 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ } ArrayList<ArrayList<Integer>> lists = new ArrayList<ArrayList<Integer>>(); for (int i = 0; i < 2; i++) { for (int i = 0; i < num_segments; i++) { lists.add(new ArrayList<Integer>()); } double [] swd = new double [2],sw = new double [2], avg= new double[2], fracw=new double[2]; for (int n = 0; n < swd.length; n++) { double [] swd = new double [num_segments], sw = new double [num_segments], avg= new double[num_segments], fracw=new double[num_segments]; for (int n = 0; n < num_segments; n++) { // swd.length; n++) { ArrayList<Integer> list = lists.get(n); int n1 = n+1; for (int i = 0; i < ipattern.length; i++) if (ipattern[i] == n1){ Loading @@ -2110,9 +2123,23 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ avg[n] = swd[n]/sw[n]; fracw[n] = (1.0-outliers_frac) * sw[n]; } double [] zone3_min_max = {Double.NaN, Double.NaN}; int zone3=3; for (int i:lists.get(zone3-1)) { if (Double.isNaN(zone3_min_max[0]) || (data[i] < zone3_min_max[0])) { zone3_min_max[0] = data[i]; } if (Double.isNaN(zone3_min_max[1]) || (data[i] > zone3_min_max[1])) { zone3_min_max[1] = data[i]; } } if (debug) { System.out.println("getAbsoluteContrast() before outliers: avg[0] ="+avg[0]+", avg[1]="+avg[1]+", avg[0]-avg[1]="+(avg[0]-avg[1])); } if (outliers_frac > 0) { for (int n = 0; n < swd.length; n++) { ArrayList<Integer> list = lists.get(n); Loading @@ -2124,7 +2151,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ return (rhsd > lhsd) ? -1 : (rhsd < lhsd) ? 1 : 0; } }); while (sw[n] >= fracw[n]) { while (!list.isEmpty() && (sw[n] >= fracw[n])) { int indx_first = list.get(0); int indx_last = list.get(list.size()-1); boolean remove_first = Math.abs(data[indx_first] - avg[n]) > Math.abs(data[indx_last] - avg[n]); Loading @@ -2137,6 +2164,11 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ } } } double threshold = obscure_frac * avg[0] + (1.0 - obscure_frac) * avg[1]; double over_thresh = (avg[0] < avg[1]) ? (zone3_min_max[0] - threshold) : (threshold - zone3_min_max[1]); if (masked_data != null) { Arrays.fill(masked_data, Double.NaN); for (int n = 0; n < lists.size(); n++) { Loading Loading @@ -2175,14 +2207,15 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ if (debug) { System.out.println("getAbsoluteContrast() after outliers: avg[0] ="+avg[0]+", avg[1]="+avg[1]+", avg[0]-avg[1]="+(avg[0]-avg[1])); } return avg[0]-avg[1]; return new double [] {avg[0]-avg[1], over_thresh}; } /** * From existing pattern (only for simple dark main scene) * create two zones to calculate average (w/o outliers) inside * the pattern and around it. Mark inner with 1, outer - 2, keep * other 0. Then later find 2 averages (removing outliers) and their * the pattern and around it. Mark inner with 1, outer - 2, * 3 - potential obscurant for partial patterns * keep other 0. Then later find 2 averages (removing outliers) and their * difference - absolute contrast. * @param patterns set of square patterns, first [0] for full pattern, * others - for half-patterns cut in different directions. Loading Loading @@ -2287,6 +2320,8 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ ( fys)*( fxs)*npatterns[n][sindx+size+1]; if (sdn/sd0 > 0.5) { ipatterns[n][indx] = ipatterns[0][indx]; // =2 } else { ipatterns[n][indx] = 3; // =2 } } } Loading @@ -2305,6 +2340,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ switch (ipatterns[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; } } } Loading Loading @@ -4297,7 +4333,7 @@ public class OrthoMap implements Comparable <OrthoMap>, Serializable{ final int dbg_pix3 = 1439211; // 2827822; final int dbg_x = 1445; final int dbg_y = 2338; final int dbg_tolerance = 10; final int dbg_tolerance = -10; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { Loading
src/main/java/com/elphel/imagej/orthomosaic/OrthoMapsCollection.java +108 −31 Original line number Diff line number Diff line Loading @@ -1816,12 +1816,13 @@ public class OrthoMapsCollection implements Serializable{ phaseCoeff[0] = 0.9; // adv_radius = 20; // pix? } else { // 100m defaults min_corrs[0] = 0.009; min_corrs[0] = 0.0119; abs_edge_frac = 0.35; abs_oversize = 2.2; filt_abs_contrast = 60; filt_abs_easepart = 0.85; phaseCoeff[0] = 0.9; abs_force_round = true; // adv_radius = 20; // pix? } } Loading Loading @@ -2359,7 +2360,7 @@ public class OrthoMapsCollection implements Serializable{ System.out.print(String.format("%4d: %4d/%4d %8.5f %3d", i, icenter_xy[0],icenter_xy[1], match.getMatchValue(gops[scene_num],sort_pattern_index), match.getMatchValue(gops[scene_num]), // ,sort_pattern_index), match.getPatternMatch(gops[scene_num]).getBestSub())); // 1-based for (int j = 0; j < match_values.length; j++) if (j != sort_pattern_index) { System.out.print(String.format(" %8.5f",match_values[j])); Loading @@ -2384,12 +2385,47 @@ public class OrthoMapsCollection implements Serializable{ extr_corr[scene_num] = new double [match_sort.size()][]; // extracted_objects[scene_num].length][]; extracted_masked[scene_num]= new double [match_sort.size()][]; // extr_corr_half[scene_num] = new double [match_sort.size()][]; // extracted_objects[scene_num].length][]; /// best_patt_indx = new int [match_sort.size()]; // Extract main scene areas around candidates // extract main scene data, needed for the absolute contrast calculations, no display showExtractedImages( // scene_num == 0 here null, // ortho_maps[indices[scene_num]].getName()+"-prefilter", // String prefix scene_num, //int scene_num, //sort_pattern_index, // int sort_pattern_index, extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, extracted_objects, // double[][][] extracted_objects, // may be null - will not be generated null, // extract_display, // double[][][] extract_display, // may be null - will not be displayed dmulti, // double[][] dmulti, width, // int width, remove_dc, // boolean remove_dc, null, // imps_extracted, //ImagePlus[] imps_extracted, // may be null - will not be displayed 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? setAbsoluteContrasts( abs_force_round, // boolean abs_force_round, abs_mode, // int mode, // 0 - keep, 1 keep type and if half +/-1, 2 - keep type, 3 - any abs_invert, // boolean neg_better, // more negative is difference - stronger result abs_outliers_frac, // double outliers_frac, abs_obscure_warm, // boolean abs_obscure_warm, // = true; // obscured can only be by warmer objects abs_obscure_frac, // double abs_obscure_frac, // 0.25; // obscured threshold between center and outer gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, extracted_objects[scene_num], //double[][] extracted_objects, // for this scene icorr_patterns[scene_num], // int [][] ipatterns, extracted_masked[scene_num], // double [][] masked_src, // if not null, will return masked extracted objects 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; /// best_patt_indx[mn] = best_patt; double [] center_xy = match.getXY(); extr_corr[scene_num][mn] = ObjectLocation.extractObjectImage( center_xy, // double [] center_xy, Loading Loading @@ -2426,11 +2462,12 @@ public class OrthoMapsCollection implements Serializable{ frac_max, // double frac_max) filt_other_rad, // double other_radius, debugLevel); // final int debugLevel) { /* // extract main scene data, needed for the absolute contrast calculations, no display showExtractedImages( // scene_num == 0 here null, // ortho_maps[indices[scene_num]].getName()+"-prefilter", // String prefix scene_num, //int scene_num, sort_pattern_index, // int sort_pattern_index, //sort_pattern_index, // int sort_pattern_index, extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, Loading @@ -2442,12 +2479,17 @@ public class OrthoMapsCollection implements Serializable{ remove_dc, // boolean remove_dc, null, // imps_extracted, //ImagePlus[] imps_extracted, // may be null - will not be displayed 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 setAbsoluteContrasts( abs_force_round, // boolean abs_force_round, abs_mode, // int mode, // 0 - keep, 1 keep type and if half +/-1, 2 - keep type, 3 - any abs_invert, // boolean neg_better, // more negative is difference - stronger result abs_outliers_frac, // double outliers_frac, abs_obscure_warm, // boolean abs_obscure_warm, // = true; // obscured can only be by warmer objects abs_obscure_frac, // double abs_obscure_frac, // 0.25; // obscured threshold between center and outer gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, match_sort, // ArrayList<Integer> match_sort, Loading @@ -2455,7 +2497,7 @@ public class OrthoMapsCollection implements Serializable{ icorr_patterns[scene_num], // int [][] ipatterns, extracted_masked[scene_num], // double [][] masked_src, // if not null, will return masked extracted objects debugLevel); // int debugLevel */ double [] scene_xy_offset = {centers[last_scene][0]-centers[0][0],centers[last_scene][1]-centers[0][1]}; boolean [] removed = null; double scene_agl = gops[last_scene].getAGL(); // second scene agl Loading @@ -2464,7 +2506,7 @@ public class OrthoMapsCollection implements Serializable{ showExtractedImages( ortho_maps[indices[scene_num]].getName()+"-prefilter1", // String prefix sn, //int scene_num, sort_pattern_index, // int sort_pattern_index, //sort_pattern_index, // int sort_pattern_index, extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, Loading Loading @@ -2553,7 +2595,7 @@ public class OrthoMapsCollection implements Serializable{ showExtractedImages( ortho_maps[indices[scene_num]].getName()+"-postfilter1", // String prefix sn, //int scene_num, sort_pattern_index, // int sort_pattern_index, //sort_pattern_index, // int sort_pattern_index, extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, Loading Loading @@ -2609,10 +2651,14 @@ public class OrthoMapsCollection implements Serializable{ ItemMatch match = matches_list.get(indx); double [] match_values = match.getMatchValues(gops[scene_num]); int [] icenter_xy = match.getIntXY(); // double best_val = match.getMatchValue(gops[scene_num],sort_pattern_index); // so it is updated after best subpattern could be changed in setAbsoluteContrasts() double best_val = match.getMatchValue(gops[scene_num]); //match_values[match.getPatternMatch(gops[scene_num]).getBestSub() -1]; System.out.print(String.format("%4d: %4d/%4d %8.5f %8.3f %3d", i, icenter_xy[0],icenter_xy[1], match.getMatchValue(gops[scene_num],sort_pattern_index), best_val, match.getAbsoluteContrast(), match.getPatternMatch(gops[scene_num]).getBestSub())); // 1-based for (int j = 0; j < match_values.length; j++) if (j != sort_pattern_index) { Loading Loading @@ -2748,7 +2794,7 @@ public class OrthoMapsCollection implements Serializable{ showExtractedImages( ortho_maps[indices[scene_num]].getName()+"-final", // String prefix sn, //int scene_num, sort_pattern_index, // int sort_pattern_index, //sort_pattern_index, // int sort_pattern_index, extr_size, // int extr_size, gops[scene_num], // GroundObjectPattern gop, matches_list, // ArrayList <ItemMatch> matches_list, Loading Loading @@ -3121,7 +3167,9 @@ public class OrthoMapsCollection implements Serializable{ int num_scenes = filt_main_other.length; boolean [] remove = new boolean [match_sort.size()]; int num_removed = 0; double [] min_corrs_other = {min_corrs[1]*min_corr_full_rel, min_corrs[1]}; double [][] min_corrs_other = { {min_corrs[0]*min_corr_full_rel, min_corrs[0]}, {min_corrs[1]*min_corr_full_rel, min_corrs[1]}}; double offset_from_height = elevationToParallelOffset( // will be negative filt_height, // double height, scene_xy_offset, // double [] scene_offset, Loading Loading @@ -3171,14 +3219,19 @@ public class OrthoMapsCollection implements Serializable{ double [] scnd_xy_proj = projectOffsetOnVector( second_xy_offsets, // double [] object_offset, scene_xy_offset); // double [] scene_offset) if (scene_num > 0) { // other scene // {best_d, eff_rad, elong, dist, cent_offs[0], cent_offs[1]} ; if (corr_val < min_corrs_other[hf]) { if (corr_val < min_corrs_other[scene_num][hf]) { System.out.println(name+": filtered out by "+NAME_MO[scene_num]+" correlation maximum value for "+ NAME_FH[hf]+ " = "+corr_val+" < "+ min_corrs_other[hf]); NAME_FH[hf]+ " = "+corr_val+" < "+ min_corrs_other[scene_num][hf]); if (scene_num == 0) { System.out.println( "For the main scene it could happen if the sub_pattern index changed after\n"+ "the absolute contrast evaluation."); } if (!remove[mn]) num_removed++; remove[mn] = true; } if (scene_num > 0) { // other scene // {best_d, eff_rad, elong, dist, cent_offs[0], cent_offs[1]} ; if (dist > filt_dist[0]) { System.out.println(name+": filtered out by "+NAME_MO[scene_num]+" distance from the main scene peak "+ NAME_FH[hf]+ " = "+dist+" > "+filt_dist[0]); Loading Loading @@ -3266,13 +3319,20 @@ public class OrthoMapsCollection implements Serializable{ * the ring around it (zone2). * On return the matches_list data is modified - absolute contrasts are set, and best_pattern may be * updated. * @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. * 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. Loading @@ -3286,9 +3346,12 @@ public class OrthoMapsCollection implements Serializable{ * @return true */ public static boolean setAbsoluteContrasts( 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, Loading @@ -3307,6 +3370,9 @@ public class OrthoMapsCollection implements Serializable{ 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 (force_round) { mode = 4; } switch (mode) { case 0: try_sub[best_patt] = true; break; // only one case 1: // keep type and +-1 for direction Loading Loading @@ -3338,27 +3404,35 @@ public class OrthoMapsCollection implements Serializable{ try_sub[i] = true; } break; case 4: try_sub[0] = true; break; // only full pattern } double [] dir_contrasts = new double [try_sub.length]; double [][] dir_contrasts = new double [try_sub.length][]; int best_index = -1; for (int i = 0; i < try_sub.length; i++) if (try_sub[i]) { dir_contrasts[i]= OrthoMap.getAbsoluteContrast( extracted_objects[mn], // double [] data, ipatterns[i], // int [] ipattern, outliers_frac, // double outliers_frac, obscure_frac, // double obscure_frac, // 0.25; // obscured threshold between center and outer null, debug); // boolean debug) if (neg_better) { dir_contrasts[i] *= -1; dir_contrasts[i][0] *= -1; } if (dir_contrasts[i] > 0) { if ((best_index < 0) || (dir_contrasts[i] > dir_contrasts[best_index])) { boolean bad_obscurant = obscure_warm && (dir_contrasts[i][1] < 0); if (!bad_obscurant && (dir_contrasts[i][0] > 0)) { if ((best_index < 0) || (dir_contrasts[i][0] > dir_contrasts[best_index][0])) { best_index = i; } } } if (best_index < 0) { best_index = best_patt; 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) { Loading @@ -3367,9 +3441,11 @@ public class OrthoMapsCollection implements Serializable{ } match.getPatternMatch(gop).setBestSub(best_index + 1); } match.setAbsoluteContrast(dir_contrasts[best_index]); // the higher the better 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]); System.out.println("setAbsoluteContrasts(): Set absolute contrast for match #"+mn+" = "+ dir_contrasts[best_index][0]+ ", over threshold="+dir_contrasts[best_index][1]+"."); } if (masked_src != null) { masked_src[mn] = new double [extracted_objects[mn].length]; Loading @@ -3377,6 +3453,7 @@ public class OrthoMapsCollection implements Serializable{ extracted_objects[mn], // double [] data, ipatterns[best_index], // int [] ipattern, outliers_frac, // double outliers_frac, obscure_frac, // double obscure_frac, // 0.25; // obscured threshold between center and outer masked_src[mn], debug); // boolean debug) } Loading @@ -3390,7 +3467,7 @@ public class OrthoMapsCollection implements Serializable{ public static void showExtractedImages( String prefix, int scene_num, int sort_pattern_index, // int sort_pattern_index, int extr_size, GroundObjectPattern gop, ArrayList <ItemMatch> matches_list, Loading Loading @@ -3452,7 +3529,7 @@ public class OrthoMapsCollection implements Serializable{ ItemMatch match = matches_list.get(indx); int [] ixy = match.getIntXY(); extr_titles[mn] = ixy[0]+"/"+ ixy[1]+":"+String.format("%7.5f", match.getMatchValue(gop,sort_pattern_index)); match.getMatchValue(gop)); // ,sort_pattern_index)); } imps_extracted[scene_num] = ShowDoubleFloatArrays.makeArrays( extract_display[scene_num], Loading