Commit 5de61cce authored by Andrey Filippov's avatar Andrey Filippov
Browse files

improving/fixing bugs in the sensors calibration.

parent 9230dca4
Loading
Loading
Loading
Loading
+144 −0
Original line number Original line Diff line number Diff line
@@ -515,6 +515,7 @@ public static MatchSimulatedPattern.DistortionParameters DISTORTION =new MatchSi
	    	0.3,   // public double weightBad =       0.3;   // reduce contrast of bad nodes
	    	0.3,   // public double weightBad =       0.3;   // reduce contrast of bad nodes
	    	0.05,  // public double weightWorst =     0.05;  // reduce contrast of bad, local worst nodes
	    	0.05,  // public double weightWorst =     0.05;  // reduce contrast of bad, local worst nodes
	    	0.1,  // double badNodeThreshold=0.1; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
	    	0.1,  // double badNodeThreshold=0.1; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
	    	0.2,  // public double badNodeThresholdLWIR=0.2; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    		1,    // int  maxBadNeighb; // maximal number of bad nodes around the corrected one to fix
    		1,    // int  maxBadNeighb; // maximal number of bad nodes around the corrected one to fix
    		10,   // int minimalValidNodes
    		10,   // int minimalValidNodes
        	1,    // int   weightMultiImageMode=1; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
        	1,    // int   weightMultiImageMode=1; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
@@ -1037,6 +1038,7 @@ if (MORE_BUTTONS) {
		addButton("Import Subsystem",           panelLWIR,color_configure);
		addButton("Import Subsystem",           panelLWIR,color_configure);
		addButton("Select LWIR grids",          panelLWIR,color_configure);
		addButton("Select LWIR grids",          panelLWIR,color_configure);
		addButton("Grid offset",                panelLWIR,color_process);
		addButton("Grid offset",                panelLWIR,color_process);
		addButton("EO Offsets",                 panelLWIR,color_process);
		addButton("LWIR to EO",                 panelLWIR,color_process);
		addButton("LWIR to EO",                 panelLWIR,color_process);
		addButton("Manual hint",                panelLWIR,color_configure);
		addButton("Manual hint",                panelLWIR,color_configure);
@@ -9420,6 +9422,13 @@ if (MORE_BUTTONS) {
		offsetGrids(0, 0, null, null);
		offsetGrids(0, 0, null, null);
		return;
		return;
	}
	}
/* ======================================================================== */
	if       (label.equals("EO Offsets")) {
		DEBUG_LEVEL=MASTER_DEBUG_LEVEL;
		EOoffsets();
		return;
	}
/* ======================================================================== */
/* ======================================================================== */
	if       (label.equals("LWIR to EO")) {
	if       (label.equals("LWIR to EO")) {
		DEBUG_LEVEL=MASTER_DEBUG_LEVEL;
		DEBUG_LEVEL=MASTER_DEBUG_LEVEL;
@@ -9435,6 +9444,141 @@ if (MORE_BUTTONS) {
	}
	}
/* ===== Other methods ==================================================== */
/* ===== Other methods ==================================================== */
//();
	public boolean EOoffsets() {
		if (LENS_DISTORTIONS == null) {
			System.out.println("LENS_DISTORTIONS is null");
			return false;
		}
		if (PATTERN_PARAMETERS == null) {
			System.out.println("PATTERN_PARAMETERS is null");
			return false;
		}
		DistortionCalibrationData dcd = LENS_DISTORTIONS.getDistortionCalibrationData();
		if (dcd == null) {
			dcd = DISTORTION_CALIBRATION_DATA;
		}
		if (dcd == null) {
			System.out.println("dcd is null");
			return false;
		}
		int min_set = 0;
		int max_set =           dcd.getNumSets()-1;
		double maximalRMS =     1.0;
		double maximalGridErr = 0.2;
		boolean adjustAttitude = true;
//		int                     extra_search = 2;
//		double                  sigma = 5;
		GenericDialog gd = new GenericDialog("Initial alignment of the secondary camera to the reference one");
		//		gd.addMessage("This command used Fitting Strategy[last] that should be set with all parameters but\n"+
		//		"GXYZ0 and GXYZ1 are set to 'fixed', and GXYZ0 and GXYZ1 are set to 'individual'.\n"+
		//				"Each selected set should already have GXYZ set correctly (e.g. by reference cameras)");
		gd.addMessage("This command uses Fitting Strategy[last] and set  parameters but\n"+
				"GXYZ0 and GXYZ1 to 'individual', all others - to 'fixed'.\n"+
				"Each selected set should already have GXYZ set correctly (e.g. by reference cameras)");
		gd.addNumericField("Image set start", min_set, 0);
		gd.addNumericField("Image set last",  max_set, 0);
		gd.addNumericField("Maximal RMS to accept",  maximalRMS, 3 ,7, "");
		gd.addNumericField("Maximal grid errot to accept",  maximalGridErr, 3 ,7, "");
		gd.addCheckbox("Adjust set attitude after grid offsets", adjustAttitude);
//		gd.addNumericField("Extra search for the non-LMA method",  extra_search, 0);
//		gd.addNumericField("Sigma for the non-LMA method",  sigma, 3 ,7, "");
		gd.showDialog();
		if (gd.wasCanceled()) return false;
		min_set = (int)      gd.getNextNumber();
		max_set = (int)      gd.getNextNumber();
		maximalRMS =         gd.getNextNumber();
		maximalGridErr =     gd.getNextNumber();
//		extra_search = (int) gd.getNextNumber();
//		sigma =              gd.getNextNumber();
		adjustAttitude =     gd.getNextBoolean();
		if (!dcd.hasSmallSensors()) {
			String msg="This system does not have any LWIR or other dependent sub-cameras";
			IJ.showMessage("Error",msg);
			return false;
		}
		for (int num_set = min_set; num_set <=max_set; num_set++) if
		((dcd.gIS[num_set] != null) && (dcd.gIS[num_set].imageSet != null)) {
			ArrayList<Integer> num_imgs_list = new ArrayList<Integer>();
			for (int nc = 0; nc < dcd.getNumChannels(); nc++) {
				if (dcd.gIS[num_set].imageSet[nc]!= null) {
					int num_img = dcd.gIS[num_set].imageSet[nc].imgNumber;
					if (dcd.isSmallSensor(num_img)) {
						continue;
					}
					double [] stats = new double [3];
					int [] uvr = LENS_DISTORTIONS.findImageGridOffset(
							num_img,  // image num
							-1,       // use last series int     ser_num, // number of series to reprogram
							true,     // boolean adjust_attitude, // true for eo, false for lwir (uses exact attitude from eo)
							true,     // boolean even, For first time - use parameter and parity of uv_rot
							PATTERN_PARAMETERS,
							stats); // rms, dU, dV
					if (uvr == null) {
						if (DEBUG_LEVEL > 0) {
							System.out.println("LMA failed for set = "+ num_set +", channel = "+nc);
						}
						continue;
					}
					if (DEBUG_LEVEL > 0) {
						System.out.println(num_img+ "("+num_set+"."+nc+"): uv_shift = "+uvr[0]+":"+uvr[1]);
						System.out.println(num_img+ "("+num_set+"."+nc+"): errors: rms= "+stats[0]+", dU="+stats[1]+", dV="+stats[2]);
					}
					if ((maximalRMS > 0.0) && (stats[0] >maximalRMS)) {
						if (DEBUG_LEVEL > 0) {
							System.out.println("LMA RMS for set = "+ num_set +", channel = "+nc+" is too high: "+stats[0]+" > "+maximalRMS);
						}
						continue;
					}
					double grid_err =Math.max(Math.abs(stats[1]), Math.abs(stats[2]));
					if ((maximalGridErr > 0.0) && (grid_err > maximalGridErr)) {
						if (DEBUG_LEVEL > 0) {
							System.out.println("Adjusted grid error for set = "+ num_set +", channel = "+nc+" is too high: "+
									grid_err+" > "+maximalGridErr);
						}
						continue;
					}
					num_imgs_list.add(num_img);
					if ((uvr[0] == 0) || (uvr[1] == 0)) {
						if (DEBUG_LEVEL > 0) {
							System.out.println("Grid alignment for set = "+ num_set +", channel = "+nc+" was correct, nothing to do");
						}
						continue;
					}
					int [] uv_shift_rot = {uvr[0],uvr[1],0};
					//						int [] new_uv_shift_rots =
					dcd.offsetGrid(
							num_img, // int img_num,
							uv_shift_rot,
							PATTERN_PARAMETERS);
					if (DEBUG_LEVEL > 0) {
						System.out.println("Grid for set = "+ num_set +", channel = "+nc+" adjusted by "+uvr[0]+":"+uvr[1]);
					}
//					num_imgs_list.add(num_img);
				}
			}
			if (adjustAttitude && !num_imgs_list.isEmpty()) {
				int [] num_imgs = new int[num_imgs_list.size()];
				for (int i = 0; i < num_imgs.length; i++) {
					num_imgs[i] = num_imgs_list.get(i);
				}
				LENS_DISTORTIONS.adjustAttitudeAfterOffset(
						num_imgs,
						-1, // number of series to reprogram
						PATTERN_PARAMETERS);
			}
		}
		return true;
	}
	public boolean lwirToEo() {
	public boolean lwirToEo() {
		if (LENS_DISTORTIONS == null) {
		if (LENS_DISTORTIONS == null) {
+27 −11
Original line number Original line Diff line number Diff line
@@ -71,6 +71,8 @@ import ij.text.TextWindow;
    	public static final int INDEX_B =        7;
    	public static final int INDEX_B =        7;
    	public static final double SMALL_FRACTION = 0.8; // consider sensor to be a "small" if average grid period < this fraction of the large
    	public static final double SMALL_FRACTION = 0.8; // consider sensor to be a "small" if average grid period < this fraction of the large


    	public static final int MAX_LWIR_WIDTH = 1023; // Consider smaller sensors to be low-res

    	Goniometer.GoniometerParameters goniometerParameters = null;
    	Goniometer.GoniometerParameters goniometerParameters = null;
    	public String pathName=null;
    	public String pathName=null;
    	public EyesisCameraParameters eyesisCameraParameters; // has "cartesian"
    	public EyesisCameraParameters eyesisCameraParameters; // has "cartesian"
@@ -967,7 +969,6 @@ import ij.text.TextWindow;
        	}
        	}
    		this.goniometerParameters =  goniometerParameters;
    		this.goniometerParameters =  goniometerParameters;
        	boolean ignore_LWIR_pointers = true; // skip LWIR absolute marks, use them later
        	boolean ignore_LWIR_pointers = true; // skip LWIR absolute marks, use them later
        	int max_lwir_width = 1023;  // use LWIR class
        	setupIndices();
        	setupIndices();
        	this.eyesisCameraParameters=eyesisCameraParameters;
        	this.eyesisCameraParameters=eyesisCameraParameters;
        	int numSubCameras=(eyesisCameraParameters==null)?1:eyesisCameraParameters.eyesisSubCameras[0].length;
        	int numSubCameras=(eyesisCameraParameters==null)?1:eyesisCameraParameters.eyesisSubCameras[0].length;
@@ -1034,7 +1035,7 @@ import ij.text.TextWindow;
        				this.gIP[numFile].setNumber =    nis;
        				this.gIP[numFile].setNumber =    nis;
        				this.gIP[numFile].gridImageSet = this.gIS[nis];
        				this.gIP[numFile].gridImageSet = this.gIS[nis];
            			this.gIS[nis].imageSet[nc]=this.gIP[numFile];
            			this.gIS[nis].imageSet[nc]=this.gIP[numFile];

            			boolean is_small = getImagePlusProperty(imp_grid,"WOI_WIDTH",0) <= MAX_LWIR_WIDTH;
        				//numFile
        				//numFile
        				if (first_in_set || read_grids) {
        				if (first_in_set || read_grids) {
        					if (read_grids) {
        					if (read_grids) {
@@ -1064,10 +1065,14 @@ import ij.text.TextWindow;
                    		this.gIS[nis].motors=                     this.gIP[numFile].motors.clone();
                    		this.gIS[nis].motors=                     this.gIP[numFile].motors.clone();
                    		this.gIP[numFile].matchedPointers =       getUsedPonters(imp_grid);
                    		this.gIP[numFile].matchedPointers =       getUsedPonters(imp_grid);
                    		if (this.gIP[numFile].matchedPointers > 0) {
                    		if (this.gIP[numFile].matchedPointers > 0) {
                    			System.out.print("<"+(this.gIP[numFile].matchedPointers));
                    			// Not using LWIR pointers here!
                    			// Not using LWIR pointers here!
                    			if (!ignore_LWIR_pointers || (getImagePlusProperty(imp_grid,"WOI_TOP",0) > max_lwir_width)) {
//                    			if (!ignore_LWIR_pointers || (getImagePlusProperty(imp_grid,"WOI_TOP",0) > MAX_LWIR_WIDTH)) {
                       			if (!ignore_LWIR_pointers || !is_small) {
                    				with_pointers = numFile;
                    				with_pointers = numFile;
                        			System.out.print("|"+numFile);
                    			}
                    			}
                    			System.out.print(">");
                    		}
                    		}
                    		double [] saturations=new double [4];
                    		double [] saturations=new double [4];
                    		for (int i=0;i<saturations.length;i++) {
                    		for (int i=0;i<saturations.length;i++) {
@@ -1108,7 +1113,8 @@ import ij.text.TextWindow;




                    			int [] numBadNodes = new int [2];
                    			int [] numBadNodes = new int [2];
                    			if (this.eyesisCameraParameters.badNodeThreshold>0.0){
                    			double badNodeThreshold = is_small? this.eyesisCameraParameters.badNodeThresholdLWIR : this.eyesisCameraParameters.badNodeThreshold;
                    			if (badNodeThreshold > 0.0){
                    				boolean thisDebug =false;
                    				boolean thisDebug =false;
                    				//                            		thisDebug|=        (fileNumber== 720); // chn 25
                    				//                            		thisDebug|=        (fileNumber== 720); // chn 25
                    				numBadNodes=fixBadGridNodes(
                    				numBadNodes=fixBadGridNodes(
@@ -1118,7 +1124,7 @@ import ij.text.TextWindow;
                                    		this.eyesisCameraParameters.removeWorst,
                                    		this.eyesisCameraParameters.removeWorst,
                                    		this.eyesisCameraParameters.weightBad,
                                    		this.eyesisCameraParameters.weightBad,
                                    		this.eyesisCameraParameters.weightWorst,
                                    		this.eyesisCameraParameters.weightWorst,
                    						this.eyesisCameraParameters.badNodeThreshold,
                    						badNodeThreshold,
                    						this.eyesisCameraParameters.maxBadNeighb,
                    						this.eyesisCameraParameters.maxBadNeighb,
                    						this.debugLevel+(thisDebug?3:0),
                    						this.debugLevel+(thisDebug?3:0),
                    						thisDebug?("fixBad-"+numFile):null
                    						thisDebug?("fixBad-"+numFile):null
@@ -1241,7 +1247,7 @@ import ij.text.TextWindow;
        							5.0, // 2.0, // sigma
        							5.0, // 2.0, // sigma
        							sensor_wh,
        							sensor_wh,
        							false); // true);
        							false); // true);
        					System.out.print(" {"+uv_shift_rot[0]+":"+uv_shift_rot[1]+"->");
        					System.out.print(" {{"+uv_shift_rot[0]+":"+uv_shift_rot[1]+"->");
                        	int [] combinedUVShiftRot=MatchSimulatedPattern.combineUVShiftRot(
                        	int [] combinedUVShiftRot=MatchSimulatedPattern.combineUVShiftRot(
                        			this.gIS[nis].imageSet[base_channel].getUVShiftRot(),
                        			this.gIS[nis].imageSet[base_channel].getUVShiftRot(),
                        			uv_shift_rot);
                        			uv_shift_rot);
@@ -3880,6 +3886,7 @@ import ij.text.TextWindow;
        				this.gIP[fileNumber].gridImage = imp_grid;
        				this.gIP[fileNumber].gridImage = imp_grid;
        			}
        			}
        		}
        		}
        		boolean is_small = getImagePlusProperty(imp_grid,"WOI_WIDTH",0) <= MAX_LWIR_WIDTH;
    			this.gIP[fileNumber].woi = new Rectangle(
    			this.gIP[fileNumber].woi = new Rectangle(
    					getImagePlusProperty(imp_grid,"WOI_LEFT",0),
    					getImagePlusProperty(imp_grid,"WOI_LEFT",0),
    					getImagePlusProperty(imp_grid,"WOI_TOP",0),
    					getImagePlusProperty(imp_grid,"WOI_TOP",0),
@@ -3922,8 +3929,10 @@ import ij.text.TextWindow;
    				woi_compensated = true;
    				woi_compensated = true;


    			}
    			}
    	        double badNodeThreshold = is_small? this.eyesisCameraParameters.badNodeThresholdLWIR : this.eyesisCameraParameters.badNodeThreshold;
    	        // TODO: maybe adjust threshold to grid period, not the sensor type?


    			if (this.eyesisCameraParameters.badNodeThreshold>0.0){
    			if (badNodeThreshold>0.0){
    				boolean thisDebug =false;
    				boolean thisDebug =false;
    				//            		thisDebug|=        (fileNumber== 720); // chn 25
    				//            		thisDebug|=        (fileNumber== 720); // chn 25
    				int [] numBadNodes=fixBadGridNodes(
    				int [] numBadNodes=fixBadGridNodes(
@@ -3933,7 +3942,7 @@ import ij.text.TextWindow;
    						this.eyesisCameraParameters.removeWorst,
    						this.eyesisCameraParameters.removeWorst,
    						this.eyesisCameraParameters.weightBad,
    						this.eyesisCameraParameters.weightBad,
    						this.eyesisCameraParameters.weightWorst,
    						this.eyesisCameraParameters.weightWorst,
    						this.eyesisCameraParameters.badNodeThreshold,
    						badNodeThreshold,
    						this.eyesisCameraParameters.maxBadNeighb,
    						this.eyesisCameraParameters.maxBadNeighb,
    						this.debugLevel+(thisDebug?3:0),
    						this.debugLevel+(thisDebug?3:0),
    						thisDebug?("fixBad-"+fileNumber):null
    						thisDebug?("fixBad-"+fileNumber):null
@@ -3941,11 +3950,11 @@ import ij.text.TextWindow;
    				if (this.debugLevel>-1) {
    				if (this.debugLevel>-1) {
    					if ((numBadNodes[0] + numBadNodes [1])>0) {
    					if ((numBadNodes[0] + numBadNodes [1])>0) {
    						if (this.eyesisCameraParameters.removeWorst) {
    						if (this.eyesisCameraParameters.removeWorst) {
    								System.out.print("  -- removed "+numBadNodes[0]+"("+numBadNodes[1]+") locally worst grid nodes,");
    								System.out.print("  --- removed "+numBadNodes[0]+"("+numBadNodes[1]+") locally worst grid nodes,");
    						} else if (this.eyesisCameraParameters.replaceBad){
    						} else if (this.eyesisCameraParameters.replaceBad){
    							System.out.print("  -- replaced "+numBadNodes[0]+"("+numBadNodes[1]+") bad grid nodes,");
    							System.out.print("  --- replaced "+numBadNodes[0]+"("+numBadNodes[1]+") bad grid nodes,");
    						} else {
    						} else {
    							System.out.print("  -- scaled "+numBadNodes[0]+"("+numBadNodes[1]+") bad grid nodes,");
    							System.out.print("  --- scaled "+numBadNodes[0]+"("+numBadNodes[1]+") bad grid nodes,");
    						}
    						}
    					}
    					}


@@ -5119,6 +5128,13 @@ import ij.text.TextWindow;
        		double      sigma=sigmaUV;
        		double      sigma=sigmaUV;
        		if(sigma<0) sigma*=-rAverage;
        		if(sigma<0) sigma*=-rAverage;
        		gb.blurDouble(this.sensorMasks[chNum], dWidth, dHeight, sigma/decimate, sigma/decimate, 0.01);
        		gb.blurDouble(this.sensorMasks[chNum], dWidth, dHeight, sigma/decimate, sigma/decimate, 0.01);
            	if (this.debugLevel >1) {
            		(new ShowDoubleFloatArrays()).showArrays(
            				this.sensorMasks[chNum],
            				dWidth,
            				dHeight,
    	        			"SensorMask"+chNum);
            	}
        	}
        	}
        	return this.sensorMasks;
        	return this.sensorMasks;
        }
        }
+20 −2
Original line number Original line Diff line number Diff line
@@ -8130,6 +8130,7 @@ List calibration
		if (!LMA_OK) {
		if (!LMA_OK) {
			return null; // LMA did not converge
			return null; // LMA did not converge
		}
		}

//		DistortionCalibrationData dcd = this.fittingStrategy.distortionCalibrationData;
//		DistortionCalibrationData dcd = this.fittingStrategy.distortionCalibrationData;
// find indices of GXYZ0 and GXYZ1 in the vector
// find indices of GXYZ0 and GXYZ1 in the vector
		int index_GXYZ0 = this.fittingStrategy.reverseParameterMap[num_img][par_index_GXYZ0];
		int index_GXYZ0 = this.fittingStrategy.reverseParameterMap[num_img][par_index_GXYZ0];
@@ -8169,11 +8170,26 @@ List calibration
    		int     num_img,
    		int     num_img,
    		int     ser_num, // number of series to reprogram
    		int     ser_num, // number of series to reprogram
    		PatternParameters patternParameters) {
    		PatternParameters patternParameters) {
    	int [] num_imgs = {num_img};
    	return  adjustAttitudeAfterOffset(
        		num_imgs,
        		ser_num, // number of series to reprogram
        		patternParameters);
    }

    public boolean adjustAttitudeAfterOffset(
    		int  []   num_imgs,
    		int     ser_num, // number of series to reprogram
    		PatternParameters patternParameters) {
    	if (ser_num < 0) {
    	if (ser_num < 0) {
    		ser_num = fittingStrategy.parameterMode.length + ser_num; // use from the last one
    		ser_num = fittingStrategy.parameterMode.length + ser_num; // use from the last one
    	}
    	}
		if (this.debugLevel > 0) {
		if (this.debugLevel > 0) {
			System.out.println("Will use/modify fitting series "+ser_num+" for to adjust  az, tilt of image "+num_img);
			System.out.print("Will use/modify fitting series "+ser_num+" for to adjust  az, tilt of images: ");
			for (int num_img: num_imgs) {
				System.out.print(" "+num_img);
			}
			System.out.println();
			System.out.println("Will adjust goniometerHorizontal, and goniometerAxial");
			System.out.println("Will adjust goniometerHorizontal, and goniometerAxial");
		}
		}
    	int was_seriesNumber = seriesNumber;
    	int was_seriesNumber = seriesNumber;
@@ -8188,7 +8204,9 @@ List calibration
		fittingStrategy.parameterMode[ser_num][par_index_goniometerAxial] =      FittingStrategy.modeIndividual; // 3
		fittingStrategy.parameterMode[ser_num][par_index_goniometerAxial] =      FittingStrategy.modeIndividual; // 3
		boolean [] selection = fittingStrategy.selectAllImages(ser_num); // enable all images in series 0
		boolean [] selection = fittingStrategy.selectAllImages(ser_num); // enable all images in series 0
		for (int i=0;i<selection.length;i++) selection[i]=false;
		for (int i=0;i<selection.length;i++) selection[i]=false;
		for (int num_img:num_imgs) {
			selection[num_img]=true;
			selection[num_img]=true;
		}
		fittingStrategy.setImageSelection(ser_num,selection);
		fittingStrategy.setImageSelection(ser_num,selection);
		seriesNumber=   ser_num; // start from 0;
		seriesNumber=   ser_num; // start from 0;
		initFittingSeries(false, filterForAll,ser_num); // will set this.currentVector, will build parameter map too
		initFittingSeries(false, filterForAll,ser_num); // will set this.currentVector, will build parameter map too
+14 −2
Original line number Original line Diff line number Diff line
@@ -78,6 +78,7 @@ import ij.gui.GenericDialog;
    	public double weightBad =       0.3;   // reduce contrast of bad nodes
    	public double weightBad =       0.3;   // reduce contrast of bad nodes
    	public double weightWorst =     0.05;  // reduce contrast of bad, local worst nodes
    	public double weightWorst =     0.05;  // reduce contrast of bad, local worst nodes
    	public double badNodeThreshold=0.1;     // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	public double badNodeThreshold=0.1;     // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	public double badNodeThresholdLWIR=0.2; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	public int    maxBadNeighb=      1; // maximal number of bad nodes around the corrected one to fix
    	public int    maxBadNeighb=      1; // maximal number of bad nodes around the corrected one to fix
    	public int    minimalValidNodes=10; // do not use images with less than this number of non-zero nodes (after all applicable weight masks)
    	public int    minimalValidNodes=10; // do not use images with less than this number of non-zero nodes (after all applicable weight masks)
    	public int    weightMultiImageMode=1; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set to weightMultiExponent power)
    	public int    weightMultiImageMode=1; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set to weightMultiExponent power)
@@ -353,6 +354,7 @@ import ij.gui.GenericDialog;
    	    	double  weightBad,
    	    	double  weightBad,
    	    	double  weightWorst,
    	    	double  weightWorst,
    	    	double badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	    	double badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	    	double badNodeThresholdLWIR, // same for the low-res sensors
    	    	int    maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    	    	int    maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    	    	int minimalValidNodes,
    	    	int minimalValidNodes,
    	    	int    weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
    	    	int    weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
@@ -394,6 +396,7 @@ import ij.gui.GenericDialog;
    				weightBad,
    				weightBad,
    				weightWorst,
    				weightWorst,
    				badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    				badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
        	    	badNodeThresholdLWIR, // same for the low-res sensors
    				maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    				maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    				minimalValidNodes,
    				minimalValidNodes,
    				weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
    				weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
@@ -435,6 +438,7 @@ import ij.gui.GenericDialog;
    	    	double  weightBad,
    	    	double  weightBad,
    	    	double  weightWorst,
    	    	double  weightWorst,
    	    	double badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	    	double badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	    	double badNodeThresholdLWIR, // same for the low-res sensors
    	    	int    maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    	    	int    maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    	    	int    minimalValidNodes,
    	    	int    minimalValidNodes,
    	    	int    weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
    	    	int    weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
@@ -475,6 +479,7 @@ import ij.gui.GenericDialog;
    				weightBad,
    				weightBad,
    				weightWorst,
    				weightWorst,
    				badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    				badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
        	    	badNodeThresholdLWIR, // same for the low-res sensors
    				maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    				maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    				minimalValidNodes,
    				minimalValidNodes,
    				weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
    				weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
@@ -516,6 +521,7 @@ import ij.gui.GenericDialog;
    	    	double weightBad,
    	    	double weightBad,
    	    	double weightWorst,
    	    	double weightWorst,
    	    	double badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	    	double badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	    	double badNodeThresholdLWIR, // same for the low-res sensors
    	    	int    maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    	    	int    maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    	    	int    minimalValidNodes,
    	    	int    minimalValidNodes,
    	    	int    weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
    	    	int    weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
@@ -544,6 +550,7 @@ import ij.gui.GenericDialog;
	    	this.weightBad = weightBad;
	    	this.weightBad = weightBad;
	    	this.weightWorst = weightWorst;
	    	this.weightWorst = weightWorst;
	    	this.badNodeThreshold=badNodeThreshold; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
	    	this.badNodeThreshold=badNodeThreshold; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
	    	this.badNodeThresholdLWIR=badNodeThresholdLWIR; // same for the low-res sensors
	    	this.maxBadNeighb=maxBadNeighb; // maximal number of bad nodes around the corrected one to fix
	    	this.maxBadNeighb=maxBadNeighb; // maximal number of bad nodes around the corrected one to fix
	    	this.minimalValidNodes=minimalValidNodes;
	    	this.minimalValidNodes=minimalValidNodes;
	    	this.weightMultiImageMode=weightMultiImageMode; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
	    	this.weightMultiImageMode=weightMultiImageMode; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
@@ -612,6 +619,7 @@ import ij.gui.GenericDialog;
    		destination.weightBad = source.weightBad;
    		destination.weightBad = source.weightBad;
    		destination.weightWorst = source.weightWorst;
    		destination.weightWorst = source.weightWorst;
    		destination.badNodeThreshold=source.badNodeThreshold; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    		destination.badNodeThreshold=source.badNodeThreshold; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    		destination.badNodeThresholdLWIR=source.badNodeThresholdLWIR; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    		destination.maxBadNeighb=source.maxBadNeighb; // maximal number of bad nodes around the corrected one to fix
    		destination.maxBadNeighb=source.maxBadNeighb; // maximal number of bad nodes around the corrected one to fix
    		destination.minimalValidNodes=source.minimalValidNodes;
    		destination.minimalValidNodes=source.minimalValidNodes;
    		destination.weightMultiImageMode=source.weightMultiImageMode; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
    		destination.weightMultiImageMode=source.weightMultiImageMode; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
@@ -674,6 +682,7 @@ import ij.gui.GenericDialog;
    		properties.setProperty(prefix+"weightBad",  this.weightBad+"");
    		properties.setProperty(prefix+"weightBad",  this.weightBad+"");
    		properties.setProperty(prefix+"weightWorst",this.weightWorst+"");
    		properties.setProperty(prefix+"weightWorst",this.weightWorst+"");
    		properties.setProperty(prefix+"badNodeThreshold",this.badNodeThreshold+"");
    		properties.setProperty(prefix+"badNodeThreshold",this.badNodeThreshold+"");
    		properties.setProperty(prefix+"badNodeThresholdLWIR",this.badNodeThresholdLWIR+"");
    		properties.setProperty(prefix+"maxBadNeighb",this.maxBadNeighb+"");
    		properties.setProperty(prefix+"maxBadNeighb",this.maxBadNeighb+"");
    		properties.setProperty(prefix+"minimalValidNodes",this.minimalValidNodes+"");
    		properties.setProperty(prefix+"minimalValidNodes",this.minimalValidNodes+"");
    		properties.setProperty(prefix+"weightMultiImageMode",this.weightMultiImageMode+"");
    		properties.setProperty(prefix+"weightMultiImageMode",this.weightMultiImageMode+"");
@@ -743,9 +752,10 @@ import ij.gui.GenericDialog;
    			this.weightBad=Double.parseDouble(properties.getProperty(prefix+"weightBad"));
    			this.weightBad=Double.parseDouble(properties.getProperty(prefix+"weightBad"));
    		if (properties.getProperty(prefix+"weightWorst")!=null)
    		if (properties.getProperty(prefix+"weightWorst")!=null)
    			this.weightWorst=Double.parseDouble(properties.getProperty(prefix+"weightWorst"));
    			this.weightWorst=Double.parseDouble(properties.getProperty(prefix+"weightWorst"));

    		if (properties.getProperty(prefix+"badNodeThreshold")!=null)
    		if (properties.getProperty(prefix+"badNodeThreshold")!=null)
    			this.badNodeThreshold=Double.parseDouble(properties.getProperty(prefix+"badNodeThreshold"));
    			this.badNodeThreshold=Double.parseDouble(properties.getProperty(prefix+"badNodeThreshold"));
    		if (properties.getProperty(prefix+"badNodeThresholdLWIR")!=null)
    			this.badNodeThresholdLWIR=Double.parseDouble(properties.getProperty(prefix+"badNodeThresholdLWIR"));
    		if (properties.getProperty(prefix+"maxBadNeighb")!=null)
    		if (properties.getProperty(prefix+"maxBadNeighb")!=null)
    			this.maxBadNeighb=Integer.parseInt(properties.getProperty(prefix+"maxBadNeighb"));
    			this.maxBadNeighb=Integer.parseInt(properties.getProperty(prefix+"maxBadNeighb"));
    		if (properties.getProperty(prefix+"minimalValidNodes")!=null)
    		if (properties.getProperty(prefix+"minimalValidNodes")!=null)
@@ -930,6 +940,7 @@ import ij.gui.GenericDialog;
    		gd.addNumericField("Scale weight of bad, locally worst nodes",                             this.weightWorst, 3,6,"");
    		gd.addNumericField("Scale weight of bad, locally worst nodes",                             this.weightWorst, 3,6,"");


    		gd.addNumericField("Filter out grid nodes with difference from quadratically predicted from 8 neighbors", this.badNodeThreshold, 2,6,"pix");
    		gd.addNumericField("Filter out grid nodes with difference from quadratically predicted from 8 neighbors", this.badNodeThreshold, 2,6,"pix");
    		gd.addNumericField("Same for the low-res (LWIR) sensors",                                   this.badNodeThresholdLWIR, 2,6,"pix");
    		gd.addNumericField("Maximal number of bad nodes around the corrected one to fix",          this.maxBadNeighb, 0);
    		gd.addNumericField("Maximal number of bad nodes around the corrected one to fix",          this.maxBadNeighb, 0);
    		gd.addNumericField("Minimal number of valid (with all filters applied) nodes in each image",this.minimalValidNodes, 0);
    		gd.addNumericField("Minimal number of valid (with all filters applied) nodes in each image",this.minimalValidNodes, 0);
    		gd.addNumericField("Increase weight of the multi-image sets (0 - do not increase, 1 - multiply by number of images in a set (to power ), 2 - same but remove single-image ",this.weightMultiImageMode, 0);
    		gd.addNumericField("Increase weight of the multi-image sets (0 - do not increase, 1 - multiply by number of images in a set (to power ), 2 - same but remove single-image ",this.weightMultiImageMode, 0);
@@ -988,6 +999,7 @@ import ij.gui.GenericDialog;
	    	this.weightBad=                 gd.getNextNumber();
	    	this.weightBad=                 gd.getNextNumber();
	    	this.weightWorst=               gd.getNextNumber();
	    	this.weightWorst=               gd.getNextNumber();
	    	this.badNodeThreshold=          gd.getNextNumber();
	    	this.badNodeThreshold=          gd.getNextNumber();
	    	this.badNodeThresholdLWIR=      gd.getNextNumber();
	    	this.maxBadNeighb=        (int) gd.getNextNumber();
	    	this.maxBadNeighb=        (int) gd.getNextNumber();
	    	this.minimalValidNodes=   (int) gd.getNextNumber();
	    	this.minimalValidNodes=   (int) gd.getNextNumber();
	    	this.weightMultiImageMode=(int) gd.getNextNumber();
	    	this.weightMultiImageMode=(int) gd.getNextNumber();