Loading src/main/java/com/elphel/imagej/vegetation/VegetationLMA.java +350 −74 Original line number Original line Diff line number Diff line Loading @@ -6,6 +6,7 @@ import java.io.FileFilter; import java.util.Arrays; import java.util.Arrays; import java.util.Collections; import java.util.Collections; import java.util.Comparator; import java.util.Comparator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger; import com.elphel.imagej.common.DoubleGaussianBlur; import com.elphel.imagej.common.DoubleGaussianBlur; Loading Loading @@ -99,6 +100,7 @@ public class VegetationLMA { // private int num_used_scenes; // -> num_par_scenes // private int num_used_scenes; // -> num_par_scenes private boolean [] used_scenes; // encode unused as NaN private boolean [] used_scenes; // encode unused as NaN private boolean [][] valid_woi = null; // transitional parameters - [0] - valid terrain and y_vector (per woi), [1] - valid vegetation, [2] - terrain has vegetation // private int [] used_scenes_indices; // private int [] used_scenes_indices; public final double [] scene_weights; public final double [] scene_weights; Loading Loading @@ -466,20 +468,46 @@ public class VegetationLMA { int min_scenes_used = min_scenes * 4; int min_scenes_used = min_scenes * 4; boolean all_terrain = true; // use all terrain tiles even not used with vegetation boolean all_terrain = true; // use all terrain tiles even not used with vegetation boolean [][] valid_woi; int [] valid_stats = new int[4]; int max_sacrifice = 5; boolean [][] valid_scenes_pixels = getValidScenesPixels ( this.woi, // final Rectangle woi, this.full, // final Rectangle full, this.terrain_rendered, // final double [][] terrain_rendered, // terrain rendered for scenes (has nulls) this.vegetation_offsets, // final double [][][] vegetation_offsets // [num_scenes][pixle]{dx,dy} differential offsets of vegetation to terrain grid max_sacrifice, // final int max_sacrifice, // may sacrifice first/last scenes to have the same valid_offset valid_stats); // final int [] stats // number of valid scenes, valid pixels if int[2] if (debugLevel > -2) { System.out.println("Got "+valid_stats[0]+" valid scenes (of "+this.terrain_rendered.length+ "), "+valid_stats[1]+" valid WOI pixels (of "+(this.woi.width*this.woi.height)+ "), of them "+valid_stats[3]+" have vegetation influence, valid vegetation pixels "+ valid_stats[2] + " (of "+(this.woi.width*this.woi.height)+")."); } if (valid_scenes_pixels == null) { System.out.println("Too few valid scenes/pixels."); return -1; } this.used_scenes = valid_scenes_pixels[0]; boolean [][] valid_woi = filterValidWoi ( if (clean_y) { valid_woi = new boolean [][] {valid_scenes_pixels[1],valid_scenes_pixels[2],valid_scenes_pixels[3]}; } else { // will not work correctly boolean [][] valid_terr_veg = filterValidWoi ( min_scenes_uses, // int min_scenes_uses, min_scenes_uses, // int min_scenes_uses, min_scenes_used, // int min_scenes_used, min_scenes_used, // int min_scenes_used, min_valid_pixels, // int min_valid_pixels, min_valid_pixels, // int min_valid_pixels, debugLevel > 3); // boolean debug_img){ // 4x? debugLevel > 3); // boolean debug_img){ // 4x? if (valid_woi == null) { if (valid_terr_veg == null) { System.out.println("Too few valid pixels (< min_valid_pixels="+min_valid_pixels+")."); System.out.println("Too few valid pixels (< min_valid_pixels="+min_valid_pixels+")."); return -1; return -1; } } valid_woi = new boolean [][] {valid_terr_veg[0],valid_scenes_pixels[1],null}; // incorrect, should always be clean_y } this.valid_woi = valid_woi; // now [3]: valid terrain/y, valid vegetation, terrain has vegetation setupParametersIndices( setupParametersIndices( valid_woi, // needs to know number of used scenes // all but scenes valid_woi, // needs to know number of used scenes // all but scenes all_terrain); // boolean all_terrain !clean_y && all_terrain); // boolean all_terrain disable all_terrain when clean_y alpha_neibs = getNeighbors( alpha_neibs = getNeighbors( TVAO_ALPHA, // final int tvao, // TVAO_VEGETATION_ALPHA TVAO_ALPHA, // final int tvao, // TVAO_VEGETATION_ALPHA Loading Loading @@ -3254,7 +3282,7 @@ public class VegetationLMA { for (int ithread = 0; ithread < threads.length; ithread++) { for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { threads[ithread] = new Thread() { public void run() { public void run() { for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) { for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) if (!clean_y || (used_scenes[nScene])){ for (int drow = 0; drow < woi.height; drow++) { for (int drow = 0; drow < woi.height; drow++) { int row = woi.y + drow; int row = woi.y + drow; for (int dcol = 0; dcol < woi.width; dcol++) { for (int dcol = 0; dcol < woi.width; dcol++) { Loading @@ -3263,7 +3291,9 @@ public class VegetationLMA { int indx = row*full.width + col; int indx = row*full.width + col; // now par_index[TVAO_TERRAIN][indx] may be -1 // now par_index[TVAO_TERRAIN][indx] may be -1 // if (!Double.isNaN(terrain_rendered[nScene][indx]) && (par_index[TVAO_TERRAIN][indx] >= 0)) { // // if (!Double.isNaN(terrain_rendered[nScene][indx]) && (par_index[TVAO_TERRAIN][indx] >= 0)) { // if (!Double.isNaN(terrain_rendered[nScene][indx])) { // boolean good_pixel = clean_y ? valid_woi[0][windx] : (!Double.isNaN(terrain_rendered[nScene][indx])); // if (!Double.isNaN(terrain_rendered[nScene][indx])) { // if (good_pixel) { // boolean has_data = false; boolean has_data = false; int [][] data_record = new int[DATA_SOURCE_ITEMS][]; int [][] data_record = new int[DATA_SOURCE_ITEMS][]; data_record[DATA_SOURCE_HEAD] = new int [DATA_SOURCE_HEAD_SIZE]; data_record[DATA_SOURCE_HEAD] = new int [DATA_SOURCE_HEAD_SIZE]; Loading @@ -3278,7 +3308,7 @@ public class VegetationLMA { data_record[DATA_SOURCE_HEAD][DATA_SOURCE_HEAD_SINDEX] = nScene; data_record[DATA_SOURCE_HEAD][DATA_SOURCE_HEAD_SINDEX] = nScene; // try building record, if no data - do not add it // try building record, if no data - do not add it double [] veg_xy = vegetation_offsets[nScene][indx]; double [] veg_xy = vegetation_offsets[nScene][indx]; if (veg_xy != null) { if ((veg_xy != null) && (!clean_y || valid_woi[2][windx])) { veg_xy = veg_xy.clone(); veg_xy = veg_xy.clone(); if (diff_offsets) { if (diff_offsets) { veg_xy[0] += col; veg_xy[0] += col; Loading Loading @@ -3328,23 +3358,28 @@ public class VegetationLMA { }; }; } } ImageDtt.startAndJoin(threads); ImageDtt.startAndJoin(threads); // should be no condensing in clean_y mode as it was pre-filtered // now condense removing nulls in data_src (cornw may still have nulls) // now condense removing nulls in data_src (cornw may still have nulls) int max_samples = 0; int max_samples = 0; for (int nscene = 0; nscene < num_scenes; nscene++) { for (int nscene = 0; nscene < num_scenes; nscene++) { max_samples = Math.max(max_samples, scene_samples[nscene]); max_samples = Math.max(max_samples, scene_samples[nscene]); } } System.out.println("Maximal samples in a scene = "+max_samples); System.out.println("Maximal samples in a scene = "+max_samples); int allowed_less = 0; int allowed_less = 0; int need_samples = max_samples-allowed_less; int need_samples = max_samples-allowed_less; int num_samples = 0; int num_samples = 0; // int dbg_early_sample = 0; // 10; // int dbg_early_sample = 0; // 10; if (!clean_y) { used_scenes = new boolean [num_scenes]; used_scenes = new boolean [num_scenes]; } final int [] used_scene_indices = new int [num_scenes]; final int [] used_scene_indices = new int [num_scenes]; int num_used_scenes = 0; int num_used_scenes = 0; for (int nscene = 0; nscene < num_scenes; nscene++) { for (int nscene = 0; nscene < num_scenes; nscene++) { int num_prev = num_samples; int num_prev = num_samples; int defined = scene_samples[nscene]; int defined = scene_samples[nscene]; boolean enough = defined >= need_samples; boolean enough = clean_y ? used_scenes[nscene] : (defined >= need_samples); if (!enough) { if (!enough) { System.out.println ("Not enough samples in scene "+nscene+" ("+defined+" < "+need_samples+"), removing it."); System.out.println ("Not enough samples in scene "+nscene+" ("+defined+" < "+need_samples+"), removing it."); } } Loading @@ -3354,7 +3389,7 @@ public class VegetationLMA { used_scenes[nscene] = true; used_scenes[nscene] = true; num_samples+= scene_samples[nscene]; num_samples+= scene_samples[nscene]; scene_samples[nscene] = num_prev; // start index scene_samples[nscene] = num_prev; // start index if (keepScene(nscene)) { if (clean_y || keepScene(nscene)) { num_used_scenes++; num_used_scenes++; } else { } else { used_scene_indices[nscene] = -1; used_scene_indices[nscene] = -1; Loading Loading @@ -3688,10 +3723,13 @@ public class VegetationLMA { int windx =dcol + drow * woi.width; int windx =dcol + drow * woi.width; if (valid_woi[1][windx]) { if (valid_woi[1][windx]) { int indx = row*full.width + col; int indx = row*full.width + col; if (!Double.isNaN(vegetation_average[indx])) { // if (!Double.isNaN(vegetation_average[indx])) { // should always be non-NaN if (!Double.isNaN(tvao[TVAO_VEGETATION][indx])) { // should always be non-NaN par_rindex[par_num][0] = TVAO_VEGETATION; par_rindex[par_num][0] = TVAO_VEGETATION; par_rindex[par_num][1] = indx; par_rindex[par_num][1] = indx; par_index[TVAO_VEGETATION][indx] = par_num++; par_index[TVAO_VEGETATION][indx] = par_num++; } else if (clean_y){ System.out.println("Double.isNaN(vegetation_average["+indx+"] 1 - should not happen"); } } } } } } Loading @@ -3707,10 +3745,12 @@ public class VegetationLMA { int windx =dcol + drow * woi.width; int windx =dcol + drow * woi.width; if (valid_woi[1][windx]) { if (valid_woi[1][windx]) { int indx = row*full.width + col; int indx = row*full.width + col; if (!Double.isNaN(vegetation_average[indx])) { if (!Double.isNaN(tvao[TVAO_ALPHA][indx])) { // vegetation_average[indx])) { par_rindex[par_num][0] = TVAO_ALPHA; par_rindex[par_num][0] = TVAO_ALPHA; par_rindex[par_num][1] = indx; par_rindex[par_num][1] = indx; par_index[TVAO_ALPHA][indx] = par_num++; par_index[TVAO_ALPHA][indx] = par_num++; } else if (clean_y){ System.out.println("Double.isNaN(vegetation_average["+indx+"] 2 - should not happen"); } } } } } } Loading @@ -3726,10 +3766,12 @@ public class VegetationLMA { int windx =dcol + drow * woi.width; int windx =dcol + drow * woi.width; if (valid_woi[1][windx]) { if (valid_woi[1][windx]) { int indx = row*full.width + col; int indx = row*full.width + col; if (!Double.isNaN(vegetation_average[indx])) { if (!Double.isNaN(tvao[TVAO_ELEVATION][indx])) { //vegetation_average[indx])) { par_rindex[par_num][0] = TVAO_ELEVATION; par_rindex[par_num][0] = TVAO_ELEVATION; par_rindex[par_num][1] = indx; par_rindex[par_num][1] = indx; par_index[TVAO_ELEVATION][indx] = par_num++; par_index[TVAO_ELEVATION][indx] = par_num++; } else if (clean_y){ System.out.println("Double.isNaN(vegetation_average["+indx+"] 3 - should not happen"); } } } } } } Loading @@ -3753,7 +3795,7 @@ public class VegetationLMA { int par_num = ind_pars_scenes; int par_num = ind_pars_scenes; int [][] par_rindex = this.par_rindex; int [][] par_rindex = this.par_rindex; for (int i = 0; i < par_index[TVAO_SCENE_OFFSET].length; i++) { for (int i = 0; i < par_index[TVAO_SCENE_OFFSET].length; i++) { if (used_scenes[i] && keepScene(i)) { if (used_scenes[i] && (clean_y || keepScene(i))) { par_rindex[par_num][0] = TVAO_SCENE_OFFSET; par_rindex[par_num][0] = TVAO_SCENE_OFFSET; par_rindex[par_num][1] = i; par_rindex[par_num][1] = i; par_index[TVAO_SCENE_OFFSET][i] = par_num++; par_index[TVAO_SCENE_OFFSET][i] = par_num++; Loading Loading @@ -3913,108 +3955,342 @@ public class VegetationLMA { /** /** * Get valid scenes and woi pixels. Valid pixel is inside woi and does not depend on any * Get valid scenes and woi pixels. Valid pixel is inside woi and does not depend on any * vegetation data outside of woi for all valid scenes. Valid scene, in turn, has all image * vegetation data outside of woi for all valid scenes. Valid scene, in turn, has all image * data for all of the valid pixels * data for all of the valid pixels. * * The same pixel may have vegetation offset in some scenes, but not in the others (on the edge), * This causes problems in later LMA fitting, so all the remaining valid_pixels should either * have or do not have such links. This requires additional boolean array for terrain/y_vector * pixels that have vegetation offset data . * * Assuming vegetation image is defined everywhere where vegetation_offsets may point. * @param woi WOI to be fully covered by the rendered data, usually this.woi * @param woi WOI to be fully covered by the rendered data, usually this.woi * @param full full image WOI (0,0,640,512); * @param full full image WOI (0,0,640,512); * @param terrain_rendered per-scene rendered images (for terrain elevation) * @param terrain_rendered per-scene rendered images (for terrain elevation) * @param vegetation_offsets per-scene, per terrain pixel - x,y offsets to the corresponding vegetation pixel * @param vegetation_offsets per-scene, per terrain pixel - x,y offsets to the corresponding vegetation pixel * @return 2d array - {valid scenes, valid pixels} * @return 2d array - {valid scenes, valid pixels, valid vegetation pixels, valid offset (vegetation influence)} */ */ public static boolean [][] getValidScenesPixels ( public static boolean [][] getValidScenesPixels ( final Rectangle woi, final Rectangle woi, final Rectangle full, final Rectangle full, final double [][] terrain_rendered, // terrain rendered for scenes (has nulls) final double [][] terrain_rendered, // terrain rendered for scenes (has nulls) final double [][][] vegetation_offsets // [num_scenes][pixle]{dx,dy} differential offsets of vegetation to terrain grid final double [][][] vegetation_offsets, // [num_scenes][pixle]{dx,dy} differential offsets of vegetation to terrain grid final int max_sacrifice, // may sacrifice first/last scenes to have the same valid_offset final int [] stats // number of valid scenes, valid pixels if int[4] ) { ) { final int num_scenes = terrain_rendered.length; final int num_scenes = terrain_rendered.length; final int woi_length = woi.width*woi.height; final boolean [] valid_scenes = new boolean [num_scenes]; final boolean [] valid_scenes = new boolean [num_scenes]; final boolean [] valid_pixels = new boolean [woi.width*woi.height]; // final boolean [] valid_pixels = new boolean [woi_length]; // final boolean [] valid_vegetation = new boolean [woi_length]; final boolean [][] valid = new boolean [num_scenes][woi_length]; // The same pixel may have vegetation offset in some scenes, but not in the others (on the edge), // This causes problems in later LMA fitting, so all the remaining valid_pixels final int [] num_scenes_used = new int [woi_length]; final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX); final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX); final AtomicInteger ai = new AtomicInteger(0); final AtomicInteger ai = new AtomicInteger(0); // first find pixels that are valid in at least one scene final AtomicInteger amax_scenes = new AtomicInteger(0); for (int ithread = 0; ithread < threads.length; ithread++) { for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { threads[ithread] = new Thread() { public void run() { public void run() { for (int wPix = ai.getAndIncrement(); wPix < valid_pixels.length; wPix = ai.getAndIncrement()) { for (int wPix = ai.getAndIncrement(); wPix < woi_length; wPix = ai.getAndIncrement()) { int x = wPix % woi.width + woi.x; int x = wPix % woi.width + woi.x; int y = wPix / woi.width + woi.y; int y = wPix / woi.width + woi.y; int npix = x + y*full.width; int npix = x + y*full.width; check_pixel: { for (int nscene = 0; nscene < num_scenes; nscene++) if (!Double.isNaN(terrain_rendered[nscene][npix])){ for (int nscene = 0; nscene < num_scenes; nscene++) if (!Double.isNaN(terrain_rendered[nscene][npix])){ if (vegetation_offsets[nscene][npix] == null) { if (vegetation_offsets[nscene][npix] == null) { valid_pixels[wPix] = true; valid[nscene][wPix] = true; break check_pixel; // no vegetation, valid num_scenes_used[wPix]++; } } else { int vx0 = (int) Math.floor(x + vegetation_offsets[nscene][npix][0]); int vx0 = (int) Math.floor(x + vegetation_offsets[nscene][npix][0]); int vy0 = (int) Math.floor(y + vegetation_offsets[nscene][npix][1]); int vy0 = (int) Math.floor(y + vegetation_offsets[nscene][npix][1]); Rectangle veg = new Rectangle(vx0,vy0,2,2); Rectangle veg = new Rectangle(vx0,vy0,2,2); if (!full.contains(veg)) { if (woi.contains(veg)) { // assuming vegetation data is everywhere defined continue; // to next scene. Maybe this scene will be removed later and do not disqualify this pixel. valid[nscene][wPix] = true; num_scenes_used[wPix]++; } } int vindx0 = vx0 + vy0 * full.width; int [] vcorners = {vindx0, vindx0+1, vindx0+full.width, vindx0+full.width+1}; check_corners: { for (int vindx:vcorners) { if (Double.isNaN(terrain_rendered[nscene][vindx])) { break check_corners; // no data for vegetation } } } } valid_pixels[wPix] = true; amax_scenes.getAndAccumulate(num_scenes_used[wPix], Math::max); break check_pixel; // no vegetation, valid } } }; } ImageDtt.startAndJoin(threads); // set valid pixels to the intersection of final int [][] start_end_scene = new int [woi_length][2]; ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { // int [] ladder = new int[num_scenes+1]; for (int wPix = ai.getAndIncrement(); wPix < woi_length; wPix = ai.getAndIncrement()) { int x = wPix % woi.width + woi.x; int y = wPix / woi.width + woi.y; int npix = x + y*full.width; int max_i = 0; int max_l = 0; int len = 0; for (int i = 0; i < num_scenes; i++) { if (valid[i][wPix]) { len++; } else { if (len > max_l) { max_l = len; max_i = i - len; } } len = 0; } } } } if (len > max_l) { max_l = len; max_i = num_scenes - len; } start_end_scene[wPix][0] = max_i; start_end_scene[wPix][1] = max_i + max_l; } } } } }; }; } } ImageDtt.startAndJoin(threads); ImageDtt.startAndJoin(threads); // now remove scenes that have at least one of the valid pixels missing final int [][] scene_pixel_product = new int [num_scenes][2]; // {max area, end} final AtomicInteger amax_area = new AtomicInteger(0); ai.set(0); ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { threads[ithread] = new Thread() { public void run() { public void run() { boolean [] selection = new boolean [woi_length]; for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) { for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) { double [] terrain = terrain_rendered[nScene]; Arrays.fill(selection, true); double [][] offsets = vegetation_offsets[nScene]; int max_area = 0; check_scene: { int last_scene = 0; for (int wy = 0; wy < woi.height; wy++) { for (int nscene1 = nScene; nscene1 < num_scenes; nscene1++) { int y = wy+woi.y; int num_pix = 0; for (int wx = 0; wx < woi.width; wx++) { for (int wpix = 0; wpix < woi_length; wpix++) { int wpix = wx + wy * woi.width; selection[wpix] &= valid[nscene1][wpix]; if (valid_pixels[wpix]) { if (selection[wpix]) { int x = wx+woi.x; num_pix++; //valid_pixels[npix] } int indx = x+ y*full.width; } if (Double.isNaN(terrain[indx])) { int area = (nscene1 - nScene + 1) * num_pix; break check_scene; if (area > max_area) { max_area=area; last_scene = nscene1; } if (area == 0) { break; // no more intersections } } scene_pixel_product[nScene][0] = max_area; scene_pixel_product[nScene][1] = last_scene + 1; // inclusive-> exclusive amax_area.getAndAccumulate(scene_pixel_product[nScene][0], Math::max); } } }; } ImageDtt.startAndJoin(threads); int max_area = amax_area.get(); int scene0 = -1, scene1 = -1; for (int nscene = 0; nscene < num_scenes; nscene++) { if (scene_pixel_product[nscene][0] == max_area) { scene0 = nscene; scene1 = scene_pixel_product[nscene][1]; break; } } System.out.println ("scene0="+scene0+", scene1="+scene1); // AND all valid_pixels for the scene range final AtomicInteger anum_pix = new AtomicInteger(0); final boolean [] valid_pixels = new boolean [woi_length]; ai.set(0); final int fscene0=scene0, fscene1 = scene1; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nPix = ai.getAndIncrement(); nPix < woi_length; nPix = ai.getAndIncrement()) { valid_pixels[nPix] = true; for (int nscene = fscene0; nscene < fscene1; nscene++) { valid_pixels[nPix] &= valid[nscene][nPix]; } if (valid_pixels[nPix]) { anum_pix.getAndIncrement(); } } } }; } ImageDtt.startAndJoin(threads); // Now mark selected and try to add more scenes if they contain all valid pixels final AtomicInteger anum_scenes = new AtomicInteger(0); ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) { if ((nScene >= fscene0) && (nScene < fscene1)) { valid_scenes[nScene] = true; anum_scenes.getAndIncrement(); } else { check_all_pixels: { for (int npix = 0; npix < woi_length; npix++) { if (!valid[nScene][npix]) { break check_all_pixels; } } valid_scenes[nScene] = true; // new added scene anum_scenes.getAndIncrement(); System.out.println("getValidScenesPixels(): added scene "+nScene+" that has all valid pixels"); } } } } }; } } if (offsets[indx] != null) { // null is OK - terrain only ImageDtt.startAndJoin(threads); int vx0 = (int) Math.floor(x + offsets[indx][0]); int vy0 = (int) Math.floor(y + offsets[indx][1]); if (stats != null) { stats[0] = anum_scenes.get(); stats[1] = anum_pix.get(); stats[2] = 0; stats[3] = 0; } if ((anum_pix.get() == 0) || (anum_scenes.get()==0)) { return null; } final AtomicInteger ati = new AtomicInteger(0); final AtomicInteger afirst_scene = new AtomicInteger(num_scenes); final AtomicInteger alast_scene = new AtomicInteger(0); final boolean [][] valid_vegetation_threaded = new boolean [threads.length][woi_length]; ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { int nthread = ati.getAndIncrement(); for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) if (valid_scenes[nScene]){ afirst_scene.getAndAccumulate(nScene, Math::min); alast_scene.getAndAccumulate(nScene, Math::max); for (int wpix = 0; wpix < woi_length; wpix++) if (valid_pixels[wpix]) { int x = wpix % woi.width + woi.x; int y = wpix / woi.width + woi.y; int npix = x + y*full.width; if (vegetation_offsets[nScene][npix] != null) { int vx0 = (int) Math.floor(x + vegetation_offsets[nScene][npix][0]); int vy0 = (int) Math.floor(y + vegetation_offsets[nScene][npix][1]); Rectangle veg = new Rectangle(vx0,vy0,2,2); Rectangle veg = new Rectangle(vx0,vy0,2,2); if (!full.contains(veg)) { if (!woi.contains(veg)) { // assuming vegetation data is everywhere defined break check_scene; // required data does not fit in the image (woi too close to the edge?) throw new IllegalArgumentException( "getValidScenesPixels() bug: vegetation quad does not fit: scene="+nScene+", x="+x+", y="+y+ ", vx0="+vx0+", vy0="+vy0+", woi=("+woi.x+", "+woi.y+", "+woi.width+", woi.height"); } } int vindx0 = vx0 + vy0 * full.width; int wvindx0 = (vx0-woi.x) + (vy0-woi.y) * woi.width; int [] vcorners = {vindx0, vindx0+1, vindx0+full.width, vindx0+full.width+1}; int [] vcorners = {wvindx0, wvindx0+1, wvindx0+woi.width, wvindx0+woi.width+1}; for (int vindx:vcorners) { for (int vindx:vcorners) { if (Double.isNaN(terrain[vindx])) { valid_vegetation_threaded[nthread][vindx] =true; break check_scene; // no data for vegetation } } } } } } } } } } }; } ImageDtt.startAndJoin(threads); final int first_scene = afirst_scene.get(); final int last_scene = alast_scene.get(); // combine final boolean [] valid_vegetation = new boolean [woi_length]; final AtomicInteger anum_veget = new AtomicInteger(0); ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nPix = ai.getAndIncrement(); nPix < woi_length; nPix = ai.getAndIncrement()) { check_valid: { for (int nthread = 0; nthread < valid_vegetation_threaded.length; nthread++) { if (valid_vegetation_threaded[nthread][nPix]) { valid_vegetation[nPix] = true; anum_veget.getAndIncrement(); break check_valid; } } } valid_scenes[nScene] = true; } } } } } } }; }; } } ImageDtt.startAndJoin(threads); ImageDtt.startAndJoin(threads); return new boolean[][] {valid_scenes, valid_pixels}; final AtomicInteger anum_offsets = new AtomicInteger(0); final boolean [] valid_offset = new boolean [woi_length]; final AtomicInteger anum_offsets_middle = new AtomicInteger(0); final AtomicBoolean [] bad_offset_scenes = new AtomicBoolean[num_scenes]; for (int nscene = first_scene; nscene <= last_scene; nscene++) { bad_offset_scenes[nscene] = new AtomicBoolean(false); } ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int wPix = ai.getAndIncrement(); wPix < woi_length; wPix = ai.getAndIncrement()) if (valid_pixels[wPix]){ int x = wPix % woi.width + woi.x; int y = wPix / woi.width + woi.y; int npix = x + y*full.width; boolean has_offset_middle = true; for (int nscene = first_scene + max_sacrifice; nscene <= last_scene-max_sacrifice; nscene++) if (valid_scenes[nscene]){ if (vegetation_offsets[nscene][npix] == null) { has_offset_middle = false; break; } } if (has_offset_middle) { boolean has_offset = true; for (int nscene = first_scene; nscene < first_scene + max_sacrifice; nscene++) if (valid_scenes[nscene]){ if (vegetation_offsets[nscene][npix] == null) { has_offset = false; bad_offset_scenes[nscene].set(true); } } for (int nscene = last_scene-max_sacrifice + 1; nscene <= last_scene; nscene++) if (valid_scenes[nscene]){ if (vegetation_offsets[nscene][npix] == null) { has_offset = false; bad_offset_scenes[nscene].set(true); } } valid_offset[wPix] = true; anum_offsets_middle.getAndIncrement(); if (has_offset) { anum_offsets.getAndIncrement(); } } } } }; } ImageDtt.startAndJoin(threads); int num_removed = 0; if (anum_offsets.get() < anum_offsets_middle.get()) { System.out.println("getValidScenesPixels(): Had to remove some scenes that do not have offset data for pixels that have it in most scenes."); System.out.println("Number of pixels with offset data in most scenes: "+anum_offsets_middle.get()+", in all scenes: "+anum_offsets.get()+"."); for (int nscene = 0; nscene < num_scenes; nscene++) if (valid_scenes[nscene]) { if (bad_offset_scenes[nscene].get()) { valid_scenes[nscene] = false; num_removed++; System.out.println("Removing scene "+nscene+"."); } } } if (stats != null) { stats[0] -= num_removed; stats[2] = anum_veget.get(); stats[3] = anum_offsets.get(); } return new boolean[][] {valid_scenes, valid_pixels, valid_vegetation, valid_offset}; } } Loading Loading
src/main/java/com/elphel/imagej/vegetation/VegetationLMA.java +350 −74 Original line number Original line Diff line number Diff line Loading @@ -6,6 +6,7 @@ import java.io.FileFilter; import java.util.Arrays; import java.util.Arrays; import java.util.Collections; import java.util.Collections; import java.util.Comparator; import java.util.Comparator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger; import com.elphel.imagej.common.DoubleGaussianBlur; import com.elphel.imagej.common.DoubleGaussianBlur; Loading Loading @@ -99,6 +100,7 @@ public class VegetationLMA { // private int num_used_scenes; // -> num_par_scenes // private int num_used_scenes; // -> num_par_scenes private boolean [] used_scenes; // encode unused as NaN private boolean [] used_scenes; // encode unused as NaN private boolean [][] valid_woi = null; // transitional parameters - [0] - valid terrain and y_vector (per woi), [1] - valid vegetation, [2] - terrain has vegetation // private int [] used_scenes_indices; // private int [] used_scenes_indices; public final double [] scene_weights; public final double [] scene_weights; Loading Loading @@ -466,20 +468,46 @@ public class VegetationLMA { int min_scenes_used = min_scenes * 4; int min_scenes_used = min_scenes * 4; boolean all_terrain = true; // use all terrain tiles even not used with vegetation boolean all_terrain = true; // use all terrain tiles even not used with vegetation boolean [][] valid_woi; int [] valid_stats = new int[4]; int max_sacrifice = 5; boolean [][] valid_scenes_pixels = getValidScenesPixels ( this.woi, // final Rectangle woi, this.full, // final Rectangle full, this.terrain_rendered, // final double [][] terrain_rendered, // terrain rendered for scenes (has nulls) this.vegetation_offsets, // final double [][][] vegetation_offsets // [num_scenes][pixle]{dx,dy} differential offsets of vegetation to terrain grid max_sacrifice, // final int max_sacrifice, // may sacrifice first/last scenes to have the same valid_offset valid_stats); // final int [] stats // number of valid scenes, valid pixels if int[2] if (debugLevel > -2) { System.out.println("Got "+valid_stats[0]+" valid scenes (of "+this.terrain_rendered.length+ "), "+valid_stats[1]+" valid WOI pixels (of "+(this.woi.width*this.woi.height)+ "), of them "+valid_stats[3]+" have vegetation influence, valid vegetation pixels "+ valid_stats[2] + " (of "+(this.woi.width*this.woi.height)+")."); } if (valid_scenes_pixels == null) { System.out.println("Too few valid scenes/pixels."); return -1; } this.used_scenes = valid_scenes_pixels[0]; boolean [][] valid_woi = filterValidWoi ( if (clean_y) { valid_woi = new boolean [][] {valid_scenes_pixels[1],valid_scenes_pixels[2],valid_scenes_pixels[3]}; } else { // will not work correctly boolean [][] valid_terr_veg = filterValidWoi ( min_scenes_uses, // int min_scenes_uses, min_scenes_uses, // int min_scenes_uses, min_scenes_used, // int min_scenes_used, min_scenes_used, // int min_scenes_used, min_valid_pixels, // int min_valid_pixels, min_valid_pixels, // int min_valid_pixels, debugLevel > 3); // boolean debug_img){ // 4x? debugLevel > 3); // boolean debug_img){ // 4x? if (valid_woi == null) { if (valid_terr_veg == null) { System.out.println("Too few valid pixels (< min_valid_pixels="+min_valid_pixels+")."); System.out.println("Too few valid pixels (< min_valid_pixels="+min_valid_pixels+")."); return -1; return -1; } } valid_woi = new boolean [][] {valid_terr_veg[0],valid_scenes_pixels[1],null}; // incorrect, should always be clean_y } this.valid_woi = valid_woi; // now [3]: valid terrain/y, valid vegetation, terrain has vegetation setupParametersIndices( setupParametersIndices( valid_woi, // needs to know number of used scenes // all but scenes valid_woi, // needs to know number of used scenes // all but scenes all_terrain); // boolean all_terrain !clean_y && all_terrain); // boolean all_terrain disable all_terrain when clean_y alpha_neibs = getNeighbors( alpha_neibs = getNeighbors( TVAO_ALPHA, // final int tvao, // TVAO_VEGETATION_ALPHA TVAO_ALPHA, // final int tvao, // TVAO_VEGETATION_ALPHA Loading Loading @@ -3254,7 +3282,7 @@ public class VegetationLMA { for (int ithread = 0; ithread < threads.length; ithread++) { for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { threads[ithread] = new Thread() { public void run() { public void run() { for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) { for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) if (!clean_y || (used_scenes[nScene])){ for (int drow = 0; drow < woi.height; drow++) { for (int drow = 0; drow < woi.height; drow++) { int row = woi.y + drow; int row = woi.y + drow; for (int dcol = 0; dcol < woi.width; dcol++) { for (int dcol = 0; dcol < woi.width; dcol++) { Loading @@ -3263,7 +3291,9 @@ public class VegetationLMA { int indx = row*full.width + col; int indx = row*full.width + col; // now par_index[TVAO_TERRAIN][indx] may be -1 // now par_index[TVAO_TERRAIN][indx] may be -1 // if (!Double.isNaN(terrain_rendered[nScene][indx]) && (par_index[TVAO_TERRAIN][indx] >= 0)) { // // if (!Double.isNaN(terrain_rendered[nScene][indx]) && (par_index[TVAO_TERRAIN][indx] >= 0)) { // if (!Double.isNaN(terrain_rendered[nScene][indx])) { // boolean good_pixel = clean_y ? valid_woi[0][windx] : (!Double.isNaN(terrain_rendered[nScene][indx])); // if (!Double.isNaN(terrain_rendered[nScene][indx])) { // if (good_pixel) { // boolean has_data = false; boolean has_data = false; int [][] data_record = new int[DATA_SOURCE_ITEMS][]; int [][] data_record = new int[DATA_SOURCE_ITEMS][]; data_record[DATA_SOURCE_HEAD] = new int [DATA_SOURCE_HEAD_SIZE]; data_record[DATA_SOURCE_HEAD] = new int [DATA_SOURCE_HEAD_SIZE]; Loading @@ -3278,7 +3308,7 @@ public class VegetationLMA { data_record[DATA_SOURCE_HEAD][DATA_SOURCE_HEAD_SINDEX] = nScene; data_record[DATA_SOURCE_HEAD][DATA_SOURCE_HEAD_SINDEX] = nScene; // try building record, if no data - do not add it // try building record, if no data - do not add it double [] veg_xy = vegetation_offsets[nScene][indx]; double [] veg_xy = vegetation_offsets[nScene][indx]; if (veg_xy != null) { if ((veg_xy != null) && (!clean_y || valid_woi[2][windx])) { veg_xy = veg_xy.clone(); veg_xy = veg_xy.clone(); if (diff_offsets) { if (diff_offsets) { veg_xy[0] += col; veg_xy[0] += col; Loading Loading @@ -3328,23 +3358,28 @@ public class VegetationLMA { }; }; } } ImageDtt.startAndJoin(threads); ImageDtt.startAndJoin(threads); // should be no condensing in clean_y mode as it was pre-filtered // now condense removing nulls in data_src (cornw may still have nulls) // now condense removing nulls in data_src (cornw may still have nulls) int max_samples = 0; int max_samples = 0; for (int nscene = 0; nscene < num_scenes; nscene++) { for (int nscene = 0; nscene < num_scenes; nscene++) { max_samples = Math.max(max_samples, scene_samples[nscene]); max_samples = Math.max(max_samples, scene_samples[nscene]); } } System.out.println("Maximal samples in a scene = "+max_samples); System.out.println("Maximal samples in a scene = "+max_samples); int allowed_less = 0; int allowed_less = 0; int need_samples = max_samples-allowed_less; int need_samples = max_samples-allowed_less; int num_samples = 0; int num_samples = 0; // int dbg_early_sample = 0; // 10; // int dbg_early_sample = 0; // 10; if (!clean_y) { used_scenes = new boolean [num_scenes]; used_scenes = new boolean [num_scenes]; } final int [] used_scene_indices = new int [num_scenes]; final int [] used_scene_indices = new int [num_scenes]; int num_used_scenes = 0; int num_used_scenes = 0; for (int nscene = 0; nscene < num_scenes; nscene++) { for (int nscene = 0; nscene < num_scenes; nscene++) { int num_prev = num_samples; int num_prev = num_samples; int defined = scene_samples[nscene]; int defined = scene_samples[nscene]; boolean enough = defined >= need_samples; boolean enough = clean_y ? used_scenes[nscene] : (defined >= need_samples); if (!enough) { if (!enough) { System.out.println ("Not enough samples in scene "+nscene+" ("+defined+" < "+need_samples+"), removing it."); System.out.println ("Not enough samples in scene "+nscene+" ("+defined+" < "+need_samples+"), removing it."); } } Loading @@ -3354,7 +3389,7 @@ public class VegetationLMA { used_scenes[nscene] = true; used_scenes[nscene] = true; num_samples+= scene_samples[nscene]; num_samples+= scene_samples[nscene]; scene_samples[nscene] = num_prev; // start index scene_samples[nscene] = num_prev; // start index if (keepScene(nscene)) { if (clean_y || keepScene(nscene)) { num_used_scenes++; num_used_scenes++; } else { } else { used_scene_indices[nscene] = -1; used_scene_indices[nscene] = -1; Loading Loading @@ -3688,10 +3723,13 @@ public class VegetationLMA { int windx =dcol + drow * woi.width; int windx =dcol + drow * woi.width; if (valid_woi[1][windx]) { if (valid_woi[1][windx]) { int indx = row*full.width + col; int indx = row*full.width + col; if (!Double.isNaN(vegetation_average[indx])) { // if (!Double.isNaN(vegetation_average[indx])) { // should always be non-NaN if (!Double.isNaN(tvao[TVAO_VEGETATION][indx])) { // should always be non-NaN par_rindex[par_num][0] = TVAO_VEGETATION; par_rindex[par_num][0] = TVAO_VEGETATION; par_rindex[par_num][1] = indx; par_rindex[par_num][1] = indx; par_index[TVAO_VEGETATION][indx] = par_num++; par_index[TVAO_VEGETATION][indx] = par_num++; } else if (clean_y){ System.out.println("Double.isNaN(vegetation_average["+indx+"] 1 - should not happen"); } } } } } } Loading @@ -3707,10 +3745,12 @@ public class VegetationLMA { int windx =dcol + drow * woi.width; int windx =dcol + drow * woi.width; if (valid_woi[1][windx]) { if (valid_woi[1][windx]) { int indx = row*full.width + col; int indx = row*full.width + col; if (!Double.isNaN(vegetation_average[indx])) { if (!Double.isNaN(tvao[TVAO_ALPHA][indx])) { // vegetation_average[indx])) { par_rindex[par_num][0] = TVAO_ALPHA; par_rindex[par_num][0] = TVAO_ALPHA; par_rindex[par_num][1] = indx; par_rindex[par_num][1] = indx; par_index[TVAO_ALPHA][indx] = par_num++; par_index[TVAO_ALPHA][indx] = par_num++; } else if (clean_y){ System.out.println("Double.isNaN(vegetation_average["+indx+"] 2 - should not happen"); } } } } } } Loading @@ -3726,10 +3766,12 @@ public class VegetationLMA { int windx =dcol + drow * woi.width; int windx =dcol + drow * woi.width; if (valid_woi[1][windx]) { if (valid_woi[1][windx]) { int indx = row*full.width + col; int indx = row*full.width + col; if (!Double.isNaN(vegetation_average[indx])) { if (!Double.isNaN(tvao[TVAO_ELEVATION][indx])) { //vegetation_average[indx])) { par_rindex[par_num][0] = TVAO_ELEVATION; par_rindex[par_num][0] = TVAO_ELEVATION; par_rindex[par_num][1] = indx; par_rindex[par_num][1] = indx; par_index[TVAO_ELEVATION][indx] = par_num++; par_index[TVAO_ELEVATION][indx] = par_num++; } else if (clean_y){ System.out.println("Double.isNaN(vegetation_average["+indx+"] 3 - should not happen"); } } } } } } Loading @@ -3753,7 +3795,7 @@ public class VegetationLMA { int par_num = ind_pars_scenes; int par_num = ind_pars_scenes; int [][] par_rindex = this.par_rindex; int [][] par_rindex = this.par_rindex; for (int i = 0; i < par_index[TVAO_SCENE_OFFSET].length; i++) { for (int i = 0; i < par_index[TVAO_SCENE_OFFSET].length; i++) { if (used_scenes[i] && keepScene(i)) { if (used_scenes[i] && (clean_y || keepScene(i))) { par_rindex[par_num][0] = TVAO_SCENE_OFFSET; par_rindex[par_num][0] = TVAO_SCENE_OFFSET; par_rindex[par_num][1] = i; par_rindex[par_num][1] = i; par_index[TVAO_SCENE_OFFSET][i] = par_num++; par_index[TVAO_SCENE_OFFSET][i] = par_num++; Loading Loading @@ -3913,108 +3955,342 @@ public class VegetationLMA { /** /** * Get valid scenes and woi pixels. Valid pixel is inside woi and does not depend on any * Get valid scenes and woi pixels. Valid pixel is inside woi and does not depend on any * vegetation data outside of woi for all valid scenes. Valid scene, in turn, has all image * vegetation data outside of woi for all valid scenes. Valid scene, in turn, has all image * data for all of the valid pixels * data for all of the valid pixels. * * The same pixel may have vegetation offset in some scenes, but not in the others (on the edge), * This causes problems in later LMA fitting, so all the remaining valid_pixels should either * have or do not have such links. This requires additional boolean array for terrain/y_vector * pixels that have vegetation offset data . * * Assuming vegetation image is defined everywhere where vegetation_offsets may point. * @param woi WOI to be fully covered by the rendered data, usually this.woi * @param woi WOI to be fully covered by the rendered data, usually this.woi * @param full full image WOI (0,0,640,512); * @param full full image WOI (0,0,640,512); * @param terrain_rendered per-scene rendered images (for terrain elevation) * @param terrain_rendered per-scene rendered images (for terrain elevation) * @param vegetation_offsets per-scene, per terrain pixel - x,y offsets to the corresponding vegetation pixel * @param vegetation_offsets per-scene, per terrain pixel - x,y offsets to the corresponding vegetation pixel * @return 2d array - {valid scenes, valid pixels} * @return 2d array - {valid scenes, valid pixels, valid vegetation pixels, valid offset (vegetation influence)} */ */ public static boolean [][] getValidScenesPixels ( public static boolean [][] getValidScenesPixels ( final Rectangle woi, final Rectangle woi, final Rectangle full, final Rectangle full, final double [][] terrain_rendered, // terrain rendered for scenes (has nulls) final double [][] terrain_rendered, // terrain rendered for scenes (has nulls) final double [][][] vegetation_offsets // [num_scenes][pixle]{dx,dy} differential offsets of vegetation to terrain grid final double [][][] vegetation_offsets, // [num_scenes][pixle]{dx,dy} differential offsets of vegetation to terrain grid final int max_sacrifice, // may sacrifice first/last scenes to have the same valid_offset final int [] stats // number of valid scenes, valid pixels if int[4] ) { ) { final int num_scenes = terrain_rendered.length; final int num_scenes = terrain_rendered.length; final int woi_length = woi.width*woi.height; final boolean [] valid_scenes = new boolean [num_scenes]; final boolean [] valid_scenes = new boolean [num_scenes]; final boolean [] valid_pixels = new boolean [woi.width*woi.height]; // final boolean [] valid_pixels = new boolean [woi_length]; // final boolean [] valid_vegetation = new boolean [woi_length]; final boolean [][] valid = new boolean [num_scenes][woi_length]; // The same pixel may have vegetation offset in some scenes, but not in the others (on the edge), // This causes problems in later LMA fitting, so all the remaining valid_pixels final int [] num_scenes_used = new int [woi_length]; final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX); final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX); final AtomicInteger ai = new AtomicInteger(0); final AtomicInteger ai = new AtomicInteger(0); // first find pixels that are valid in at least one scene final AtomicInteger amax_scenes = new AtomicInteger(0); for (int ithread = 0; ithread < threads.length; ithread++) { for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { threads[ithread] = new Thread() { public void run() { public void run() { for (int wPix = ai.getAndIncrement(); wPix < valid_pixels.length; wPix = ai.getAndIncrement()) { for (int wPix = ai.getAndIncrement(); wPix < woi_length; wPix = ai.getAndIncrement()) { int x = wPix % woi.width + woi.x; int x = wPix % woi.width + woi.x; int y = wPix / woi.width + woi.y; int y = wPix / woi.width + woi.y; int npix = x + y*full.width; int npix = x + y*full.width; check_pixel: { for (int nscene = 0; nscene < num_scenes; nscene++) if (!Double.isNaN(terrain_rendered[nscene][npix])){ for (int nscene = 0; nscene < num_scenes; nscene++) if (!Double.isNaN(terrain_rendered[nscene][npix])){ if (vegetation_offsets[nscene][npix] == null) { if (vegetation_offsets[nscene][npix] == null) { valid_pixels[wPix] = true; valid[nscene][wPix] = true; break check_pixel; // no vegetation, valid num_scenes_used[wPix]++; } } else { int vx0 = (int) Math.floor(x + vegetation_offsets[nscene][npix][0]); int vx0 = (int) Math.floor(x + vegetation_offsets[nscene][npix][0]); int vy0 = (int) Math.floor(y + vegetation_offsets[nscene][npix][1]); int vy0 = (int) Math.floor(y + vegetation_offsets[nscene][npix][1]); Rectangle veg = new Rectangle(vx0,vy0,2,2); Rectangle veg = new Rectangle(vx0,vy0,2,2); if (!full.contains(veg)) { if (woi.contains(veg)) { // assuming vegetation data is everywhere defined continue; // to next scene. Maybe this scene will be removed later and do not disqualify this pixel. valid[nscene][wPix] = true; num_scenes_used[wPix]++; } } int vindx0 = vx0 + vy0 * full.width; int [] vcorners = {vindx0, vindx0+1, vindx0+full.width, vindx0+full.width+1}; check_corners: { for (int vindx:vcorners) { if (Double.isNaN(terrain_rendered[nscene][vindx])) { break check_corners; // no data for vegetation } } } } valid_pixels[wPix] = true; amax_scenes.getAndAccumulate(num_scenes_used[wPix], Math::max); break check_pixel; // no vegetation, valid } } }; } ImageDtt.startAndJoin(threads); // set valid pixels to the intersection of final int [][] start_end_scene = new int [woi_length][2]; ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { // int [] ladder = new int[num_scenes+1]; for (int wPix = ai.getAndIncrement(); wPix < woi_length; wPix = ai.getAndIncrement()) { int x = wPix % woi.width + woi.x; int y = wPix / woi.width + woi.y; int npix = x + y*full.width; int max_i = 0; int max_l = 0; int len = 0; for (int i = 0; i < num_scenes; i++) { if (valid[i][wPix]) { len++; } else { if (len > max_l) { max_l = len; max_i = i - len; } } len = 0; } } } } if (len > max_l) { max_l = len; max_i = num_scenes - len; } start_end_scene[wPix][0] = max_i; start_end_scene[wPix][1] = max_i + max_l; } } } } }; }; } } ImageDtt.startAndJoin(threads); ImageDtt.startAndJoin(threads); // now remove scenes that have at least one of the valid pixels missing final int [][] scene_pixel_product = new int [num_scenes][2]; // {max area, end} final AtomicInteger amax_area = new AtomicInteger(0); ai.set(0); ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { threads[ithread] = new Thread() { public void run() { public void run() { boolean [] selection = new boolean [woi_length]; for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) { for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) { double [] terrain = terrain_rendered[nScene]; Arrays.fill(selection, true); double [][] offsets = vegetation_offsets[nScene]; int max_area = 0; check_scene: { int last_scene = 0; for (int wy = 0; wy < woi.height; wy++) { for (int nscene1 = nScene; nscene1 < num_scenes; nscene1++) { int y = wy+woi.y; int num_pix = 0; for (int wx = 0; wx < woi.width; wx++) { for (int wpix = 0; wpix < woi_length; wpix++) { int wpix = wx + wy * woi.width; selection[wpix] &= valid[nscene1][wpix]; if (valid_pixels[wpix]) { if (selection[wpix]) { int x = wx+woi.x; num_pix++; //valid_pixels[npix] } int indx = x+ y*full.width; } if (Double.isNaN(terrain[indx])) { int area = (nscene1 - nScene + 1) * num_pix; break check_scene; if (area > max_area) { max_area=area; last_scene = nscene1; } if (area == 0) { break; // no more intersections } } scene_pixel_product[nScene][0] = max_area; scene_pixel_product[nScene][1] = last_scene + 1; // inclusive-> exclusive amax_area.getAndAccumulate(scene_pixel_product[nScene][0], Math::max); } } }; } ImageDtt.startAndJoin(threads); int max_area = amax_area.get(); int scene0 = -1, scene1 = -1; for (int nscene = 0; nscene < num_scenes; nscene++) { if (scene_pixel_product[nscene][0] == max_area) { scene0 = nscene; scene1 = scene_pixel_product[nscene][1]; break; } } System.out.println ("scene0="+scene0+", scene1="+scene1); // AND all valid_pixels for the scene range final AtomicInteger anum_pix = new AtomicInteger(0); final boolean [] valid_pixels = new boolean [woi_length]; ai.set(0); final int fscene0=scene0, fscene1 = scene1; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nPix = ai.getAndIncrement(); nPix < woi_length; nPix = ai.getAndIncrement()) { valid_pixels[nPix] = true; for (int nscene = fscene0; nscene < fscene1; nscene++) { valid_pixels[nPix] &= valid[nscene][nPix]; } if (valid_pixels[nPix]) { anum_pix.getAndIncrement(); } } } }; } ImageDtt.startAndJoin(threads); // Now mark selected and try to add more scenes if they contain all valid pixels final AtomicInteger anum_scenes = new AtomicInteger(0); ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) { if ((nScene >= fscene0) && (nScene < fscene1)) { valid_scenes[nScene] = true; anum_scenes.getAndIncrement(); } else { check_all_pixels: { for (int npix = 0; npix < woi_length; npix++) { if (!valid[nScene][npix]) { break check_all_pixels; } } valid_scenes[nScene] = true; // new added scene anum_scenes.getAndIncrement(); System.out.println("getValidScenesPixels(): added scene "+nScene+" that has all valid pixels"); } } } } }; } } if (offsets[indx] != null) { // null is OK - terrain only ImageDtt.startAndJoin(threads); int vx0 = (int) Math.floor(x + offsets[indx][0]); int vy0 = (int) Math.floor(y + offsets[indx][1]); if (stats != null) { stats[0] = anum_scenes.get(); stats[1] = anum_pix.get(); stats[2] = 0; stats[3] = 0; } if ((anum_pix.get() == 0) || (anum_scenes.get()==0)) { return null; } final AtomicInteger ati = new AtomicInteger(0); final AtomicInteger afirst_scene = new AtomicInteger(num_scenes); final AtomicInteger alast_scene = new AtomicInteger(0); final boolean [][] valid_vegetation_threaded = new boolean [threads.length][woi_length]; ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { int nthread = ati.getAndIncrement(); for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()) if (valid_scenes[nScene]){ afirst_scene.getAndAccumulate(nScene, Math::min); alast_scene.getAndAccumulate(nScene, Math::max); for (int wpix = 0; wpix < woi_length; wpix++) if (valid_pixels[wpix]) { int x = wpix % woi.width + woi.x; int y = wpix / woi.width + woi.y; int npix = x + y*full.width; if (vegetation_offsets[nScene][npix] != null) { int vx0 = (int) Math.floor(x + vegetation_offsets[nScene][npix][0]); int vy0 = (int) Math.floor(y + vegetation_offsets[nScene][npix][1]); Rectangle veg = new Rectangle(vx0,vy0,2,2); Rectangle veg = new Rectangle(vx0,vy0,2,2); if (!full.contains(veg)) { if (!woi.contains(veg)) { // assuming vegetation data is everywhere defined break check_scene; // required data does not fit in the image (woi too close to the edge?) throw new IllegalArgumentException( "getValidScenesPixels() bug: vegetation quad does not fit: scene="+nScene+", x="+x+", y="+y+ ", vx0="+vx0+", vy0="+vy0+", woi=("+woi.x+", "+woi.y+", "+woi.width+", woi.height"); } } int vindx0 = vx0 + vy0 * full.width; int wvindx0 = (vx0-woi.x) + (vy0-woi.y) * woi.width; int [] vcorners = {vindx0, vindx0+1, vindx0+full.width, vindx0+full.width+1}; int [] vcorners = {wvindx0, wvindx0+1, wvindx0+woi.width, wvindx0+woi.width+1}; for (int vindx:vcorners) { for (int vindx:vcorners) { if (Double.isNaN(terrain[vindx])) { valid_vegetation_threaded[nthread][vindx] =true; break check_scene; // no data for vegetation } } } } } } } } } } }; } ImageDtt.startAndJoin(threads); final int first_scene = afirst_scene.get(); final int last_scene = alast_scene.get(); // combine final boolean [] valid_vegetation = new boolean [woi_length]; final AtomicInteger anum_veget = new AtomicInteger(0); ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int nPix = ai.getAndIncrement(); nPix < woi_length; nPix = ai.getAndIncrement()) { check_valid: { for (int nthread = 0; nthread < valid_vegetation_threaded.length; nthread++) { if (valid_vegetation_threaded[nthread][nPix]) { valid_vegetation[nPix] = true; anum_veget.getAndIncrement(); break check_valid; } } } valid_scenes[nScene] = true; } } } } } } }; }; } } ImageDtt.startAndJoin(threads); ImageDtt.startAndJoin(threads); return new boolean[][] {valid_scenes, valid_pixels}; final AtomicInteger anum_offsets = new AtomicInteger(0); final boolean [] valid_offset = new boolean [woi_length]; final AtomicInteger anum_offsets_middle = new AtomicInteger(0); final AtomicBoolean [] bad_offset_scenes = new AtomicBoolean[num_scenes]; for (int nscene = first_scene; nscene <= last_scene; nscene++) { bad_offset_scenes[nscene] = new AtomicBoolean(false); } ai.set(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { for (int wPix = ai.getAndIncrement(); wPix < woi_length; wPix = ai.getAndIncrement()) if (valid_pixels[wPix]){ int x = wPix % woi.width + woi.x; int y = wPix / woi.width + woi.y; int npix = x + y*full.width; boolean has_offset_middle = true; for (int nscene = first_scene + max_sacrifice; nscene <= last_scene-max_sacrifice; nscene++) if (valid_scenes[nscene]){ if (vegetation_offsets[nscene][npix] == null) { has_offset_middle = false; break; } } if (has_offset_middle) { boolean has_offset = true; for (int nscene = first_scene; nscene < first_scene + max_sacrifice; nscene++) if (valid_scenes[nscene]){ if (vegetation_offsets[nscene][npix] == null) { has_offset = false; bad_offset_scenes[nscene].set(true); } } for (int nscene = last_scene-max_sacrifice + 1; nscene <= last_scene; nscene++) if (valid_scenes[nscene]){ if (vegetation_offsets[nscene][npix] == null) { has_offset = false; bad_offset_scenes[nscene].set(true); } } valid_offset[wPix] = true; anum_offsets_middle.getAndIncrement(); if (has_offset) { anum_offsets.getAndIncrement(); } } } } }; } ImageDtt.startAndJoin(threads); int num_removed = 0; if (anum_offsets.get() < anum_offsets_middle.get()) { System.out.println("getValidScenesPixels(): Had to remove some scenes that do not have offset data for pixels that have it in most scenes."); System.out.println("Number of pixels with offset data in most scenes: "+anum_offsets_middle.get()+", in all scenes: "+anum_offsets.get()+"."); for (int nscene = 0; nscene < num_scenes; nscene++) if (valid_scenes[nscene]) { if (bad_offset_scenes[nscene].get()) { valid_scenes[nscene] = false; num_removed++; System.out.println("Removing scene "+nscene+"."); } } } if (stats != null) { stats[0] -= num_removed; stats[2] = anum_veget.get(); stats[3] = anum_offsets.get(); } return new boolean[][] {valid_scenes, valid_pixels, valid_vegetation, valid_offset}; } } Loading