VegetationModel.java 192 KB
Newer Older
Andrey Filippov's avatar
Andrey Filippov committed
1
package com.elphel.imagej.vegetation;
Andrey Filippov's avatar
Andrey Filippov committed
2 3

import java.awt.Rectangle;
4
import java.io.File;
5
import java.text.SimpleDateFormat;
Andrey Filippov's avatar
Andrey Filippov committed
6
import java.util.Arrays;
7
import java.util.Calendar;
Andrey Filippov's avatar
Andrey Filippov committed
8 9
import java.util.concurrent.atomic.AtomicInteger;

10 11
import org.apache.commons.math3.analysis.interpolation.PiecewiseBicubicSplineInterpolatingFunction;

Andrey Filippov's avatar
Andrey Filippov committed
12
import com.elphel.imagej.cameras.CLTParameters;
13
import com.elphel.imagej.common.DoubleGaussianBlur;
Andrey Filippov's avatar
Andrey Filippov committed
14
import com.elphel.imagej.common.ShowDoubleFloatArrays;
15
import com.elphel.imagej.correction.SyncCommand;
16
import com.elphel.imagej.jp4.JP46_Reader_camera;
Andrey Filippov's avatar
Andrey Filippov committed
17
import com.elphel.imagej.orthomosaic.OrthoMap;
Andrey Filippov's avatar
Andrey Filippov committed
18 19 20 21 22 23 24
import com.elphel.imagej.tileprocessor.ErsCorrection;
import com.elphel.imagej.tileprocessor.ImageDtt;
import com.elphel.imagej.tileprocessor.OpticalFlow;
import com.elphel.imagej.tileprocessor.QuadCLT;
import com.elphel.imagej.tileprocessor.TileNeibs;
import com.elphel.imagej.tileprocessor.TileProcessor;

25
import ij.IJ;
Andrey Filippov's avatar
Andrey Filippov committed
26
import ij.ImagePlus;
Andrey Filippov's avatar
Andrey Filippov committed
27
import ij.ImageStack;
28
import ij.Prefs;
29
import ij.io.FileSaver;
Andrey Filippov's avatar
Andrey Filippov committed
30 31 32 33 34 35 36 37 38

/*
 * in -INTER-INTRA-LMA.tiff: for pixels where ground==terrain, use disparity layer for both, where they are different
 * use terrain for terrain, and disparity - for vegetation.
 * Variant - use terrain== ground where they ate the same, and disparity - where they differ
 * 
 *  
 */
public class VegetationModel {
39 40
	
	
Andrey Filippov's avatar
Andrey Filippov committed
41 42 43 44
	public static final String  RENDERED_PATH = "/media/elphel/SSD3-4GB/lwir16-proc/berdich3/debug/vegetation/essential/1697877491_613999-terrain_vegetation_render.tiff";
	public static final String  WARP_PATH =     "/media/elphel/SSD3-4GB/lwir16-proc/berdich3/debug/vegetation/essential/1697877491_613999-combo_offset.tiff";
	public static final int     WARP_HYPER =     3; // number of hyper-slices in warp file
	public static final int     RENDERED_HYPER = 2; // number of hyper-slices in rendered
45
	public static final String  TERRVEG_EXT = ".terrveg-tiff";
Andrey Filippov's avatar
Andrey Filippov committed
46

Andrey Filippov's avatar
Andrey Filippov committed
47 48 49 50 51 52 53 54 55 56 57
	public static final int [][] FOUR_CORNERS_Z =
		{
				{0,1,8,2},
				{8,2,4,3},
				{6,8,5,4},
				{7,0,6,8}
		};
	public static final int [][] XY_OFFS = {{0,-1},{0,0},{-1,0},{-1,-1}};
			
	public static final int PXPYD_LEN=3;
	public static final int [][] CORN_CW_OFFS = {{0,0},{1,0},{1,1},{0,1}};
58 59 60 61

	public double [][]   terrain_scenes_render =     null;
	public double []     terrain_average_render =    null;
	public double []     vegetation_average_render = null;
62 63 64
	public double []     terrain_filled =            null;
	public double []     vegetation_full =           null; // ~same as vegetation_average_render
	public double []     vegetation_filtered =       null; // more NaNs than in vegetation_average_render
65
	public double []     vegetation_pull =           null; // filled around actual vegetation to use as LMA pull
66 67
//	public double        initial_transparent=        0.0;
//	public double        initial_opaque=             1.0;
68 69
	
	
70
	public double [][][] vegetation_warp =           null;
71
	public double [][][] vegetation_inv_warp =       null;
72
	public double [][][] vegetation_warp_md =        null; // magnitude+direction
73 74 75 76 77
	public double [][][] vegetation_inv_warp_md =    null; // magnitude+direction
	
	public double []     elevations=                 null;
	public double [][][] scale_dirs=                 null;
	
78
	public String []     scene_names =               null; // TODO: Implement!
79 80 81 82
	
	
	
	
83 84 85 86 87
	public boolean       diff_mode =                 true;
	public Rectangle     full;
	public boolean       failed = false;
	public String        model_directory;
	public String        model_top_directory;
Andrey Filippov's avatar
Andrey Filippov committed
88 89
	public String        reference_scene; // timestamp
	public int           reference_index; // timestamp
Andrey Filippov's avatar
Andrey Filippov committed
90
	
91 92 93 94 95
	// Source directory and title where this instance was read from to be able to write back more data
	public String        src_dir = null;
	public String        src_title = null;

	
96
	public double [][] tva;
97
	int                step_restore;
98 99
	public boolean     tile_woi;
	
100
	
101
	public final SyncCommand SYNC_COMMAND;
102 103 104
	public boolean isFailed() {
		return failed;
	}
Andrey Filippov's avatar
Andrey Filippov committed
105
	
106 107 108 109 110
	public void saveState(
			String dir,
			String title) {
		int num_scenes = terrain_scenes_render.length; // == vegetation_warp.length
		final int full_length = full.width*full.height;
111 112 113 114 115 116 117
		
		final int num_slices1 = num_scenes * 5 + 2;
		final int num_slices2 = num_scenes * 7 + 2 + 1;
		final boolean mode2 = (elevations != null) && (scale_dirs != null);
		final int num_slices = mode2?  num_slices2 : num_slices1;
//		String [] titles = new String[5*num_scenes + 2]; //[3*num_scenes + 2];
		String [] titles = new String[num_slices]; //[3*num_scenes + 2];
118 119 120 121 122
		final double [][] data = new double [titles.length][];
		titles[0] = "terrain_accumulated";
		data[0] =   terrain_average_render;
		titles[1] = "vegetation_accumulated";
		data  [1] = vegetation_average_render;
123 124 125 126 127 128 129
		if (mode2) {
			System.out.println("saveState(): writing in mode2 (with elevations and scales/directions).");
			titles[num_slices1 + 0] = "elevations";
			data  [num_slices1 + 0] = elevations;
		} else {
			System.out.println("saveState(): writing mode1 (without elevations and scales/directions).");
		}
130 131 132
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		for (int n = 0; n < num_scenes; n++) {
Andrey Filippov's avatar
Andrey Filippov committed
133
			titles[2 + n] = scene_names[n]; // timestamp "terrain_scene_"+n;
134 135 136 137 138 139
			data  [2 + n] = terrain_scenes_render[n];
			final int fslice = 2 + num_scenes + 2*n;
			titles[fslice + 0] = "vegetation_warp_"+n+"_x";
			titles[fslice + 1] = "vegetation_warp_"+n+"_y";
			data  [fslice + 0] = new double [full_length];
			data  [fslice + 1] = new double [full_length];
140 141 142 143 144 145 146 147 148 149 150 151 152
			final int fslice_inv = 2 + 3*num_scenes + 2*n;
			titles[fslice_inv + 0] = "vegetation_iwarp_"+n+"_x";
			titles[fslice_inv + 1] = "vegetation_iwarp_"+n+"_y";
			data  [fslice_inv + 0] = new double [full_length];
			data  [fslice_inv + 1] = new double [full_length];
			
			final int fslice2 = mode2 ? ( num_slices1 + 1 + n) : 0;
			if (mode2) {
				titles[fslice2 + 0] =          "elevation_"+n+"_scale";
				titles[fslice2 + num_scenes] = "elevation_"+n+"_direction";
				data  [fslice2 + 0] =          new double [full_length];
				data  [fslice2 + num_scenes] = new double [full_length];
			}			
153 154 155 156 157 158 159 160 161 162 163 164 165
			ai.set(0);
			final int fscene = n;
			for (int ithread = 0; ithread < threads.length; ithread++) {
				threads[ithread] = new Thread() {
					public void run() {
						for (int nPix = ai.getAndIncrement(); nPix < full_length; nPix = ai.getAndIncrement()) {
							if (vegetation_warp[fscene][nPix] == null) {
								data  [fslice + 0][nPix] = Double.NaN;
								data  [fslice + 1][nPix] = Double.NaN;
							} else {
								data  [fslice + 0][nPix] = vegetation_warp[fscene][nPix][0];
								data  [fslice + 1][nPix] = vegetation_warp[fscene][nPix][1];
							}
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
							if (vegetation_inv_warp[fscene][nPix] == null) {
								data  [fslice_inv + 0][nPix] = Double.NaN;
								data  [fslice_inv + 1][nPix] = Double.NaN;
							} else {
								data  [fslice_inv + 0][nPix] = vegetation_inv_warp[fscene][nPix][0];
								data  [fslice_inv + 1][nPix] = vegetation_inv_warp[fscene][nPix][1];
							}
							if (mode2) {
								if (scale_dirs[fscene][nPix] == null) {
									data  [fslice2 + 0]         [nPix] = Double.NaN;
									data  [fslice2 + num_scenes][nPix] = Double.NaN;
								} else {
									data  [fslice2 + 0]         [nPix] = scale_dirs[fscene][nPix][0];
									data  [fslice2 + num_scenes][nPix] = scale_dirs[fscene][nPix][1];
								}
							}
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	                    }
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
		}
		//TERRVEG_EXT
		ImagePlus imp = ShowDoubleFloatArrays.makeArrays(
				data,
				full.width,
				full.height,
				title, // +"-objects_"+scene_num,				
				titles); // test_titles,
		imp.setProperty("FULL_X",                    ""+full.x);
		imp.setProperty("FULL_Y",                    ""+full.y);
		imp.setProperty("FULL_WIDTH",                ""+full.width);
		imp.setProperty("FULL_HEIGHT",               ""+full.height);
		imp.setProperty("DIFF_MODE",                 ""+diff_mode);
		imp.setProperty("MODEL_DIRECTORY",           ""+model_directory);
		imp.setProperty("MODEL_TOP_DIRECTORY",       ""+model_top_directory);
Andrey Filippov's avatar
Andrey Filippov committed
202 203
		imp.setProperty("REFERENCE_SCENE",           ""+reference_scene);
		imp.setProperty("REFERENCE_INDEX",           ""+reference_index);
204
		imp.setProperty("NUM_SCENES",                ""+num_scenes);
205 206 207 208 209 210 211 212 213 214 215 216
		
		
		String path = VegetationLMA.getSavePath(
				dir, // String dir,
				title, // String title)
				TERRVEG_EXT); // String ext) {
		JP46_Reader_camera.encodeProperiesToInfo(imp);			
		FileSaver fs=new FileSaver(imp);
		fs.saveAsTiff(path);
		System.out.println("VegetationModel.saveState(): saved "+path); // dir+title+PAR_EXT);
	}

217 218 219 220 221 222 223 224 225 226
	public String getSegmentsDir(String sub_dir) {
		//model_directory
		String segments_path = VegetationLMA.getSavePath(model_directory,sub_dir,"");
		// create if it does not exist
		File  segments_dir = new File(segments_path);
		segments_dir.mkdirs();
		return segments_path;
	}
	
	
227
	public VegetationModel(
228 229 230 231
			String       dir,
			String       title,
			SyncCommand  SYNC_COMMAND) {
		this.SYNC_COMMAND = SYNC_COMMAND;
232 233 234 235
		String path = VegetationLMA.getSavePath(
				dir, // String dir,
				title, // String title)
				TERRVEG_EXT); // String ext) {
236 237
		src_dir = dir;     // to be able to write back more data
		src_title = title; // to be able to write back more data
238 239 240 241 242 243 244
		ImagePlus imp_pars = new ImagePlus (path);
		if (imp_pars.getWidth()==0) {
			throw new IllegalArgumentException("VegetationModel(): Could not read "+path);
		}
		int num_slices = imp_pars.getStack().getSize();

		JP46_Reader_camera.decodeProperiesFromInfo(imp_pars);
245 246 247 248 249
		
		
		//		imp.setProperty("NUM_SCENES",                ""+num_scenes);
//		if (imp_pars.getProperty("MODEL_DIRECTORY") != null) model_directory = (String) imp_pars.getProperty("MODEL_DIRECTORY");

250 251 252 253 254
		full = new Rectangle (
				Integer.parseInt((String) imp_pars.getProperty("FULL_X")),
				Integer.parseInt((String) imp_pars.getProperty("FULL_Y")),
				Integer.parseInt((String) imp_pars.getProperty("FULL_WIDTH")),
				Integer.parseInt((String) imp_pars.getProperty("FULL_HEIGHT")));
255 256 257
		int file_num_scenes = -1;
		if (imp_pars.getProperty("NUM_SCENES") != null) file_num_scenes = Integer.parseInt((String) imp_pars.getProperty("NUM_SCENES"));
		final int num_scenes = (file_num_scenes > 0)? file_num_scenes: ((num_slices - 2) / 5);
258
		final int full_length = full.width*full.height;
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
		int num_slices1 = num_scenes * 5 + 2;
		int num_slices2 = num_scenes * 7 + 2 + 1;
		boolean mode1 = num_slices == num_slices1;
		boolean mode2 = num_slices == num_slices2;
		if (mode2) {
			System.out.println("VegetationModel(): reading in mode2 (with elevations and scales/directions).");
		} else {
			System.out.println("VegetationModel(): reading in mode1 (without elevations and scales/directions).");
		}
		
		if (!mode1 && ! mode2) {
			throw new IllegalArgumentException("VegetationModel(): Wrong number of slices: expected "+num_slices1+
					" or "+num_slices2+", got "+num_slices);
		}
		
274 275 276 277 278 279
				
		diff_mode = Boolean.parseBoolean((String) imp_pars.getProperty("DIFF_MODE"));
		terrain_scenes_render =    new double [num_scenes][full_length];
		terrain_average_render=    new double [full_length];
		vegetation_average_render= new double [full_length];
		vegetation_warp =          new double [num_scenes][full_length][];
280
		vegetation_inv_warp =      new double [num_scenes][full_length][];
281 282
		if (imp_pars.getProperty("MODEL_DIRECTORY") != null) model_directory = (String) imp_pars.getProperty("MODEL_DIRECTORY");
		if (imp_pars.getProperty("MODEL_TOP_DIRECTORY") != null) model_top_directory = (String) imp_pars.getProperty("MODEL_TOP_DIRECTORY");
Andrey Filippov's avatar
Andrey Filippov committed
283 284
		if (imp_pars.getProperty("REFERENCE_SCENE") != null) reference_scene = (String) imp_pars.getProperty("REFERENCE_SCENE");
		if (imp_pars.getProperty("REFERENCE_INDEX") != null) reference_index = Integer.parseInt((String) imp_pars.getProperty("REFERENCE_INDEX"));
285 286 287 288 289 290
		
		scene_names = new String[num_scenes];
		for (int n = 0; n < num_scenes; n++) {
			scene_names[n] = imp_pars.getStack().getSliceLabel(n+3);
		}

Andrey Filippov's avatar
Andrey Filippov committed
291 292 293 294 295
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
296 297 298 299 300
					for (int n = ai.getAndIncrement(); n < (num_scenes + 2); n = ai.getAndIncrement()) {
						double [] data = (n==0) ? terrain_average_render :((n==1)? vegetation_average_render : terrain_scenes_render[n-2]);
						float [] pixels = (float[]) imp_pars.getStack().getPixels(n + 1);
						for (int i = 0; i < full_length; i++) {
							data[i] = pixels[i];
Andrey Filippov's avatar
Andrey Filippov committed
301
						}
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318

                    }
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		ai.set(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int n = ai.getAndIncrement(); n < num_scenes; n = ai.getAndIncrement()) {
						double [][] data = vegetation_warp[n];
						float [] pixels_x = (float[]) imp_pars.getStack().getPixels(2 + num_scenes + 2 * n + 1);
						float [] pixels_y = (float[]) imp_pars.getStack().getPixels(2 + num_scenes + 2 * n + 2);
						for (int i = 0; i < full_length; i++) {
							if (!Float.isNaN(pixels_x[i]) && !Float.isNaN(pixels_y[i])) {
								data[i] = new double [] {pixels_x[i],pixels_y[i]};
Andrey Filippov's avatar
Andrey Filippov committed
319 320
							}
						}
321
                    }
Andrey Filippov's avatar
Andrey Filippov committed
322 323
				}
			};
324
		}
Andrey Filippov's avatar
Andrey Filippov committed
325
		ImageDtt.startAndJoin(threads);
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
		
		ai.set(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int n = ai.getAndIncrement(); n < num_scenes; n = ai.getAndIncrement()) {
						double [][] data = vegetation_inv_warp[n];
						float [] pixels_x = (float[]) imp_pars.getStack().getPixels(2 + 3* num_scenes + 2 * n + 1);
						float [] pixels_y = (float[]) imp_pars.getStack().getPixels(2 + 3* num_scenes + 2 * n + 2);
						for (int i = 0; i < full_length; i++) {
							if (!Float.isNaN(pixels_x[i]) && !Float.isNaN(pixels_y[i])) {
								data[i] = new double [] {pixels_x[i],pixels_y[i]};
							}
						}
                    }
				}
			};
		}
		ImageDtt.startAndJoin(threads);
		if (mode2) {
			elevations=       new double [full_length];
			final float [] fpix_elev = (float[]) imp_pars.getStack().getPixels(2 + 5* num_scenes + 1);
			ai.set(0);
			for (int ithread = 0; ithread < threads.length; ithread++) {
				threads[ithread] = new Thread() {
					public void run() {
						for (int nPix = ai.getAndIncrement(); nPix < full_length; nPix = ai.getAndIncrement()) {
							elevations[nPix] = fpix_elev[nPix];
	                    }
					}
				};
			}
			ImageDtt.startAndJoin(threads);
			scale_dirs =      new double [num_scenes][full_length][];
			ai.set(0);
			for (int ithread = 0; ithread < threads.length; ithread++) {
				threads[ithread] = new Thread() {
					public void run() {
						for (int n = ai.getAndIncrement(); n < num_scenes; n = ai.getAndIncrement()) {
							double [][] data = scale_dirs[n];
							float [] pixels_scale = (float[]) imp_pars.getStack().getPixels(3 + 5* num_scenes + n + 1);
							float [] pixels_dir =   (float[]) imp_pars.getStack().getPixels(3 + 6* num_scenes + n + 1);
							for (int i = 0; i < full_length; i++) {
								if (!Float.isNaN(pixels_scale[i]) && !Float.isNaN(pixels_dir[i])) {
									data[i] = new double [] {pixels_scale[i],pixels_dir[i]};
								}
							}
	                    }
					}
				};
			}
			ImageDtt.startAndJoin(threads);
		}
379
		return;
Andrey Filippov's avatar
Andrey Filippov committed
380 381
	}
	
382 383 384 385 386 387
	public void setTVA(double [][] tva) {
		this.tva = tva;
	}
	public double [][] getTVA(){
		return this.tva;
	}
Andrey Filippov's avatar
Andrey Filippov committed
388
	
389 390 391 392 393 394 395 396 397
	/**
	 * Prepare terrain rendered, terrain average, vegetation average and vegetation offset. Created from test_vegetation
	 * @param clt_parameters
	 * @param quadCLTs
	 * @param ref_index
	 * @param debugLevel
	 * @return
	 */
	public VegetationModel (
Andrey Filippov's avatar
Andrey Filippov committed
398 399 400
    		CLTParameters  clt_parameters,
    		QuadCLT []     quadCLTs,
    		int            ref_index,
401
    		SyncCommand    SYNC_COMMAND,
402
    		int            debugLevel) {
403
		this.SYNC_COMMAND = SYNC_COMMAND;
404
		boolean show_debug = false;
Andrey Filippov's avatar
Andrey Filippov committed
405 406 407 408 409 410 411
		TileProcessor tp = quadCLTs[ref_index].getTileProcessor();
		int tilesX = tp.getTilesX();
		int tilesY = tp.getTilesY();
		int tileSize = tp.getTileSize();
		double      min_above = 0.02; // 0.025; // .04;
		int         patch_neib = 4; // 5;
		double      neibs_pow =  0.5;
Andrey Filippov's avatar
Andrey Filippov committed
412
		final int   grow_vegetation = clt_parameters.imp.terr_veget_grow;
Andrey Filippov's avatar
Andrey Filippov committed
413 414
		double [][] combo_dsn =quadCLTs[ref_index].restoreComboDSI(true);
		double [][] terr_veg = getTerrainVegetation(
Andrey Filippov's avatar
Andrey Filippov committed
415 416 417 418 419 420
				combo_dsn,        // double [][] combo_dsn);
				tilesX,           // final int         tilesX,
				min_above,        // final double      min_above, // absolute disparity difference
				patch_neib,       // final int         patch_neib){
				neibs_pow,        // final double      neibs_pow);
				grow_vegetation); // final int grow_vegetation);
Andrey Filippov's avatar
Andrey Filippov committed
421
				
422 423 424 425 426 427 428 429 430 431
		if (show_debug) {
			String [] titles_terr_veg = {"terrain","vegetation"};
			ShowDoubleFloatArrays.showArrays(
					terr_veg,
					tilesX,
					tilesY,
					true,
					quadCLTs[ref_index].getImageName()+"-terrain_vegetation-min_ab"+min_above+".tiff",
					titles_terr_veg);
		}
Andrey Filippov's avatar
Andrey Filippov committed
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
		double []     reference_xyz = OpticalFlow.ZERO3;
		double []     reference_atr = OpticalFlow.ZERO3;
		double [][][] terrain_pXpYD =  getPxPyDs(
				reference_xyz, // double []      reference_xyz, // offset reference camera {x,y,z}
				reference_atr, // double []      reference_atr,    		
				terr_veg[0],   // double []      ref_disparity,			
				quadCLTs,      // QuadCLT []     quadCLTs,
	    		null,          // boolean []     scene_selection, // null or same length as quadCLTs
	    		ref_index,     // int            ref_index,
	    		debugLevel);   // int            debugLevel)
		double [][][] vegetation_pXpYD =  getPxPyDs(
				reference_xyz, // double []      reference_xyz, // offset reference camera {x,y,z}
				reference_atr, // double []      reference_atr,    		
				terr_veg[1],   // double []      ref_disparity,			
				quadCLTs,      // QuadCLT []     quadCLTs,
	    		null,          // boolean []     scene_selection, // null or same length as quadCLTs
	    		ref_index,     // int            ref_index,
	    		debugLevel);   // int            debugLevel)
		double [][][] terrain_diff =  diffPxPyDs(
				terrain_pXpYD,    // final double [][][] pXpYD,
				ref_index);       // final int ref_index)
453 454 455 456
		if (terrain_diff == null) {
			failed = true;
			return;
		}
Andrey Filippov's avatar
Andrey Filippov committed
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
		double [][][] vegetation_diff =  diffPxPyDs(
				vegetation_pXpYD, // final double [][][] pXpYD,
				ref_index);       // final int ref_index)
		
		
		int num_scenes =  quadCLTs.length;
		int num_tiles =   terrain_pXpYD[0].length;
		int num_pixels = num_tiles*tileSize*tileSize;
		if (show_debug) {
			String [] titles_frame = {"terr-pX","veg-pX","terr-pY","veg-pY","terr-D","veg-D"};
			String [] titles_scene = new String [num_scenes];
			
			double [][][] data_dbg = new double [titles_frame.length][num_scenes][num_tiles];
			for (int i = 0; i < data_dbg.length;i++) {
				for (int j = 0; j < data_dbg[0].length;j++) {
					Arrays.fill(data_dbg[i][j], Double.NaN);
				}
			}
			for (int nscene = 0; nscene < num_scenes; nscene++) {
				titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
				if ((terrain_pXpYD[nscene] == null) || (vegetation_pXpYD[nscene]==null)){
					System.out.println("test_vegetation(): null for nscene="+nscene);
				} else {
					for (int ntile = 0; ntile < num_tiles; ntile++) {
						if (terrain_pXpYD[nscene][ntile] != null) {
							for (int k = 0; k < terrain_pXpYD[nscene][ntile].length; k++) {
								data_dbg[2*k + 0][nscene][ntile] = terrain_pXpYD[nscene][ntile][k];
							}
						}
						if (vegetation_pXpYD[nscene][ntile] != null) {
							for (int k = 0; k < vegetation_pXpYD[nscene][ntile].length; k++) {
								data_dbg[2*k + 1][nscene][ntile] = vegetation_pXpYD[nscene][ntile][k];
							}
						}
					}
				}
			}
			ShowDoubleFloatArrays.showArraysHyperstack(
					data_dbg, // double[][][] pixels, 
					tilesX,                    // int          width, 
497
					quadCLTs[ref_index].getImageName()+"-terrain_vegetation_pXpYD",      // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
Andrey Filippov's avatar
Andrey Filippov committed
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
					titles_scene,           // String []    titles, // all slices*frames titles or just slice titles or null
					titles_frame,           // String []    frame_titles, // frame titles or null
					true);                  // boolean      show)
			
			// same for differences
			
			double [][][] diff_dbg = new double [6][num_scenes][num_tiles];
			for (int i = 0; i < diff_dbg.length;i++) {
				for (int j = 0; j < diff_dbg[0].length;j++) {
					Arrays.fill(diff_dbg[i][j], Double.NaN);
				}
			}
			for (int nscene = 0; nscene < num_scenes; nscene++) {
//				titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
				if ((terrain_diff[nscene] == null) || (vegetation_diff[nscene]==null)){
					System.out.println("test_vegetation(): null for nscene="+nscene);
				} else {
					for (int ntile = 0; ntile < num_tiles; ntile++) {
						if (terrain_diff[nscene][ntile] != null) {
							for (int k = 0; k < terrain_diff[nscene][ntile].length; k++) {
								diff_dbg[2*k + 0][nscene][ntile] = terrain_diff[nscene][ntile][k];
							}
						}
						if (vegetation_diff[nscene][ntile] != null) {
							for (int k = 0; k < vegetation_diff[nscene][ntile].length; k++) {
								diff_dbg[2*k + 1][nscene][ntile] = vegetation_diff[nscene][ntile][k];
							}
						}
					}
				}
			}
			ShowDoubleFloatArrays.showArraysHyperstack(
					diff_dbg, // double[][][] pixels, 
					tilesX,                    // int          width, 
532
					quadCLTs[ref_index].getImageName()+"-terrain_vegetation_pXpYD_differetial",      // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
Andrey Filippov's avatar
Andrey Filippov committed
533 534 535 536 537 538 539
					titles_scene,           // String []    titles, // all slices*frames titles or just slice titles or null
					titles_frame,           // String []    frame_titles, // frame titles or null
					true);                  // boolean      show)
			
			
		}
		int dbg_scene = -64;
540
		boolean use_bicubic = true;
Andrey Filippov's avatar
Andrey Filippov committed
541 542 543 544 545 546
		double [][][] terrain_pix =    new double [num_scenes][][];
		double [][][] vegetation_pix = new double [num_scenes][][];
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			if (nscene == dbg_scene) {
				System.out.println("test_vegetation(): nscene="+nscene);
			}
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
			if (use_bicubic) {
				terrain_pix[nscene] =  interpolatePxPyDBicubic(
						terrain_diff[nscene],    // final double [][] pXpYD_tile,
						tilesX,                  // final int         tilesX,
						tileSize);               // final int         tile_size)
				vegetation_pix[nscene] =  interpolatePxPyDBicubic(
						vegetation_diff[nscene], // final double [][] pXpYD_tile,
						tilesX,                  // final int         tilesX,
						tileSize);               // final int         tile_size)
			} else {
				terrain_pix[nscene] =  interpolatePxPyDBilinear(
						terrain_diff[nscene],    // final double [][] pXpYD_tile,
						tilesX,                  // final int         tilesX,
						tileSize);               // final int         tile_size)
				vegetation_pix[nscene] =  interpolatePxPyDBilinear(
						vegetation_diff[nscene], // final double [][] pXpYD_tile,
						tilesX,                  // final int         tilesX,
						tileSize);               // final int         tile_size)
			}
Andrey Filippov's avatar
Andrey Filippov committed
566
		}
567

Andrey Filippov's avatar
Andrey Filippov committed
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
		
		if (show_debug) {
			String [] titles_frame = {"terr-pX","veg-pX","terr-pY","veg-pY","terr-D","veg-D"};
			String [] titles_scene = new String [num_scenes];
			
			double [][][] data_dbg = new double [titles_frame.length][num_scenes][num_pixels];
			for (int i = 0; i < data_dbg.length;i++) {
				for (int j = 0; j < data_dbg[0].length;j++) {
					Arrays.fill(data_dbg[i][j], Double.NaN);
				}
			}
			for (int nscene = 0; nscene < num_scenes; nscene++) {
				titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
				if ((terrain_pix[nscene] == null) || (vegetation_pix[nscene]==null)){
					System.out.println("test_vegetation(): null for nscene="+nscene);
				} else {
					for (int npix = 0; npix < num_pixels; npix++) {
						if (terrain_pix[nscene][npix] != null) {
							for (int k = 0; k < terrain_pix[nscene][npix].length; k++) {
								data_dbg[2*k + 0][nscene][npix] = terrain_pix[nscene][npix][k];
							}
						}
						if (vegetation_pix[nscene][npix] != null) {
							for (int k = 0; k < vegetation_pix[nscene][npix].length; k++) {
								data_dbg[2*k + 1][nscene][npix] = vegetation_pix[nscene][npix][k];
							}
						}
					}
				}
			}
598
			String title = quadCLTs[ref_index].getImageName()+"-terrain_vegetation_pix"+ (use_bicubic?"-bicubic":"-bilinear")+".tiff";
Andrey Filippov's avatar
Andrey Filippov committed
599 600 601
			ShowDoubleFloatArrays.showArraysHyperstack(
					data_dbg,                 // double[][][] pixels, 
					tilesX*tileSize,          // int          width, 
602
					title,                    // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
Andrey Filippov's avatar
Andrey Filippov committed
603 604 605 606 607 608
					titles_scene,             // String []    titles, // all slices*frames titles or just slice titles or null
					titles_frame,             // String []    frame_titles, // frame titles or null
					true);                    // boolean      show)
		}
		
		double [][][] vegetation_inv = new double [num_scenes][][];
609
		double [][][] terrain_inv =    new double [num_scenes][][];
610 611 612 613
		full =      new Rectangle(0,0,640,512);
		diff_mode =  true;
		boolean out_diff = diff_mode;
		Rectangle   out_window =full;
Andrey Filippov's avatar
Andrey Filippov committed
614
		int         patch_min_neibs = 6;
615 616
		int []      pnum_patched_v = new int[1];
		int []      pnum_patched_t = new int[1];
Andrey Filippov's avatar
Andrey Filippov committed
617 618 619 620
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			if (nscene == dbg_scene) {
				System.out.println("test_vegetation(): nscene="+nscene);
			}
621
			vegetation_inv[nscene] = invertMap( // used earlier
Andrey Filippov's avatar
Andrey Filippov committed
622 623 624 625 626 627
					vegetation_pix[nscene], // final double [][] map_in,
					tilesX*tileSize,        // final int         width,
					out_window,             // final Rectangle   out_window,
					true,                   // final boolean     in_diff,
					out_diff,               // final boolean     out_diff)
					patch_min_neibs,        // final int         patch_min_neibs)
628 629 630 631 632 633 634 635 636
					pnum_patched_v);          // final int []      pnum_patched)
			terrain_inv[nscene] = invertMap( // added
					terrain_pix[nscene], // final double [][] map_in,
					tilesX*tileSize,        // final int         width,
					out_window,             // final Rectangle   out_window,
					true,                   // final boolean     in_diff,
					out_diff,               // final boolean     out_diff)
					patch_min_neibs,        // final int         patch_min_neibs)
					pnum_patched_t);          // final int []      pnum_patched)
Andrey Filippov's avatar
Andrey Filippov committed
637 638 639
		}
		/* */
		double [][][] veg_to_terr =    new double [num_scenes][][];
640
		double [][][] terr_to_veg =    new double [num_scenes][][];
641
		Rectangle   window1 = new Rectangle(0,0,640,512);
Andrey Filippov's avatar
Andrey Filippov committed
642 643 644 645 646
		Rectangle   window2 = out_window;
		boolean     map_diff1 = true;
		boolean     map_diff2 = out_diff; // true;
		boolean     map_diff_out = true;
		for (int nscene = 0; nscene < num_scenes; nscene++) {
647
			veg_to_terr[nscene] = combineMaps( // used earlier
Andrey Filippov's avatar
Andrey Filippov committed
648 649 650 651 652 653 654
					terrain_pix[nscene],    // final double [][] map1,
					window1,                // final Rectangle   window1,
					map_diff1,              // final boolean     map_diff1,
					vegetation_inv[nscene], // final double [][] map2,
					window2,                // final Rectangle   window2,
					map_diff2,              // final boolean     map_diff2,
					map_diff_out);          // final boolean     map_diff_out) 
655 656 657 658 659 660 661 662
			terr_to_veg[nscene] = combineMaps( // added for inverse
					vegetation_pix[nscene],    // final double [][] map1,
					window1,                // final Rectangle   window1,
					map_diff1,              // final boolean     map_diff1,
					terrain_inv[nscene], // final double [][] map2,
					window2,                // final Rectangle   window2,
					map_diff2,              // final boolean     map_diff2,
					map_diff_out);          // final boolean     map_diff_out) 
663
		}
Andrey Filippov's avatar
Andrey Filippov committed
664
		
665
		vegetation_warp = veg_to_terr;
666
		vegetation_inv_warp = terr_to_veg;
667 668 669
		full = out_window;
		
		/* USED */
Andrey Filippov's avatar
Andrey Filippov committed
670
		if (show_debug) {
671 672 673 674 675
		showOffsetsCombo(
				veg_to_terr,                       // final double [][][] map_combo,
				tilesX*tileSize,                   // final int           width,
				quadCLTs,                          // QuadCLT []          quadCLTs, // just for names
				quadCLTs[ref_index].getImageName()+"-combo_offset.tiff"); // String              title) { // with .tiff
Andrey Filippov's avatar
Andrey Filippov committed
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
		}
		
		
		boolean mb_en =       clt_parameters.imp.mb_en; //  && (fov_tiles==null) && (mode3d > 0);
		double  mb_max_gain = clt_parameters.imp.mb_max_gain; // 5.0;   // motion blur maximal gain (if more - move second point more than a pixel
		
		double [][][] terrain_render = renderDouble(
				clt_parameters,   // CLTParameters  clt_parameters,
				mb_en,            // boolean        mb_en,
				mb_max_gain,      // double         mb_max_gain,
				reference_xyz,    // double []      reference_xyz, // offset reference camera {x,y,z}
				reference_atr,    // double []      reference_atr,    		
				terr_veg[0],      // double []      ref_disparity,			
				quadCLTs,         // QuadCLT []     quadCLTs,
	    		null,             // boolean []     scene_selection, // null or same length as quadCLTs
	    		ref_index,        // int            ref_index,
	    		terrain_pXpYD,    // double [][][]  pXpYD, 
	    		debugLevel);      // int            debugLevel){
694 695
		
		
Andrey Filippov's avatar
Andrey Filippov committed
696 697 698 699 700 701 702 703 704 705 706 707
		double [][][] vegetation_render = renderDouble(
				clt_parameters,   // CLTParameters  clt_parameters,
				mb_en,            // boolean        mb_en,
				mb_max_gain,      // double         mb_max_gain,
				reference_xyz,    // double []      reference_xyz, // offset reference camera {x,y,z}
				reference_atr,    // double []      reference_atr,    		
				terr_veg[1],      // double []      ref_disparity,			
				quadCLTs,         // QuadCLT []     quadCLTs,
	    		null,             // boolean []     scene_selection, // null or same length as quadCLTs
	    		ref_index,        // int            ref_index,
	    		vegetation_pXpYD, // double [][][]  pXpYD, 
	    		debugLevel);      // int            debugLevel){
708 709 710

		terrain_scenes_render =                new double [num_scenes][];
		double [][] vegetation_scenes_render = new double [num_scenes][]; 
Andrey Filippov's avatar
Andrey Filippov committed
711
		for (int i = 0; i < num_scenes; i++) {
712 713
			terrain_scenes_render[i] =    terrain_render[i][0];
			vegetation_scenes_render[i] = vegetation_render[i][0];
714
		}
715 716 717 718
		terrain_average_render =    averageMono(terrain_scenes_render);
		vegetation_average_render = averageMono(vegetation_scenes_render);
		model_directory =           quadCLTs[ref_index].getX3dDirectory(); // getX3dTopDirectory();
		model_top_directory =       quadCLTs[ref_index].getX3dTopDirectory();
Andrey Filippov's avatar
Andrey Filippov committed
719 720 721 722 723 724
		reference_scene =           quadCLTs[ref_index].getImageName();
		reference_index =           ref_index; 
		scene_names =               new String[num_scenes];
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			scene_names[nscene] =quadCLTs[nscene].getImageName(); 
		}
725 726 727
		return;
	}
	
728

Andrey Filippov's avatar
Andrey Filippov committed
729
	
730 731 732 733 734 735 736
	
	
	public static double [][] getTerrainVegetation(
			final double [][] combo_dsn,
			final int         tilesX,
			final double      min_above, // absolute disparity difference
			final int         patch_neibs,
Andrey Filippov's avatar
Andrey Filippov committed
737 738
			final double      neibs_pow, // raise strength to this power when averaging neighbors
			final int grow_vegetation){
739 740 741 742 743 744 745
		final double [] terrain = combo_dsn[OpticalFlow.COMBO_DSN_INDX_TERRAIN]; 
		final double [] ground =  combo_dsn[OpticalFlow.COMBO_DSN_INDX_GROUND]; 
		final double [] disparity =  combo_dsn[OpticalFlow.COMBO_DSN_INDX_LMA]; 
		final double [] strength =  combo_dsn[OpticalFlow.COMBO_DSN_INDX_STRENGTH]; 
		final int num_pixels = disparity.length;
		final double [] vegetation = new double [num_pixels];
		Arrays.fill(vegetation, Double.NaN);
Andrey Filippov's avatar
Andrey Filippov committed
746
		final int dbg_tile = (25+20*80);
Andrey Filippov's avatar
Andrey Filippov committed
747 748 749 750 751
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
					TileNeibs tn = new TileNeibs(tilesX, vegetation.length/tilesX);
					for (int nTile = ai.getAndIncrement(); nTile < vegetation.length; nTile = ai.getAndIncrement()) {
						if (nTile==dbg_tile) {
							System.out.println("getTerrainVegetation(): nTile="+nTile);
						}
						if ((ground[nTile] > terrain[nTile]) &&(disparity[nTile]-terrain[nTile] > min_above)) { 
							vegetation[nTile] = disparity[nTile];
						} else {
							int num_neibs = 0;
							double sum_wd = 0.0, sum_w = 0.0;
							for (int dir = 0; dir < TileNeibs.DIRS; dir++) {
								int nTile1 = tn.getNeibIndex(nTile, dir);
								if ((nTile1 >= 0) &&
										(disparity[nTile1]-terrain[nTile1] > min_above) &&
										(ground[nTile1] > terrain[nTile1]) && 
										(strength[nTile1] > 0)) {
									double d = disparity[nTile1];
									double w = Math.pow(strength[nTile1], neibs_pow);
									sum_wd += d*w;
									sum_w += w;
									num_neibs++;
								}
							}
							if (num_neibs >= patch_neibs) {
								vegetation[nTile] = sum_wd/sum_w;
Andrey Filippov's avatar
Andrey Filippov committed
777 778 779 780 781 782 783
							}
						}
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
Andrey Filippov's avatar
Andrey Filippov committed
784 785 786 787 788 789 790 791 792 793 794 795 796 797
		double [] veget=vegetation;
		if (grow_vegetation > 0) {
			veget= TileProcessor.fillNaNs(
					vegetation, // final double [] data,
					null,                     // final boolean [] prohibit,
					tilesX,          // int       width,
					// CAREFUL ! Remaining NaN is grown by unsharp mask filter ************* !
					grow_vegetation,           // 100, // 2*width, // 16,           // final int grow,
					0.7,            // double    diagonal_weight, // relative to ortho
					100,            // int       num_passes,
					0.03);           // final double     max_rchange, //  = 0.01 - does not need to be accurate

		}
		return new double [][] {terrain,veget}; // ation};
798 799 800 801 802
	}
	
	
	
	
803 804 805 806 807 808 809 810
	/**
	 * Run from batch process for multi-scene sequences, prepare data for vegetation processing
	 * @param clt_parameters
	 * @param quadCLTs
	 * @param ref_index
	 * @param debugLevel
	 */
	public static void prepareVegetationData(
811 812 813 814 815 816 817 818 819
    		CLTParameters  clt_parameters,
    		QuadCLT []     quadCLTs,
    		int            ref_index,
    		int debugLevel) {
//Testing 
		VegetationModel vegetationModel = new VegetationModel (
				clt_parameters, // CLTParameters  clt_parameters,
				quadCLTs,       // QuadCLT []     quadCLTs,
				ref_index,      // int            ref_index,
820
				null,           // SyncCommand    SYNC_COMMAND,
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
				debugLevel);    // int            debugLevel)
		if (vegetationModel.isFailed()) {
			System.out.println("Creation of VegetationModel failed!");
			return;
		}
		
		String dir_state = quadCLTs[ref_index].getX3dDirectory(); // getX3dTopDirectory();
		String title_state = quadCLTs[ref_index].getImageName()+"-TERR-VEG-STATE";
		vegetationModel.saveState(
				dir_state, // String dir,
				title_state); // String title)
		if (debugLevel < 1000) {
			return;
		}
		
		
		
		
		TileProcessor tp = quadCLTs[ref_index].getTileProcessor();
		int tilesX = tp.getTilesX();
		int tilesY = tp.getTilesY();
		int tileSize = tp.getTileSize();
		double      min_above = 0.02; // 0.025; // .04;
		int         patch_neib = 4; // 5;
		double      neibs_pow =  0.5;
Andrey Filippov's avatar
Andrey Filippov committed
846
		final int   grow_vegetation = clt_parameters.imp.terr_veget_grow;
847 848 849 850 851 852
		double [][] combo_dsn =quadCLTs[ref_index].restoreComboDSI(true);
		double [][] terr_veg = getTerrainVegetation(
				combo_dsn,   // double [][] combo_dsn);
				tilesX,      // final int         tilesX,
				min_above,   // final double      min_above, // absolute disparity difference
				patch_neib,  // final int         patch_neib){
Andrey Filippov's avatar
Andrey Filippov committed
853 854
				neibs_pow,        // final double      neibs_pow);
				grow_vegetation); // final int grow_vegetation);
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288
				
		String [] titles_terr_veg = {"terrain","vegetation"};
		ShowDoubleFloatArrays.showArrays(
				terr_veg,
				tilesX,
				tilesY,
				true,
				quadCLTs[ref_index].getImageName()+"-terrain_vegetation-min_ab"+min_above+".tiff",
				titles_terr_veg);
		double []     reference_xyz = OpticalFlow.ZERO3;
		double []     reference_atr = OpticalFlow.ZERO3;
		double [][][] terrain_pXpYD =  getPxPyDs(
				reference_xyz, // double []      reference_xyz, // offset reference camera {x,y,z}
				reference_atr, // double []      reference_atr,    		
				terr_veg[0],   // double []      ref_disparity,			
				quadCLTs,      // QuadCLT []     quadCLTs,
	    		null,          // boolean []     scene_selection, // null or same length as quadCLTs
	    		ref_index,     // int            ref_index,
	    		debugLevel);   // int            debugLevel)
		double [][][] vegetation_pXpYD =  getPxPyDs(
				reference_xyz, // double []      reference_xyz, // offset reference camera {x,y,z}
				reference_atr, // double []      reference_atr,    		
				terr_veg[1],   // double []      ref_disparity,			
				quadCLTs,      // QuadCLT []     quadCLTs,
	    		null,          // boolean []     scene_selection, // null or same length as quadCLTs
	    		ref_index,     // int            ref_index,
	    		debugLevel);   // int            debugLevel)
		double [][][] terrain_diff =  diffPxPyDs(
				terrain_pXpYD,    // final double [][][] pXpYD,
				ref_index);       // final int ref_index)
		double [][][] vegetation_diff =  diffPxPyDs(
				vegetation_pXpYD, // final double [][][] pXpYD,
				ref_index);       // final int ref_index)
		
		
		int num_scenes =  quadCLTs.length;
		int num_tiles =   terrain_pXpYD[0].length;
		int num_pixels = num_tiles*tileSize*tileSize;
		boolean show_debug = true;
		if (show_debug) {
			String [] titles_frame = {"terr-pX","veg-pX","terr-pY","veg-pY","terr-D","veg-D"};
			String [] titles_scene = new String [num_scenes];
			
			double [][][] data_dbg = new double [titles_frame.length][num_scenes][num_tiles];
			for (int i = 0; i < data_dbg.length;i++) {
				for (int j = 0; j < data_dbg[0].length;j++) {
					Arrays.fill(data_dbg[i][j], Double.NaN);
				}
			}
			for (int nscene = 0; nscene < num_scenes; nscene++) {
				titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
				if ((terrain_pXpYD[nscene] == null) || (vegetation_pXpYD[nscene]==null)){
					System.out.println("test_vegetation(): null for nscene="+nscene);
				} else {
					for (int ntile = 0; ntile < num_tiles; ntile++) {
						if (terrain_pXpYD[nscene][ntile] != null) {
							for (int k = 0; k < terrain_pXpYD[nscene][ntile].length; k++) {
								data_dbg[2*k + 0][nscene][ntile] = terrain_pXpYD[nscene][ntile][k];
							}
						}
						if (vegetation_pXpYD[nscene][ntile] != null) {
							for (int k = 0; k < vegetation_pXpYD[nscene][ntile].length; k++) {
								data_dbg[2*k + 1][nscene][ntile] = vegetation_pXpYD[nscene][ntile][k];
							}
						}
					}
				}
			}
			ShowDoubleFloatArrays.showArraysHyperstack(
					data_dbg, // double[][][] pixels, 
					tilesX,                    // int          width, 
					"terrain_vegetation_pXpYD",      // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
					titles_scene,           // String []    titles, // all slices*frames titles or just slice titles or null
					titles_frame,           // String []    frame_titles, // frame titles or null
					true);                  // boolean      show)
			
			// same for differences
			
			double [][][] diff_dbg = new double [6][num_scenes][num_tiles];
			for (int i = 0; i < diff_dbg.length;i++) {
				for (int j = 0; j < diff_dbg[0].length;j++) {
					Arrays.fill(diff_dbg[i][j], Double.NaN);
				}
			}
			for (int nscene = 0; nscene < num_scenes; nscene++) {
//				titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
				if ((terrain_diff[nscene] == null) || (vegetation_diff[nscene]==null)){
					System.out.println("test_vegetation(): null for nscene="+nscene);
				} else {
					for (int ntile = 0; ntile < num_tiles; ntile++) {
						if (terrain_diff[nscene][ntile] != null) {
							for (int k = 0; k < terrain_diff[nscene][ntile].length; k++) {
								diff_dbg[2*k + 0][nscene][ntile] = terrain_diff[nscene][ntile][k];
							}
						}
						if (vegetation_diff[nscene][ntile] != null) {
							for (int k = 0; k < vegetation_diff[nscene][ntile].length; k++) {
								diff_dbg[2*k + 1][nscene][ntile] = vegetation_diff[nscene][ntile][k];
							}
						}
					}
				}
			}
			ShowDoubleFloatArrays.showArraysHyperstack(
					diff_dbg, // double[][][] pixels, 
					tilesX,                    // int          width, 
					"terrain_vegetation_pXpYD_differetial",      // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
					titles_scene,           // String []    titles, // all slices*frames titles or just slice titles or null
					titles_frame,           // String []    frame_titles, // frame titles or null
					true);                  // boolean      show)
			
			
		}
		int dbg_scene = -64;
		boolean use_bicubic = true;
		double [][][] terrain_pix =    new double [num_scenes][][];
		double [][][] vegetation_pix = new double [num_scenes][][];
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			if (nscene == dbg_scene) {
				System.out.println("test_vegetation(): nscene="+nscene);
			}
			if (use_bicubic) {
				terrain_pix[nscene] =  interpolatePxPyDBicubic(
						terrain_diff[nscene],    // final double [][] pXpYD_tile,
						tilesX,                  // final int         tilesX,
						tileSize);               // final int         tile_size)
				vegetation_pix[nscene] =  interpolatePxPyDBicubic(
						vegetation_diff[nscene], // final double [][] pXpYD_tile,
						tilesX,                  // final int         tilesX,
						tileSize);               // final int         tile_size)
			} else {
				terrain_pix[nscene] =  interpolatePxPyDBilinear(
						terrain_diff[nscene],    // final double [][] pXpYD_tile,
						tilesX,                  // final int         tilesX,
						tileSize);               // final int         tile_size)
				vegetation_pix[nscene] =  interpolatePxPyDBilinear(
						vegetation_diff[nscene], // final double [][] pXpYD_tile,
						tilesX,                  // final int         tilesX,
						tileSize);               // final int         tile_size)
			}
		}

		
		if (show_debug) {
			String [] titles_frame = {"terr-pX","veg-pX","terr-pY","veg-pY","terr-D","veg-D"};
			String [] titles_scene = new String [num_scenes];
			
			double [][][] data_dbg = new double [titles_frame.length][num_scenes][num_pixels];
			for (int i = 0; i < data_dbg.length;i++) {
				for (int j = 0; j < data_dbg[0].length;j++) {
					Arrays.fill(data_dbg[i][j], Double.NaN);
				}
			}
			for (int nscene = 0; nscene < num_scenes; nscene++) {
				titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
				if ((terrain_pix[nscene] == null) || (vegetation_pix[nscene]==null)){
					System.out.println("test_vegetation(): null for nscene="+nscene);
				} else {
					for (int npix = 0; npix < num_pixels; npix++) {
						if (terrain_pix[nscene][npix] != null) {
							for (int k = 0; k < terrain_pix[nscene][npix].length; k++) {
								data_dbg[2*k + 0][nscene][npix] = terrain_pix[nscene][npix][k];
							}
						}
						if (vegetation_pix[nscene][npix] != null) {
							for (int k = 0; k < vegetation_pix[nscene][npix].length; k++) {
								data_dbg[2*k + 1][nscene][npix] = vegetation_pix[nscene][npix][k];
							}
						}
					}
				}
			}
			String title = "terrain_vegetation_pix"+ (use_bicubic?"-bicubic":"-bilinear")+".tiff";
			ShowDoubleFloatArrays.showArraysHyperstack(
					data_dbg,                 // double[][][] pixels, 
					tilesX*tileSize,          // int          width, 
					title,                    // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
					titles_scene,             // String []    titles, // all slices*frames titles or just slice titles or null
					titles_frame,             // String []    frame_titles, // frame titles or null
					true);                    // boolean      show)
		}
		
		
		double [][][] terrain_inv =    new double [num_scenes][][];
		double [][][] vegetation_inv = new double [num_scenes][][];
		Rectangle   out_window = new Rectangle(0,0,640,512);
		boolean     out_diff = true;
		int         patch_min_neibs = 6;
		int []      pnum_patched = new int[1];
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			if (nscene == dbg_scene) {
				System.out.println("test_vegetation(): nscene="+nscene);
			}
			terrain_inv[nscene] = invertMap(
					terrain_pix[nscene],    // final double [][] map_in,
					tilesX*tileSize,        // final int         width,
					out_window,             // final Rectangle   out_window,
					true,                   // final boolean     in_diff,
					out_diff,               // final boolean     out_diff)
					patch_min_neibs,        // final int         patch_min_neibs)
					pnum_patched);          // final int []      pnum_patched)

					
					
			vegetation_inv[nscene] = invertMap(
					vegetation_pix[nscene], // final double [][] map_in,
					tilesX*tileSize,        // final int         width,
					out_window,             // final Rectangle   out_window,
					true,                   // final boolean     in_diff,
					out_diff,               // final boolean     out_diff)
					patch_min_neibs,        // final int         patch_min_neibs)
					pnum_patched);          // final int []      pnum_patched)
		}
		/* */
		double [][][] veg_to_terr =    new double [num_scenes][][];
		double [][][] terr_to_terr =    new double [num_scenes][][];
		Rectangle   window1 = new Rectangle(0,0,640,512);
		Rectangle   window2 = out_window;
		boolean     map_diff1 = true;
		boolean     map_diff2 = out_diff; // true;
		boolean     map_diff_out = true;
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			veg_to_terr[nscene] = combineMaps(
					terrain_pix[nscene],    // final double [][] map1,
					window1,                // final Rectangle   window1,
					map_diff1,              // final boolean     map_diff1,
					vegetation_inv[nscene], // final double [][] map2,
					window2,                // final Rectangle   window2,
					map_diff2,              // final boolean     map_diff2,
					map_diff_out);          // final boolean     map_diff_out) 
			terr_to_terr[nscene] = combineMaps(
					terrain_pix[nscene],    // final double [][] map1,
					window1,                // final Rectangle   window1,
					map_diff1,              // final boolean     map_diff1,
					terrain_inv[nscene], // final double [][] map2,
					window2,                // final Rectangle   window2,
					map_diff2,              // final boolean     map_diff2,
					map_diff_out);          // final boolean     map_diff_out) 
		}		
		/* */
		
		if (show_debug) {
			String [] titles_frame = {"terr-pX","veg-pX","terr-pY","veg-pY"};
			String [] titles_scene = new String [num_scenes];
			
			double [][][] data_dbg = new double [titles_frame.length][num_scenes][num_pixels];
			for (int i = 0; i < data_dbg.length;i++) {
				for (int j = 0; j < data_dbg[0].length;j++) {
					Arrays.fill(data_dbg[i][j], Double.NaN);
				}
			}
			for (int nscene = 0; nscene < num_scenes; nscene++) {
				titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
				if ((terrain_inv[nscene] == null) || (vegetation_inv[nscene]==null)){
					System.out.println("test_vegetation(): null for nscene="+nscene);
				} else {
					for (int npix = 0; npix < num_pixels; npix++) {
						if (terrain_inv[nscene][npix] != null) {
							for (int k = 0; k < terrain_inv[nscene][npix].length; k++) {
								data_dbg[2*k + 0][nscene][npix] = terrain_inv[nscene][npix][k];
							}
						}
						if (vegetation_inv[nscene][npix] != null) {
							for (int k = 0; k < vegetation_inv[nscene][npix].length; k++) {
								data_dbg[2*k + 1][nscene][npix] = vegetation_inv[nscene][npix][k];
							}
						}
					}
				}
			}
			ShowDoubleFloatArrays.showArraysHyperstack(
					data_dbg,                 // double[][][] pixels, 
					tilesX*tileSize,          // int          width, 
					quadCLTs[ref_index].getImageName()+"-terrain_vegetation_inv", // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
					titles_scene,             // String []    titles, // all slices*frames titles or just slice titles or null
					titles_frame,             // String []    frame_titles, // frame titles or null
					true);                    // boolean      show)
			
			showOffsetsDiff(
					terrain_inv,                       // final double [][][] terrain,
					vegetation_inv,                    // final double [][][] vegetation,
					tilesX*tileSize,                   // final int           width,
					quadCLTs,                          // QuadCLT []          quadCLTs, // just for names
					quadCLTs[ref_index].getImageName()+"-terrain_vegetation_offset.tiff"); // String              title) { // with .tiff
			/* USED */
			showOffsetsCombo(
					veg_to_terr,                       // final double [][][] map_combo,
					tilesX*tileSize,                   // final int           width,
					quadCLTs,                          // QuadCLT []          quadCLTs, // just for names
					
					quadCLTs[ref_index].getImageName()+"-combo_offset.tiff"); // String              title) { // with .tiff
			showOffsetsCombo(
					terr_to_terr,                       // final double [][][] map_combo,
					tilesX*tileSize,                   // final int           width,
					quadCLTs,                          // QuadCLT []          quadCLTs, // just for names
					quadCLTs[ref_index].getImageName()+"-combo_terr-terr.tiff"); // String              title) { // with .tiff
			/*	*/
		}

		
		
		
		boolean mb_en =       clt_parameters.imp.mb_en; //  && (fov_tiles==null) && (mode3d > 0);
		double  mb_max_gain = clt_parameters.imp.mb_max_gain; // 5.0;   // motion blur maximal gain (if more - move second point more than a pixel
		
		double [][][] terrain_render = renderDouble(
				clt_parameters,   // CLTParameters  clt_parameters,
				mb_en,            // boolean        mb_en,
				mb_max_gain,      // double         mb_max_gain,
				reference_xyz,    // double []      reference_xyz, // offset reference camera {x,y,z}
				reference_atr,    // double []      reference_atr,    		
				terr_veg[0],      // double []      ref_disparity,			
				quadCLTs,         // QuadCLT []     quadCLTs,
	    		null,             // boolean []     scene_selection, // null or same length as quadCLTs
	    		ref_index,        // int            ref_index,
	    		terrain_pXpYD,    // double [][][]  pXpYD, 
	    		debugLevel);      // int            debugLevel){
		double [][][] vegetation_render = renderDouble(
				clt_parameters,   // CLTParameters  clt_parameters,
				mb_en,            // boolean        mb_en,
				mb_max_gain,      // double         mb_max_gain,
				reference_xyz,    // double []      reference_xyz, // offset reference camera {x,y,z}
				reference_atr,    // double []      reference_atr,    		
				terr_veg[1],      // double []      ref_disparity,			
				quadCLTs,         // QuadCLT []     quadCLTs,
	    		null,             // boolean []     scene_selection, // null or same length as quadCLTs
	    		ref_index,        // int            ref_index,
	    		vegetation_pXpYD, // double [][][]  pXpYD, 
	    		debugLevel);      // int            debugLevel){
		double [][] terrain_mono =    new double [num_scenes][]; 
		double [][] vegetation_mono = new double [num_scenes][]; 
		double [][][] terrain_vegetation_all =     new double [2][num_scenes+1][]; 
		for (int i = 0; i < num_scenes; i++) {
			terrain_mono[i] =    terrain_render[i][0];
			vegetation_mono[i] = vegetation_render[i][0];
		}
		System.arraycopy(terrain_mono,    0, terrain_vegetation_all[0], 0, num_scenes);
		System.arraycopy(vegetation_mono, 0, terrain_vegetation_all[1], 0, num_scenes);
		terrain_vegetation_all[0][num_scenes] = averageMono(terrain_mono);
		terrain_vegetation_all[1][num_scenes] = averageMono(vegetation_mono);
		
		/* Used */
		if (show_debug) {
			String [] titles_frame = {"terrain","vegetation"};
			String [] titles_scene = new String [num_scenes+1];
			for (int nscene = 0; nscene < num_scenes; nscene++) {
				titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
			}
			titles_scene[num_scenes] = "average";
			ShowDoubleFloatArrays.showArraysHyperstack(
					terrain_vegetation_all,           // double[][][] pixels, 
					tilesX * tileSize,                // int          width, 
					quadCLTs[ref_index].getImageName()+"-terrain_vegetation_render.tiff", // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
					titles_scene,                     // String []    titles, // all slices*frames titles or just slice titles or null
					titles_frame,                     // String []    frame_titles, // frame titles or null
					true);                            // boolean      show)
		}
		/* */
		double [][] vegetation_mapped =    new double [num_scenes][];
		double      scale_map = 1.0; // 0.5;
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			vegetation_mapped[nscene] = applyMap(
//					vegetation_render[nscene][0], // final double []   img,
					terrain_vegetation_all[1][num_scenes], // final double []   img, // average vegetation
					tilesX * tileSize,         // final int         img_width,
					veg_to_terr[nscene],       // final double [][] map,
					window1,                   // final Rectangle   window,
					map_diff_out,              // final boolean     map_diff)
					scale_map);                    // final double      scale) { // debug feature, only works with differential map

		}	

		if (show_debug) {
			double [][] diff_veg =  new double [num_scenes][num_pixels];
			double [][] diff_terr = new double [num_scenes][num_pixels];
			for (int nscene = 0; nscene < num_scenes; nscene++) {
				Arrays.fill(diff_veg[nscene],Double.NaN);
				Arrays.fill(diff_terr[nscene],Double.NaN);
				for (int nPix = 0; nPix < num_pixels; nPix++) {
					diff_veg[nscene][nPix] = terrain_mono[nscene][nPix] - vegetation_mapped[nscene][nPix];
					if (!Double.isNaN(diff_veg[nscene][nPix])) {
						diff_terr[nscene][nPix] = terrain_mono[nscene][nPix] - terrain_vegetation_all[1][num_scenes][nPix];
					}
				}
			}
			String [] titles_frame = {"terrain","diff_veg", "diff_terr","mapped_vegetation", "vegetation"};
			String [] titles_scene = new String [num_scenes];
			for (int nscene = 0; nscene < num_scenes; nscene++) {
				titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
			}
			double [][][] render3 = {terrain_mono, diff_veg, diff_terr, vegetation_mapped, vegetation_mono};
			ShowDoubleFloatArrays.showArraysHyperstack(
					render3,           // double[][][] pixels, 
					tilesX * tileSize,                // int          width, 
					quadCLTs[ref_index].getImageName()+"-terrain_vegetation_mapped-scale"+scale_map+".tiff", // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
					titles_scene,                     // String []    titles, // all slices*frames titles or just slice titles or null
					titles_frame,                     // String []    frame_titles, // frame titles or null
					true);                            // boolean      show)
			
			ShowDoubleFloatArrays.showArrays(
					vegetation_mapped,
					tilesX * tileSize,
					tilesY * tileSize,
					true,
					quadCLTs[ref_index].getImageName()+"-vegetation_mapped.tiff",
					titles_terr_veg);
			ShowDoubleFloatArrays.showArrays(
					diff_veg,
					tilesX * tileSize,
					tilesY * tileSize,
					true,
					quadCLTs[ref_index].getImageName()+"-diff-veg.tiff",
					titles_terr_veg);
			ShowDoubleFloatArrays.showArrays(
					diff_terr,
					tilesX * tileSize,
					tilesY * tileSize,
					true,
					quadCLTs[ref_index].getImageName()+"-diff-terr.tiff",
					titles_terr_veg);

			
			
		}
		/* */
		return;
	}

	
	
	
	
	
	
1289
	public static void processVegetation(
1290
			SyncCommand  SYNC_COMMAND,
1291 1292 1293
			CLTParameters    clt_parameters,
			boolean combine_segments) 
	{
1294
		
1295
		String    model_directory =        clt_parameters.imp.terr_model_path;          // Model directory path with version.
1296
		String    model_state_file =       clt_parameters.imp.terr_model_state;         // Model vegetation source data (w/o extension).
1297
		int       terr_debug=              clt_parameters.imp.terr_debug;
1298

1299
		// Temporary , it is actually a model directory // 1697877487_245877-TERR-VEG-STATE.terrveg-tiff
1300 1301 1302 1303 1304
		//		String model_directory = "/media/elphel/SSD3-4GB/lwir16-proc/berdich3/linked/linked_1697875868-1697879449-b/1697877487_245877/v35";
		//		String model_state_file = "1697877487_245877-TERR-VEG-STATE"; 

		if (model_state_file == null) {
			System.out.println("model_state_file==null, old test version removed" );
1305
		}
1306 1307
		VegetationModel vegetationModel = new VegetationModel(
				model_directory, // String dir,
1308 1309 1310
				model_state_file, // String title)
				SYNC_COMMAND);    // SyncCommand    SYNC_COMMAND,

1311
		vegetationModel.processVegetationLMA(
1312 1313
				combine_segments, // boolean combine_segments,
				clt_parameters, // CLTParameters      clt_parameters,
1314
				terr_debug); // int                debugLevel) {
1315 1316
	}
	
1317 1318 1319 1320 1321
	
	
	
	
	
1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352
	public static void unsharpMaskMulti(
			final double [][] data,
			final int         width,
			final double      um_sigma,
			final double      um_weight) {
		final int height = data[0].length / width;
		final int num_images = data.length;
		  final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		  final AtomicInteger ai = new AtomicInteger(0);
		  for (int ithread = 0; ithread < threads.length; ithread++) {
			  threads[ithread] = new Thread() {
				  public void run() {
					  double [] data_orig = new double [width * height];
					  for (int nimg = ai.getAndIncrement(); nimg < num_images; nimg = ai.getAndIncrement()) {
						  double [] data_slice = data[nimg];
						  System.arraycopy(data_slice, 0, data_orig,0,data_orig.length);
						  (new DoubleGaussianBlur()).blurDouble(
								  data_slice,          //
								  width,
								  height,
								  um_sigma,              // double sigmaX,
								  um_sigma,              // double sigmaY,
								  0.01);                 // double accuracy)
						  for (int i = 0; i < data_orig.length; i++) {
							  data_slice[i] = data_orig[i] - um_weight * data_slice[i];
						  }
					  }
				  }
			  };
		  }		      
		  ImageDtt.startAndJoin(threads);
1353
		  return;
1354 1355
	}
	
1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
	public static void unsharpMask(
			final double [] data,
			final int       width,
			final double    um_sigma,
			final double    um_weight) {
		final int height = data.length / width;
		double [] data_orig = new double [width * height];
		System.arraycopy(data, 0, data_orig, 0, data_orig.length);
		(new DoubleGaussianBlur()).blurDouble(
				data,          //
				width,
				height,
				um_sigma,              // double sigmaX,
				um_sigma,              // double sigmaY,
				0.01);                 // double accuracy)
		for (int i = 0; i < data_orig.length; i++) {
			data[i] = data_orig[i] - um_weight * data[i];
		}
	}

	
	
	
1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
	public static Rectangle nextTileWoi(
			Rectangle enclosing,
			Rectangle step,
			Rectangle single,
			Rectangle current,
			boolean tile_woi) {
		if (!tile_woi) {
			if (current == null) {
				return single;
			} else {
				return null;
			}
		} else {
			if (current == null) {
				return new Rectangle (enclosing.x, enclosing.y, step.width, step.height);
			} else {
				int x = current.x + step.x;
				int y = current.y + step.y;
				if (x < enclosing.x + enclosing.width) {
					return new Rectangle (x, current.y, step.width, step.height);
				} else if (y <  enclosing.y + enclosing.height) {
					return new Rectangle (enclosing.x, y, step.width, step.height);
				} else {
					return null;
				}
			}
		}
	}
	
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454
	public static boolean [] thisOrLast(
			int          index,
			boolean [][] sequence) {
		boolean [] selected = new boolean [sequence.length];
		for (int i = 0; i <selected.length; i++) {
			selected[i] = sequence[i][Math.min(index, sequence[i].length - 1)];
		}
		return selected;
	}

	public static boolean thisOrLast(
			int        index,
			boolean [] sequence) {
		return sequence[Math.min(index, sequence.length - 1)];
	}

	public static int thisOrLast(
			int        index,
			int [] sequence) {
		return sequence[Math.min(index, sequence.length - 1)];
	}
	public static double thisOrLast(
			int        index,
			double [] sequence) {
		return sequence[Math.min(index, sequence.length - 1)];
	}
	
	public static int [] thisOrLast(
			int          index,
			int [][]     sequence) {
		int [] selected = new int [sequence.length];
		for (int i = 0; i <selected.length; i++) {
			selected[i] = sequence[i][Math.min(index, sequence[i].length - 1)];
		}
		return selected;
	}
	public static double [] thisOrLast(
			int          index,
			double [][]   sequence) {
		double [] selected = new double [sequence.length];
		for (int i = 0; i <selected.length; i++) {
			selected[i] = sequence[i][Math.min(index, sequence[i].length - 1)];
		}
		return selected;
	}
	
	
1455
	public void processVegetationLMA(
1456
			boolean            combine_segments,
1457 1458 1459 1460
			CLTParameters      clt_parameters,
			int                debugLevel) {
		
		
1461
		boolean   run_combine =  combine_segments; //          true;   // if true, run combining instead of LMA
1462
		String    segments_sub =      clt_parameters.imp.terr_segments_dir;         //
1463
		String    segments_suffix =   clt_parameters.imp.terr_segments_suffix;
1464 1465 1466
		String    parameters_dir =    clt_parameters.imp.terr_par_dir;         //
		String    parameters_file =   clt_parameters.imp.terr_par_file;         //
		boolean   par_restore =       clt_parameters.imp.terr_par_restore;               // true;
1467 1468
//		int       
		step_restore=       par_restore? clt_parameters.imp.terr_step_restore : 0;
1469
		
1470 1471
//		boolean   um_en =             clt_parameters.imp.terr_um_en;               // true;
		double    um_sigma =          clt_parameters.imp.terr_um_sigma;            // 1.0; // use for render 
1472
		double    um_weight =         clt_parameters.imp.terr_um_weight;           // 0.8;
1473
		double    nan_tolerance =     0; // um_en ? clt_parameters.imp.terr_nan_tolerance : 0;       // 0.001;
1474 1475 1476 1477 1478 1479
		int       nan_grow =          clt_parameters.imp.terr_nan_grow;            //  20;
		int       shrink_veget =      clt_parameters.imp.terr_shrink_veget;        //  20;
		int       shrink_terrain =    clt_parameters.imp.terr_shrink_terrain;      //  20;
		double    vegetation_over =   clt_parameters.imp.terr_vegetation_over;     //  35;
		int       filter_veget =      clt_parameters.imp.terr_filter_veget;        //  10;
		
1480 1481 1482
		
		
		
1483 1484
//		boolean  
		tile_woi =                    clt_parameters.imp.terr_tile_woi;            // scan woi_enclosing, false - run a single woi_last
1485 1486 1487 1488 1489
		Rectangle woi_enclosing =     clt_parameters.imp.terr_woi_enclos;          // new Rectangle(0,  0, 200, 160); // will be tiled, using width/height from woi_step;
		Rectangle woi_step =          clt_parameters.imp.terr_woi_step;            // new Rectangle(10,10,20,20);
		Rectangle woi_last =          clt_parameters.imp.terr_woi_last;            // new Rectangle(160,310,20,20); // 170
		boolean   skip_existing_woi = clt_parameters.imp.terr_skip_exist;          // skip existing woi/parameters, already saved.
		boolean   continue_woi =      clt_parameters.imp.terr_continue;            // false;
1490
		double    max_warp =          clt_parameters.imp.terr_max_warp; // 1.8 - do not use scenes where distance between vegetation projection exceeds this
1491
		int       max_elevation =     clt_parameters.imp.terr_max_elevation;       // maximal "elevation" to consider 
1492 1493
		int       max_elev_terr =     clt_parameters.imp.terr_max_elev_terr;       // 2.0 maximal "elevation" to consider 
		double    max_elev_terr_chg = clt_parameters.imp.terr_max_elev_chng;     // 0.5 maximal terrain elevation change from last successfully used 
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503
		
//		int       min_scenes =        clt_parameters.imp.terr_min_scenes;          // 1;
//		int       min_samples_scene = clt_parameters.imp.terr_min_samples_scene;   //10;
//		int       min_total_scenes =  clt_parameters.imp.terr_min_total_scenes;    // 2;
//		int       min_pixels =        clt_parameters.imp.terr_min_pixels;          //10;
		
//		boolean   start_warm_veget =  clt_parameters.imp.terr_warm_veget;          // start with vegetation warmer than terrain
//		double    terrain_warmest =   clt_parameters.imp.terr_warmest;             // pull vegetations to warm, terrain to cold
//		double    initial_split =     clt_parameters.imp.terr_initial_split;       // pull vegetations to warm, terrain to cold
//		double    min_split_frac =    clt_parameters.imp.terr_min_split_frac;// 0.15;
1504 1505
//		double    terr_difference =   clt_parameters.imp.terr_difference;          // Pull vegetation to be this warmer
//		double    terr_pull_cold =    clt_parameters.imp.terr_pull_cold;           // pull vegetations to warm, terrain to cold
1506
		double    elevation_radius =  clt_parameters.imp.terr_elevation_radius;    // Radius of elevation/vegetation influence 
1507 1508 1509
		double    terr_elev_radius =  clt_parameters.imp.terr_terr_elev_radius;    // Terrain elevation radius 
		double    elev_radius_extra = clt_parameters.imp.terr_elev_radius_extra;   // scale both radii when setupElevationLMA(), setupTerrainElevationLMA(), and setupTerrainElevationPixLMA()  
		
1510 1511 1512 1513 1514 1515 1516
//		double    alpha_initial_contrast = clt_parameters.imp.terr_alpha_contrast; // initial alpha contrast (>=1.0)

		double    alpha_sigma =       clt_parameters.imp.terr_alpha_sigma;         // 8.0;  // Initial alpha: Gaussian blur sigma to find local average for vegetation temperature.
		double    alpha_init_min=     clt_parameters.imp.terr_alpha_init_min;      // 0.7;  // Initial alpha: fraction for transparent 
		double    alpha_init_max=     clt_parameters.imp.terr_alpha_init_max;      // 0.9;  // Initial alpha: fraction for opaque 
        double    alpha_init_offs=    clt_parameters.imp.terr_alpha_init_offs;     // 0.01; // Initial alpha: opaque/transparent offset from 1.0/0.0 
        
1517
//		double    default_alpha =     clt_parameters.imp.terr_alpha_dflt;          //  0.5; //  0.8;
1518 1519
		double    alpha_loss =        clt_parameters.imp.terr_alpha_loss;          //100.0; // alpha quadratic growing loss for when out of [0,1] range 
		double    alpha_loss_lin =    clt_parameters.imp.terr_alpha_loss_lin;      // alpha linear growing loss for when out of [0,1] range and below minimal vegetation alpha 
1520 1521
		double    alpha_offset =      clt_parameters.imp.terr_alpha_offset;        //  0.0; // 0.02; // 0.03;    // if >0,  start losses below 1.0;
		double    alpha_0offset =     clt_parameters.imp.terr_alpha_0offset;       //  0.0; // if >0,  start losses above 0.0
1522 1523
		double    alpha_min_veg =     clt_parameters.imp.terr_alpha_min_veg;       //  0.5  // Minimal vegetation alpha. If (alpha-alpha_offset)/(1-2*alpha_offset) < alpha_min_veg, pull down to lpha_offset
		
1524 1525 1526
		double    alpha_max_terrain = clt_parameters.imp.terr_alpha_max_terrain;   //  0.75; // () increase pull vegetation if below
		double    alpha_pull_pwr =    clt_parameters.imp.terr_alpha_pull_pwr;      //  1.0;  // () raise extra pull to that power
		
1527
		double    alpha_lpf =         clt_parameters.imp.terr_alpha_lpf;           //  2.5; // 5; /// 2; // 5; /// 10; /// 15; // 10.0; // 5.0; // 10.0; // 3; // 10;   // 20; // 6.0; // 3.0; // 2.0;    // 1.5; // 5.0; // 0.5;         // pull to average of 4 neighbors
1528
		double    alpha_lpf_border =  clt_parameters.imp.terr_alpha_lpf_border;    //  10.0; // pull to average of 4 neighbors for border tiles (to keep stable)
1529 1530 1531 1532 1533 1534 1535
		boolean   alpha_piece_linear =clt_parameters.imp.terr_alpha_piece_linear;  // true; // false; // true;
		double    alpha_scale_avg =   clt_parameters.imp.terr_alpha_scale_avg;     //  1.0; // 1.1; // 0.9; // 2.0; // 1.5; // scale average alpha (around 0.5) when pulling to it
		double    alpha_push =        clt_parameters.imp.terr_alpha_push;          // 12; // 10.0; // 15.0;   // push from alpha==0.5
		double    alpha_push_neutral =clt_parameters.imp.terr_alpha_push_neutral;  // 0.5; // 0.6; // 0.8; // alpha point from which push (closer to opaque)
		double    alpha_push_center = clt_parameters.imp.terr_alpha_weight_center; // 1.5; // weight of center alpha pixel relative to each of the 4 ortho ones
		boolean   alpha_en_holes =    clt_parameters.imp.terr_en_holes;            // true; // false; // true;
		double    alpha_mm_hole =     clt_parameters.imp.terr_alpha_mm_hole;       // 0.1; // NaN to disable. Local "almost minimum" (lower than this fraction between min and max neighbor) is not subject to alpha_lpf
1536 1537
		double    alpha_diff_hole =   clt_parameters.imp.terr_alpha_diff_hole;     // 0.01; Minimal alpha difference between min and max neighbor to be considered a hole
		
1538 1539
		double    terr_lpf =          clt_parameters.imp.terr_terr_lpf;            // 0.1; // 0.15; /// 0.2;  /// 0.1;    // pull terrain to average of 4 neighbors (very small)
		double    veget_lpf =         clt_parameters.imp.terr_veget_lpf;           // 0.2; //0.15; /// 0.2; //// 0.01; /// 0.1;    // pull vegetation to average of 4 neighbors (very small - maybe not needed)
1540
		double    elevation_lpf =     clt_parameters.imp.terr_elev_lpf;		
1541
		double    terr_elev_lpf =     clt_parameters.imp.terr_terr_elev_lpf;		
1542
		double    terr_pull0 =        clt_parameters.imp.terr_terr_pull0;          // 0.1; //0.03; ////// 0.05; ///// 0.1; //// 0.01; /// 0.2; /// 0.1;    //pull terrain to zero (makes sense with UM
1543 1544
		double    terr_pull_up =      clt_parameters.imp.terr_terr_pull_up;        // 0.2; // Terrain pixels pull to initial (pre-adjustment) values when it is colder than initial.
		double    terr_pull_avg =     clt_parameters.imp.terr_terr_pull_avg;       // 0.1; // Pull terrain to the initial offset by the average offset of all terrain pixels  
1545
		double    veget_pull0 =       clt_parameters.imp.terr_veget_pull0;         // 0.05; //0.1; // 0.03; ////// 0.05; ///// 0.1; //// 0.01; /// 0.1;    // pull vegetation to zero (makes sense with UM
1546 1547 1548

		double    veget_pull_low_alpha = clt_parameters.imp.terr_veget_pull_low_alpha; //10;   // scale pull0 for low alpha (mostly terrain)
		
1549
		double    elev_pull0 =        clt_parameters.imp.terr_elev_pull0; 
1550 1551 1552 1553
		double    terr_elev_pull0 =   clt_parameters.imp.terr_terr_elev_pull0; 

		
		boolean   elev_alpha_en=      clt_parameters.imp.terr_elev_alpha_en; // false;// Enable loss for low vegetation with high opacity
1554 1555 1556
	    double    elev_alpha =        clt_parameters.imp.terr_elev_alpha;    // 1.0;  // multiply alpha by under-low elevation for loss
	    double    elev_alpha_pwr =    clt_parameters.imp.terr_elev_alpha_pwr;    // 1.0;  // multiply alpha by under-low elevation for loss
	    double    low_veget =         clt_parameters.imp.terr_low_veget;     // 2.0;  // (pix) Elevation considered low (lower loss for high alpha)
1557
		double    scenes_pull0 =      clt_parameters.imp.terr_scenes_pull0;        // 1.0
1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
		   // scaling elevation losses for high elevations (decrease pull and/or lpf for high elevations)
		double    elev_scale_thresh = clt_parameters.imp.terr_elev_scale_thresh;   // 1.0;   // reduce losses for higher (initial) elevations TODO: consider actual elevations
		boolean   elev_scale_pull =   clt_parameters.imp.terr_elev_scale_pull;     // false; // scale elevation pull losses for high elevations 
		boolean   elev_scale_lpf =    clt_parameters.imp.terr_elev_scale_lpf;      // false; // scale elevation diffusion losses for high elevations 
			// tree-top removal
		boolean   ttop_en =           clt_parameters.imp.terr_ttop_en;             // false; // remove tree tops from transparency weights
		double    ttop_gb =           clt_parameters.imp.terr_ttop_gb;             // 1.0;  // Elevation Gaussian blur sigma to detect tree tops
		double    ttop_min =          clt_parameters.imp.terr_ttop_min;            // 3.0;   // Minimal tree top elevation
		double    ttop_rel_lev =      clt_parameters.imp.terr_ttop_rel_lev;        // 0.9;   // Relative (to the top height) sample level
		double    ttop_rel_rad =      clt_parameters.imp.terr_ttop_rel_rad;        // 0.25;  // Relative (to the top height) sample ring radius
		double    ttop_frac =         clt_parameters.imp.terr_ttop_frac;           // 0.5;   // Minimal fraction of the ring pixels below sample level
		double    ttop_rem_rad =      clt_parameters.imp.terr_ttop_rem_rad;        // 0.25;  // Relative (to the top height) remove transparency radius
		boolean   ttop_no_border =     true;  // No maximums on the border allowed
1571 1572 1573 1574
		// LMA parameters        
		double    boost_parallax =    clt_parameters.imp.terr_boost_parallax;      // 3.0; /// 1.0; /////// 5.0; /// 1.0; //  5;
		double    max_parallax =      clt_parameters.imp.terr_max_parallax;        // 10;  
		double    hifreq_weight =     clt_parameters.imp.terr_hifreq_weight;       // 22.5; //  0 - do not use high-freq. Relative weight of laplacian components		double    reg_weights =   0.25;        // fraction of the total weight used for regularization
1575
		double    terrain_correction= clt_parameters.imp.terr_terr_corr; 
1576 1577 1578 1579 1580 1581
		
		boolean   fit_terr =          clt_parameters.imp.terr_fit_terr;       // true;  // adjust terrain pixels
		boolean   fit_veget =         clt_parameters.imp.terr_fit_veget;      // true;  // adjust vegetation pixels
		boolean   fit_alpha =         clt_parameters.imp.terr_fit_alpha;      // true;  // adjust vegetation alpha pixels
		boolean   fit_scenes =        clt_parameters.imp.terr_fit_scenes;     // true;  // adjust scene offsets (start from 0 always?)
		boolean   fit_elevations =    clt_parameters.imp.terr_fit_elevations; // false; // adjust elevation pixels (not yet implemented)
1582
		boolean   fit_terr_elev =     clt_parameters.imp.terr_fit_terr_elev;  // false; // adjust terrain elevation (common, maybe add per-pixel later)
1583
		boolean   fit_terr_elev_pix =     clt_parameters.imp.terr_fit_terr_elev_pix;  // false; // adjust terrain elevation (common, maybe add per-pixel later)
1584
		
1585 1586 1587 1588
		boolean [][] fits_disable = new boolean[clt_parameters.imp.terr_fits_disable.length][];
		for (int i = 0; i < fits_disable.length; i++) {
			fits_disable[i] =      clt_parameters.imp.terr_fits_disable[i].clone();
		}
1589
		
Andrey Filippov's avatar
Andrey Filippov committed
1590 1591 1592
		boolean [][] fits_disable_terronly = new boolean[clt_parameters.imp.terr_only_fits_disable.length][];
		for (int i = 0; i < fits_disable_terronly.length; i++) {
			fits_disable_terronly[i] =   clt_parameters.imp.terr_only_fits_disable[i].clone();
1593 1594
		}
		
1595 1596 1597 1598 1599 1600
		double    reg_weights =       clt_parameters.imp.terr_reg_weights;         // 0.25;        // fraction of the total weight used for regularization
		double    lambda =            clt_parameters.imp.terr_lambda;              // 5.0; // 0.1;
		double    lambda_scale_good = clt_parameters.imp.terr_lambda_scale_good;   // 0.5;
		double    lambda_scale_bad =  clt_parameters.imp.terr_lambda_scale_bad;    // 8.0;
		double    lambda_max =        clt_parameters.imp.terr_lambda_max;          // 1000;
		double    rms_diff =          clt_parameters.imp.terr_rms_diff;            // 1e-8; // 0.0001; virtually forever
1601
		int []    num_iters =         clt_parameters.imp.terr_num_iters;           // {25}; // 100;
1602
		int       last_series =       clt_parameters.imp.terr_last_series;         // -1;   // 100;
1603
		if (last_series < 0) last_series = num_iters.length - 1; 
1604
		
Andrey Filippov's avatar
Andrey Filippov committed
1605 1606
///		boolean   terr_only_special = clt_parameters.imp.terr_only_special;        // true;  // special sequences for terrain-only tiles
///		boolean   terr_only_pix =     clt_parameters.imp.terr_only_pix;            // true;  // force per-pixel terrain elevation in terrain-only mode, overwrite fits_disable[TVAO_TERR_ELEV_PIX]
1607 1608 1609 1610
		int       terr_only_series =  clt_parameters.imp.terr_only_series;         //  -1;   // similar to terr_last_series but for terrain-only mode (<0 - length of terr_only_num_iters)
		int []    terr_only_num_iters=clt_parameters.imp.terr_only_num_iters;      // {25};  // number of iterations
		if (terr_only_series < 0) terr_only_series = terr_only_num_iters.length - 1; 
		
1611 1612 1613 1614 1615 1616 1617 1618 1619 1620
		// Combine  parameters
		int       border_width =      clt_parameters.imp.terr_border_width;        // 6;
		boolean   render_open =       clt_parameters.imp.terr_render_open;         // true;  // render open areas (no vegetation offset)
		boolean   render_no_alpha =   clt_parameters.imp.terr_render_no_alpha;     // true;  // render where no opacity is available
		double    alpha_min =         clt_parameters.imp.terr_alpha_min;           // 0.1;   // below - completely transparent vegetation
		double    alpha_max =         clt_parameters.imp.terr_alpha_max;           // 0.8;   // above - completely opaque
		double    weight_opaque =     clt_parameters.imp.terr_weight_opaque;       // 0.02;  // render through completely opaque vegetation
		double    boost_parallax_render = clt_parameters.imp.terr_boost_render;    // 3;     // increase weights of scenes with high parallax relative to the reference one
		double    max_parallax_render =   clt_parameters.imp.terr_max_render;      //10;     // do not consider maximal parallax above this (consider it a glitch) 
		int       num_exaggerate =    clt_parameters.imp.terr_num_exaggerate;      // 3;   
1621

1622 1623 1624 1625 1626
		// Experimental reconstruction
		double    threshold_terrain = clt_parameters.imp.terr_threshold_terrain;   // 0.05;
		double    min_max_terrain=    clt_parameters.imp.terr_min_max_terrain;     // 0.1;
		double    min_terrain =       clt_parameters.imp.terr_min_terrain;         // 0.001;
		double    min_vegetation =    clt_parameters.imp.terr_min_vegetation;      // 0.5;				
1627 1628
		
		
1629
		Rectangle woi_last_done =  continue_woi ? woi_last : null; //    new Rectangle(150, 270, 20, 20); // null; // to be able to continue 
1630
		
1631
		boolean     show_final_result = !tile_woi; // true; (maybe make saving results in tiled mode?
1632

1633
		boolean [] recalc_weights =       clt_parameters.imp.terr_recalc_weights ;      //false; // recalculate weight depending on terrain visibility
1634 1635 1636
		double    transparency_opaque =   1.0 - clt_parameters.imp.terr_recalc_opaque ; // 0.9;  // above is opaque  
		double    transparency_pedestal = clt_parameters.imp.terr_recalc_pedestal ;     // 0.05; // weight of opaque tiles  
		double    transparency_frac =     clt_parameters.imp.terr_recalc_frac ;         // 1.0;  // increase weight for far pixels (double if scale differece == this)  
1637 1638 1639 1640
		double    transparency_dist =     clt_parameters.imp.terr_recalc_dist ;         // 0.0;  // weight of opaque tiles  
		double    transparency_pow =      clt_parameters.imp.terr_recalc_pwr ;          //  1.0; // Raise transparency to this power when calculating weight  
		double    transparency_gb =       clt_parameters.imp.terr_recalc_gb ;           //  2.0; // Blur transparency-based confidence  
		double    transparency_boost =    clt_parameters.imp.terr_recalc_boost ;        //  5.0; // Maximal boost while increasing low-confidence pixel weights  
1641 1642
		boolean   recalc_average =        clt_parameters.imp.terr_recalc_average ;      // false; // apply transparency to average mismatch
		String 	  debug_path =            clt_parameters.imp.terr_debug_path;           // Directory to save debug images
1643 1644
		boolean   debug_save_improved =   clt_parameters.imp.terr_debug_improved;
		boolean   debug_save_worsened =   clt_parameters.imp.terr_debug_worsened;
1645 1646
		int       debug_length =          clt_parameters.imp.terr_debug_length;
		
1647 1648
		boolean   rebuild_elev =          clt_parameters.imp.terr_rebuild_elev;
		int       elevations1_grow =      clt_parameters.imp.terr_elev_grow;
1649
		
1650
//		boolean restore_mode =   false;
1651 1652 1653
		boolean save_par_files = true; // false;
		String segments_dir = getSegmentsDir(segments_sub);
		boolean   read_pars =     false; // true; /// false; /// true; //   false; // true;
1654
		boolean   crop_combo =   clt_parameters.imp.terr_crop; // Crop output image to the bounds of all segments
1655
		boolean   keep_partial =   clt_parameters.imp.terr_keep_partial; // Crop output image to the bounds of all segments
1656

1657
		// debug feature to read to continue - needs to be cleaned up/replaced
1658
//		String  parameters_path = "/media/elphel/SSD3-4GB/lwir16-proc/berdich3/debug/vegetation/essential/parameters_vector_data_x143-y317-w35-h35-al100.0-alo0.0-alp10.0-alin-tl0.2-vl0.01-tp0.01-vp0.01-bp5.0-um1.0_0.8.tiff";
1659 1660 1661

		
		boolean last_run =       false;
1662
		
1663
		boolean test_laplacian = false;
1664
		
1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677
		if (test_laplacian) {
			double [][] laplacian_in =  new double [2 + terrain_scenes_render.length][];
			double [][] laplacian_all = new double [2 + terrain_scenes_render.length][];
			String [] titles_laplacian = new String[laplacian_in.length];
			System.arraycopy(terrain_scenes_render, 0, laplacian_in, 0, terrain_scenes_render.length);
			laplacian_in[terrain_scenes_render.length + 0] = terrain_average_render;
			laplacian_in[terrain_scenes_render.length + 1] = vegetation_average_render;
			titles_laplacian[terrain_scenes_render.length + 0] = "terrain_average";
			titles_laplacian[terrain_scenes_render.length + 1] = "vegetation_average";
			double    weight_diag = .7;
			for (int n = 0; n < laplacian_all.length; n++) {
				if (n < terrain_scenes_render.length) {
					titles_laplacian[n]=scene_names[n];
1678
				}
1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697
				laplacian_in[n] = laplacian_in[n].clone(); // isolate from UM
				zerosToNans ( 
						laplacian_in[n], // final double [][] data,
						full.width,      // 	final int       width,
						0.0, // nan_tolerance,   // 	final double    tolerance, 0 OK if no UM !
						false,           //  negative_nan, // final boolean   negative_nan,
						nan_grow);       // 	final int       grow)
				laplacian_all[n] = VegetationLMA.laplacian(
						false,           // final boolean   gaussian,
						laplacian_in[n], // final double [] data_in,
						full.width,      // final int       width,
						weight_diag);    // final double    weight_diag);
				ShowDoubleFloatArrays.showArraysHyperstack(
						new double [][][]{laplacian_in, laplacian_all},           // double[][][] pixels, 
						full.width,                // int          width, 
						reference_scene+"-laplacian-"+weight_diag+".tiff", // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
						titles_laplacian,                     // String []    titles, // all slices*frames titles or just slice titles or null
						new String[] {"source","laplacian"},                     // String []    frame_titles, // frame titles or null
						true);                            // boolean      show)
1698 1699 1700
			}
		}

Andrey Filippov's avatar
Andrey Filippov committed
1701
		
1702
		double [][] initial_terrain_vegetation =  getInitialTerrainVegetation(
1703 1704 1705 1706 1707 1708 1709 1710 1711 1712
				terrain_average_render,       // double [] terrain_average_zeros,
				vegetation_average_render,    // double [] vegetation_average_zeros,
				 vegetation_warp,             // final double [][][] vegetation_warp, // to count all pixels used				
				full.width,                   // int       width,
				shrink_veget,                 // int       shrink_veget,
				shrink_terrain + shrink_veget,// int       shrink_terrain) {
				vegetation_over,              // double    vegetation_over_terrain, // trust vegetation that is hotter than filled terrain
				filter_veget,                //int       filter_vegetation)     // shrink+grow filtered vegetation to remove small clusters
				100); // final int           extra_pull_vegetation){

1713
		if (debugLevel > 3) { // -2){
1714 1715 1716 1717 1718 1719
			ShowDoubleFloatArrays.showArrays(
					initial_terrain_vegetation,
					full.width,
					full.height,
					true,
					reference_scene+"-terrain_vegetation_conditioned.tiff",
1720
					new String[] {"terrain_filled", "vegetation_full", "vegetation_filtered", "pull_vegetation"});
1721
			
1722
		}
1723 1724 1725 1726
		
		terrain_filled =      initial_terrain_vegetation[0];
		vegetation_full =     initial_terrain_vegetation[1];
		vegetation_filtered = initial_terrain_vegetation[2];
1727
		vegetation_pull =     initial_terrain_vegetation[3];
1728 1729 1730 1731 1732
		zerosToNans ( 
				 terrain_scenes_render, // final double [][] data,
				 full.width,    // 	final int       width,
				 nan_tolerance, // 	final double    tolerance,
				 false,         // final boolean   negative_nan,			 
1733
				 nan_grow);     // 	final int       grow)
1734 1735 1736 1737
		
		
		// maybe it is better to set NaN before UM and then use UM with fillNaN
		
1738
		/*
1739
		if (um_en) { // not used anymore
1740 1741 1742 1743
			double [][] um_data = new double [terrain_scenes_render.length+2][];
			System.arraycopy(terrain_scenes_render, 0, um_data, 0, terrain_scenes_render.length);
			um_data[terrain_scenes_render.length + 0] = terrain_average_render;
			um_data[terrain_scenes_render.length + 1] = vegetation_average_render;
1744
			unsharpMaskMulti(
1745 1746 1747 1748
					um_data,         // final double [][] data, SHOULD not have NaN
					full.width,      // final int         width,
					um_sigma,        // final double      um_sigma,
					um_weight);    // final double      um_weight)
1749
		}
1750
		*/ 
1751
		double      dir_sigma = 16;
1752
		
1753 1754 1755 1756 1757
		if ((elevations == null) || (scale_dirs == null) || rebuild_elev){
			if ((debugLevel > -10) && rebuild_elev) {
				System.out.println("***** Forced elevations rebuild. Turn it off next time! *****");
				
			}
1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
//Moving it here to generate needed vegetation_inv_warp_md
			if (debugLevel > -3) { //  3) { //-2) {
				// probably will not use these, but as optional
				vegetation_warp_md = new double [vegetation_warp.length][][];
				for (int nscene = 0; nscene < vegetation_warp.length; nscene++) {
					boolean     after_ref = (nscene > reference_index);
					vegetation_warp_md[nscene ]= warpMagnitudeDirection(
							vegetation_warp[nscene], // final double [][] warp_dxy,
							full.width,              // final int         width,
							dir_sigma,             // final double      dir_sigma,// Gaussian blur sigma to smooth direction variations
							after_ref); // final boolean     after_ref);
				}		
				vegetation_inv_warp_md = new double [vegetation_warp.length][][];
				for (int nscene = 0; nscene < vegetation_warp.length; nscene++) {
					boolean     after_ref = (nscene > reference_index);
					vegetation_inv_warp_md[nscene ]= warpMagnitudeDirection(
							vegetation_inv_warp[nscene], // final double [][] warp_dxy,
							full.width,              // final int         width,
							dir_sigma,             // final double      dir_sigma,// Gaussian blur sigma to smooth direction variations
							after_ref); // final boolean     after_ref);
				}
				
				double [][][] dbg_img = new double[12][vegetation_warp.length][vegetation_warp[0].length];
				for (int n = 0; n < dbg_img.length; n++) {
					for (int nscene = 0; nscene < vegetation_warp.length; nscene++) {
						Arrays.fill(dbg_img[n][nscene], Double.NaN);
					}
				}
				for (int nscene = 0; nscene < vegetation_warp.length; nscene++) {
					for (int npix = 0; npix < vegetation_warp[0].length; npix++) {
						if (vegetation_warp[nscene][npix] != null) {
							double dx =vegetation_warp[nscene][npix][0];
							double dy =vegetation_warp[nscene][npix][1];
							dbg_img[0][nscene][npix] = Math.sqrt(dx*dx + dy*dy);
							dbg_img[1][nscene][npix] = Math.atan2(dy, dx);
							dbg_img[2][nscene][npix] = dx; 
							dbg_img[3][nscene][npix] = dy; 
						}
						if (vegetation_warp_md[nscene][npix] != null) {
							dbg_img[4][nscene][npix] = vegetation_warp_md[nscene][npix][0]; // magnitude 
							dbg_img[5][nscene][npix] = vegetation_warp_md[nscene][npix][1]; // direction
						}

						if (vegetation_inv_warp[nscene][npix] != null) {
							double dx =vegetation_inv_warp[nscene][npix][0];
							double dy =vegetation_inv_warp[nscene][npix][1];
							dbg_img[6][nscene][npix] = Math.sqrt(dx*dx + dy*dy);
							dbg_img[7][nscene][npix] = Math.atan2(dy, dx);
							dbg_img[8][nscene][npix] = dx; 
							dbg_img[9][nscene][npix] = dy; 
						}
						if (vegetation_inv_warp_md[nscene][npix] != null) {
							dbg_img[10][nscene][npix] = vegetation_inv_warp_md[nscene][npix][0]; // magnitude 
							dbg_img[11][nscene][npix] = vegetation_inv_warp_md[nscene][npix][1]; // direction
						}
1813 1814


1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846
					}
				}

				ShowDoubleFloatArrays.showArraysHyperstack(
						dbg_img,           // double[][][] pixels, 
						full.width,                // int          width, 
						reference_scene+"-vegetation_offsets.tiff", // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
						scene_names,                     // String []    titles, // all slices*frames titles or just slice titles or null
						new String[] {"dist","angle","dx","dy","mag","dir","inv-dist","inv-angle","inv-dx","inv-dy","inv-mag","inv-dir"},                     // String []    frame_titles, // frame titles or null
						true);                            // boolean      show)

				ShowDoubleFloatArrays.showArrays(
						terrain_scenes_render,
						full.width,
						full.height,
						true,
						reference_scene+"-terrain_rendered.tiff",
						scene_names);

				ShowDoubleFloatArrays.showArrays(
						new double [][] {terrain_average_render,vegetation_average_render},
						full.width,
						full.height,
						true,
						reference_scene+"-terrain_vegetation_averages.tiff",
						new String[] {"terrain","vegetation"});
			}
			
			
			
			

1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873
			//		boolean     use_min = false;
			int reliable_shrink = 30;
			boolean [][] reliable_scene_pix = farFromNan(
					terrain_scenes_render, // final double [][] data,
					full.width,            // final int       width,
					reliable_shrink);      // final int       shrink)

			double [] max_offsets = getScenesOverlap(
					vegetation_inv_warp_md,         // final double [][][] mag_dirs,
					reliable_scene_pix,             // final boolean [][]  reliable, // or null
					false,                          // final boolean     use_min,
					0,                              // final int         start_scene,
					vegetation_inv_warp_md.length); // 				final int         endplus1_scene)
			double [] min_offsets = getScenesOverlap(
					vegetation_inv_warp_md,         // final double [][][] mag_dirs,
					reliable_scene_pix,             // final boolean [][]  reliable, // or null
					true,                        // final boolean     use_min,
					0,                              // final int         start_scene,
					vegetation_inv_warp_md.length); // 				final int         endplus1_scene)
			ShowDoubleFloatArrays.showArrays(
					new double [][] {max_offsets, min_offsets},
					full.width,
					full.height,
					true,
					reference_scene+"-max_min_offsets.tiff",
					new String[] {"max_offsets", "min_offsets"});

1874
//			int           elevations1_grow = 1024; // 20; // 64
1875 1876 1877 1878 1879 1880 1881 1882 1883
			double        dir_sigma_scales = 128; //  8; //
			int           radius_outliers =  8; // 8;
			double        min_frac =       0.02; // 0.2; // minimal fraction of the full square to keep (0.25 for the corners
			double        remove_frac_hi = 0.25; // 0.2;
			double        remove_frac_lo = 0.25; // 0.2; // total,
			boolean       debug_enhance_elevations = true;
			double        min_elevation = 2.0; // for scales - use only elevations avove this.
//			double [][]   elevation_scales = new double[vegetation_inv_warp_md.length][];
			double [][][]   elevation_scale_dirs = new double[vegetation_inv_warp_md.length][][];
1884 1885 1886 1887 1888
			
			if (debugLevel > -3) {
				System.out.println("***** Will grow elevation/sceles by "+elevations1_grow+" pixels (ortho).");
			}
			
1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905
			double [] elevations = enhanceElevations(
					max_offsets, // final double []     elevations,
					vegetation_inv_warp_md, // final double [][][] mag_dirs,
					reliable_scene_pix,     // final boolean [][]  reliable, // or null			
					full.width,             // final int           width,
					elevations1_grow,       // final int           grow,
					min_elevation,          // final double        min_elevation,
					dir_sigma_scales,       // final double        dir_sigma,// Gaussian blur sigma to smooth direction variations
					radius_outliers,        // final int           radius,
					min_frac,               // final double        min_frac, // minimal fraction of the full square to keep (0.25 for the corners
					remove_frac_hi,         // final double        remove_frac_hi,
					remove_frac_lo,         // final double        remove_frac_lo, // total,
					elevation_scale_dirs,       // final double [][]   elevation_scale_dirs,
					debug_enhance_elevations);
			this.elevations = elevations;
			this.scale_dirs = elevation_scale_dirs;
			
1906

1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933
			if (debugLevel > -2) {
				ShowDoubleFloatArrays.showArrays(
						new double [][] {max_offsets, elevations},// elevations2, elevations3, elevations4},
						full.width,
						full.height,
						true,
						reference_scene+"-elevations_enhanced.tiff",
						new String[] {"max_offsets", "enhanced"});//, "enhanced2", "enhanced3", "enhanced4"});

				int num_pixels = full.width*full.height;
				int num_scenes = vegetation_inv_warp_md.length;
				String [] compare_titles = {"direction","offsets","simulated_offsets","offset_difference"};
				double [][][] compare_offsets = new double [compare_titles.length][num_scenes][num_pixels];
				for (int nscene = 0; nscene < vegetation_inv_warp_md.length; nscene ++) {
					for (int n = 0; n < compare_offsets.length; n++) {
						Arrays.fill(compare_offsets[n][nscene], Double.NaN);
					}
					for (int npix = 0; npix < num_pixels; npix++) {
						if (vegetation_inv_warp_md[nscene][npix] != null) {
							compare_offsets[1][nscene][npix] = vegetation_inv_warp_md[nscene][npix][0];
						}
						if (elevation_scale_dirs[nscene][npix] != null) {
							compare_offsets[0][nscene][npix] = elevation_scale_dirs[nscene][npix][1]; // direction
							compare_offsets[2][nscene][npix] = elevation_scale_dirs[nscene][npix][0] * elevations[npix];
						}
						compare_offsets[3][nscene][npix] = compare_offsets[2][nscene][npix]-compare_offsets[1][nscene][npix];
					}
Andrey Filippov's avatar
Andrey Filippov committed
1934
				}
1935 1936 1937 1938 1939 1940 1941
				ShowDoubleFloatArrays.showArraysHyperstack(
						compare_offsets,           // double[][][] pixels, 
						full.width,                // int          width, 
						reference_scene+"-compare_offsets.tiff", // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
						scene_names,                      // String []    titles, // all slices*frames titles or just slice titles or null
						compare_titles,                   // String []    frame_titles, // frame titles or null
						true);                            // boolean      show)
Andrey Filippov's avatar
Andrey Filippov committed
1942
			}
1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969


			if (debugLevel > 3) { //-2) {
				// probably will not use these, but as optional
				vegetation_warp_md = new double [vegetation_warp.length][][];
				for (int nscene = 0; nscene < vegetation_warp.length; nscene++) {
					boolean     after_ref = (nscene > reference_index);
					vegetation_warp_md[nscene ]= warpMagnitudeDirection(
							vegetation_warp[nscene], // final double [][] warp_dxy,
							full.width,              // final int         width,
							dir_sigma,             // final double      dir_sigma,// Gaussian blur sigma to smooth direction variations
							after_ref); // final boolean     after_ref);
				}		
				vegetation_inv_warp_md = new double [vegetation_warp.length][][];
				for (int nscene = 0; nscene < vegetation_warp.length; nscene++) {
					boolean     after_ref = (nscene > reference_index);
					vegetation_inv_warp_md[nscene ]= warpMagnitudeDirection(
							vegetation_inv_warp[nscene], // final double [][] warp_dxy,
							full.width,              // final int         width,
							dir_sigma,             // final double      dir_sigma,// Gaussian blur sigma to smooth direction variations
							after_ref); // final boolean     after_ref);
				}
				
				double [][][] dbg_img = new double[12][vegetation_warp.length][vegetation_warp[0].length];
				for (int n = 0; n < dbg_img.length; n++) {
					for (int nscene = 0; nscene < vegetation_warp.length; nscene++) {
						Arrays.fill(dbg_img[n][nscene], Double.NaN);
1970
					}
1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998
				}
				for (int nscene = 0; nscene < vegetation_warp.length; nscene++) {
					for (int npix = 0; npix < vegetation_warp[0].length; npix++) {
						if (vegetation_warp[nscene][npix] != null) {
							double dx =vegetation_warp[nscene][npix][0];
							double dy =vegetation_warp[nscene][npix][1];
							dbg_img[0][nscene][npix] = Math.sqrt(dx*dx + dy*dy);
							dbg_img[1][nscene][npix] = Math.atan2(dy, dx);
							dbg_img[2][nscene][npix] = dx; 
							dbg_img[3][nscene][npix] = dy; 
						}
						if (vegetation_warp_md[nscene][npix] != null) {
							dbg_img[4][nscene][npix] = vegetation_warp_md[nscene][npix][0]; // magnitude 
							dbg_img[5][nscene][npix] = vegetation_warp_md[nscene][npix][1]; // direction
						}

						if (vegetation_inv_warp[nscene][npix] != null) {
							double dx =vegetation_inv_warp[nscene][npix][0];
							double dy =vegetation_inv_warp[nscene][npix][1];
							dbg_img[6][nscene][npix] = Math.sqrt(dx*dx + dy*dy);
							dbg_img[7][nscene][npix] = Math.atan2(dy, dx);
							dbg_img[8][nscene][npix] = dx; 
							dbg_img[9][nscene][npix] = dy; 
						}
						if (vegetation_inv_warp_md[nscene][npix] != null) {
							dbg_img[10][nscene][npix] = vegetation_inv_warp_md[nscene][npix][0]; // magnitude 
							dbg_img[11][nscene][npix] = vegetation_inv_warp_md[nscene][npix][1]; // direction
						}
Andrey Filippov's avatar
Andrey Filippov committed
1999 2000 2001
					}
				}

2002 2003 2004 2005 2006 2007 2008
				ShowDoubleFloatArrays.showArraysHyperstack(
						dbg_img,           // double[][][] pixels, 
						full.width,                // int          width, 
						reference_scene+"-vegetation_offsets.tiff", // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
						scene_names,                     // String []    titles, // all slices*frames titles or just slice titles or null
						new String[] {"dist","angle","dx","dy","mag","dir","inv-dist","inv-angle","inv-dx","inv-dy","inv-mag","inv-dir"},                     // String []    frame_titles, // frame titles or null
						true);                            // boolean      show)
2009

2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032
				ShowDoubleFloatArrays.showArrays(
						terrain_scenes_render,
						full.width,
						full.height,
						true,
						reference_scene+"-terrain_rendered.tiff",
						scene_names);

				ShowDoubleFloatArrays.showArrays(
						new double [][] {terrain_average_render,vegetation_average_render},
						full.width,
						full.height,
						true,
						reference_scene+"-terrain_vegetation_averages.tiff",
						new String[] {"terrain","vegetation"});
			}
			if ((src_dir != null) && (src_title != null)) {
				System.out.println("Saving augmented data to model directory "+src_dir); //
				saveState(
						src_dir, // String dir,
						src_title); // String title)
			}
			
2033

2034 2035 2036
		} // if ((elevations == null) || (scale_dirs == null)){
		VegetationLMA vegetationLMA = new VegetationLMA (
				this,
2037 2038 2039 2040 2041 2042 2043
				alpha_init_offs,  // 0.01; // double    alpha_offs,
				alpha_init_min,   // 0.7;  // double    alpha_min,
				alpha_init_max,   // 0.9;  // double    alpha_max,
				alpha_sigma, // 8.0;  // double    alpha_sigma,
				debugLevel); // int debugLevel) 
		
		
2044 2045 2046 2047 2048
		if (debugLevel > -2) {
			double [][] dbg_img = {
					vegetationLMA.tvao[VegetationLMA.TVAO_TERRAIN],
					vegetationLMA.tvao[VegetationLMA.TVAO_VEGETATION],
					vegetationLMA.tvao[VegetationLMA.TVAO_ALPHA]};
Andrey Filippov's avatar
Andrey Filippov committed
2049
			ShowDoubleFloatArrays.showArrays(
2050
					dbg_img,
2051 2052
					full.width,
					full.height,
Andrey Filippov's avatar
Andrey Filippov committed
2053
					true,
2054 2055
					reference_scene+"-terrain_vegetation_alpha.tiff",
					new String[] {"terrain", "vegetation", "alpha"});
Andrey Filippov's avatar
Andrey Filippov committed
2056
		}
2057
		
2058
		
2059
		if (run_combine) {
2060 2061 2062 2063 2064 2065 2066 2067 2068 2069
			VegetationSegment [] segments = vegetationLMA.readAllSegments(
					segments_dir,          // String dir_path)
					segments_suffix,       // String suffix);
					transparency_opaque,   // double        transparency_opaque,
					transparency_pedestal, // double        transparency_pedestal,
					transparency_frac,     // 					double        transparency_frac,
					transparency_dist,     // double        transparency_dist,			
					transparency_pow,      // double        transparency_pow,			
					transparency_gb,       // double        transparency_gb,
					debugLevel,            // int           debugLevel,
2070
					debug_path,            // String        debug_path,
2071 2072 2073 2074
					debug_save_improved,   // boolean       debug_save_improved, // Save debug image after successful LMA step."); 
					debug_save_worsened);  // boolean       debug_save_worsened) { // Save debug image after unsuccessful LMA step.");
			VegetationSegment.combineSegments(
					vegetationLMA, // VegetationLMA        vegetationLMA,
2075
					segments,
2076
					crop_combo,             // boolean              crop_combo,
2077
					keep_partial,           // boolean   keep_partial,
2078
					border_width,           // int                  width);
2079 2080
					um_sigma,               // final double         um_sigma,
					um_weight,              //final double         um_weight,
2081 2082 2083 2084 2085 2086
					render_open,            // boolean   render_open,      // render open areas (no vegetation offset)
					render_no_alpha,        // boolean   render_no_alpha,  // render where no opacity is available
					alpha_min,              // double    alpha_min,        // below - completely transparent vegetation
					alpha_max,              // double    alpha_max,        // above - completely opaque
					weight_opaque,          // double    weight_opaque,    // render through completely opaque vegetation
					boost_parallax_render,  // double boost_parallax) {    // increase weights of scenes with high parallax relative to the reference one
2087
					max_parallax_render,    // final double        max_parallax,			
2088 2089 2090 2091
					num_exaggerate);        // final int            num_exaggerate) { // show amplified difference from filtering
			return;
		}
		Rectangle woi = woi_last_done;
2092 2093 2094 2095 2096 2097 2098 2099
		
		// save parameters to be restored after terrain-only mode
		int last_series_save =           last_series;
		int[] num_iters_save =           num_iters;
		boolean [][] fits_disable_save = fits_disable; 
		
		
		
2100
		while (true) {
2101 2102 2103 2104 2105
			// Restore for the next tile after terrain-only mode
			last_series =  last_series_save;
			num_iters =    num_iters_save;
			fits_disable = fits_disable_save;

2106 2107 2108 2109 2110
			if (debugLevel>-2) {
				Runtime runtime = Runtime.getRuntime();
				runtime.gc();
				System.out.println("--- Free memory="+runtime.freeMemory()+" (of "+runtime.totalMemory()+")");
			}
2111
			step_restore=       par_restore? clt_parameters.imp.terr_step_restore : 0;
2112 2113 2114
			woi = nextTileWoi(
					woi_enclosing, // Rectangle enclosing,
					woi_step, // Rectangle step,
Andrey Filippov's avatar
Andrey Filippov committed
2115
					woi_last, // Rectangle single,
2116 2117 2118 2119 2120 2121
					woi, // Rectangle current,
					tile_woi); // boolean tile_woi)
			if (woi == null) {
				System.out.println ("===== No WOIs to process left, exiting");
				break;
			}
2122 2123 2124 2125 2126 2127
			if (tile_woi) {
				System.out.println("===== Will process WOI ("+woi.x+", "+woi.y+", "+woi.width+", "+woi.height+") of the enclosing WOI ("+
						+woi_enclosing.x+", "+woi_enclosing.y+", "+woi_enclosing.width+", "+woi_enclosing.height+").");
			} else {
				System.out.println("===== Will process a single WOI ("+woi.x+", "+woi.y+", "+woi.width+", "+woi.height+").");
			}
2128

2129
			int num_samples = vegetationLMA.prepareLMA(
2130 2131
					false,             // final boolean            keep_parameters,
					woi,               // final Rectangle   woi,
2132
					null, // final Rectangle          woi_veg_in, // used when loading from file (may be different)
2133
					null, // final Rectangle          woi_terr_in, // used when loading from file (may be different)
2134
					max_warp,          // final double             max_warp, // 1.8 - do not use scenes where distance between vegetation projection exceeds this
2135 2136
					max_elevation,     // final int                max_offset,               // maximal "elevation" to consider
					max_elev_terr,     // final int                max_elev_terr,               // maximal terrain "elevation" to consider
2137
					max_elev_terr_chg, // final double             max_elev_terr_chg,// 0.5 maximal terrain elevation change from last successfully used
2138
					elevation_radius,  // final double             elevation_radius, // Radius of elevation/vegetation influence.
2139 2140
					terr_elev_radius,  // final double             terr_elev_radius, //  = 1.5; 
					elev_radius_extra, // final double             elev_radius_extra, //  =  1.2;      // scale both radii when setupElevationLMA(), setupTerrainElevationLMA(), and setupTerrainElevationPixLMA() 
2141
					null, // final boolean []         valid_scene_pix,         
2142
					hifreq_weight,     //final double             hifreq_weight,  // 22.5 0 - do not use high-freq. Relative weight of laplacian components
2143
					terrain_correction,// final double             terrain_correction,
2144 2145 2146 2147 2148
					fit_terr,          // final boolean            adjust_terr,
					fit_veget,         // final boolean            adjust_veget,
					fit_alpha,         // final boolean            adjust_alpha,
					fit_scenes,        // final boolean            adjust_scenes,
					fit_elevations,    // final boolean            adjust_elevations,
2149
					fit_terr_elev,     // final boolean            fit_terr_elev,					
2150
					fit_terr_elev_pix, // final boolean            fit_terr_elev_pix,					
Andrey Filippov's avatar
Andrey Filippov committed
2151 2152
					thisOrLast(step_restore, fits_disable), // fits_disables[0], // final boolean []         fit_disable,
					thisOrLast(step_restore, fits_disable_terronly),//			final boolean []         fits_disable_terronly,
2153 2154 2155
					reg_weights,       // final double             reg_weights,        // fraction of the total weight used for regularization
					alpha_loss,        // final double             alpha_loss,         // alpha quadratic growing loss for when out of [0,1] range
					alpha_loss_lin,    // final double             alpha_loss_lin, // alpha linear growing loss for when out of [0,1] range and below minimal vegetation alpha
2156 2157
					alpha_offset,      // final double             alpha_offset,       // quadratic loss when alpha > 1.0 - alpha_offset
					alpha_0offset,     // final double             alpha_0ffset,       // quadratic loss when alpha < alpha0_offset
2158
					alpha_min_veg,     // final double             alpha_min_veg, // 0.5; // if (alpha-alpha_offset)/(1-2*alpha_offset) < alpha_min_veg, pull down to lpha_offset
2159 2160
					alpha_max_terrain, // final double             alpha_max_terrain, // 0.75; // () increase pull vegetation if below
					alpha_pull_pwr,    //final double             alpha_pull_pwr,    // 1.0;  // () raise extra pull to that power
2161
					alpha_lpf,         // final double             alpha_lpf,          // pull to average of 4 neighbors
2162
					alpha_lpf_border,  // final double             alpha_lpf_border, //  pull to average of 4 neighbors for border tiles (to keep stable)
2163
					alpha_piece_linear, // final boolean       alpha_piece_linear, // true - piece-linear, false - half-cosine
2164 2165
					alpha_scale_avg,    // final double             alpha_scale_avg, //  = 1.2; // scale average alpha (around 0.5) when pulling to it
					alpha_push,         //  final double             alpha_push,      //  5.0;   // push from alpha==0.5
2166
					alpha_push_neutral, // double    alpha_push_neutral = 0.8; // alpha point from which push (closer to opaque)
2167
					alpha_push_center,  // final double             alpha_push_center,// 1.5; // weight of center alpha pixel relative to each of the 4 ortho ones
2168
					alpha_en_holes, // final boolean            alpha_en_holes, // Search for small semi-transparent holes, disable diffusion of local alpha minimums
2169
					alpha_mm_hole,  //  double    alpha_mm_hole    = 0.1; // NaN to disable. Local "almost minimum" (lower than this fraction between min and max neighbor) is not subject to alpha_lpf
2170
					alpha_diff_hole,//final double             alpha_diff_hole, // 0.01; // Minimal alpha difference between min and max neighbor to be considered a hole
2171 2172
					terr_lpf,       // final double             terr_lpf,           // pull terrain to average of 4 neighbors (very small)
					veget_lpf,      // final double             veget_lpf,          // pull vegetation to average of 4 neighbors (very small - maybe not needed)
2173
					elevation_lpf,  // final double             elevation_lpf,
2174 2175
					terr_elev_lpf,  // final double             terr_elev_lpf,
					
2176
					terr_pull0,     // final double             terr_pull0,     // pull terrain to initial (pre-adjustment) values
2177
					terr_pull_up,   // final double             terr_pull_up,   // Terrain pixels pull to initial (pre-adjustment) values when it is colder than initial.
2178
					terr_pull_avg,  // final double             terr_pull_avg,  // pull terrain to the initial offset by the average offset of all terrain pixels  
2179
					veget_pull0,    // final double             veget_pull0,    // pull vegetation to initial (pre-adjustment) values
2180
					veget_pull_low_alpha, //final double        veget_pull_low_alpha, //  10; // ()scale pull0 for low alpha (mostly terrain) 
2181
					elev_pull0,     // final double             elev_pull0,     // pull elevation to initial (pre-adjustment) values
2182
					terr_elev_pull0, // final double             terr_elev_pull0, // pull terrain elevation to segment average
2183 2184 2185 2186
					elev_alpha_en,  // final boolean            elev_alpha_en,  // false; // Enable loss for low vegetation with high opacity
					elev_alpha,     // final double             elev_alpha,     // 1.0;   // multiply alpha by under-low elevation for loss
					elev_alpha_pwr, // final double             elev_alpha_pwr, // 2.0;  // raise alpha to this power (when alpha > 0)
					low_veget,      // final double             low_veget,      // 2.0;   // (pix) Elevation considered low (lower loss for high alpha)
2187
					scenes_pull0,   // final double             scenes_pull0,
2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198
					
			        elev_scale_thresh, // final double             elev_scale_thresh, // 1.0;// reduce losses for higher (initial) elevations TODO: consider actual elevations
			        elev_scale_pull,// final boolean            elev_scale_pull,// false; // scale elevation pull losses for high elevations 
			        elev_scale_lpf, // final boolean            elev_scale_lpf, // false; // scale elevation diffusion losses for high elevations 
			        ttop_en,        // final boolean            ttop_en,        // false; // remove tree tops from transparency weights
			        ttop_gb,        // final double             ttop_gb,        // 1.0;   // Elevation Gaussian blur sigma to detect tree tops
			        ttop_min,       // final double             ttop_min,       // 3.0;   // Minimal tree top elevation
			        ttop_rel_lev,   // final double             ttop_rel_lev,   // 0.9;   // Relative (to the top height) sample level
			        ttop_rel_rad,   // final double             ttop_rel_rad,   // 0.25;  // Relative (to the top height) sample ring radius
			        ttop_frac,      // final double             ttop_frac,      // 0.5;   // Minimal fraction of the ring pixels below sample level
			        ttop_rem_rad,   // final double             ttop_rem_rad,   // 0.25;  // Relative (to the top height) remove transparency radius
Andrey Filippov's avatar
Andrey Filippov committed
2199 2200
///			        terr_only_special,// final boolean          terr_only_special,//true; // special sequences for terrain-only tiles
///			        terr_only_pix,  // final boolean            terr_only_pix,    //true; // force per-pixel terrain elevation in terrain-only mode, overwrite fits_disable[TVAO_TERR_ELEV_PIX]
2201 2202 2203
					boost_parallax, // final double             boost_parallax,     // increase weight of scene with maximal parallax relative to the reference scene
					max_parallax,   //final double              max_parallax,  // do not consider maximal parallax above this (consider it a glitch) 
					debugLevel,          // final int           debugLevel);
2204
					debug_path,          // final String             debug_path,
2205 2206 2207
					debug_save_improved, // final boolean            debug_save_improved, // Save debug image after successful LMA step."); 
					debug_save_worsened);// final boolean            debug_save_worsened) // Save debug image after unsuccessful LMA step.");

2208 2209 2210 2211
			if (num_samples <= 0) {
				System.out.println("Insufficient data in this segment, skipping it.");
				continue;
			}
2212
			/// Handle terrain-only tiles
Andrey Filippov's avatar
Andrey Filippov committed
2213
			if (vegetationLMA.getWoiVeg() == null) {// && terr_only_special) {
2214 2215 2216 2217
				last_series = terr_only_series;
				num_iters = terr_only_num_iters;
			}
			
2218
			
2219
			if (save_par_files && skip_existing_woi) { // check that segment already exists
2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242
				File[] segment_files = vegetationLMA.getSegmentFiles(segments_dir);
				if (tile_woi) {
					if (segment_files.length > 0) {
						for (File f : segment_files) {
							System.out.println("File "+f+"\n with the same woi already exists, skipping this woi");
						}
						continue;
					}
				} else {
					String save_path = VegetationLMA.getSavePath(
							segments_dir, // String dir,
							vegetationLMA.getParametersDebugTitle()); // String title)
					if (new File (save_path).exists()) {
						System.out.println("File "+save_path+"\n (exactly) already exists, skipping this woi");
//						for (File f : segment_files) {
//							System.out.println("File "+f+"\n (exactly) already exists, skipping this woi");
//						}
						continue;
					}
					if (new File (save_path.replace("-new-","-file-")).exists()) {
						System.out.println("File "+save_path+"\n already exists, skipping this woi");
						continue;
					}
2243 2244
				}
			}
2245
			int num_iter = num_iters[step_restore]; //
2246
			if (par_restore) { // always use last number of iterations - not anymore
2247
				String par_path = parameters_dir;
2248 2249 2250 2251 2252 2253 2254 2255
				if (!par_path.endsWith(Prefs.getFileSeparator())) {
					par_path+=Prefs.getFileSeparator();
				}
				par_path+=parameters_file;
				System.out.println("Restoring parameters from "+par_path);
				vegetationLMA.restoreParametersFile( //FIXME: Not finished for real import ! 
						par_path, // String        path,
						true,     // boolean       keep_settings,
2256
///						null,     // Rectangle [] file_wois);      //  if not null, should be Rectangle[2] {woi_veg,woi} - will return woi data and not input parameters to this instance
2257
						null);    // double [] other_pars)
2258 2259
				if (thisOrLast(step_restore,recalc_weights)) {
					System.out.println ("---- Recalculating weights from transparency after loading parameters");
2260
					String dbg_title= (!tile_woi && (debugLevel > -2)) ?("transparency_"+step_restore): null;
2261 2262 2263 2264 2265
					vegetationLMA.applyTransparency(
							null,                  // final double []   vector,
							transparency_opaque,   // final double      transparency_opaque,
							transparency_pedestal, // final double      transparency_pedestal,
							transparency_frac,     // final double      transparency_frac,
2266 2267 2268 2269 2270
							transparency_dist,     // final double      transparency_dist,
							transparency_pow,      // final double      transparency_pow,
							transparency_gb,       // final double      transparency_gb,
							transparency_boost,    // final double      transparency_boost, 
							recalc_average,       // final boolean     recalc_average);
2271 2272 2273 2274 2275 2276 2277 2278 2279 2280
							ttop_en,               // final boolean     ttop_en,      //     false; // Remove tree tops from transparency weights
							ttop_gb,               // final double      ttop_gb,      //     1.0;   // Elevation Gaussian blur sigma to detect tree tops
							ttop_min,              // final double      ttop_min,     //     3.0;   // Minimal tree top elevation
							ttop_rel_lev,          // final double      ttop_rel_lev, //     0.9;   // Relative (to the top height) sample level
							ttop_rel_rad,          // final double      ttop_rel_rad, //     0.25;  // Relative (to the top height) sample ring radius
							ttop_frac,             // final double      ttop_frac,    //     0.5;   // Minimal fraction of the ring pixels below sample level
							ttop_rem_rad,          // final double      ttop_rem_rad, //     0.25;  // Relative (to the top height) remove transparency radius
							ttop_no_border,        // final boolean     ttop_no_border,//    true;  // No maximums on the border allowed
							dbg_title,             // String title)
							debugLevel);           // final int         debugLevel);
2281
				}
2282
			}
2283
			
2284
			if ((show_final_result) && (debugLevel > -2)) { // 0)) {
2285
				String reconstructed_title = reference_scene+"-recnstr-pre-step"+step_restore; // 
2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296
				vegetationLMA.showYfX(
						null, // double [] vector,
						reconstructed_title); // String title)
			}
			
			
			
			if (debugLevel > 0) { // make save w/o showing?
				vegetationLMA.showYfX(
						null, // double [] vector,
						"reconstructed_model-x"+woi.x+"-y"+woi.y+"-w"+woi.width+"-h"+woi.height); // String title)
2297
				/*
2298 2299 2300 2301 2302 2303
				vegetationLMA.showResults(
						"terr_split-x"+woi.x+"-y"+woi.y+"-w"+woi.width+"-h"+woi.height,                        // String      title,
						vegetationLMA.getParametersVector(), // double []   vector,
						threshold_terrain, // double      threshold_terrain,
						min_max_terrain, // double      min_max_terrain, //0.1
						min_terrain,                         //double      min_terrain, //0.001
2304 2305 2306
						min_vegetation);                     // double      min_vegetation) { // 0.5
						*/
								
2307 2308 2309
			}
			/// next_run = true;
			vegetationLMA.debug_index = 0;
2310 2311
			vegetationLMA.debug_length = debug_length;
			vegetationLMA.debug_image = new double [vegetationLMA.debug_length][]; // num_iter][];
2312 2313

			int lma_rslt= vegetationLMA.runLma( // <0 - failed, >=0 iteration number (1 - immediately)
2314 2315 2316 2317 2318
					lambda,           // double lambda,           // 0.1
					lambda_scale_good,// double lambda_scale_good,// 0.5
					lambda_scale_bad, // double lambda_scale_bad, // 8.0
					lambda_max,       // double lambda_max,       // 100
					rms_diff,         // double rms_diff,         // 0.001
2319
					num_iter, // num_iters[0],     // 15, // num_iter,         //int    num_iter,         // 20
2320 2321 2322
					last_run,         // boolean last_run,
					null,             // String dbg_prefix,
					debugLevel);      // int    debug_level)
2323 2324 2325 2326 2327 2328 2329 2330 2331
  			if (save_par_files) {
  				String  restore_dir =    segments_dir; // vegetationLMA.debug_path;
  				String  debug_title =    vegetationLMA.getParametersDebugTitle();
  				vegetationLMA.saveParametersFile(
  						restore_dir, // String dir,
  						debug_title+"_post"+step_restore, // String title, // no .par-tiff
  						null); // double [] vector)
  			}
			
2332 2333
	  		if (debugLevel > -2) { // 1) {
				System.out.println((new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime()))+
2334
						" LMA finished - first run (step "+step_restore+").");
2335
			}
2336 2337
	  		step_restore++;
	  		for (; step_restore <= last_series; step_restore++) {
Andrey Filippov's avatar
Andrey Filippov committed
2338 2339 2340 2341 2342 2343 2344
	  			for (int i = 0; i < VegetationLMA.TVAO_TYPES; i++) {
	  				vegetationLMA.fits_disable[i] =    thisOrLast(step_restore,fits_disable)[i];
	  			}
///	  			vegetationLMA.fits_disable[VegetationLMA.TVAO_ELEVATION] =    thisOrLast(step_restore,fits_disable)[VegetationLMA.TVAO_ELEVATION];
///	  			vegetationLMA.fits_disable[VegetationLMA.TVAO_TERR_ELEV] =    thisOrLast(step_restore,fits_disable)[VegetationLMA.TVAO_TERR_ELEV];
///	  			vegetationLMA.fits_disable[VegetationLMA.TVAO_TERR_ELEV_PIX] =thisOrLast(step_restore,fits_disable)[VegetationLMA.TVAO_TERR_ELEV_PIX];
	  			vegetationLMA.fits_disable[VegetationLMA.TVAO_TERR_ELEV] |= !vegetationLMA.fits_disable[VegetationLMA.TVAO_TERR_ELEV_PIX]; 
2345 2346
				if (thisOrLast(step_restore,recalc_weights)) {
					System.out.println ("---- Recalculating weights from transparency");
2347
					String dbg_title= (!tile_woi && (debugLevel > -2)) ? ("transparency_"+step_restore) : null;
2348 2349 2350 2351 2352 2353 2354 2355 2356 2357
					vegetationLMA.applyTransparency(
							null,                  // final double []   vector,
							transparency_opaque,   // final double      transparency_opaque,
							transparency_pedestal, // final double      transparency_pedestal,
							transparency_frac,     // final double      transparency_frac,
							transparency_dist,     // final double      transparency_dist,
							transparency_pow,      // final double      transparency_pow,
							transparency_gb,       // final double      transparency_gb,
							transparency_boost,    // final double      transparency_boost, 
							recalc_average,        // final boolean     recalc_average);
2358 2359 2360 2361 2362 2363 2364 2365 2366 2367
							ttop_en,               // final boolean     ttop_en,      //     false; // Remove tree tops from transparency weights
							ttop_gb,               // final double      ttop_gb,      //     1.0;   // Elevation Gaussian blur sigma to detect tree tops
							ttop_min,              // final double      ttop_min,     //     3.0;   // Minimal tree top elevation
							ttop_rel_lev,          // final double      ttop_rel_lev, //     0.9;   // Relative (to the top height) sample level
							ttop_rel_rad,          // final double      ttop_rel_rad, //     0.25;  // Relative (to the top height) sample ring radius
							ttop_frac,             // final double      ttop_frac,    //     0.5;   // Minimal fraction of the ring pixels below sample level
							ttop_rem_rad,          // final double      ttop_rem_rad, //     0.25;  // Relative (to the top height) remove transparency radius
							ttop_no_border,        // final boolean     ttop_no_border,//    true;  // No maximums on the border allowed
							dbg_title,             // final String title
							debugLevel);           // final int         debugLevel);
2368 2369
				}
				
2370 2371 2372 2373 2374
	  			if (save_par_files) {
	  				String  restore_dir =    segments_dir; // vegetationLMA.debug_path;
	  				String  debug_title =    vegetationLMA.getParametersDebugTitle();
	  				vegetationLMA.saveParametersFile(
	  						restore_dir, // String dir,
2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393
	  						debug_title+"_pre"+step_restore, // String title, // no .par-tiff
	  						null); // double [] vector)
	  			}
	  			lma_rslt= vegetationLMA.runLma( // <0 - failed, >=0 iteration number (1 - immediately)
	  					lambda,           // double lambda,           // 0.1
	  					lambda_scale_good,// double lambda_scale_good,// 0.5
	  					lambda_scale_bad, // double lambda_scale_bad, // 8.0
	  					lambda_max,       // double lambda_max,       // 100
	  					rms_diff,         // double rms_diff,         // 0.001
	  					num_iters[step_restore],     // num_iter,         //int    num_iter,         // 20
	  					last_run,         // boolean last_run,
	  					null,             // String dbg_prefix,
	  					debugLevel);      // int    debug_level)
	  			if (save_par_files) {
	  				String  restore_dir =    segments_dir; // vegetationLMA.debug_path;
	  				String  debug_title =    vegetationLMA.getParametersDebugTitle();
	  				vegetationLMA.saveParametersFile(
	  						restore_dir, // String dir,
	  						debug_title+"_post"+step_restore, // String title, // no .par-tiff
2394 2395
	  						null); // double [] vector)
	  			}
2396
	  			
2397 2398 2399
	  			if (debugLevel > -2) { // 1) {
	  				System.out.println((new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime()))+
	  						" LMA finished, run " + step_restore);
2400
	  			}
2401
	  		} // for (; step_restore < num_iters.lenggth; step_restore++) {
2402
			
2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413
// save results
			if (save_par_files) {
				String  restore_dir =    segments_dir; // vegetationLMA.debug_path;
				String  debug_title =    vegetationLMA.getParametersDebugTitle();
				vegetationLMA.saveParametersFile(
						restore_dir, // String dir,
						debug_title, // String title, // no .par-tiff
						null); // double [] vector)
//				continue;
			}
			if (show_final_result) {
2414
				String reconstructed_title = reference_scene+"-recnstr-"+vegetationLMA.getParametersDebugTitle()+"-ser"+last_series;
2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445
				vegetationLMA.showYfX(
						null, // double [] vector,
						reconstructed_title); // String title)
			}
			
		}
		return; // 

	}
	/*
	public static double [] getInitialElevations(
			final double [][] mag_dirs,
			final int         width) {
		
	}
*/
	
	public static boolean [] removeLocalOutliers(
			final double [] data,
			final int       width,
			final int       radius,
			final double    min_frac, // minimal fraction of the full square to keep (0.25 for the corners
			final double    remove_frac_hi,
			final double    remove_frac_lo, // total,
			final boolean   nan_removed) { // make removed data NaN
		final int num_pixels = data.length;
		final int height = num_pixels/width;
		final int size = radius*2+1;
		final int area = size* size;
		final int min_num = (int) Math.round(area * min_frac); 
		final boolean [] keep = new boolean [num_pixels];
2446
		final int dbg_pix = -(640*171+172);
2447 2448 2449 2450 2451 2452 2453 2454
		final Thread[]      threads =     ImageDtt.newThreadArray();
		final AtomicInteger ai =          new AtomicInteger(0);
		
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				TileNeibs tn = new TileNeibs(width, height);
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) if  (!Double.isNaN(data[nPix])){
2455 2456 2457
						if (nPix == dbg_pix) {
							System.out.println("removeLocalOutliers(): nPix="+nPix);
						}
2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469
						int num_below = 0, num_above=0, num=0;
						double d = data[nPix];
						for (int dy = -radius; dy <= radius; dy++) {
							for (int dx = -radius; dx <= radius; dx++) {
								int npix1 = tn.getNeibIndex(nPix, dx, dy);
								if (npix1 >= 0) {
									num++;
									double d1 = data[npix1];
									if (d1 >= d) num_above++; // equal should go to both
									if (d1 <= d) num_below++; // equal should go to both
								}
							}
2470 2471 2472
						}
						if ((num >= min_num) && (num_above >= num*remove_frac_hi) && (num_below >= num * remove_frac_lo)){
							keep[nPix] = true;
2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510
						}
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		if (nan_removed) {
			ai.set(0);
			for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
				threads[ithread] = new Thread() {
					public void run() {
						for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) if  (!keep[nPix]){
							data[nPix] = Double.NaN;
						}
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
		}
		return keep;
		
	}
	
	
	public static double [] enhanceElevations(
			final double []     elevations,
			final double [][][] mag_dirs,
			final boolean [][]  reliable, // or null			
			final int           width,
			final int           grow,
			final double        min_elevation,
			final double        dir_sigma,// Gaussian blur sigma to smooth direction variations
			final int           radius,
			final double        min_frac, // minimal fraction of the full square to keep (0.25 for the corners
			final double        remove_frac_hi,
			final double        remove_frac_lo, // total,
			final double [][][] elevation_scale_dirs,
			final boolean       debug) {
2511
		final int debugLevelFillNan = 1;
2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565
		final int num_scenes = mag_dirs.length;
		final int num_pixels = elevations.length;
		final String [] titles_top = {"scales","outliers_removed","ext_scales", "smooth_scales"};
		final String [] titles_scene = debug? new String[num_scenes]:null;
		if (titles_scene != null) {
			for (int nscene = 0; nscene < num_scenes; nscene++) {
				titles_scene[nscene] = "scene_"+nscene;
			}
		}
		final double [][][] dbg_img = debug? new double [titles_top.length][num_scenes][] : null;
		final double [][] scales = new double [num_scenes][num_pixels];
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			Arrays.fill(scales[nscene], Double.NaN);
		}

		final Thread[]      threads =     ImageDtt.newThreadArray();
		final AtomicInteger ai =          new AtomicInteger(0);
		
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) if  (!Double.isNaN(elevations[nPix])){
						for (int nscene = 0; nscene < num_scenes; nscene++) if (mag_dirs[nscene][nPix] != null){
							if ((reliable == null) || reliable[nscene][nPix]) {
								if (elevations[nPix] > min_elevation) {
									scales[nscene][nPix] = mag_dirs[nscene][nPix][0] / elevations[nPix];
								}
							}
						}
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		
		
		if (dbg_img != null) {
			for (int nscene = 0; nscene < num_scenes; nscene++) if (scales[nscene] != null){
				dbg_img[0][nscene] = scales[nscene].clone();
			}			
		}
		if ((grow > 0) || (radius > 0)) { 
			for (int nscene = 0; nscene < num_scenes; nscene++) if (scales[nscene] != null){
				removeLocalOutliers(
						scales[nscene], // final double [] data,
						width, // final int       width,
						radius,       // final int       radius,
						min_frac, // final double    min_frac, // minimal fraction of the full square to keep (0.25 for the corners
						remove_frac_hi, // final double    remove_frac_hi,
						remove_frac_lo, // final double    remove_frac_lo, // total,
						true); // final boolean   nan_removed) { // make removed data NaN
				if (dbg_img != null) {
					dbg_img[1][nscene] = scales[nscene].clone();
				}
2566
				/*
2567 2568 2569 2570 2571 2572 2573
				scales[nscene] = TileProcessor.fillNaNs(
						scales[nscene], // final double [] data,
						null,                     // final boolean [] prohibit,
						width,          // int       width,
						// CAREFUL ! Remaining NaN is grown by unsharp mask filter ************* !
						grow,           // 100, // 2*width, // 16,           // final int grow,
						0.7,            // double    diagonal_weight, // relative to ortho
2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590
						10, // 100,     // int       num_passes,
						0.03);          // final double     max_rchange, //  = 0.01 - does not need to be accurate
				*/
				int        decimate_step = 16;
				int        num_decimate  =  1;
				String           debugTitle = null; // "fillNaN-"+nscene;
				scales[nscene] = TileProcessor.fillNaNs(
						scales[nscene], // final double [] data,
						width,          // int       width_full,
						decimate_step,  // final int        decimate_step, // 16
						num_decimate,   // final int        num_decimate,
						100, // 100,    // int       num_passes,
						0.03,           // final double     max_rchange, //  = 0.01 - does not need to be accurate
						debugTitle,    // String           debugTitle);//
						debugLevelFillNan); // final int        debugLevel) {  // 0 - none, 1 - when done, 2 - all iterations

				
2591 2592
				if (debug) {
					System.out.println("enhanceElevations() nscene="+nscene);
2593 2594 2595 2596 2597 2598 2599 2600
					/*
					ShowDoubleFloatArrays.showArrays(
							scales[nscene],
							width,
							scales[nscene].length/width,
							"scales-"+nscene);
							*/
					
2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621
				}
			}
			ai.set(0);
			for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
				threads[ithread] = new Thread() {
					public void run() {
						for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()){
							double sw = 0, swd = 0;
							for (int npix=0;npix < num_pixels; npix++) if (!Double.isNaN(scales[nScene][npix])) {
								sw+=1.0;
								swd += scales[nScene][npix];
							}
							double mean = swd/sw;
							for (int npix=0;npix < num_pixels; npix++) if (Double.isNaN(scales[nScene][npix])) {
								scales[nScene][npix] = mean;
							}							
						}
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
2622 2623
			
			
2624
			
2625 2626
			if (debug) {
				System.out.println("enhanceElevations() Grow done.");
2627
			}
2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654
		}
		if (dbg_img != null) {
			for (int nscene = 0; nscene < num_scenes; nscene++) if (scales[nscene] != null){
				dbg_img[2][nscene] = scales[nscene].clone();
			}			
		}
		
		if (dir_sigma > 0) {
			ai.set(0);
			for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
				threads[ithread] = new Thread() {
					public void run() {
						for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()){
							(new DoubleGaussianBlur()).blurDouble(
									scales[nScene],          //
									width,
									num_pixels/width,
									dir_sigma,              // double sigmaX,
									dir_sigma,              // double sigmaY,
									0.01);                 // double accuracy)
						}
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
			if (debug) {
				System.out.println("enhanceElevations() GB done.");
2655
			}
2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671

		}
		if (dbg_img != null) {
			for (int nscene = 0; nscene < num_scenes; nscene++) if (scales[nscene] != null){
				dbg_img[3][nscene] = scales[nscene].clone();
			}			
		}
		if (dbg_img != null) {
			ShowDoubleFloatArrays.showArraysHyperstack(
					dbg_img,           // double[][][] pixels, 
					width,                // int          width, 
					"scene_scales.tiff", // "terrain_vegetation_render.tiff", // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
					titles_scene,                     // String []    titles, // all slices*frames titles or just slice titles or null
					titles_top,                     // String []    frame_titles, // frame titles or null
					true);                            // boolean      show)
				System.out.println("enhanceElevations() shown.");
2672 2673
			
		}
2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695
		
		final double [] sum_offs=   new double[num_pixels];
		final double [] sum_scales= new double[num_pixels];
		final double [] elevations_out= new double[num_pixels];
		Arrays.fill(elevations_out, Double.NaN);
		ai.set(0);
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()){
						for (int nscene = 0; nscene < num_scenes; nscene++) { //  if (mag_dirs[nscene][nPix] != null){
							double s = scales[nscene][nPix];
							if (mag_dirs[nscene][nPix] != null){
								if (s > 0) {
									sum_scales[nPix] += s;
									sum_offs[nPix] += mag_dirs[nscene][nPix][0];
								} else if (s < 0) {
									sum_scales[nPix] -= s;
									sum_offs[nPix] -= mag_dirs[nscene][nPix][0];
								}
							}
						}
2696 2697
					}
				}
2698 2699 2700 2701 2702 2703 2704 2705 2706 2707
			};
		}		      
		ImageDtt.startAndJoin(threads);
		ai.set(0);
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()){
						for (int nscene = 0; nscene < num_scenes; nscene++) if (sum_scales[nPix] > 0) {
							elevations_out[nPix] = sum_offs[nPix]/sum_scales[nPix];
2708
						}
2709
					}
2710
				}
2711 2712 2713 2714
			};
		}		      
		ImageDtt.startAndJoin(threads);
		if (elevation_scale_dirs != null) {
2715
			/*
2716 2717 2718 2719 2720 2721 2722 2723 2724
			ai.set(0);
			for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
				threads[ithread] = new Thread() {
					public void run() {
						for (int nScene = ai.getAndIncrement(); nScene < num_scenes; nScene = ai.getAndIncrement()){
							elevation_scale_dirs[nScene] = new double[num_pixels][]; 
							for (int npix = 0; npix < num_pixels; npix++) if ((mag_dirs[nScene][npix] != null) &&( !Double.isNaN(scales[nScene][npix]))) {
								elevation_scale_dirs[nScene][npix] = new double [] {scales[nScene][npix],mag_dirs[nScene][npix][1]};
							}
2725
						}
2726 2727 2728 2729 2730
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
//			System.arraycopy(scales, 0, elevation_scales, 0, num_scenes);
2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745
			 */
			int        decimate_step = 16;
			int        num_decimate  =  1;
			String     debugTitle =   null;
			fillScaleDirs(
					mag_dirs,             // final double [][][] mag_dirs,
					scales,               // final double [][]   scales,
					elevation_scale_dirs, // final double [][][] elevation_scale_dirs,
					width,                // final int           width,
					decimate_step,        // final int           decimate_step, // 16
					num_decimate,         // final int           num_decimate,
					100,                  // final int           num_passes,
					0.03,                 // final double        max_rchange, //
					debugTitle,           // final String        debugTitle,
					1);                   // final int           debugLevel)
2746 2747 2748 2749 2750
		}
//		IJ.getNumber("Any number", 0);
		return elevations_out;
	}
	
2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839
	/*
	 * Fill NaNs in mag_dirs and copy results to elevation_scale_dirs[num_scenes][][]
	 * @param mag_dirs per scene, per pixel - null or a pair of {magnitude, direction} 
	 * @param scales filtered no-NaN scales to be used as magnitudes
	 * @param elevation_scale_dirs array initialized with number of scenes of nulls to accommodate parirs of {magnitude,direction} 
	 * @param width image width
	 */
	
	
	/**
	 * Fill NaNs in mag_dirs and copy results to elevation_scale_dirs[num_scenes][][]
	 * @param mag_dirs      per scene, per pixel - null or a pair of {magnitude, direction} 
	 * @param scales        filtered no-NaN scales to be used as magnitudes
	 * @param elevation_scale_dirs array initialized with number of scenes of nulls to accommodate parirs of {magnitude,direction} 
	 * @param width         image width
	 * @param decimate_step decimation step (such as 16)
	 * @param num_decimate  number of decimation steps (should be >=1). If 1 just two passes
	 * @param num_passes    number of passes for each decimation step (100)
	 * @param max_rchange   maximal relative change to exit
	 * @param debugTitle    Generate images if non-null
	 * @param debugLevel    debug inner method: 0 - none, 1 - when done, 2 - all iterations
	 */
	public static  void fillScaleDirs(
			final double [][][] mag_dirs,
			final double [][]   scales,
			final double [][][] elevation_scale_dirs,
			final int           width,
			final int           decimate_step, // 16
			final int           num_decimate,
			final int           num_passes,
			final double        max_rchange, //
			final String        debugTitle,
			final int           debugLevel) {
		final int num_scenes = mag_dirs.length;
		final int num_pixels = mag_dirs[0].length;
		final Thread[]      threads =     ImageDtt.newThreadArray();
		final AtomicInteger ai =          new AtomicInteger(0);
		final double [][] scales_xy = new double [2][num_pixels];
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			final int fnscene = nscene;
			if (debugLevel > -1) {
				System.out.println("fillScaleDirs(): "+(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime()))+
						" processing scene "+fnscene);
			}
			for (int i = 0; i < scales_xy.length; i++) {
				Arrays.fill(scales_xy[i], Double.NaN);
			}
			ai.set(0);
			for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
				threads[ithread] = new Thread() {
					public void run() {
						for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) if ((mag_dirs[fnscene][nPix] != null) &&( !Double.isNaN(scales[fnscene][nPix]))) {
							scales_xy[0][nPix] = scales[fnscene][nPix] * Math.cos(mag_dirs[fnscene][nPix][1]);
							scales_xy[1][nPix] = scales[fnscene][nPix] * Math.sin(mag_dirs[fnscene][nPix][1]);
						}
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
			for (int i = 0; i < scales_xy.length; i++) {
				scales_xy[i] = TileProcessor.fillNaNs(
						scales_xy[i],   // final double [] data,
						width,          // int       width_full,
						decimate_step,  // final int        decimate_step, // 16
						num_decimate,   // final int        num_decimate,
						100,            // int       num_passes,
						0.03,           // final double     max_rchange, //  = 0.01 - does not need to be accurate
						debugTitle,     // String           debugTitle);//
						debugLevel);    // final int        debugLevel) {  // 0 - none, 1 - when done, 2 - all iterations
			}
			elevation_scale_dirs[fnscene] = new double [num_pixels][2];
			ai.set(0);
			for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
				threads[ithread] = new Thread() {
					public void run() {
						for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) {
							elevation_scale_dirs[fnscene][nPix][0] = Math.sqrt (scales_xy[0][nPix]*scales_xy[0][nPix] + scales_xy[1][nPix] *scales_xy[1][nPix]);
							elevation_scale_dirs[fnscene][nPix][1] = Math.atan2(scales_xy[1][nPix],scales_xy[0][nPix]);
						}
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
		}
		return;
	}
	
	
	
2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886
	public static double [] getScenesOverlap(
			final double [][][] mag_dirs,
			final boolean [][]  reliable, // or null
			final boolean     use_min,
			final int         start_scene,
			final int         endplus1_scene) {
		final int num_scenes = mag_dirs.length;
		final int num_pixels = mag_dirs[start_scene].length;
		final double [] max_offset = new double [num_pixels];
		Arrays.fill(max_offset, Double.NaN);
		final AtomicInteger [] anum_max = new AtomicInteger[num_scenes];
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			anum_max[nscene] = new AtomicInteger(0);
		}
		final int scene0 = Math.max(0, start_scene);
		final int scene1 = Math.min(num_scenes, endplus1_scene);

		final boolean [] in_all_scenes = new boolean [num_pixels];
		final Thread[]      threads =     ImageDtt.newThreadArray();
		final AtomicInteger ai =          new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) {
						int scene_max = -1;
						double mx = 0; 
						look_all: {
							for (int nscene = scene0; nscene < scene1; nscene++) {
								if ((mag_dirs[nscene]== null) || (mag_dirs[nscene][nPix] == null)) {
									break look_all;
								}
								if ((reliable != null) && !reliable[nscene][nPix]) {
									break look_all;
								}
								double d = mag_dirs[nscene][nPix][0]; // magnitude, may be negative
								if (use_min) {
									d = -d;
								}
								if (d > mx) {
									mx = d;
									scene_max = nscene;		
								}
							}
							if (scene_max >= 0) {
								anum_max[scene_max].getAndIncrement();
							}
							in_all_scenes[nPix] = true;
2887
						}
2888
					}
2889
				}
2890 2891 2892 2893 2894 2895 2896 2897 2898
			};
		}		      
		ImageDtt.startAndJoin(threads);
		int nmax = 0;
		int scene_max = -1;
		for (int nscene = scene0; nscene < scene1; nscene++) {
			if (anum_max[nscene].get() > nmax) {
				nmax = anum_max[nscene].get();
				scene_max = nscene;
2899
			}
2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914
		}
		final int fscene_max = scene_max;
		ai.set(0);
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) if  (in_all_scenes[nPix]){
						max_offset[nPix] = 	 mag_dirs[fscene_max][nPix][0];					
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		return max_offset;
	}
2915 2916
	
	
Andrey Filippov's avatar
Andrey Filippov committed
2917
	
2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038

	public static double [][] warpMagnitudeDirection(
			final double [][] warp_dxy,
			final int         width,
			final double      dir_sigma,// Gaussian blur sigma to smooth direction variations
			final boolean     after_ref){
		//  Assuming objects have positive elevation, so dx, dy sign does not change
		final int num_pixels = warp_dxy.length;
		final double [][] warp_md = new double [warp_dxy.length][];
		final Thread[]      threads =     ImageDtt.newThreadArray();
		final AtomicInteger ai =          new AtomicInteger(0);
		final AtomicInteger ati =         new AtomicInteger(0);
		final double [][] sdxy = new double [threads.length][2];
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					int nthread =  ati.getAndIncrement();
					double sdx = 0, sdy = 0;
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) {
						double [] dxy = warp_dxy[nPix];
						if (dxy != null) {
							sdx += dxy[0];
							sdy += dxy[1];
						}
					}
					sdxy[nthread][0] = sdx;
					sdxy[nthread][1] = sdy;
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);

		double sdx = 0, sdy = 0;
		for (int nthread =0; nthread < sdxy.length; nthread++) {
			sdx += sdxy[nthread][0];
			sdy += sdxy[nthread][1];
		}
		final double avg_dir = Math.atan2(sdy, sdx); // average angle to avoid crossing full rotation
		final double [] mags = new double[num_pixels], dirs = new double[num_pixels], mag_dirs=new double[num_pixels];
		//	Arrays.fill(dirs, Double.NaN);
		ai.set(0);
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) {
						double [] dxy = warp_dxy[nPix];
						if (dxy != null) {
							double m = Math.sqrt(dxy[0]*dxy[0]+dxy[1]*dxy[1]);
							if (!Double.isNaN(m)) {
								mags[nPix] = m;
								double angle = Math.atan2(dxy[1],dxy[0]) - avg_dir;
								if (angle > Math.PI) {
									angle -= 2 * Math.PI;
								} else if (angle < -Math.PI){
									angle += 2 * Math.PI;
								}
								dirs[nPix] = angle;
								mag_dirs[nPix] = m * angle;
							}
						}					
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		final double [] mag_blur = mags.clone();
		(new DoubleGaussianBlur()).blurDouble(
				mag_blur,          //
				width,
				num_pixels/width,
				dir_sigma,              // double sigmaX,
				dir_sigma,              // double sigmaY,
				0.01);                 // double accuracy)
		(new DoubleGaussianBlur()).blurDouble(
				mag_dirs,          //
				width,
				num_pixels/width,
				dir_sigma,              // double sigmaX,
				dir_sigma,              // double sigmaY,
				0.01);                 // double accuracy)
		ai.set(0);
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) {
						double [] dxy = warp_dxy[nPix];
						if (dxy != null) {
							double mag = mags[nPix];
							double angle = mag_dirs[nPix]/mag_blur[nPix] + avg_dir;
							if (Double.isNaN(angle) || Double.isNaN(mag)) {
								warp_md[nPix] = new double [2];								
							} else {
								if (after_ref) {
									angle += Math.PI;
									mag = -mag;
								}
								if (angle > Math.PI) {
									while (angle > Math.PI) {
										angle -= 2 * Math.PI;
									}
								} else if (angle < -Math.PI){
									angle += 2 * Math.PI;
								}
								if (Double.isNaN(angle)) {
									angle = 0;
								}
								warp_md[nPix] = new double [] {mag, angle};
							}
						}					
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		return warp_md;
	}





Andrey Filippov's avatar
Andrey Filippov committed
3039 3040 3041 3042 3043 3044 3045 3046 3047
	public static ImagePlus showOffsetsCombo(
			final double [][][] map_combo,
			final int           width,
			QuadCLT []          quadCLTs, // just for names
			String              title) { // with .tiff
		final int num_scenes = map_combo.length;
		final int num_pixels = map_combo[0].length;
		final String [] titles_top = {"dist","dx","dy"};
		String [] titles_scene = new String[num_scenes];
3048

Andrey Filippov's avatar
Andrey Filippov committed
3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082
		final double [][][] img_data = new double [ titles_top.length][num_scenes][num_pixels];
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
			final int fscene = nscene;
			ai.set(0);
			for (int ntype = 0; ntype < img_data.length; ntype++) {
				Arrays.fill(img_data[ntype][nscene], Double.NaN);
			}
			for (int ithread = 0; ithread < threads.length; ithread++) {
				threads[ithread] = new Thread() {
					public void run() {
						for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) if (map_combo[fscene][nPix] != null){
							double dx = map_combo[fscene][nPix][0];
							double dy = map_combo[fscene][nPix][1];
							double d = Math.sqrt(dx*dx+dy*dy);
							img_data[0][fscene][nPix] = d;
							img_data[1][fscene][nPix] = dx;
							img_data[2][fscene][nPix] = dy;
						}
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
		}
		ImagePlus imp =  ShowDoubleFloatArrays.showArraysHyperstack(
				img_data,           // double[][][] pixels, 
				width,                // int          width, 
				title, // "terrain_vegetation_render.tiff", // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
				titles_scene,                     // String []    titles, // all slices*frames titles or just slice titles or null
				titles_top,                     // String []    frame_titles, // frame titles or null
				true);                            // boolean      show)
		return imp;
3083

Andrey Filippov's avatar
Andrey Filippov committed
3084 3085
	}

3086 3087


Andrey Filippov's avatar
Andrey Filippov committed
3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136
	
	public static ImagePlus showOffsetsDiff(
			final double [][][] terrain,
			final double [][][] vegetation,
			final int           width,
			QuadCLT []          quadCLTs, // just for names
			String              title) { // with .tiff
		final int num_scenes = terrain.length;
		final int num_pixels = terrain[0].length;
		final String [] titles_top = {"dist","dx","dy"};
		String [] titles_scene = new String[num_scenes];
		
		final double [][][] img_data = new double [ titles_top.length][num_scenes][num_pixels];
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		for (int nscene = 0; nscene < num_scenes; nscene++) {
			titles_scene[nscene] = nscene+":"+quadCLTs[nscene].getImageName();
			final int fscene = nscene;
			ai.set(0);
			for (int ntype = 0; ntype < img_data.length; ntype++) {
				Arrays.fill(img_data[ntype][nscene], Double.NaN);
			}
			for (int ithread = 0; ithread < threads.length; ithread++) {
				threads[ithread] = new Thread() {
					public void run() {
//						TileNeibs tn = new TileNeibs(out_window.width, out_window.height);
						for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) if ((terrain[fscene][nPix] != null) && (vegetation[fscene][nPix] != null)){
							double dx = vegetation[fscene][nPix][0] - terrain[fscene][nPix][0];
							double dy = vegetation[fscene][nPix][1] - terrain[fscene][nPix][1];
							double d = Math.sqrt(dx*dx+dy*dy);
							img_data[0][fscene][nPix] = d;
							img_data[1][fscene][nPix] = dx;
							img_data[2][fscene][nPix] = dy;
						}
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
		}
		ImagePlus imp =  ShowDoubleFloatArrays.showArraysHyperstack(
				img_data,           // double[][][] pixels, 
				width,                // int          width, 
				title, // "terrain_vegetation_render.tiff", // String       title, "time_derivs-rt"+diff_time_rt+"-rxy"+diff_time_rxy,
				titles_scene,                     // String []    titles, // all slices*frames titles or just slice titles or null
				titles_top,                     // String []    frame_titles, // frame titles or null
				true);                            // boolean      show)
		return imp;
		
	}
3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148


	/**
	 * Apply a map to an image (with bi-linear interpolation) and output warped image	
	 * @param img       source image, has NaN-s
	 * @param img_width source image width
	 * @param map       map (absolute or differential), where each pixel is either null or a pair or 
	 *                  fractional source image coordinates. In differential mode it is a pair of offsets
	 *                  from the map x,y indices. 
	 * @param window    Rectangle with {width, height} specifying output image size and (in
	 *                  differential mode only) {x,y} corresponds to absolute origin
	 * @param map_diff  true for differential mode, false - for absolute.
3149
	 * @param scale     debug feature, only works with differential map
3150 3151 3152 3153 3154 3155 3156
	 * @return          warped image in line-scan order, may have NaN-s.
	 */
	public static double [] applyMap(
			final double []   img,
			final int         img_width,
			final double [][] map,
			final Rectangle   window,
3157 3158
			final boolean     map_diff,
			final double      scale) { // debug feature, only works with differential map
3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173
		final int img_height = img.length /img_width;
		final int num_pixels = window.width*window.height;
		final double [] render_out = new double [num_pixels];
		Arrays.fill(render_out, Double.NaN);
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) if (map[nPix] != null){
						pix_label: {
							int ix = nPix % window.width;
							int iy = nPix / window.width;
							double [] pxy = map[nPix].clone();
							if (map_diff) {
3174 3175 3176 3177
								pxy[0] *= scale;
								pxy[1] *= scale;
								pxy[0] += ix - window.x; //  + 0.5;
								pxy[1] += iy - window.y; //  + 0.5;
3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222
							}
//							pxy[0] += window2.x;
//							pxy[1] += window2.y;
							int x0 = (int) Math.floor(pxy[0]);
							int y0 = (int) Math.floor(pxy[1]);
							if ((x0 < 0) || (y0 < 0) || (x0 >= (img_width -1)) || (y0 >= (img_height-1))) {
								break pix_label; // all 4 corners should fit
							}
							int img_pix = x0+ y0 * img_width;
							double [][] corners = {
									{img[img_pix],                 img[img_pix + 1]},
									{img[img_pix + img_width], img[img_pix + img_width + 1]}};
							
							for (int dy = 0; dy < 2; dy++) {
								for (int dx = 0; dx < 2; dx++) {
									double corner = corners[dy][dx];
									if (Double.isNaN(corner)) {
										break pix_label; // all 4 corners should be defined
									}
//									if (map_diff2) {
//										corner[0] += x0 + dx  + 0.5 - window2.x;
//										corner[1] += y0 + dy  + 0.5 - window2.y;
//									}
								}
							}

							double fx = pxy[0] - x0;
							double fy = pxy[1] - y0;
							render_out[nPix] = 
										(1-fx)*(1-fy)*corners[0][0] +
										(  fx)*(1-fy)*corners[0][1] +
										(1-fx)*(  fy)*corners[1][0] +
										(  fx)*(  fy)*corners[1][1];
						}

					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		
		
		
		return render_out;
	}
Andrey Filippov's avatar
Andrey Filippov committed
3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257
	
	/**
	 * Combine maps: map1 and map2 (map2 transforms result of map1)
	 * @param map1 first map defined for a grid, each element is either null or a pair {mapped_X, mapped_Y}
	 * @param window1 Rectangle window for the first map ([window1.width*window1.height], width*window1.x
	 *        and window1.y specifies a zero point if the map is differential 
	 * @param map_diff1 true if map1 is differential, specifying relative x,y, not absolute ones  
	 * @param map2 second map to be combined (applied to the results of map1
	 * @param window2 similar to window1 for the second map
	 * @param map_diff2 true if map2 is differential, specifying relative x,y, not absolute ones
	 * @param map_diff_out generate differential map rather than absolute one
	 * @return map that combines map1 and map2 using linear interpolation.
	 */
	public static double [][] combineMaps(
			final double [][] map1,
			final Rectangle   window1,
			final boolean     map_diff1,
			final double [][] map2,
			final Rectangle   window2,
			final boolean     map_diff2,
			final boolean     map_diff_out) {
		final int odepth = 2; // just x,y. if 3 - will have 0 for disparity
		final int num_pixels1 = window1.width*window1.height;
		final double [][] combo_map = new double [num_pixels1][];
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels1; nPix = ai.getAndIncrement()) if (map1[nPix] != null){
						pix_label: {
							int ix = nPix % window1.width;
							int iy = nPix / window1.width;
							double [] pxy = map1[nPix].clone();
							if (map_diff1) {
3258 3259
								pxy[0] += ix - window1.x; //  + 0.5;
								pxy[1] += iy - window1.y; //  + 0.5;
Andrey Filippov's avatar
Andrey Filippov committed
3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280
							}
							pxy[0] += window2.x;
							pxy[1] += window2.y;
							int x0 = (int) Math.floor(pxy[0]);
							int y0 = (int) Math.floor(pxy[1]);
							if ((x0 < 0) || (y0 < 0) || (x0 >= (window2.width -1)) || (y0 >= (window2.height-1))) {
								break pix_label; // all 4 corners should fit
							}
							int opix00 = x0+ y0*window2.width;
							double [][][] corners = {
									{map2[opix00],                 map2[opix00 + 1]},
									{map2[opix00 + window2.width], map2[opix00 + window2.width + 1]}};
							for (int dy = 0; dy < 2; dy++) {
								for (int dx = 0; dx < 2; dx++) {
									double [] corner = corners[dy][dx];
									if (corner == null) {
										break pix_label; // all 4 corners should be defined
									}
									corners[dy][dx] = corners[dy][dx].clone();
									corner = corners[dy][dx];
									if (map_diff2) {
3281 3282
										corner[0] += x0 + dx - window2.x; //  + 0.5;
										corner[1] += y0 + dy - window2.y; //  + 0.5;
Andrey Filippov's avatar
Andrey Filippov committed
3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297
									}
								}
							}

							double fx = pxy[0] - x0;
							double fy = pxy[1] - y0;
							double [] cxy = new double [odepth];
							for (int i = 0; i < odepth; i++) {
								cxy[i] = 
										(1-fx)*(1-fy)*corners[0][0][i] +
										(  fx)*(1-fy)*corners[0][1][i] +
										(1-fx)*(  fy)*corners[1][0][i] +
										(  fx)*(  fy)*corners[1][1][i];
							}
							if (map_diff_out) {
3298 3299
									cxy[0] -= ix - window1.x; // + 0.5;
									cxy[1] -= iy - window1.y; // + 0.5;
Andrey Filippov's avatar
Andrey Filippov committed
3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375
							}						
							combo_map[nPix] = cxy;
						}

					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		return combo_map;
	}
	
	
	
	
	/**
	 * Invert map that defines x,y for the uniform grid 
	 * @param map_in each pixel is null or {X,Y,...} 
	 * @param width input map width
	 * @param out_window Rectangle, width and height define output size, x and y - coordinates of the
	 *        output point corresponding to X=0, Y=0 of the input data
	 * @param in_diff  input map is differential, add x+0.5, y+0.5 to X,Y
	 * @param out_diff make output map differential by subtracting (x-out_window.x-0.5) from output x,
	 *        and (y-out_window.y-0.5) from output y;
	 * @param patch_min_neibs patch holes that have at least this number of defined neighbors (do not patch if 0)
	 * @param pnum_patched - null or int[1] to return number of patched tiles;       
	 * @return per-pixel array of {pX,pY} pairs (or nulls) for each output pixel in line-scal order 
	 */
	public static double [][] invertMap(
			final double [][] map_in,
			final int         width,
			final Rectangle   out_window,
			final boolean     in_diff,
			final boolean     out_diff,
			final int         patch_min_neibs,
			final int []      pnum_patched){
		final int dbg_opix = (width < 0) ? 1:  (-(30+36*640)); // just to remove warning when not used
		final int odepth = 2; // just x,y. if 3 - will have 0 for disparity
		final int height = map_in.length/width;
		final int num_opixels = out_window.width * out_window.height;
		final double [][] map_out = new double [num_opixels][];
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		final AtomicInteger anum_patched = new AtomicInteger(0);
		if (pnum_patched !=null) {
			pnum_patched[0] = 0;
		}
		// use multiple passes to avoid unlikely races
		for (int offs_y = 0; offs_y < 2; offs_y++) {
			final int height2 = (height + 1 - offs_y);
			for (int offs_x = 0; offs_x < 2; offs_x++) {
				final int width2 = (width + 1 - offs_x);
				final int num_pix2 = height2 * width2;
				final int [] offs = {offs_x,offs_y};
				ai.set(0);
				for (int ithread = 0; ithread < threads.length; ithread++) {
					threads[ithread] = new Thread() {
						public void run() {
							double [][] corners_cw = new double [4][2];
							int [][] bounds = new int[2][2]; // [y_not_x][max_not_min]
							for (int nPix2 = ai.getAndIncrement(); nPix2 < num_pix2; nPix2 = ai.getAndIncrement()) {
								int ix0 =offs[0] + 2 * (nPix2 % width2);
								int iy0 =offs[1] + 2 * (nPix2 / width2);
								boolean has_nan = false;
								for (int dir = 0; dir < corners_cw.length; dir++) {
									int ix1 = ix0 + CORN_CW_OFFS[dir][0];
									int iy1 = iy0 + CORN_CW_OFFS[dir][1];
									if ((ix1 >= width) || (iy1 >= height) || (map_in[ix1+width*iy1] == null)) {
										has_nan = true;
										break;
									}
									int in_indx = ix1+width*iy1;
									for (int ynx = 0; ynx < 2; ynx++) { 
										corners_cw[dir][ynx] = map_in[in_indx][ynx];
									}
									if (in_diff) {
3376 3377
										corners_cw[dir][0] += ix1; //  +  0.5;
										corners_cw[dir][1] += iy1; //  +  0.5;
Andrey Filippov's avatar
Andrey Filippov committed
3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453
									}
									corners_cw[dir][0] += out_window.x;
									corners_cw[dir][1] += out_window.y;
								}
								if (has_nan) {
									continue;
								}
								// all 4 corners are defined;
								bounds[0][0] = (int) Math.floor(corners_cw[0][0]);
								bounds[1][0] = (int) Math.floor(corners_cw[0][1]);
								bounds[0][1] = bounds[0][0]; 
								bounds[1][1] = bounds[1][0];
								for (int dir = 1; dir < corners_cw.length; dir++) {
									for (int ynx = 0; ynx < 2; ynx++) {
										int mn = (int) Math.floor(corners_cw[dir][ynx]);
										int mx = (int) Math.ceil (corners_cw[dir][ynx]);
										if (mn < bounds[ynx][0]) {
											bounds[ynx][0] = mn;
										}
										if (mx > bounds[ynx][1]) {
											bounds[ynx][1] = mx;
										}
									}
								}
								if (bounds[0][0] < 0) bounds[0][0] = 0;
								if (bounds[1][0] < 0) bounds[1][0] = 0;
								if (bounds[0][1] >= out_window.width)  bounds[0][1] = out_window.width - 1;
								if (bounds[1][1] >= out_window.height) bounds[1][1] = out_window.height - 1;
								if ((bounds[0][0] > bounds[0][1]) || (bounds[1][0] > bounds[1][1])) {
									continue; // completely outside output window
								}
								if (dbg_opix >= 0)  {
									int dbg_ox = dbg_opix % out_window.width;
									int dbg_oy = dbg_opix / out_window.width;
									if ((dbg_ox >= bounds[0][0]) && (dbg_ox <= bounds[0][1]) && (dbg_oy >= bounds[1][0]) && (dbg_oy <= bounds[1][1])) {
										System.out.println("invertMap(): "+bounds[0][0]+"<=ox<="+bounds[0][1]);
										System.out.println("invertMap(): "+bounds[1][0]+"<=oy<="+bounds[1][1]);
									}
								}
								
								double [] v10 = {corners_cw[1][0]-corners_cw[0][0], corners_cw[1][1]-corners_cw[0][1]};
								double [] v30 = {corners_cw[3][0]-corners_cw[0][0], corners_cw[3][1]-corners_cw[0][1]};
								double [] v12 = {corners_cw[1][0]-corners_cw[2][0], corners_cw[1][1]-corners_cw[2][1]};
								double [] v32 = {corners_cw[3][0]-corners_cw[2][0], corners_cw[3][1]-corners_cw[2][1]};
								
								double l2_10 = v10[0]*v10[0]+v10[1]*v10[1];
								double l2_30 = v30[0]*v30[0]+v30[1]*v30[1];
								double l2_12 = v12[0]*v12[0]+v12[1]*v12[1];
								double l2_32 = v32[0]*v32[0]+v32[1]*v32[1];
								
								
								for (int oy = bounds[1][0]; oy <= bounds[1][1]; oy++) {
									for (int ox = bounds[0][0]; ox <= bounds[0][1]; ox++) {
										// see if it is inside 4 corners
										double [] vp0 = {ox-corners_cw[0][0], oy-corners_cw[0][1]};
										if (vp0[0]*v10[1] > vp0[1]*v10[0]) {
											continue;
										}
										if (vp0[0]*v30[1] < vp0[1]*v30[0]) {
											continue;
										}
										double [] vp2 = {ox-corners_cw[2][0], oy-corners_cw[2][1]};
										if (vp2[0]*v12[1] < vp2[1]*v12[0]) {
											continue;
										}
										if (vp2[0]*v32[1] > vp2[1]*v32[0]) {
											continue;
										}
										// inside, now interpolate. First get effective coordinates
										double lp0 = Math.sqrt(vp0[0]*vp0[0]+vp0[1]*vp0[1]);
										double lp2 = Math.sqrt(vp2[0]*vp2[0]+vp2[1]*vp2[1]);
										double u0 =       (vp0[0]*v10[0] + vp0[1]*v10[1])/l2_10;
										double v0 =       (vp0[0]*v30[0] + vp0[1]*v30[1])/l2_30;
										double u1 = 1.0 - (vp2[0]*v32[0] + vp2[1]*v32[1])/l2_32;
										double v1 = 1.0 - (vp2[0]*v12[0] + vp2[1]*v12[1])/l2_12;
										// Use arithmetic average as some of u0,u1,v0,v1 can be small negatives
3454 3455 3456 3457 3458
										//double u = 0.5 * (u0 + u1);
										//double v = 0.5 * (v0 + v1);
										double denom = 1-(u1-u0)*(v1-v0);
										double u = (u0 +(u1-u0)*v0)/denom;
										double v = (v0 +(v1-v0)*u0)/denom;
Andrey Filippov's avatar
Andrey Filippov committed
3459 3460 3461 3462 3463
										int oindx = ox + oy*out_window.width;
										map_out[oindx] = new double [odepth];
										map_out[oindx][0] = ix0 + u;
										map_out[oindx][1] = iy0 + v;
										if (out_diff) {
3464 3465
											map_out[oindx][0] -= ox-out_window.x; // -0.5;
											map_out[oindx][1] -= oy-out_window.y; // -0.5;
Andrey Filippov's avatar
Andrey Filippov committed
3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526
										}
									}									
								}
							}
						}
					};
				}		      
				ImageDtt.startAndJoin(threads);
			}
		}
		if (patch_min_neibs > 0) {
			ai.set(0);
			final double [][] map_out_filtered = new double [num_opixels][];
			for (int ithread = 0; ithread < threads.length; ithread++) {
				threads[ithread] = new Thread() {
					public void run() {
						TileNeibs tn = new TileNeibs(out_window.width, out_window.height);
						for (int nPix = ai.getAndIncrement(); nPix < num_opixels; nPix = ai.getAndIncrement()) if (map_out[nPix] != null){
							map_out_filtered[nPix] = map_out[nPix]; // no need to clone;
						} else {
							int num_neibs = 0;
							for (int dir = 0; dir< TileNeibs.DIRS; dir++) {
								int nPix1 = tn.getNeibIndex(nPix, dir);
								if ((nPix1 >=0) && (map_out[nPix1] != null)){
									num_neibs ++;
								}
							}
							if (num_neibs >= patch_min_neibs) {
								map_out_filtered[nPix] = new double [odepth];
								for (int dir = 0; dir< TileNeibs.DIRS; dir++) {
									int nPix1 = tn.getNeibIndex(nPix, dir);
									if ((nPix1 >=0) && (map_out[nPix1] != null)){
										for (int i = 0; i < odepth; i++) {
											map_out_filtered[nPix][i] +=  map_out[nPix1][i];
										}
									}
								}
								for (int i = 0; i < odepth; i++) {
									map_out_filtered[nPix][i] /=  num_neibs;
								}
								anum_patched.getAndIncrement();
							}
						}
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
			if (anum_patched.get() > 0) {
//				System.out.println("invertMap(): patched "+anum_patched.get()+" null pixels.");
				if (pnum_patched !=null) {
					pnum_patched[0] = anum_patched.get();
				}
			}
			return map_out_filtered;
		}
		
		// patch possible holes that appeared on the borders?
		return map_out;
		
	}
	
3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653
	public static double [][] interpolatePxPyDBicubic(
			final double [][] pXpYD_tile,
			final int         tilesX,
			final int         tile_size){
		final int odepth = 3; // just x,y. if 3 - will have 0 for disparity
		final int width = tilesX * tile_size; 
		final int htile_size = tile_size/2;
		int num_tiles = pXpYD_tile.length;
		int num_pixels = num_tiles * tile_size * tile_size; 
		final int tilesY = num_tiles/tilesX;
		final double [][] pXpYD_pixel = new double [num_pixels][];
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		final double [][] tslices = new double [odepth][(tilesX+2)*(tilesY+2)]; // extended by 1 each of 4 sides
		final double [][] pslices = new double [odepth][num_pixels];
		for (int ns = 0; ns < odepth; ns++) {
			Arrays.fill(tslices[ns], Double.NaN);
		}
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int nTile = ai.getAndIncrement(); nTile < num_tiles; nTile = ai.getAndIncrement()) if (pXpYD_tile[nTile] != null){
						int tileX = nTile % tilesX;
						int tileY = nTile / tilesX;
						int nTile_ex = (tileX + 1) + (tileY + 1) * (tilesX+2); 
						for(int ns = 0; ns < odepth; ns++) {
							tslices[ns][nTile_ex] = pXpYD_tile[nTile][ns];
						}
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		ai.set(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					TileNeibs tn = new TileNeibs(tilesX+2,tilesY+2);
					for (int ns = ai.getAndIncrement(); ns < odepth; ns = ai.getAndIncrement()){
						OrthoMap.fillNaNs(
								tslices[ns], // double [] data,
								tn,         // TileNeibs tn,
								3);         // int min_neibs)
					}
				}
			};
		}
		ImageDtt.startAndJoin(threads);
		final double [] y = new double [tilesY+2]; // f is [col][row] ! 
		final double [] x = new double [tilesX+2];
		for (int i = 0; i < x.length; i++) {
			x[i] = -htile_size + tile_size*i;
		}
		for (int i = 0; i < y.length; i++) {
			y[i] = -htile_size + tile_size*i;
		}
		for (int nslice = 0; nslice < odepth; nslice++) {
			final double [] tslice = tslices[nslice]; 
			final double [] pslice = pslices[nslice];
			final double [][] tslice2 = new double [tilesY+2][tilesX+2];
			for (int i = 0; i < tslice2.length; i++) {
				System.arraycopy(
						tslice,
						i * (tilesX+2),
						tslice2[i],
						0,
						(tilesX+2));
			}
			final PiecewiseBicubicSplineInterpolatingFunction pbsif= new PiecewiseBicubicSplineInterpolatingFunction(y, x, tslice2);
			ai.set(0);
			for (int ithread = 0; ithread < threads.length; ithread++) {
				threads[ithread] = new Thread() {
					public void run() {
						for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()){
							int pixX = nPix % width; 
							int pixY = nPix / width;
//							if (pbsif.isValidPoint(pixY,pixX)) { // then overwrite with bicubic
							pslice[nPix] = pbsif.value(pixY,pixX);   
//							}
						}
					}
				};
			}
			ImageDtt.startAndJoin(threads);
		}
		
		ai.set(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()){
						int pixX = nPix % width;
						int pixY = nPix / width;
						int tileX = pixX/tile_size;
						int tileY = pixY/tile_size;
						if (pXpYD_tile[tileX+tileY*tilesX] != null) {
							boolean defined = true;
							for (int i = 0; (i < odepth) && defined; i++) {
								defined &= !Double.isNaN(pslices[i][nPix]);
							}
							if (defined) {
								pXpYD_pixel[nPix] = new double [odepth];
								for (int i = 0; i < odepth; i++) {
									pXpYD_pixel[nPix][i] = pslices[i][nPix];
								}
							}
						}
					}
				}
			};
		}
		ImageDtt.startAndJoin(threads);
		/*
		ShowDoubleFloatArrays.showArrays(
				pslices,
				tilesX * tile_size,
				tilesY * tile_size,
				true,
				"test_bicubic",
				new String[] {"pX","pY","D"});
		*/
		return pXpYD_pixel;
	}
	
	
	
	
Andrey Filippov's avatar
Andrey Filippov committed
3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876
	/**
	 * Expand defined tile pXpYD so each defined tile has at least 3 consecutive neighbors: 2 ortho and diagonal between them
	 * @param pXpYD_tile
	 * @param tilesX
	 * @param tile_size
	 * @return
	 */
	public static double [][] interpolatePxPyDBilinear(
			final double [][] pXpYD_tile,
			final int         tilesX,
			final int         tile_size){
		final int width = tilesX * tile_size; 
		final int htile_size = tile_size/2;
		final double pix_step = 1.0/tile_size;
		int num_tiles = pXpYD_tile.length;
		int num_pixels = num_tiles * tile_size * tile_size; 
		final int tilesY = num_tiles/tilesX;
		final double [][] pXpYD_pixel = new double [num_pixels][];
		final int tilesXE = tilesX+1;
		final int tilesYE = tilesY+1;
		final int num_tiles_ext = tilesXE*tilesYE;
		final int dbg_tile = -(20+46*80);
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					TileNeibs tn = new TileNeibs(tilesX,tilesY);
					double [][] pXpYD_neibs = new double[TileNeibs.DIRS+1][];
					for (int ntile = ai.getAndIncrement(); ntile < num_tiles; ntile = ai.getAndIncrement()) if (pXpYD_tile[ntile] != null){
						if (ntile== dbg_tile) {
							System.out.println("interpolatePxPyDBilinear(): nTile="+ntile);
						}
						Arrays.fill(pXpYD_neibs, null);
						pXpYD_neibs[8] = pXpYD_tile[ntile];
						int tileX = ntile % tilesX;
						int tileY = ntile / tilesX;
						int num_defined = 0;
						int num_ortho = 0;
						for (int dir = 0; dir< TileNeibs.DIRS; dir++) {
							int ntile1 = tn.getNeibIndex(ntile, dir);
							if ((ntile1 >= 0) && (pXpYD_tile[ntile1] != null)) {
								pXpYD_neibs[dir] = pXpYD_tile[ntile1];
								num_defined++;
								if ((dir & 1)==0) num_ortho++;
							}
						}
						// extrapolate to fill missing
						if (num_defined < TileNeibs.DIRS) {
							for (int dir = 0; dir< TileNeibs.DIRS; dir++) if ((pXpYD_neibs[dir] == null) && (pXpYD_neibs[(dir + 4) % 8] != null)){
								double [] pXpYD_opposite = pXpYD_neibs[(dir + 4) % 8];
								num_defined++;
								if ((dir & 1)==0) num_ortho++;
								pXpYD_neibs[dir] = new double [PXPYD_LEN];
								for (int i = 0; i < PXPYD_LEN; i++) {
									pXpYD_neibs[dir][i] = 2 * pXpYD_neibs[8][i] - pXpYD_opposite[i];
								}
							}
						}
						// any ortho are still not filled - juxt copy the central one
						if (num_ortho < 4) {
							for (int dir = 0; dir< TileNeibs.DIRS; dir+=2) if (pXpYD_neibs[dir] == null) {
								pXpYD_neibs[dir] = pXpYD_neibs[8].clone();
								num_ortho++;
							}
						}
						
						// ortho opposite always exist, only diagonal can be missing (like in the corner (x=0, y=0)
						if (num_defined < TileNeibs.DIRS) { // all ortho are already filled
							for (int dir = 1; dir< TileNeibs.DIRS; dir += 2) if (pXpYD_neibs[dir] == null) {
								double [] pXpYD1 = pXpYD_neibs[(dir + 7) % 8];
								double [] pXpYD2 = pXpYD_neibs[(dir + 1) % 8];
								pXpYD_neibs[dir] = new double [PXPYD_LEN];
								for (int i = 0; i < PXPYD_LEN; i++) {
									pXpYD_neibs[dir][i] = pXpYD1[i] + pXpYD2[i] - pXpYD_neibs[8][i];
								}
							}
						}
						// all 8 are now filled in, can fill data in 4 squares
						int xc = htile_size + tile_size * tileX;
						int yc = htile_size + tile_size * tileY;
//						double fx0 = 0.5 + 0.5/tile_size; 
//						double fy0 = 0.5 + 0.5/tile_size; 
						for (int dir = 0; dir < 4; dir++) {
							double [] pxy00 = pXpYD_neibs[FOUR_CORNERS_Z[dir][0]];  
							double [] pxy01 = pXpYD_neibs[FOUR_CORNERS_Z[dir][1]];  
							double [] pxy10 = pXpYD_neibs[FOUR_CORNERS_Z[dir][2]];  
							double [] pxy11 = pXpYD_neibs[FOUR_CORNERS_Z[dir][3]];
							int ix0 = xc + XY_OFFS[dir][0] * htile_size; // absolute pixel X of the top-left corner of the quadrant
							int iy0 = yc + XY_OFFS[dir][1] * htile_size; // absolute pixel Y of the top-left corner of the quadrant
							double fx0 = 0.5*(pix_step - XY_OFFS[dir][0]);
							double fy0 = 0.5*(pix_step - XY_OFFS[dir][1]);
							
							for (int y = 0; y < htile_size; y++) {
								double fy = fy0 + y * pix_step;
								int iy = iy0 + y;
								for (int x = 0; x < htile_size; x++) {
									int ix = ix0 + x;
									int iPix = iy * width + ix;
									double fx = fx0 + x * pix_step;
									pXpYD_pixel[iPix] = new double [PXPYD_LEN];
									for (int i = 0; i < PXPYD_LEN; i++) {
										pXpYD_pixel[iPix][i] =
												(1-fy)*(1-fx) * pxy00[i] +
												(1-fy)*(  fx) * pxy01[i] +
												(  fy)*(1-fx) * pxy10[i] +
												(  fy)*(  fx) * pxy11[i];
									}
								}
							}
						}
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		return pXpYD_pixel;
	}
	
	
	public static double [] averageMono(
			final double [][] data) {
		final int num_scenes = data.length;
		final int num_pixels = data[0].length;
		final double [] average_data = new double[num_pixels];
		Arrays.fill(average_data, Double.NaN);
		final double [] weights =      new double[num_pixels];
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) {
						double swd = 0, sw = 0;
						for (int nscene = 0; nscene < num_scenes; nscene++) {
							double d = data[nscene][nPix];
							if (!Double.isNaN(d)) {
								swd += d;
								sw  += 1;
							}
						}
						if (sw > 0) {
							average_data[nPix] = swd/sw;
						}
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		
		
		return average_data;
	}
	
	
	
	public static double [][][] getPxPyDs(
			///    		CLTParameters  clt_parameters,
			//    		boolean        mb_en,
			//    		double         mb_max_gain,
			//    		Rectangle      fov_tiles,
			double []      reference_xyz, // offset reference camera {x,y,z}
			double []      reference_atr,    		
			double []      ref_disparity,			
			QuadCLT []     quadCLTs,
			boolean []     scene_selection, // null or same length as quadCLTs
			int            ref_index,
			int            debugLevel){
		//		boolean mb_en =       clt_parameters.imp.mb_en; //  && (fov_tiles==null) && (mode3d > 0);
		//		double  mb_tau =      clt_parameters.imp.mb_tau;      // 0.008; // time constant, sec
		//		double  mb_max_gain = clt_parameters.imp.mb_max_gain; // 5.0;   // motion blur maximal gain (if more - move second point more than a pixel
		ErsCorrection ers_reference = quadCLTs[ref_index].getErsCorrection();
		int dbg_scene = -95;
		double [][][] pXpYD = new double [quadCLTs.length][][];
		for (int nscene =  0; nscene < quadCLTs.length ; nscene++) if ((quadCLTs[nscene] != null) && ((scene_selection==null) || scene_selection[nscene])){
			if (nscene== dbg_scene) {
				System.out.println("renderSceneSequence(): nscene = "+nscene);
			}
			String ts = quadCLTs[nscene].getImageName();
			double []   scene_xyz = OpticalFlow.ZERO3;
			double []   scene_atr = OpticalFlow.ZERO3;
			if (nscene != ref_index) { // Check even for raw, so video frames will match in all modes 
				scene_xyz = ers_reference.getSceneXYZ(ts);
				scene_atr = ers_reference.getSceneATR(ts);
				if ((scene_atr==null) || (scene_xyz == null)) {
					continue;
				}
				double []   scene_ers_xyz_dt = ers_reference.getSceneErsXYZ_dt(ts);
				double []   scene_ers_atr_dt = ers_reference.getSceneErsATR_dt(ts);
				quadCLTs[nscene].getErsCorrection().setErsDt(
						scene_ers_xyz_dt, // double []    ers_xyz_dt,
						scene_ers_atr_dt); // double []    ers_atr_dt)(ers_scene_original_xyz_dt);
			}
			if (reference_xyz != null) { // offset all, including reference scene
				double [][] combo_xyzatr = ErsCorrection.combineXYZATR(
						reference_xyz,  // double [] reference_xyz,
						reference_atr,  // double [] reference_atr, 
						scene_xyz,   // double [] scene_xyz,
						scene_atr);  // double [] scene_atr) 
				scene_xyz = combo_xyzatr[0];
				scene_atr = combo_xyzatr[1];
			}
			//					double [][] dxyzatr_dt = null;
			// should get velocities from HashMap at reference scene from timestamp , not re-calculate.
			//					if (mb_en) {
			//						dxyzatr_dt = new double[][] { // for all, including ref
			//							quadCLTs[nscene].getErsCorrection().getErsXYZ_dt(),
			//							quadCLTs[nscene].getErsCorrection().getErsATR_dt()};				
			//					}

			// No use of ERS !
			pXpYD[nscene] =  QuadCLT.getScenePxPyD( 
					null, // final Rectangle   full_woi_in,      // show larger than sensor WOI in tiles (or null)
					ref_disparity, // double []         disparity_ref,
					scene_xyz,           // final double []   scene_xyz, // camera center in world coordinates
					scene_atr,           // final double []   scene_atr, // camera orientation relative to world frame
					quadCLTs[nscene],    // final QuadCLT     scene,
					quadCLTs[ref_index]); // final QuadCLT     ref_scene, // now - may be null - for testing if scene is rotated ref

		}   // for (int nscene      
		return pXpYD;
	}
	
3877 3878 3879 3880 3881 3882 3883 3884
	
	
	/**
	 * Calculate pXpYD difference from the reference scene
	 * @param pXpYD
	 * @param ref_index
	 * @return
	 */
Andrey Filippov's avatar
Andrey Filippov committed
3885 3886 3887 3888
	public static double [][][] diffPxPyDs(
			final double [][][] pXpYD,
			final int ref_index){
		final int num_scenes = pXpYD.length;
3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899
		int npix = 0;
		for (int i = 0; i < pXpYD.length; i++) if (pXpYD[i] != null){
			npix = pXpYD[i].length;
			break;
		}
		if (npix == 0) {
			System.out.println("diffPxPyDs(): no data available!");
			return null; 
		}
		final int num_pixels = npix;
		
Andrey Filippov's avatar
Andrey Filippov committed
3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017
		final double [][][] diff_pXpYD = new double [num_scenes][num_pixels][];
		final Thread[] threads = ImageDtt.newThreadArray(QuadCLT.THREADS_MAX);
		final AtomicInteger ai = new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) if (pXpYD[ref_index][nPix] != null){ // probably always
						int num_comp = pXpYD[ref_index][nPix].length;
						for (int nscene =0; nscene < num_scenes; nscene++) if ((pXpYD[nscene] != null) && (pXpYD[nscene][nPix] != null)){
							diff_pXpYD[nscene][nPix] = new double [num_comp];
							for (int i = 0; i < num_comp; i++) {
								diff_pXpYD[nscene][nPix][i] = pXpYD[nscene][nPix][i] - 	pXpYD[ref_index][nPix][i];
							}
						}
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		return diff_pXpYD;
	}
	
	

	public static double [][][] renderDouble(
    		CLTParameters  clt_parameters,
    		boolean        mb_en,
    		double         mb_max_gain,
//    		Rectangle      fov_tiles,
    		double []      reference_xyz, // offset reference camera {x,y,z}
    		double []      reference_atr,    		
    		double []      ref_disparity,			
    		QuadCLT []     quadCLTs,
    		boolean []     scene_selection, // null or same length as quadCLTs
    		int            ref_index,
    		double [][][]  pXpYD, 
    		int            debugLevel){
		//		boolean mb_en =       clt_parameters.imp.mb_en; //  && (fov_tiles==null) && (mode3d > 0);
		double  mb_tau =      clt_parameters.imp.mb_tau;      // 0.008; // time constant, sec
		//		double  mb_max_gain = clt_parameters.imp.mb_max_gain; // 5.0;   // motion blur maximal gain (if more - move second point more than a pixel
		ErsCorrection ers_reference = quadCLTs[ref_index].getErsCorrection();
		int dbg_scene = -95;
		double [][][] double_render = new double[quadCLTs.length][][];
		double [][] ref_pXpYD = OpticalFlow.transformToScenePxPyD( // now should work with offset ref_scene
				null, // fov_tiles,            // final Rectangle [] extra_woi,    // show larger than sensor WOI (or null)
				ref_disparity,                 // final double []   disparity_ref, // invalid tiles - NaN in disparity
				OpticalFlow.ZERO3,             // final double []   scene_xyz, // camera center in world coordinates
				OpticalFlow.ZERO3,             // final double []   scene_atr, // camera orientation relative to world frame
				quadCLTs[ref_index],           // final QuadCLT     scene_QuadClt,
				quadCLTs[ref_index],           // final QuadCLT     reference_QuadClt, // now - may be null - for testing if scene is rotated ref
				QuadCLT.THREADS_MAX);          // int               threadsMax)
		for (int nscene =  0; nscene < quadCLTs.length ; nscene++) if ((quadCLTs[nscene] != null) && ((scene_selection==null) || scene_selection[nscene])){
			if (nscene== dbg_scene) {
				System.out.println("renderSceneSequence(): nscene = "+nscene);
			}
			String ts = quadCLTs[nscene].getImageName();
			double []   scene_xyz = OpticalFlow.ZERO3;
			double []   scene_atr = OpticalFlow.ZERO3;
			if (nscene != ref_index) { // Check even for raw, so video frames will match in all modes 
				scene_xyz = ers_reference.getSceneXYZ(ts);
				scene_atr = ers_reference.getSceneATR(ts);
				if ((scene_atr==null) || (scene_xyz == null)) {
					continue;
				}
				double []   scene_ers_xyz_dt = ers_reference.getSceneErsXYZ_dt(ts);
				double []   scene_ers_atr_dt = ers_reference.getSceneErsATR_dt(ts);
				quadCLTs[nscene].getErsCorrection().setErsDt(
						scene_ers_xyz_dt, // double []    ers_xyz_dt,
						scene_ers_atr_dt); // double []    ers_atr_dt)(ers_scene_original_xyz_dt);
			}
			if (reference_xyz != null) { // offset all, including reference scene
				double [][] combo_xyzatr = ErsCorrection.combineXYZATR(
						reference_xyz,  // double [] reference_xyz,
						reference_atr,  // double [] reference_atr, 
						scene_xyz,   // double [] scene_xyz,
						scene_atr);  // double [] scene_atr) 
				scene_xyz = combo_xyzatr[0];
				scene_atr = combo_xyzatr[1];
			}
			double [][] dxyzatr_dt = null;
			// should get velocities from HashMap at reference scene from timestamp , not re-calculate.
			if (mb_en) {
				dxyzatr_dt = new double[][] { // for all, including ref
					quadCLTs[nscene].getErsCorrection().getErsXYZ_dt(),
					quadCLTs[nscene].getErsCorrection().getErsATR_dt()};				
			}
			double [][] motion_blur = null;
			if (mb_en && (dxyzatr_dt != null)) {
				motion_blur = OpticalFlow.getMotionBlur(
						quadCLTs[ref_index],   // QuadCLT        ref_scene,
						quadCLTs[nscene],      // QuadCLT        scene,         // can be the same as ref_scene
						ref_pXpYD,             // double [][]    ref_pXpYD,     // here it is scene, not reference!
						scene_xyz,             // double []      camera_xyz,
						scene_atr,             // double []      camera_atr,
						dxyzatr_dt[0],         // double []      camera_xyz_dt,
						dxyzatr_dt[1],         // double []      camera_atr_dt,
						0,                     // int            shrink_gaps,  // will gaps, but not more that grow by this
						debugLevel);           // int            debug_level)
			}
			double_render[nscene] = QuadCLT.renderDoubleGPUFromDSI(
					-1,                  // final int         sensor_mask,
					null,                // final Rectangle   full_woi_in,      // show larger than sensor WOI (or null)
					clt_parameters,      // CLTParameters     clt_parameters,
					ref_disparity,       // double []         disparity_ref,
					// motion blur compensation 
					mb_tau,              // double            mb_tau,      // 0.008; // time constant, sec
					mb_max_gain,         // double            mb_max_gain, // 5.0;   // motion blur maximal gain (if more - move second point more than a pixel
					motion_blur,         // double [][]       mb_vectors,  //
					//								scene_xyz,           // final double []   scene_xyz, // camera center in world coordinates
					//								scene_atr,           // final double []   scene_atr, // camera orientation relative to world frame
					quadCLTs[nscene],    // final QuadCLT     scene,
					quadCLTs[ref_index], // final QuadCLT     ref_scene, // now - may be null - for testing if scene is rotated ref
					clt_parameters.imp.show_mono_nan, //final boolean     show_nan,
					pXpYD[nscene],       // double [][]       pXpYD,
					debugLevel);         // int         debugLevel)
		}   // for (int nscene      
		return double_render;
	}
4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088

	public static boolean [][] farFromNan(
			final double [][] data,
			final int       width,
			final int       shrink) { // 50
		final int num_scenes = data.length;
		final boolean [][] reliable = new boolean[num_scenes][];
		final Thread[]      threads =     ImageDtt.newThreadArray();
		final AtomicInteger ai =          new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int nscene = ai.getAndIncrement(); nscene <  num_scenes; nscene = ai.getAndIncrement()) {
						reliable[nscene] =  farFromNanSingle(
								data[nscene], // final double [] data,
								width,       // final int       width,
								shrink);      // final int       shrink)
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		return reliable;
	}
	
	public static boolean [] farFromNan(
			final double [] data,
			final int       width,
			final int       shrink) {
		final int num_pixels = data.length;
		final boolean [] reliable = new boolean[num_pixels];
		
		final Thread[]      threads =     ImageDtt.newThreadArray();
		final AtomicInteger ai =          new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < num_pixels; nPix = ai.getAndIncrement()) if (!Double.isNaN(data[nPix])) {
						reliable[nPix] = true;
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		TileNeibs tn = new TileNeibs(width, num_pixels/width);
		tn.shrinkSelection(
				shrink,   // final int        shrink,           // grow tile selection by 1 over non-background tiles 1: 4 directions, 2 - 8 directions, 3 - 8 by 1, 4 by 1 more
				reliable, // final boolean [] tiles,  
				null);    // final boolean [] prohibit)
		return reliable;
	}
	
	public static boolean [] farFromNanSingle(
			final double [] data,
			final int       width,
			final int       shrink) {
		final int num_pixels = data.length;
		final boolean [] reliable = new boolean[num_pixels];

		for (int npix = 0; npix < num_pixels; npix++) if (!Double.isNaN(data[npix])) {
			reliable[npix] = true;
		}
		TileNeibs tn = new TileNeibs(width, num_pixels/width);
		tn.shrinkSelection(
				shrink,   // final int        shrink,           // grow tile selection by 1 over non-background tiles 1: 4 directions, 2 - 8 directions, 3 - 8 by 1, 4 by 1 more
				reliable, // final boolean [] tiles,  
				null);    // final boolean [] prohibit)
		return reliable;
	}

	
Andrey Filippov's avatar
Andrey Filippov committed
4089 4090
	
	
4091 4092 4093 4094
	public static void  zerosToNans ( 
			final double [][] data,
			final int       width,
			final double    tolerance,
4095
			final boolean   negative_nan,
4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106
			final int       grow) {
		final Thread[]      threads =     ImageDtt.newThreadArray();
		final AtomicInteger ai =          new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int n = ai.getAndIncrement(); n < data.length; n = ai.getAndIncrement()) {
						 zerosToNans ( // not threaded to be used by threads
									data[n], // double [] data,
									width, // int       width,
									tolerance, // double    tolerance,
4107
									negative_nan, //final boolean   negative_nan,
4108 4109 4110 4111 4112 4113 4114 4115 4116
									grow); //int       grow)
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
	}

	public static void zerosToNans ( // not threaded to be used by threads
4117 4118 4119 4120 4121
			final double [] data,
			final int       width,
			final double    tolerance,
			final boolean   negative_nan,
			final int       grow) {
4122 4123 4124 4125
		int length = data.length;
		int height = length/width;
		TileNeibs tn = new TileNeibs(width,height);
		boolean [] zeros = new boolean[length];
4126 4127 4128 4129 4130
		int dbg_pix = -(195+130*width);
		for (int pix = 0; pix < length; pix++) if ((Math.abs(data[pix]) <= tolerance) || (negative_nan && (data[pix] < 0))){
			if (pix == dbg_pix) {
				System.out.println("zerosToNans(): pix="+pix);
			}
4131 4132 4133
			check_zero: {
				for (int dir = 0; dir < TileNeibs.DIRS; dir++) { // 
					int pix1 = tn.getNeibIndex(pix, dir);
4134
					if ((pix1 >=0) && ((data[pix1] > tolerance) || (!negative_nan && (data[pix1] < -tolerance)))) {
4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149
						break check_zero;
					}
				}
				zeros[pix] = true;
			}
		}
		tn.growSelection(
				grow,           // int              grow,           // grow tile selection by 1 over non-background tiles 1: 4 directions, 2 - 8 directions, 3 - 8 by 1, 4 by 1 more
				zeros, // final boolean [] tiles,
				null); // final boolean [] prohibit)
		for (int pix = 0; pix < length; pix++) if (zeros[pix]){
			data[pix] = Double.NaN;
		}
		return;
	}
4150 4151 4152 4153 4154 4155 4156 4157
	public static double [][] getInitialTerrainVegetation(
			final double []     terrain_average_zeros,
			final double []     vegetation_average_zeros,
			final double [][][] vegetation_warp, // to count all pixels used
			final int           width,
			final int           shrink_veget,
			final int           shrink_terrain,
			final double        vegetation_over_terrain, // trust vegetation that is hotter than filled terrain
4158 4159
			final int           filter_vegetation,     // shrink+grow filtered vegetation to remove small clusters
			final int           extra_pull_vegetation){
4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190
		boolean debug_img = false; // true;
		double [] terrain_filled =    terrain_average_zeros.clone();
		double [] vegetation_holes = vegetation_average_zeros.clone();
		 zerosToNans ( 
				 vegetation_holes, // final double [][] data,
				 width,      // 	final int       width,
				 0.0, // nan_tolerance,   // 	final double    tolerance, 0 OK if no UM !
				 true,           //  negative_nan, // final boolean   negative_nan,
				 shrink_veget);       // 	final int       grow)
		boolean [] terrain_nan = new boolean [vegetation_holes.length];
		for (int i = 0; i < terrain_filled.length; i++){
			terrain_nan[i] = !Double.isNaN(vegetation_holes[i]);
		}
		TileNeibs tn = new TileNeibs(width, terrain_filled.length/width);
		tn.growSelection(
				shrink_terrain,           // int              grow,           // grow tile selection by 1 over non-background tiles 1: 4 directions, 2 - 8 directions, 3 - 8 by 1, 4 by 1 more
				terrain_nan, // final boolean [] tiles,
				null); // final boolean [] prohibit)
		for (int i = 0; i < terrain_filled.length; i++) if (terrain_nan[i]){
			terrain_filled[i] = Double.NaN;
		}
		
		double [] terrain_dbg = debug_img? terrain_filled.clone() : null;
		terrain_filled = TileProcessor.fillNaNs(
				terrain_filled, // final double [] data,
				null,                     // final boolean [] prohibit,
				width,        // int       width,
				// CAREFUL ! Remaining NaN is grown by unsharp mask filter ************* !
				2* width, // 100, // 2*width, // 16,           // final int grow,
				0.7,          // double    diagonal_weight, // relative to ortho
				100,          // int       num_passes,
4191
				0.03);         // final double     max_rchange, //  = 0.01 - does not need to be accurate
4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245
 
		
		if (debug_img) {
			String [] titles = {"terrain","vegetation","vegetation_nan","terrain_nan","terrain_filled"};
			double [][] dbg_img = {terrain_average_zeros,vegetation_average_zeros,vegetation_holes,terrain_dbg,terrain_filled};
			ShowDoubleFloatArrays.showArrays(
					dbg_img,
					width,
					terrain_filled.length/width,
					true,
					"terrain_with_patched_holes",
					titles);
		}
		final double [] vegetation_full = vegetation_average_zeros.clone();
		final Thread[]      threads =     ImageDtt.newThreadArray();
		final AtomicInteger ai =          new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) { // first sum for pairs
			threads[ithread] = new Thread() {
				public void run() {
					for (int nPix = ai.getAndIncrement(); nPix < vegetation_full.length; nPix = ai.getAndIncrement()) {
						check_defined: {
							for (int nscene = 0; nscene < vegetation_warp.length; nscene++) {
								if (vegetation_warp[nscene][nPix] != null) {
									break check_defined;
								}
							}
							vegetation_full[nPix] = Double.NaN;
						}
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);

		
		
		
		
		double [] vegetation_filtered = vegetation_holes.clone();
		boolean [] vegetation_mask = new boolean [vegetation_holes.length];
		for (int i = 0; i < vegetation_mask.length; i++) {
			vegetation_mask[i] = (vegetation_holes[i] -  terrain_filled[i]) >= vegetation_over_terrain;
		}
		tn.shrinkSelection(
				filter_vegetation,  // int        shrink
				vegetation_mask, // final boolean [] tiles,
				null); // final boolean [] prohibit)
		tn.growSelection(
				filter_vegetation,  // int              grow,           // grow tile selection by 1 over non-background tiles 1: 4 directions, 2 - 8 directions, 3 - 8 by 1, 4 by 1 more
				vegetation_mask, // final boolean [] tiles,
				null); // final boolean [] prohibit)
		for (int i = 0; i < vegetation_mask.length; i++) if (!vegetation_mask[i]){
			vegetation_filtered[i] = Double.NaN;
		}
4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261
		// Create pull vegetation
//		boolean 
//extra_pull_vegetation		
		double [] pull_vegetation = TileProcessor.fillNaNs(
				vegetation_filtered,   // final double [] data,
				null,                  // final boolean [] prohibit,
				width,                 // int       width,
				// CAREFUL ! Remaining NaN is grown by unsharp mask filter ************* !
				extra_pull_vegetation, // 100, // 2*width, // 16,           // final int grow,
				0.7,                   // double    diagonal_weight, // relative to ortho
				100,                   // int       num_passes,
				0.03,                  // final double     max_rchange, //  = 0.01 - does not need to be accurate
				ImageDtt.THREADS_MAX); // final int threadsMax)      // maximal number of threads to launch 

		
		double [][] result = {terrain_filled, vegetation_full, vegetation_filtered, pull_vegetation} ;
4262 4263
		return result;
	}
4264 4265
	
	
Andrey Filippov's avatar
Andrey Filippov committed
4266 4267 4268
	

}