/**
** -----------------------------------------------------------------------------**
** Eyesis_Correction.java
**
** De-mosaic and correct aberrations using array of inverted PSF kernels
**
**
** Copyright (C) 2010-2012 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
**  Eyesis_Correction.java is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program.  If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.correction;

import java.awt.Button;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.MouseInfo;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
//import javax.imageio.plugins.tiff.ExifGPSTagSet;
import javax.imageio.plugins.tiff.TIFFDirectory;
import javax.swing.JButton;
//import javax.imageio.plugins.tiff.TIFFTagSet;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.filechooser.FileFilter;

import org.json.JSONException;
import org.w3c.dom.Node;

import com.elphel.imagej.calibration.CalibrationFileManagement;
import com.elphel.imagej.calibration.CalibrationIllustration;
import com.elphel.imagej.calibration.CalibrationIllustrationParameters;
import com.elphel.imagej.calibration.DeBayerScissors;
import com.elphel.imagej.calibration.PixelMapping;
import com.elphel.imagej.cameras.CLTParameters;
import com.elphel.imagej.cameras.ColorProcParameters;
import com.elphel.imagej.cameras.EyesisCorrectionParameters;
import com.elphel.imagej.cameras.ThermalColor;
import com.elphel.imagej.common.CholeskyBlockTest;
import com.elphel.imagej.common.CholeskyLDLTMulti;
//import com.elphel.imagej.common.CholeskyLLTMulti;
import com.elphel.imagej.common.DoubleFHT;
import com.elphel.imagej.common.DoubleGaussianBlur;
import com.elphel.imagej.common.GenericJTabbedDialog;
import com.elphel.imagej.common.GenericJTabbedDialogMcp;
import com.elphel.imagej.common.LogTee;
import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.common.WindowTools;
import com.elphel.imagej.cuas.CuasMotion;
import com.elphel.imagej.dct.FactorConvKernel;
import com.elphel.imagej.gpu.GPUTileProcessor;
import com.elphel.imagej.gpu.GpuQuad;
import com.elphel.imagej.gpu.JCuda_ImageJ_Example_Plugin;
import com.elphel.imagej.ims.DjiSrt;
import com.elphel.imagej.ims.DjiSrtReader;
import com.elphel.imagej.mcp.McpServer;
import com.elphel.imagej.mcp.FileSelectMcp;
import com.elphel.imagej.ims.EventLogger;
import com.elphel.imagej.ims.QuatVertLMA;
import com.elphel.imagej.ims.UasLogReader;
//import com.elphel.imagej.ims.Imx5;
import com.elphel.imagej.jp4.JP46_Reader_camera;
import com.elphel.imagej.lwir.LwirReader;
import com.elphel.imagej.orthomosaic.OrthoMap;
import com.elphel.imagej.orthomosaic.ComboMatch;
import com.elphel.imagej.orthomosaic.OrangeTest;
import com.elphel.imagej.readers.ChangeImageResolution;
import com.elphel.imagej.readers.DumpImageMetadata;
import com.elphel.imagej.readers.ElphelTiffReader;
import com.elphel.imagej.readers.EyesisTiff;
import com.elphel.imagej.tensorflow.TensorflowInferModel;
import com.elphel.imagej.tileprocessor.Clt1d;
import com.elphel.imagej.tileprocessor.DttRad2;
import com.elphel.imagej.tileprocessor.ErsCorrection;
import com.elphel.imagej.tileprocessor.ImageDtt;
import com.elphel.imagej.tileprocessor.MLStats;
import com.elphel.imagej.tileprocessor.MultisceneLY;
import com.elphel.imagej.tileprocessor.OpticalFlow;
import com.elphel.imagej.tileprocessor.QuadCLT;
import com.elphel.imagej.tileprocessor.QuadCLTCPU;
import com.elphel.imagej.tileprocessor.SymmVector;
import com.elphel.imagej.tileprocessor.TwoQuadCLT;
import com.elphel.imagej.tileprocessor.lwoc.LwirWorld;
import com.elphel.imagej.vegetation.VegetationModel;
//import com.elphel.imagej.vegetation.VegetationSegment;
import com.elphel.imagej.vegetation.VegetationSynthesis;

import ij.CompositeImage;
import ij.IJ;
import ij.ImageJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.Macro;
import ij.Prefs;
import ij.WindowManager;
import ij.gui.GUI;
import ij.gui.GenericDialog;
import ij.gui.Plot;
import ij.gui.PlotWindow;
import ij.gui.Roi;
import ij.io.FileInfo;
import ij.io.FileSaver;
import ij.io.OpenDialog;
import ij.io.Opener;
import ij.plugin.PlugIn;
import ij.plugin.frame.PlugInFrame;
import ij.process.ColorProcessor;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import loci.common.services.DependencyException;
import loci.common.services.ServiceException;
import loci.formats.FormatException;

public class Eyesis_Correction implements PlugIn, ActionListener {
	/**
	 *
	 */
	private Boolean headless = GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance();
	String prefsPath;
	JP46_Reader_camera JP4_INSTANCE = null;
//   private deBayerScissors debayer_instance;
	private DoubleFHT FHT_INSTANCE =  new DoubleFHT();
	static Properties PROPERTIES = new Properties();
	public static int DEBUG_LEVEL = 1;
	public static int MASTER_DEBUG_LEVEL = 1;
	public static boolean UPDATE_STATUS = true; // update ImageJ status info
	public static EyesisCorrectionParameters.SplitParameters SPLIT_PARAMETERS = new EyesisCorrectionParameters.SplitParameters(
			2, // oversample;
			32, // addLeft
			32, // addTop
			32, // addRight
			32 // addBottom
	);

	public static EyesisCorrectionParameters.DCTParameters DCT_PARAMETERS = new EyesisCorrectionParameters.DCTParameters(
			32, // dct_size
			6, // asym_size
			10, // asym_pixels = 10; // maximal number of non-zero pixels in direct convolution
				// kernel
			2, // asym_distance = 2; // how far to try a new asym kernel pixel from existing
				// ones
			1, // dct_window
			1.0, // compactness
			5, // asym_tax_free,
			8 // seed_size
	);

	public static CalibrationIllustration CALIBRATION_ILLUSTRATION = null;
	public static CLTParameters CLT_PARAMETERS = new CLTParameters();
	public static CalibrationIllustrationParameters CALIBRATION_ILLUSTRATION_PARAMETERS = new CalibrationIllustrationParameters(
			CLT_PARAMETERS.lwir, // LWIR_PARAMETERS,
			null); // EYESIS_CAMERA_PARAMETERS); // null
	public static FootageOrganize FOOTAGE_ORGANIZE = new FootageOrganize();

	public static EyesisDCT EYESIS_DCT = null;
	public static QuadCLT QUAD_CLT = null;
	public static QuadCLT QUAD_CLT_AUX = null;
	public static TwoQuadCLT TWO_QUAD_CLT = null;
	public static GPUTileProcessor GPU_TILE_PROCESSOR = null;
	// Add macro for GPU_QUAD?
	public static GpuQuad GPU_QUAD = null;
	public static GpuQuad GPU_QUAD_AUX = null;
	public static LwirReader LWIR_READER = null;
	public static SymmVector SYMM_VECTOR_EO = null;
	public static SymmVector SYMM_VECTOR_LWIR = null;

	public static EyesisCorrectionParameters.DebayerParameters DEBAYER_PARAMETERS = new EyesisCorrectionParameters.DebayerParameters(
			64, // size //128;
			0.5, // polarStep - size of largest polar cell to Cartesian one;
			0.35, // debayerThreshold - Measuring maximal spectral component amplitude in
					// mid-frequencies. If below this - use default de-bayer mask
			1.5, // debayerRelativeWidthGreen - result green mask mpy by scaled default
					// (diamond))
			1.5, // debayerRelativeWidthRedblue - result red/blue mask mpy by scaled default
					// (square)
			1.3, // debayerRelativeWidthRedblueMain - green mask when applied to red/blue, main
					// (center)
			2.0, // debayerRelativeWidthRedblueClones - green mask when applied to red/blue,
					// clones
			0.3, // public double debayerGamma; - gamma applied to spectrum amplitude
			0.5, // public double debayerBonus; //0.5 //0.8 ; //0.4; // increase far pixels value
					// (relative to distance to nearest alias)
			0.6, // mainToAlias - relative main/alias amplitudes to enable pixels (i.e. 0.5 means
					// that if alias is >0.5*main, the pixel will be masked out)
			1.6, // debayerMaskBlur - sigma for gaussian blur of the green and red/blue masks
			true, // debayerUseScissors - use "scissors", if false - just apply "diamond" ands
					// "square" with DEBAYER_PARAMETERS.debayerRelativeWidthGreen and
					// DEBAYER_PARAMETERS.debayerRelativeWidthRedblue
//		   false, // showEnergy - plot debayer high frequency energy (use to select between "scissors" and default uniform

			false, // debug - display internal data for the tile around selected pixel
			2592, // xDebug - near the center
			1936, // yDebug - near the center
			true // debayerStacks - show debayer debug images as stack (false - individual)
	);

	public static EyesisCorrectionParameters.NonlinParameters NONLIN_PARAMETERS = new EyesisCorrectionParameters.NonlinParameters(
			false, // true, // useRejectBlocksFilter - apply blocking artifacts rejection filter to
					// the mask
			true, // combineBothModes - combine (sqrt(A*B) mask with rejected blocks and the one
					// without
			256, // maskFFTSize- FFT size of the sliding filter for the mask
			32, // blockPeriod - size of the JPEG macroblock (16) scaled for oversampling
			1.0, // rejectFreqSigma - width of the rejection filter zeros (in frequency counts)
			2.5, // 5.0 // lowPassSigma - sigma for the nonlinear filtering (higher the sigma,
					// farther from the edges is the "sharpened" image used)
			0.006, // 1.0, //0.005, // filtMin - minimal low-pass filtered squared difference
					// between the corrected and original pixels to trigger sharpness enhancement
			0.2, // 5.0, //0.025, // filtMax - squared low-pass filtered difference between the
					// corrected and original pixels, so above that level 100% corrected image is
					// used
			1.0, // thresholdCorrection_11 - multiply filtMin and filtMax for channel 1-1
			1.0, // thresholdCorrection_12,
			1.0, // thresholdCorrection_13,
			1.0, // thresholdCorrection_21,
			1.0, // thresholdCorrection_22,
			1.0, // thresholdCorrection_23,
			1.0, // thresholdCorrection_31,
			1.0, // thresholdCorrection_32,
			1.0, // thresholdCorrection_33,
			0.25, // threshold - when blurred intensity is below this value, use it as a
					// denominator
			true, // useDiffNoiseGains;
			0.0, // noiseGainWeights_0, // r weights used to calculate noise gains from
					// individual color. Currently mask is calculated for green only
			0.0, // noiseGainWeights_1, // b
			1.0, // noiseGainWeights_2, // g
			1.0, // blurSigma, // blur sigma for mask calculation (blur convolution kernels for
					// noise gain calculation
			3.0, // noiseGainPower;
			// ring filter
			true, // useRingFilter; // filter out spots on denoise mask
			0.3, // minMaxValue; // minimal value (relative to filtMax) of the local maximum to
					// be processed
			1.2, // overRingThreshold; // ratio of local max. and maximal value in the
					// surrounding ring to trigger filter
			1.1, // overRingLimit; // limit values in the center circle to scaled maximum in a
					// ring
			6.0, // ringIR; // ring inner radius (center circle radius)
			9.0 // ringOR; // ring outer radius

	);
	public static ColorProcParameters COLOR_PROC_PARAMETERS = new ColorProcParameters(false, // boolean lwir_islwir, //
																								// false;
			27000, // double lwir_low, // 27000;
			31000, // double lwir_high, // 31000;
			true, // boolean lwir_autorange, // true;
			100.0, // double lwir_too_cold, // 100.0; // discard this number of pixels too cold
			3.0, // double lwir_too_hot, // 3.0; // discard this number of pixels too hot
			true, // lwir_pseudocolor,
			1, // int lwir_palette, // 0 - white - hot, 1 - black - hot, 2+ - pseudocolored
			false, // boolean lwir_subtract_dc, // = false;
			true, // boolean lwir_eq_chn = true; // adjust average temperature between channels
			true, // boolean correct_vignetting, // = true;
			1.0, // 1.245, // balanceRed - manual color balance, gain 1.0 matches 0.0.255.0 range
					// of the input Bayer data
			1.0, // 1.34, // balanceBlue;
			0.45, // 1.45, // gain;
			1.0, // weightScaleR; - (now not used) additional correction for the weights of
					// colors for different sub-pixels in a Bayer cell
			1.0, // weightScaleB;
//		2.0,   // sigma;
			0.5, // gamma; - Gaussian sigma to low-pass color components when calculating
					// "smooth" color
			0.003, // minLin;
			0.299, // kr;
			0.114, // kb;
			1.0, // 2.5, // saturationRed;
			1.0, // 2.5, // saturationBlue;
			true, // useFirstY;
			3.0, // maskSigma, 3);
			0.1, // maskMin, 3);
			0.35, // maskMax, 3);
			true, // combineWithSharpnessMask, // combine chroma mask with sharpness mask to
					// reduce color leak through borders
			2.0, // chromaBrightSigma, 3);
			10.0, // chromaDarkSigma, 3);
			false, // corrBlueLeak //Remove blue color leak in the darks near saturation
			32, // satDetSquareSize; // Size of sliding square do detect potential overexposure
			0.005, // satDetRelDiff; // Most pixels in square should be within this difference from
					// average
			0.9, // satDetPartInside; // Fraction of all pixels in the square to fit inside
			1.0, // minimal value for average compared to average over the whole picture
			0.01, // satDetFinRelDiff; // maximal difference from average for the saturated tile
					// to be considered saturated
			0.01, // satDetGrowRelDiff
			0.3, // satDetNewWeight, // weight of new pixel when expanding overexposed areas

			64, // satDetExpSym; // number of overexposure expand steps, not allowing any
				// brighter
			16, // satDetExpOver;// number of overexposure expand steps, limited under, any over

			6, // satDetExpCleanUp; // number of overexposure expand steps, not allowing any
				// brighter (final to clean up oscillations)
			0.03, // satDetGrowRelDiffCleanUp; // maximal difference from start tile average
					// during growing of overexposed areas (final to clean up oscillations)
			2, // blueOverShrink; // shrink blue overexposed area by this number of pixels (to
				// get to undisturbed R/G)
			8, // blueOverGrow; // grow blue overexposed area by this number of pixels
			12, // blueBandWidth; // average amount of blue leak in pixels
			70, // blueBandWidthDark; // average amount of blue leak in pixels (slope at dark)

			1.0, // blueNeutral; // Value of Yb/Yrg ratio for the small areas where safe color c
					// an not be found
			100.0, // blueSolutionRadius; // How far to trust blue color ratio from the found
					// solution (in pixels)
			false, // blueLeakNoHint, // use blueNeutral in the small areas that do not have
					// reliable color sample
			true, // blueLeakNoBrighten; // Do not brighten corrected areas, only darken
			true, // blueLeakFixWires; //Fix thin objects with saturated blue, but not R+G
			5, // blueLeakWiresSize; //size (in pixels) of the small objects to fix blue
			0.03, // blueLeakWiresThreshold; //size (in pixels) of the small objects to fix blue
			true // use8 // use 8 neighbors (false - only 4)

	);
	public static ColorProcParameters COLOR_PROC_PARAMETERS_AUX = null;

	// ColorCalibrationParameters
	public static CorrectionColorProc.ColorGainsParameters CHANNEL_GAINS_PARAMETERS = new CorrectionColorProc.ColorGainsParameters();
	public static CorrectionColorProc.ColorGainsParameters CHANNEL_GAINS_PARAMETERS_AUX = null;
	/*
	 * public static EyesisCorrectionParameters.ColorCalibParameters
	 * COLOR_CALIB_PARAMETERS= new EyesisCorrectionParameters.ColorCalibParameters(
	 * 1.05, // gain_11, 1.05, // gain_12, 1.04, // gain_13, 1.05, // gain_21, 1.00,
	 * // gain_22, 0.97, // gain_23, 2.05, // gain_31, 1.10, // gain_32, 1.15, //
	 * gain_33, 0.97, // balanceRed_11, 1.02, // balanceRed_12, 1.04, //
	 * balanceRed_13, 1.09, // balanceRed_21, 0.98, // balanceRed_22, 0.98, //
	 * balanceRed_23, 1.10, // balanceRed_31, 1.005,// balanceRed_32, 0.96, //
	 * balanceRed_33, 0.95, // balanceBlue_11, 1.0, // balanceBlue_12, 0.97, //
	 * balanceBlue_13, 1.0, // balanceBlue_21, 0.96, // balanceBlue_22, 1.0, //
	 * balanceBlue_23, 1.25, // balanceBlue_31, 1.0, // balanceBlue_32, 0.96 //
	 * balanceBlue_33){ );
	 */
	public static EyesisCorrectionParameters.RGBParameters RGB_PARAMETERS = new EyesisCorrectionParameters.RGBParameters(
			0.075, // r_min;
			0.075, // g_min;
			0.075, // b_min;
			1.0, // r_max;
			1.0, // g_max;
			1.0, // b_max;
			0.0, // alpha_min;
			1.0 // alpha_max;
	);
	public static EyesisCorrectionParameters.ProcessParameters PROCESS_PARAMETERS = new EyesisCorrectionParameters.ProcessParameters(
			true, // eyesisMode
			true, true, true, // first camera channel
			true, true, true, // second camera channel
			true, true, true, // third camera channel
			true, // selectFile
			false, // thisFileOnly;
			1, // subChannelToProcess=subChannelToProcess (1..3)
			true, // split;
			true, // debayer;
			false, // showDebayerEnergy;
			true, // saveDebayerEnergy;
			true, // deconvolve;
			true, // combine;
			false, // showDenoiseMask;
			true, // saveDenoiseMask;
			false, // showChromaDenoiseMask;
			true, // saveChromaDenoiseMask;
			false, // showNoiseGains;
			false, // saveNoiseGains;
			true, // colorProc;
			true, // blueProc;
			true, // toRGB;
			true, // rotate;
			true, // crop - crop image to the sensor size
			true, // jpeg - convert to 8-bit RGB and save jpeg (if save is true)
			true, // save - save result
			false, // true, // save16 - save 16-bit tiff also if the end result is 8 bit
			false, // true, // save32 - save 32-bit tiff also if the end result is 8 or 16 bit
			true, // show;
			95, // JPEG_quality
			0.5, // JPEG_scale
			true);
	public static PostProcessing POST_PROCESSING = new PostProcessing();
	SyncCommand SYNC_COMMAND = new SyncCommand();
	public static EyesisCorrectionParameters.CorrectionParameters CORRECTION_PARAMETERS = new EyesisCorrectionParameters.CorrectionParameters();
	public static EyesisCorrections EYESIS_CORRECTIONS = null;
	public static EyesisCorrections EYESIS_CORRECTIONS_AUX = null;
	public static EyesisCorrectionParameters.EquirectangularParameters EQUIRECTANGULAR_PARAMETERS = new EyesisCorrectionParameters.EquirectangularParameters();
///	public static EventLogger EVENT_LOGGER = null;

	public static int CONVOLVE_FFT_SIZE = 128; // FFT size for sliding convolution with kernel
	public static int THREADS_MAX = 100; // testing multi-threading, limit maximal number of threads

///	public double GAUSS_WIDTH = 0.4; // 0 - use Hamming window
	public static double GAUSS_WIDTH = 0.4; // 0 - use Hamming window

	/* replace */
	public static int PSF_SUBPIXEL_SHOULD_BE_4 = 4; // sub-pixel decimation

//   public float [][] OUT_PIXELS=null; // (global, used with threads to accumulate output result
	public double[] DEBAYER_ENERGY = null; // (global, used with threads to accumulate output result)
	public int DEBAYER_ENERGY_WIDTH; // width of the DEBAYER_ENERGY image

	public double[] DENOISE_MASK = null; // (global, used to return denoise mask to save/show
	public int DENOISE_MASK_WIDTH; // width of the DENOISE_MASK image

	public double[] DENOISE_MASK_CHROMA = null; // (global, used to return denoise mask to save/show
	public int DENOISE_MASK_CHROMA_WIDTH; // width of the DENOISE_MASK_CHROMA image

	public double[] MASK_LOHIRES = null; // (global, used with threads to accumulate output result)
//   public float []  MASK_LOHIRES=null; // (global, used with threads to accumulate output result)

//   public static String [] stackColorNames={"red","green","blue"};
	public static String[] stackColorNames = { "Red", "Green", "Blue" };

	public static ImageStack convolutionKernelStack = null; // select to use for image convolution
	public static ImageStack convolutionKernelStack2 = null; // select to use for image convolution
	public static ImagePlus imp_gaussian = null;
	public static String DEFAULT_DIRECTORY = null;
	// codex 2026-01-28: remember last MCP-selected paths per dialog title
	private static final HashMap<String, String> MCP_LAST_PATHS = new HashMap<String, String>();
	// codex 2026-01-28: current command origin (MCP vs GUI) for file dialogs
	private static volatile boolean MCP_COMMAND_CONTEXT = false;
	// codex 2026-01-28: MCP debug output control
	public static final int MINIMAL_DEBUG_MCP = -2;
	public static int MCP_DEBUG_LEVEL = -1;
	public static boolean ADVANCED_MODE = false; // true; // show all buttons
	public static boolean DCT_MODE = false; // true; // show all buttons
	public static boolean MODE_3D = false; // 3D-related commands
	public static boolean GPU_MODE = false; // 3D-related commands
	public static boolean LWIR_MODE = false; // LWIR-related commands
	public PixelMapping.InterSensor.DisparityTiles DISPARITY_TILES = null;
	public ImagePlus DBG_IMP = null;
	public ImagePlus CORRELATE_IMP = null;
	public TensorflowInferModel TENSORFLOW_INFER_MODEL = null;

//	public boolean use_swing = true; // false;
	private PlugInFrame plugInFrame;
	private JFrame plugInJFrame;
//	static Frame instance;
	private Panel panel1, panel2, panel3, panel4, panel5, panel5a, panel6, panel7, panelPostProcessing1,
	panelPostProcessing2, panelPostProcessing3, panelDct1, panelClt1, panelClt2, panelClt3, panelClt4,
	panelClt5, panelClt5aux, panelClt_GPU, panelLWIR, panelLWIR16, panelLWIRWorld, panelOrange;
	private JPanel jpanel1, jpanel2, jpanel3, jpanel4, jpanel5, jpanel5a, jpanel6, jpanel7, jpanelPostProcessing1,
	jpanelPostProcessing2, jpanelPostProcessing3, jpanelDct1, jpanelClt1, jpanelClt2, jpanelClt3, jpanelClt4,
	jpanelClt5, jpanelClt5aux, jpanelClt_GPU, jpanelLWIR, jpanelLWIR16, jpanelLWIRWorld, jpanelOrange;
	private boolean commandFromMcp = false;
	private volatile boolean shutdownRequested = false;
	
//	EyesisTopFrame eyesisTopFrame = new EyesisTopFrame();	
//	System.out.println("Launched EyesisTopFrame");
	
	@Override
	public void run(String arg) {
		String options = Macro.getOptions();
		try {
			prefsPath = Macro.getValue(options, "prefs",
					Prefs.getPrefsDir() + Prefs.getFileSeparator() + "Eyesis_Correction.xml");
		} catch (Exception e) {
			prefsPath = Prefs.getPrefsDir() + Prefs.getFileSeparator() + "Eyesis_Correction.xml";
		}
		if (IJ.versionLessThan("1.43q"))
			return;
		/*
		if (instance != null) {
			instance.toFront();
			return;
		}
		*/
		/*
		if (plugInFrame != null) {
			plugInFrame.toFront();
			return;
		}
		*/
		/*
  LogTee.install();         // once, early
  LogTee.setSceneLog(path); // when you want to start per‑scene logging
  LogTee.clearSceneLog();   // stop per‑scene logging

		 */
		LogTee.install();         // once, early
		// codex 2026-01-25: start MCP server + enable MCP dialog mode by default
		GenericJTabbedDialogMcp.setDefaultMcpMode(getMcpMode());
		GenericJTabbedDialogMcp.setDialogTimeoutMs(getMcpDialogTimeoutMs());
		MCP_DEBUG_LEVEL = DEBUG_LEVEL-2;
		// codex 2026-01-27: enable MCP-mode SyncCommand control
		SYNC_COMMAND.setMcpMode(getMcpMode());
		McpServer.startIfNeeded(this, getMcpPort());
		// codex 2026-01-25: optional SLF4J/logback root level override
		applySlf4jRootLevel();
		try {
			loadPrefs();
		} catch (IOException e1) {
			System.out.println("failed to load preferences");
//			e1.printStackTrace();
		}
		EYESIS_CORRECTIONS = new EyesisCorrections(SYNC_COMMAND.stopRequested, CORRECTION_PARAMETERS);
		if (options != null || headless) {
			processFiles();
		} else {
			initGuiSwing();
		}
	}

	public void initGuiSwing() {
		///		EyesisTopFrame eyesisTopFrame = new EyesisTopFrame();	
				Color color_configure = new Color(200, 200, 160);
				Color color_configure_aux = new Color(190, 190, 180);
				Color color_process = new Color(180, 180, 240);
				Color color_process_aux = new Color(175, 175, 250);
				Color color_conf_process = new Color(180, 240, 240);
				Color color_conf_process_aux = new Color(175, 235, 250);
				Color color_restore = new Color(160, 240, 160);
				Color color_stop = new Color(255, 160, 160);
				Color color_report = new Color(180, 220, 180);
				
				
				plugInJFrame = new JFrame("Eyesis_Correction") {
					private static final long serialVersionUID = -4138832568507690332L;

					@Override
					public void processWindowEvent(WindowEvent e) {
						super.processWindowEvent(e);
						if (e.getID() == WindowEvent.WINDOW_CLOSING) {
							plugInJFrame=null; // instance = null;
						}
					}
				};
//				Font font = plugInJFrame.getFont();
//				instance = plugInJFrame;
				plugInJFrame.addKeyListener(IJ.getInstance());
//				int menuRows=4 + (ADVANCED_MODE?4:0) + (MODE_3D?3:0) + (DCT_MODE?6:0) + (GPU_MODE?1:0) +(LWIR_MODE?2:0);
				int menuRows = 4 + (ADVANCED_MODE ? 4 : 0) + (MODE_3D ? 3 : 0) + (DCT_MODE ? 7 : 0) + (GPU_MODE ? 1 : 0)
						+ (LWIR_MODE ? 4 : 0);
				plugInJFrame.setLayout(new GridLayout(menuRows, 1));
				jpanel6 = new JPanel();
				jpanel6.setLayout(new GridLayout(1, 0, 5, 5));
				addJButton("Save", jpanel6, color_process); // , "Save configuration");
				addJButton("Save Clean", jpanel6, color_process); // , "Save clean configuration");
				addJButton("Restore", jpanel6, color_restore); // , "Restore configuration");
				addJButton("Stop", jpanel6, color_stop);
				addJButton("Abort", jpanel6, color_stop);
				addJButton("Exit", jpanel6, color_stop);

				plugInJFrame.add(jpanel6);

				jpanel5 = new JPanel();
				jpanel5.setLayout(new GridLayout(1, 0, 5, 5));
				addJButton("Configure spilt", jpanel5, color_configure);
				addJButton("Configure demosaic", jpanel5, color_configure);
				plugInJFrame.add(jpanel5);
				jpanel5a = new JPanel();
				jpanel5a.setLayout(new GridLayout(1, 0, 5, 5));
				addJButton("Configure convolution", jpanel5a, color_configure);
				addJButton("Configure denoise", jpanel5a, color_configure);
				addJButton("Configure color", jpanel5a, color_configure);
				addJButton("Configure AUX color", jpanel5a, color_configure_aux);
//				addJButton("Channel gains", jpanel5a, color_configure);
				addJButton("Configure RGB", jpanel5a, color_configure);
				plugInJFrame.add(jpanel5a);

				// Debug/development options

				if (ADVANCED_MODE) {
					jpanel1 = new JPanel();
					jpanel1.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("Split Image", jpanel1, null);
					addJButton("Debayer Image", jpanel1, null);
					plugInJFrame.add(jpanel1);

					jpanel2 = new JPanel();
					jpanel2.setLayout(new GridLayout(1, 0, 5, 5));
					addJButton("Select kernel stack", jpanel2, null);
					addJButton("Convolve with stack", jpanel2, null);
					plugInJFrame.add(jpanel2);

					jpanel3 = new JPanel();
					jpanel3.setLayout(new GridLayout(1, 0, 5, 5));
					addJButton("Select lo-res", jpanel3, null);
					addJButton("Combine pair", jpanel3, null);
					addJButton("Colors", jpanel3, null);
					addJButton("RGB", jpanel3, null);
					plugInJFrame.add(jpanel3);

					jpanel4 = new JPanel();
					jpanel4.setLayout(new GridLayout(1, 0, 5, 5));
					addJButton("Test", jpanel4, null);
					addJButton("Test Debayer", jpanel4, null);
					plugInJFrame.add(jpanel4);
				}
				jpanel7 = new JPanel();
				jpanel7.setLayout(new GridLayout(1, 0, 5, 5));
				addJButton("Configure correction", jpanel7, color_configure);
				addJButton("Channel gains/colors", jpanel7, color_configure);
				addJButton("Configure warping", jpanel7, color_conf_process);
				addJButton("Select source files", jpanel7, color_configure);
				addJButton("Select source sets", jpanel7, color_configure);
				addJButton("Process files", jpanel7, color_process);
				if (ADVANCED_MODE) {
					addJButton("Tiff Writer", jpanel7, null);
					addJButton("Tiff Properties", jpanel7, null);
					addJButton("Add TIFF resolution", jpanel7, null);
				}
				plugInJFrame.add(jpanel7);

				if (MODE_3D) {
					jpanelPostProcessing1 = new JPanel();
					jpanelPostProcessing1.setLayout(new GridLayout(1, 0, 5, 5));
					addJButton("Configure PP", jpanelPostProcessing1, color_configure);
					addJButton("Source PP", jpanelPostProcessing1, color_configure);
					addJButton("ConvertPP", jpanelPostProcessing1, color_process);
					addJButton("Linear Features", jpanelPostProcessing1, null);
					addJButton("Intercam correlations", jpanelPostProcessing1, null);
					plugInJFrame.add(jpanelPostProcessing1, null);

					jpanelPostProcessing2 = new JPanel();
					jpanelPostProcessing2.setLayout(new GridLayout(1, 0, 5, 5));
					addJButton("Tile correlations", jpanelPostProcessing2, null);
					addJButton("Load correlations", jpanelPostProcessing2, null);
					addJButton("Section correlations", jpanelPostProcessing2, null);
					addJButton("Setup Z-map", jpanelPostProcessing2, null);
					addJButton("Fill FG gaps", jpanelPostProcessing2, null);
					addJButton("Filter Z-map", jpanelPostProcessing2, null);
					addJButton("Occluding FG", jpanelPostProcessing2, null);
					plugInJFrame.add(jpanelPostProcessing2, null);

					jpanelPostProcessing3 = new JPanel();
					jpanelPostProcessing3.setLayout(new GridLayout(1, 0, 5, 5));
					addJButton("Refine Disparities", jpanelPostProcessing3, null);
					addJButton("Init Photometry", jpanelPostProcessing3, null);
					addJButton("Plane Likely", jpanelPostProcessing3, null);
					plugInJFrame.add(jpanelPostProcessing3, null);
				}
				if (DCT_MODE) {
					jpanelDct1 = new JPanel();
					jpanelDct1.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("DCT test 1", jpanelDct1, color_process);
					addJButton("select MDCT image", jpanelDct1, color_configure);
					addJButton("MDCT scale", jpanelDct1, color_process);
					addJButton("MDCT stack", jpanelDct1, color_process);
					addJButton("DCT test 3", jpanelDct1, color_process);
					addJButton("DCT test 4", jpanelDct1, color_process);
					addJButton("Test Kernel Factorization", jpanelDct1, color_process);
					addJButton("Min Kernel Factorization", jpanelDct1, color_process);
					addJButton("Select kernels image", jpanelDct1, color_configure);
					addJButton("Create DCT kernels", jpanelDct1, color_process);
					addJButton("Read DCT kernels", jpanelDct1, color_process);
					addJButton("Reset DCT kernels", jpanelDct1, color_stop);
					addJButton("Setup DCT parameters", jpanelDct1, color_configure);
					addJButton("DCT process files", jpanelDct1, color_process);
					plugInJFrame.add(jpanelDct1);
				}
				if (DCT_MODE) {
					jpanelClt1 = new JPanel();
					jpanelClt1.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("Select CLT image", jpanelClt1, color_configure);
					addJButton("CLT stack", jpanelClt1, color_process);
					addJButton("Select second CLT image", jpanelClt1, color_configure);
					addJButton("CLT correlate", jpanelClt1, color_process);
					addJButton("Reset Geometry", jpanelClt1, color_stop);
					addJButton("Reset AUX Geometry", jpanelClt1, color_stop);
					addJButton("Create CLT kernels", jpanelClt1, color_process);
					addJButton("Create AUX CLT kernels", jpanelClt1, color_process);
					addJButton("Read CLT kernels", jpanelClt1, color_process_aux);
					addJButton("Reset CLT kernels", jpanelClt1, color_stop);

					addJButton("CLT process files", jpanelClt1, color_process);
					addJButton("CLT process sets", jpanelClt1, color_process);

					plugInJFrame.add(jpanelClt1);
				}
				if (DCT_MODE) {
					jpanelClt2 = new JPanel();
					jpanelClt2.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("Setup CLT parameters", jpanelClt2, color_configure);
					addJButton("CLT 4 images", jpanelClt2, color_conf_process);
					addJButton("CLT disparity scan", jpanelClt2, color_conf_process);
					addJButton("CLT reset fine corr", jpanelClt2, color_stop);
					addJButton("CLT reset extrinsic corr", jpanelClt2, color_stop);
					addJButton("ERS reset", jpanelClt2, color_stop);
					addJButton("CLT show geometry", jpanelClt2, color_configure);
					addJButton("CLT show fine corr", jpanelClt2, color_report);
					addJButton("CLT apply fine corr", jpanelClt2, color_process);
					addJButton("CLT test fine corr", jpanelClt2, color_process);
					addJButton("CLT process fine corr", jpanelClt2, color_conf_process);
					addJButton("CLT infinity corr", jpanelClt2, color_conf_process);
					addJButton("CLT ext infinity corr", jpanelClt2, color_conf_process);
					addJButton("CLT reset 3D", jpanelClt2, color_stop);
					addJButton("MAIN extrinsics", jpanelClt2, color_process);
					addJButton("CLT Poly corr", jpanelClt2, color_process);
					addJButton("DRY RUN", jpanelClt2, color_configure);
					addJButton("CLT 3D", jpanelClt2, color_process);
					addJButton("CLT planes", jpanelClt2, color_conf_process);
					addJButton("CLT ASSIGN", jpanelClt2, color_process);
					addJButton("CLT OUT 3D", jpanelClt2, color_process);

					plugInJFrame.add(jpanelClt2);
				}
				if (DCT_MODE) {
					jpanelClt3 = new JPanel();
					jpanelClt3.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("Setup CLT", jpanelClt3, color_configure);
					addJButton("Infinity offset", jpanelClt3, color_configure);
					addJButton("Setup CLT Batch parameters", jpanelClt3, color_configure);
					addJButton("CLT batch process", jpanelClt3, color_process);
					addJButton("CM Test", jpanelClt3, color_stop);
					addJButton("Show scan", jpanelClt3, color_report);
					addJButton("Show all scans", jpanelClt3, color_report);
					addJButton("Periodic", jpanelClt3, color_configure);
					plugInJFrame.add(jpanelClt3);
				}

				if (DCT_MODE) {
					jpanelClt4 = new JPanel();
					jpanelClt4.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("Import Aux", jpanelClt4, color_restore);
					addJButton("Setup CLT Batch parameters", jpanelClt4, color_configure);
					addJButton("CLT rig edit", jpanelClt4, color_configure);
					addJButton("Rig offset", jpanelClt4, color_configure);
					addJButton("Save offset", jpanelClt4, color_process);
					addJButton("SHOW extrinsics", jpanelClt4, color_report);
					addJButton("Set Infinity", jpanelClt4, color_configure);
					addJButton("RIG DSI", jpanelClt4, color_conf_process);
					addJButton("MAIN extrinsics", jpanelClt4, color_process);
					addJButton("AUX extrinsics", jpanelClt4, color_process);
					addJButton("RIG extrinsics", jpanelClt4, color_conf_process);
					addJButton("SAVE extrinsics", jpanelClt4, color_process); // , "Save configuration");

					addJButton("Rig8 images", jpanelClt4, color_conf_process);

					addJButton("Reset GT", jpanelClt4, color_stop);
					addJButton("Ground truth", jpanelClt4, color_conf_process);
					addJButton("Show biscan", jpanelClt4, color_report);
					addJButton("Poles GT", jpanelClt4, color_process);
					addJButton("ML export", jpanelClt4, color_conf_process);
					addJButton("JP4 copy", jpanelClt4, color_conf_process);
					addJButton("DSI show", jpanelClt4, color_report);
					addJButton("Rig batch", jpanelClt4, color_process);
					plugInJFrame.add(jpanelClt4);
				}

				if (DCT_MODE) {
					jpanelClt5 = new JPanel();
					jpanelClt5.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("LIST extrinsics", jpanelClt5, color_report);
					addJButton("DSI histogram", jpanelClt5, color_report);
					addJButton("ML recalc", jpanelClt5, color_process);
					addJButton("Inter Test", jpanelClt5, color_stop);
					addJButton("Inter Pairs", jpanelClt5, color_process);
					addJButton("Inter LMA", jpanelClt5, color_stop);
					addJButton("Inter Series", jpanelClt5, color_process);
					addJButton("Inter Accumulate", jpanelClt5, color_process);
					addJButton("Inter Noise", jpanelClt5, color_process);
					addJButton("Inter Debug Noise", jpanelClt5, color_report);
					addJButton("Noise Stats", jpanelClt5, color_process);
					addJButton("Test 1D", jpanelClt5, color_process);
					addJButton("Colorize Depth", jpanelClt5, color_process);
					addJButton("Main LY series", jpanelClt5, color_process);
					plugInJFrame.add(jpanelClt5);
				}

				if (DCT_MODE) {
					jpanelClt5aux = new JPanel();
					jpanelClt5aux.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("LIST extrinsics", jpanelClt5aux, color_report);
					addJButton("Aux Build Series", jpanelClt5aux, color_stop);
					addJButton("Aux Inter Test", jpanelClt5aux, color_stop);
					addJButton("Aux Inter Pairs", jpanelClt5aux, color_process);
					addJButton("Aux Inter LMA", jpanelClt5aux, color_stop);
					addJButton("Aux Inter Series", jpanelClt5aux, color_process);
					addJButton("Aux Inter Accumulate", jpanelClt5aux, color_process);
					addJButton("Inter Noise Aux", jpanelClt5aux, color_process);
					addJButton("Batch Noise Aux", jpanelClt5aux, color_report);
					addJButton("Noise Stats Aux", jpanelClt5aux, color_process);
					addJButton("Colorize Depth", jpanelClt5aux, color_process);
					addJButton("Inter Intra ML", jpanelClt5aux, color_report);
					addJButton("Aux LY series", jpanelClt5aux, color_process);
					plugInJFrame.add(jpanelClt5aux);
				}

				if (GPU_MODE) {
					jpanelClt_GPU = new JPanel();
					jpanelClt_GPU.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("JCUDA TEST", jpanelClt_GPU, null);
					addJButton("TF TEST", jpanelClt_GPU, null);
					addJButton("GPU simulate", jpanelClt_GPU, color_conf_process);
					addJButton("GPU RUN", jpanelClt_GPU, color_conf_process);
//					addJButton("ShowGPU",                    jpanelClt_GPU, color_conf_process);

					addJButton("LWIR_TEST", jpanelClt_GPU, color_conf_process);
					addJButton("LWIR_ACQUIRE", jpanelClt_GPU, color_conf_process);
					addJButton("ERS reset", jpanelClt2, color_stop);
					addJButton("IMU main", jpanelClt_GPU, color_conf_process);
					addJButton("ERS main", jpanelClt_GPU, color_process);
					addJButton("IMU aux", jpanelClt_GPU, color_conf_process_aux);
					addJButton("ERS aux", jpanelClt_GPU, color_process_aux);

					addJButton("Rotations_test", jpanelClt_GPU, color_stop);
					addJButton("GPU simulate", jpanelClt_GPU, color_conf_process);

					plugInJFrame.add(jpanelClt_GPU);
				}
				if (LWIR_MODE) {
					jpanelLWIR = new JPanel();
					jpanelLWIR.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("Setup CLT Batch parameters", jpanelLWIR, color_configure);
					addJButton("Reset Geometry", jpanelLWIR, color_stop);
					addJButton("Reset AUX Geometry", jpanelLWIR, color_stop);
					addJButton("Create CLT kernels", jpanelLWIR, color_process);
					addJButton("Create AUX CLT kernels", jpanelLWIR, color_process_aux);
					addJButton("Select source sets", jpanelLWIR, color_configure);
					addJButton("Configure color", jpanelLWIR, color_configure);
					addJButton("CORR TEST", jpanelLWIR, color_conf_process);
					addJButton("CLT 4 images", jpanelLWIR, color_conf_process);
					addJButton("CLT 3D", jpanelLWIR, color_process);
					addJButton("CLT planes", jpanelLWIR, color_conf_process);
					addJButton("CLT ASSIGN", jpanelLWIR, color_process);
					addJButton("CLT OUT 3D", jpanelLWIR, color_process);
					addJButton("Configure AUX color", jpanelLWIR, color_configure_aux);
					addJButton("AUX 4 images", jpanelLWIR, color_conf_process_aux);
					addJButton("AUX 3D", jpanelLWIR, color_process);
					addJButton("AUX planes", jpanelLWIR, color_conf_process_aux);
					addJButton("AUX ASSIGN", jpanelLWIR, color_process_aux);
					addJButton("AUX OUT 3D", jpanelLWIR, color_process_aux);
					addJButton("Main img AUX", jpanelLWIR, color_process_aux);
					addJButton("Main to AUX", jpanelLWIR, color_process_aux);
					addJButton("LWIR batch", jpanelClt4, color_process);
//					addJButton("LWIR_TEST",                  jpanelLWIR, color_conf_process);
//					addJButton("LWIR_ACQUIRE",               jpanelLWIR, color_conf_process);
					plugInJFrame.add(jpanelLWIR);

					jpanelLWIR16 = new JPanel();
					jpanelLWIR16.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("SHOW extrinsics", jpanelLWIR16, color_report);
					addJButton("Set Infinity",    jpanelLWIR16, color_configure);
					addJButton("Reset Geometry", jpanelLWIR16, color_stop);
					addJButton("Reset AUX Geometry", jpanelLWIR16, color_stop);
					addJButton("Generate Sym Vectors", jpanelLWIR16, color_configure);
					addJButton("Image Properties", jpanelLWIR16, color_conf_process);
					addJButton("Illustrations Configure", jpanelLWIR16, color_conf_process);
					addJButton("Footage Organize", jpanelLWIR16, color_conf_process);
					addJButton("Super batch",     jpanelLWIR16, color_process);
					addJButton("Remove source paths",     jpanelLWIR16, color_stop);
					plugInJFrame.add(jpanelLWIR16);
					jpanelLWIRWorld = new JPanel();
					jpanelLWIRWorld.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("Aux Build Series", jpanelLWIRWorld, color_stop);
					addJButton("CUAS Combine",     jpanelLWIRWorld, color_stop, "Combine previously processed CUAS series");
					addJButton("CUAS Video",       jpanelLWIRWorld, color_stop, "Combine previously rendered  CUAS videos");
					addJButton("CUAS RT",          jpanelLWIRWorld, color_stop, "CUAS Real-time Tests");
					addJButton("Build World", jpanelLWIRWorld, color_process);
					addJButton("Test IMX5", jpanelLWIRWorld, color_process);
					addJButton("Show mice", jpanelLWIRWorld, color_process);
					addJButton("Set pair",  jpanelLWIRWorld, color_process);
					addJButton("Warp pair",  jpanelLWIRWorld, color_process);
					addJButton("Read Tiff",  jpanelLWIRWorld, color_process);
					addJButton("Test video",  jpanelLWIRWorld, color_process);
					addJButton("Ortho",         jpanelLWIRWorld, color_process);
					addJButton("Ortho Pairs",  jpanelLWIRWorld, color_process);
					addJButton("Manual pair",  jpanelLWIRWorld, color_process);
					addJButton("Extract Objects",  jpanelLWIRWorld, color_process);
					addJButton("Mismatched resolutions",  jpanelLWIRWorld, color_process);
					addJButton("Generate DATI",  jpanelLWIRWorld, color_process);
					addJButton("Create mine",  jpanelLWIRWorld, color_process);
					addJButton("Pattern correlate",  jpanelLWIRWorld, color_process);
					addJButton("Properties compare",  jpanelLWIRWorld, color_process);
					plugInJFrame.add(jpanelLWIRWorld);

					jpanelOrange = new JPanel();
					jpanelOrange.setLayout(new GridLayout(1, 0, 5, 5)); // rows, columns, vgap, hgap
					addJButton("Test Orange", jpanelOrange, color_process);
					addJButton("Process Merged", jpanelOrange, color_process);
					addJButton("Vegetation LMA", jpanelOrange, color_process);
					addJButton("Combine LMA Segments", jpanelOrange, color_process);
					addJButton("Render synthetic", jpanelOrange, color_process);
					addJButton("Generate LWIR target", jpanelOrange, color_process);
//					addJButton("Test LDLT Cholesky", jpanelOrange, color_process);
					addJButton("Test LLT Cholesky",  jpanelOrange, color_process);
					addJButton("UAS log",  jpanelOrange, color_process);
					addJButton("DJI SRT",  jpanelOrange, color_process);
					addJButton("SRT to KML",      jpanelOrange, color_process);
					addJButton("Target Analyze",  jpanelOrange, color_process);
//					addJButton("Motion_CUAS",  jpanelOrange, color_stop);
//					addJButton("Motion_scan",  jpanelOrange, color_stop);
					//
					plugInJFrame.add(jpanelOrange);
				}
				plugInJFrame.pack(); // Adjusts the window size to fit components
		//"LWIR batch"
				GUI.center(plugInJFrame);
				plugInJFrame.setVisible(true);
//				FHT_INSTANCE = new DoubleFHT();
				// main loop
				while (true) {
					synchronized (this.SYNC_COMMAND) {
						try {
							this.SYNC_COMMAND.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						this.SYNC_COMMAND.isRunning = true;
					}
					if (this.SYNC_COMMAND.stopRequested.get() == 0) {
						try {
							this.commandFromMcp = this.SYNC_COMMAND.fromMcp;
							this.SYNC_COMMAND.fromMcp = false;
							runMenuCommand(this.SYNC_COMMAND.buttonLabel);
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
							System.out.println(stack2string(e));
							IJ.showMessage("Exception", stack2string(e));
						}
					}
					this.commandFromMcp = false;
					this.SYNC_COMMAND.isRunning = false;
					this.SYNC_COMMAND.stopRequested.set(0);
					if (this.shutdownRequested) {
						shutdownApplication();
						break;
					}
				}

			}
		
	private Properties prefsProperties = new Properties();

	public void loadPrefs() throws IOException {
		InputStream is;
		try {
			is = new FileInputStream(this.prefsPath);
		} catch (FileNotFoundException e) {
			String msg = "Failed to open configuration file: " + this.prefsPath;
			System.out.println("Warning: " + msg);
			return; // file does not exist, use current motor position (if it is initialized)
		}
		try {
			prefsProperties.loadFromXML(is);
			System.out.println("Skipping getAllProperties(prefsProperties)");
//    		getAllProperties(prefsProperties);
//   		if (DEBUG_LEVEL>0) System.out.println("Configuration parameters are restored from "+this.prefsPath);

		} catch (IOException e) {
			String msg = "Failed to read XML configuration file: " + this.prefsPath;
			IJ.showMessage("Error", msg);
			throw new IOException(msg);
		}
		try {
			is.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String sValue = this.prefsProperties.getProperty("ADVANCED_MODE");
		if (sValue != null) {
			ADVANCED_MODE = Boolean.parseBoolean(sValue);
			System.out.println("Read ADVANCED_MODE=" + ADVANCED_MODE);
		} else {
			String msg = "ADVANCED_MODE is undefined in " + this.prefsPath;
			IJ.showMessage("Error", msg);
			throw new IOException(msg);
		}
		sValue = this.prefsProperties.getProperty("MODE_3D");
		if (sValue != null) {
			MODE_3D = Boolean.parseBoolean(sValue);
			System.out.println("Read MODE_3D=" + MODE_3D);
		} else {
			String msg = "MODE_3D is undefined in " + this.prefsPath;
			IJ.showMessage("Error", msg);
			throw new IOException(msg);
		}
		sValue = this.prefsProperties.getProperty("DCT_MODE");
		if (sValue != null) {
			DCT_MODE = Boolean.parseBoolean(sValue);
			System.out.println("Read DCT_MODE=" + DCT_MODE);
		} else {
//			String msg="DCT_MODE is undefined in "+this.prefsPath;
//			IJ.showMessage("Error",msg);
//			throw new IOException (msg);
		}
		sValue = this.prefsProperties.getProperty("GPU_MODE");
		if (sValue != null) {
			GPU_MODE = Boolean.parseBoolean(sValue);
			System.out.println("Read GPU_MODE=" + GPU_MODE);
		} else {
			String msg = "GPU_MODE is undefined in " + this.prefsPath;
			IJ.showMessage("Error", msg);
			throw new IOException(msg);
		}
		sValue = this.prefsProperties.getProperty("LWIR_MODE");
		if (sValue != null) {
			LWIR_MODE = Boolean.parseBoolean(sValue);
			System.out.println("Read LWIR_MODE=" + LWIR_MODE);
		} else {
			String msg = "LWIR_MODE is undefined in " + this.prefsPath;
			IJ.showMessage("Error", msg);
			throw new IOException(msg);
		}
	}

	public static String stack2string(Exception e) {
		try {
			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw);
			e.printStackTrace(pw);
			return sw.toString();
		} catch (Exception e2) {
			return "bad stack2string";
		}
	}

	public class AwtToolTip extends MouseAdapter {

		@SuppressWarnings("unused")
		private String toolTipText = "...";
		@SuppressWarnings("unused")
		private Component component = null;
		private Dialog dialog;

		public AwtToolTip(String toolTipText, Component component) {
			this.toolTipText = toolTipText;
			this.component = component;
			dialog = new Dialog(new Frame());
			dialog.add(new Label(toolTipText));
			dialog.setLocationRelativeTo(component);
			dialog.pack();
		}

		@Override
		public void mouseEntered(MouseEvent mouseEvent) {
			Point location = MouseInfo.getPointerInfo().getLocation();
			int x = (int) location.getX();
			int y = (int) location.getY();

			dialog.setLocation(x, y);

			dialog.setVisible(true);
		}

		@Override
		public void mouseExited(MouseEvent mouseEvent) {
			dialog.setVisible(false);

		}

	}

	void addButton(String label, Panel panel, Color color) {
		Button b = new Button(label);
		if (color != null) {
			b.setBackground(color);
		}
		b.addActionListener(this);
		b.addKeyListener(IJ.getInstance());
		panel.add(b);
	}


	void addJButton(String label, JPanel panel, Color color, String tooltip) {
		JButton b = new JButton(label);
		if (color != null) {
			b.setBackground(color);
		}
		b.addActionListener(this);
		b.addKeyListener(IJ.getInstance());
		b.setToolTipText(tooltip);
		panel.add(b);
	}

	
	void addJButton(String label, JPanel panel, Color color) {
		addJButton(
				label,  // String label,
				panel,  // JPanel panel,
				color,  // Color color,
				label); // String tooltip);
	}
	
	
	@Override
	public void actionPerformed(ActionEvent e) {
		triggerCommand(e.getActionCommand());
	}

	// codex 2026-01-25: MCP-visible command trigger
	public void triggerCommand(String label) {
		triggerCommand(label, false);
	}

	// codex 2026-01-28: mark MCP-originated commands
	public void triggerCommand(String label, boolean fromMcp) {
		if (label == null) {
			return;
		}
		if (label.equals("Exit")) {
			this.shutdownRequested = true;
			if (this.SYNC_COMMAND.isRunning) {
				// Request immediate stop; main loop will exit after current command unwinds.
				this.SYNC_COMMAND.confirm = false;
				this.SYNC_COMMAND.stopRequested.set(SyncCommand.STOP_ASAP);
				return;
			}
			// If idle, exit immediately to avoid notify/wait race in the command loop.
			shutdownApplication();
			return;
		}
		if (label.equals("Abort")) {
			this.SYNC_COMMAND.stopRequested.set(1);
			this.SYNC_COMMAND.confirm = true;
			return;
		} else if (label.equals("Stop")) {
			this.SYNC_COMMAND.stopRequested.set(2);
			this.SYNC_COMMAND.confirm = true;
			return;
		}
//		System.out.println("triggerCommand: SwingUtilities.isEventDispatchThread()="+SwingUtilities.isEventDispatchThread());
		if (this.SYNC_COMMAND.isRunning) {
			this.SYNC_COMMAND.interruptCommand(); 
			return;
		}
		synchronized (this.SYNC_COMMAND) {
			this.SYNC_COMMAND.fromMcp = fromMcp;
			this.SYNC_COMMAND.buttonLabel = label;
			this.SYNC_COMMAND.notify();
		}
	}

	private void shutdownApplication() {
		if (DEBUG_LEVEL > -3) {
			System.out.println("Exit requested, shutting down application");
		}
		try {
			if (this.plugInJFrame != null) {
				this.plugInJFrame.dispose();
				this.plugInJFrame = null;
			}
		} catch (Exception e) {
			System.out.println("Failed to dispose Eyesis frame: " + e.getMessage());
		}
		ImageJ imagej = IJ.getInstance();
		if (imagej != null) {
			imagej.quit();
		} else {
			System.exit(0);
		}
	}

	// codex 2026-01-25: MCP status accessors
	public boolean isSyncRunning() {
		return this.SYNC_COMMAND.isRunning;
	}

	public int getSyncStopRequested() {
		return this.SYNC_COMMAND.stopRequested.get();
	}

	public String getSyncButtonLabel() {
		return this.SYNC_COMMAND.buttonLabel;
	}

	// codex 2026-01-27: MCP interrupt controls
	public boolean requestSyncStop(boolean confirm, boolean asap) {
		return this.SYNC_COMMAND.requestStop(confirm, asap);
	}

	public boolean confirmSyncStop(boolean stop) {
		return this.SYNC_COMMAND.confirmStop(stop);
	}

	public boolean isSyncConfirmPending() {
		return this.SYNC_COMMAND.isConfirmPending();
	}

	public boolean isSyncConfirmConvenient() {
		return this.SYNC_COMMAND.isConfirmConvenient();
	}

	private int getMcpPort() {
		String value = System.getProperty("elphel.mcp.port");
		if (value == null || value.isEmpty()) {
			return 48888;
		}
		try {
			return Integer.parseInt(value);
		} catch (NumberFormatException e) {
			return 48888;
		}
	}

	private long getMcpDialogTimeoutMs() {
		String value = System.getProperty("elphel.mcp.dialogTimeoutMs");
		if (value == null || value.isEmpty()) {
			return 30000L;
		}
		try {
			return Long.parseLong(value);
		} catch (NumberFormatException e) {
			return 30000L;
		}
	}


	private boolean getMcpMode() {
		String value = System.getProperty("elphel.mcp.mode");
		if (value == null || value.isEmpty()) {
			return true;
		}
		return Boolean.parseBoolean(value);
	}

	// codex 2026-01-25: allow JVM flag to quiet SLF4J/logback and JUL noise
	private void applySlf4jRootLevel() {
		String value = System.getProperty("elphel.slf4j.level");
		if (value == null || value.isEmpty()) {
			return;
		}
		try {
			Object factory = org.slf4j.LoggerFactory.getILoggerFactory();
			if (factory instanceof ch.qos.logback.classic.LoggerContext) {
				ch.qos.logback.classic.Logger root =
						((ch.qos.logback.classic.LoggerContext) factory).getLogger(
								org.slf4j.Logger.ROOT_LOGGER_NAME);
				ch.qos.logback.classic.Level level =
						ch.qos.logback.classic.Level.toLevel(value, ch.qos.logback.classic.Level.ERROR);
				root.setLevel(level);
				// also quiet common Bio-Formats namespaces
				((ch.qos.logback.classic.LoggerContext) factory).getLogger("loci").setLevel(level);
				((ch.qos.logback.classic.LoggerContext) factory).getLogger("ome").setLevel(level);
				((ch.qos.logback.classic.LoggerContext) factory).getLogger("org.openmicroscopy").setLevel(level);
				((ch.qos.logback.classic.LoggerContext) factory)
						.getLogger("loci.common.services.ServiceFactory").setLevel(level);
			}
			try {
				loci.common.DebugTools.setRootLevel(value);
			} catch (Throwable t2) {
				System.out.println("loci.common not yet available: "+t2.getMessage());
				// ignore if loci.common not yet available
			}
			applyJulRootLevel(value);
		} catch (Throwable t) {
			System.out.println("SLF4J level override failed: " + t.getMessage());
		}
	}

	// codex 2026-01-25: set our package to LOG_LEVEL while keeping 3rd-party quiet
	private void applySelectiveLogLevels(String logLevel) {
		if (logLevel == null || logLevel.isEmpty()) {
			return;
		}
		try {
			Object factory = org.slf4j.LoggerFactory.getILoggerFactory();
			if (factory instanceof ch.qos.logback.classic.LoggerContext) {
				ch.qos.logback.classic.LoggerContext ctx =
						(ch.qos.logback.classic.LoggerContext) factory;
				ch.qos.logback.classic.Level mine =
						ch.qos.logback.classic.Level.toLevel(logLevel, ch.qos.logback.classic.Level.WARN);
				ch.qos.logback.classic.Level third = ch.qos.logback.classic.Level.ERROR;
				// 3rd-party quiet
				ctx.getLogger("loci").setLevel(third);
				ctx.getLogger("ome").setLevel(third);
				ctx.getLogger("org.openmicroscopy").setLevel(third);
				ctx.getLogger("loci.common.services.ServiceFactory").setLevel(third);
				// our code adjustable
				ctx.getLogger("com.elphel.imagej").setLevel(mine);
			}
		} catch (Throwable t) {
			System.out.println("Selective log level override failed: " + t.getMessage());
		}
	}

	// codex 2026-01-25: reduce java.util.logging output if Bio-Formats uses JUL
	private void applyJulRootLevel(String value) {
		java.util.logging.Level level = julLevel(value);
		java.util.logging.Logger root = java.util.logging.Logger.getLogger("");
		root.setLevel(level);
		for (java.util.logging.Handler h : root.getHandlers()) {
			h.setLevel(level);
		}
	}

	// codex 2026-01-25: map string to JUL level
	private java.util.logging.Level julLevel(String value) {
		if (value == null) {
			return java.util.logging.Level.SEVERE;
		}
		String v = value.trim().toUpperCase();
		if (v.equals("OFF")) return java.util.logging.Level.OFF;
		if (v.equals("ERROR") || v.equals("SEVERE")) return java.util.logging.Level.SEVERE;
		if (v.equals("WARN") || v.equals("WARNING")) return java.util.logging.Level.WARNING;
		if (v.equals("INFO")) return java.util.logging.Level.INFO;
		if (v.equals("DEBUG") || v.equals("FINE")) return java.util.logging.Level.FINE;
		if (v.equals("TRACE") || v.equals("FINER")) return java.util.logging.Level.FINER;
		return java.util.logging.Level.SEVERE;
	}

	public void processFiles() {
		DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
		String configPath = null;
		if (!headless && EYESIS_CORRECTIONS.correctionsParameters.saveSettings) {
			configPath = EYESIS_CORRECTIONS.correctionsParameters.selectResultsDirectory(true, true);
			if (configPath == null) {
				String msg = "No results directory selected, command aborted";
				System.out.println("Warning: " + msg);
				IJ.showMessage("Warning", msg);
				return;
			}
			configPath += Prefs.getFileSeparator() + "autoconfig";
			try {
				saveTimestampedProperties(configPath, // full path or null
						null, // use as default directory if path==null
						true, PROPERTIES);
			} catch (Exception e) {
				String msg = "Failed to save configuration to " + configPath + ", command aborted";
				System.out.println("Error: " + msg);
				IJ.showMessage("Error", msg);
				return;
			}
		}
		EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);
		int numChannels = EYESIS_CORRECTIONS.getNumChannels();
		NONLIN_PARAMETERS.modifyNumChannels(numChannels);
		CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);

		if (CORRECTION_PARAMETERS.deconvolve && (NONLIN_PARAMETERS.noiseGainPower != 0)) {
			EYESIS_CORRECTIONS.updateImageNoiseGains(NONLIN_PARAMETERS, // EyesisCorrectionParameters.NonlinParameters
																		// nonlinParameters,
					CONVOLVE_FFT_SIZE, // int fftSize, // 128 - fft size, kernel size should be size/2
					THREADS_MAX, // int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // boolean updateStatus,
					DEBUG_LEVEL); // int globalDebugLevel){
		}

		EYESIS_CORRECTIONS.processChannelImages(SPLIT_PARAMETERS, // EyesisCorrectionParameters.SplitParameters
																	// splitParameters,
				DEBAYER_PARAMETERS, // EyesisCorrectionParameters.DebayerParameters debayerParameters,
				NONLIN_PARAMETERS, // EyesisCorrectionParameters.NonlinParameters nonlinParameters,
				COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
				CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
				RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
				EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
											// equirectangularParameters,
				CONVOLVE_FFT_SIZE, // int convolveFFTSize, // 128 - fft size, kernel size should be size/2
				THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
				UPDATE_STATUS, // final boolean updateStatus,
				DEBUG_LEVEL); // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
	}

	public void runMenuCommand(String label) {
		MCP_COMMAND_CONTEXT = this.commandFromMcp;
		try {
		int i, j; // ,l,iq;
//    String label = e.getActionCommand();
		Runtime runtime = Runtime.getRuntime();
		runtime.gc();
		if (DEBUG_LEVEL > 0)
			System.out.println("--- Free memory=" + runtime.freeMemory() + " (of " + runtime.totalMemory() + ")");
		CLT_PARAMETERS.batch_run = false;
		if (label == null)
			return;
		if (label.equals("Exit")) {
			if (DEBUG_LEVEL > -3) {
				System.out.println("Exit command received");
			}
			return;
		}
		if ((CLT_PARAMETERS != null) && (CLT_PARAMETERS.lwir != null)) {
			String LOG_LEVEL;
			switch (CLT_PARAMETERS.lwir.getDebugLevel()) {
			case -2:
				LOG_LEVEL = "FATAL";
				break;
			case -1:
				LOG_LEVEL = "ERROR";
				break;
			case 0:
				LOG_LEVEL = "WARN";
				break;
			case 1:
				LOG_LEVEL = "INFO";
				break;
			case 2:
				LOG_LEVEL = "DEBUG";
				break;
			default:
				LOG_LEVEL = "OFF";
			}
//			LOG_LEVEL = "ERROR"; // setting it here to shut up?
//			LOG_LEVEL = "WARN"; // setting it here to shut up? works
			// codex 2026-01-25: keep 3rd-party logs quiet, adjust only our package
			applySelectiveLogLevels(LOG_LEVEL);
			/*
			boolean LOG_LEVEL_SET = loci.common.DebugTools.enableLogging(LOG_LEVEL);
			if (!LOG_LEVEL_SET) { // only first time true
				loci.common.DebugTools.setRootLevel(LOG_LEVEL);
			}
			*/
			System.out.println("DEBUG_LEVEL = " + DEBUG_LEVEL + ", CLT_PARAMETERS.lwir.getDebugLevel() = "
					+ CLT_PARAMETERS.lwir.getDebugLevel() + " LOG_LEVEL=" + LOG_LEVEL); //  + " LOG_LEVEL_SET="
//					+ LOG_LEVEL_SET);
			//https://www.javadoc.io/doc/org.openmicroscopy/ome-common/6.0.5/loci/common/DebugTools.html#enableLogging(java.lang.String)
		}

		/* ======================================================================== */
		if (label.equals("Configure spilt")) {
			showSplitBayerToStackDialog(SPLIT_PARAMETERS);
			return;
		}
		/* ======================================================================== */
		if (label.equals("Configure demosaic")) {
			showDeBayerDialog(DEBAYER_PARAMETERS, PROCESS_PARAMETERS);
			return;
		}
		/* ======================================================================== */
		if (label.equals("Configure convolution")) {
			showStackConvolutionDialog();
			return;
		}
		/* ======================================================================== */
		if (label.equals("Configure denoise")) {
			showCombinePairDialog(NONLIN_PARAMETERS, PROCESS_PARAMETERS);
			return;
		}
		/* ======================================================================== */
		if (label.equals("Configure color")) {
			COLOR_PROC_PARAMETERS.showColorProcessDialog();
			return;
		}
		/* ======================================================================== */
		if (label.equals("Configure AUX color")) {
			if (COLOR_PROC_PARAMETERS_AUX == null) {
				COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
			}
			COLOR_PROC_PARAMETERS_AUX.showColorProcessDialog(COLOR_PROC_PARAMETERS);
			return;
		}
		/* ======================================================================== */
//    if (label.equals("Channel gains")) {
//    	showColorCalibDialog(COLOR_CALIB_PARAMETERS);
		// return;
		// }
		/* ======================================================================== */
		if (label.equals("Configure RGB")) {
			showRGBProcessDialog(RGB_PARAMETERS);
			return;
		}

//
		/* ======================================================================== */
		if (label.equals("Split Image")) {
			if (!showSplitBayerToStackDialog(SPLIT_PARAMETERS))
				return;
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_src = WindowManager.getCurrentImage();
			if (imp_src == null) {
				IJ.showMessage("Error", "Bayer Image required");
				return;
			}
			ImageStack sourceStack = bayerToStack(imp_src, // source Bayer image, linearized, 32-bit (float))
					SPLIT_PARAMETERS);
			ImagePlus imp_srcStack = new ImagePlus(imp_src.getTitle() + "-EXP", sourceStack);
			imp_srcStack.getProcessor().resetMinAndMax();
			imp_srcStack.show();
			return;
			/* ======================================================================== */
		} else if (label.equals("Debayer Image")) {
			if (!showDeBayerDialog(DEBAYER_PARAMETERS, PROCESS_PARAMETERS))
				return;
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_src = WindowManager.getCurrentImage();
			if ((imp_src == null) || (imp_src.getStackSize() < 3)) {
				IJ.showMessage("Error", "Bayer image stack required");
				return;
			}

			ImageStack imageStack = aliasScissorsStack(imp_src.getStack(), // stack with 3 colors/slices with the image
					DEBAYER_PARAMETERS, PROCESS_PARAMETERS.showDebayerEnergy, THREADS_MAX, // number of image pixels/
																							// sensor pixels (each
																							// direction) == 2
					UPDATE_STATUS);// update status info
			if (PROCESS_PARAMETERS.showDebayerEnergy) {
				ShowDoubleFloatArrays.showArrays(DEBAYER_ENERGY, DEBAYER_ENERGY_WIDTH,
						DEBAYER_ENERGY.length / DEBAYER_ENERGY_WIDTH, "Debayer-Energy");
			}

			ImagePlus imp_debyrStack = new ImagePlus(imp_src.getTitle() + "-DeBayer", imageStack);
			imp_debyrStack.getProcessor().resetMinAndMax();
			imp_debyrStack.show();
			return;

			/* ======================================================================== */
		} else if (label.equals("Select kernel stack")) {
//      int loop_debug_level=1;
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_kernels = WindowManager.getCurrentImage();
			if (imp_kernels == null) {
				IJ.showMessage("Error", "There is no kernel stack to process");
				return;
			}
			if (imp_kernels.getStackSize() < 3) {
				IJ.showMessage("Error", "Need a 3-layer stack with kernels");
				return;
			}
			convolutionKernelStack = imp_kernels.getStack();
			System.out.println("Select kernel stack " + imp_kernels.getTitle());
			return;

			/* ======================================================================== */
		} else if (label.equals("Convolve with stack")) {
			if (convolutionKernelStack == null) {
				IJ.showMessage("Error",
						"Convolution kernel stack needed, please select one with 'Select kernel stack' command");
				return;
			}
//      int loop_debug_level=1;
			if (!showStackConvolutionDialog())
				return;
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_src = WindowManager.getCurrentImage();
			if (imp_src == null) {
				IJ.showMessage("Error", "There is no kernel stack to process");
				return;
			}
			if (imp_src.getStackSize() < 3) {
				IJ.showMessage("Error", "Need a 3-layer stack with kernels");
				return;
			}
			ImageStack convolvedStack = convolveStackWithKernelStack(imp_src.getStack(), // stack with 3 colors/slices
																							// with the image
					convolutionKernelStack, // stack with 3 colors/slices convolution kernels
					CONVOLVE_FFT_SIZE, // 128 - fft size, kernel size should be size/2
					THREADS_MAX, UPDATE_STATUS); // update status info

			ImagePlus imp_convolvedStack = new ImagePlus(imp_src.getTitle() + "-convolved", convolvedStack);
			imp_convolvedStack.getProcessor().resetMinAndMax();
			imp_convolvedStack.show();
			return;
			/* ======================================================================== */
		} else if (label.equals("Select lo-res")) {
			imp_gaussian = WindowManager.getCurrentImage();
			if ((imp_gaussian == null) || (imp_gaussian.getStackSize() < 3)) {
				IJ.showMessage("Error",
						"Please select image stack of 3 (float) slices (r,g,b), resulted from convolving input bayer image with Gaussian kernels");
				return;
			}
			if (DEBUG_LEVEL > 1)
				System.out.println(
						"Read image " + imp_gaussian.getTitle() + " as the image convolved with Gaussian kernels");

			return;
			/* ======================================================================== */
		} else if (label.equals("Combine pair")) {
			if ((imp_gaussian == null) || (imp_gaussian.getStackSize() < 3)) {
				IJ.showMessage("Error",
						"Wrong/empty Gaussian convolved image, please use 'Read Gaussian' command first");
				return;
			}
			ImagePlus imp_convolved = WindowManager.getCurrentImage();
			if ((imp_convolved == null) || (imp_convolved.getStackSize() < 3)) {
				IJ.showMessage("Error",
						"Please select image stack of 3 (float) slices (r,g,b), resulted from convolving input bayer image with the reversed PSF kernels");
				return;
			}
			if (!showCombinePairDialog(NONLIN_PARAMETERS, PROCESS_PARAMETERS))
				return;
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (DEBUG_LEVEL > 1)
				System.out.println("Read image " + imp_convolved.getTitle()
						+ " as the image convolved with the reversed PSF kernels");
			NONLIN_PARAMETERS.showMask = PROCESS_PARAMETERS.showDenoiseMask;
			ImageStack stack_combo = combineLoHiStacks(imp_convolved.getStack(), // ImageStack with the image, convolved
																					// with the reversed PSF (sharp but
																					// with high noise)
					imp_gaussian.getStack(), // ImageStack with the image, convolved with the Gaussian (just lateral
												// compensated) (blurred, but low noise)
					-1, // do not scale filtMin and filtMax for individual channel/
					-1, NONLIN_PARAMETERS, // show mask generated and used
					null, // noiseMask, // 2-d array of kernelsNoiseGain (divide mask by it)
					32, // noiseStep, // linear pixels per noiseMask pixels (32)

					THREADS_MAX, UPDATE_STATUS); // update status info

			if (PROCESS_PARAMETERS.showDenoiseMask) {
				ShowDoubleFloatArrays.showArrays(DENOISE_MASK, DENOISE_MASK_WIDTH, DENOISE_MASK.length / DENOISE_MASK_WIDTH,
						"mask");
			}

			ImagePlus imp_stack_combo = new ImagePlus(imp_convolved.getTitle() + "combo-rgb", stack_combo);
			imp_stack_combo.getProcessor().resetMinAndMax();
			imp_stack_combo.show();
			return;
			/* ======================================================================== */
		} else if (label.equals("Colors")) {

			ImagePlus imp_convolved = WindowManager.getCurrentImage();
			if ((imp_convolved == null) || (imp_convolved.getStackSize() < 3)) {
				IJ.showMessage("Error", "Please select image stack of 5 (float) slices (r,g,b) and 2 weight slices");
				return;
			}
			if (!COLOR_PROC_PARAMETERS.showColorProcessDialog())
				return;

			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (DEBUG_LEVEL > 1)
				System.out.println("Read image " + imp_convolved.getTitle()
						+ " as the image convolved with the reversed PSF kernels");
			ImageStack stack_convolved = imp_convolved.getStack();

			/* Set stack sequence to r-g-b */
// public static String [] stackColorNames={"red","green","blue"};

			/*
			 * find number of the green channel - should be called "green", if none - use
			 * last
			 */
			if (!fixSliceSequence(stack_convolved)) {
				return;
			}
			processColorsWeights(stack_convolved, 255.0 / PSF_SUBPIXEL_SHOULD_BE_4 / PSF_SUBPIXEL_SHOULD_BE_4,
					COLOR_PROC_PARAMETERS, null, // no channel calibration
					0, 0);

//COLOR_PROC_PARAMETERS.useFirstY
			YPrPbToRGB(stack_convolved, COLOR_PROC_PARAMETERS.kr, // 0.299;
					COLOR_PROC_PARAMETERS.kb, // 0.114;
					COLOR_PROC_PARAMETERS.useFirstY ? 9 : 8, // int sliceY,
					6, // int slicePr,
					7// int slicePb
			);

			imp_convolved.getProcessor().resetMinAndMax();
			imp_convolved.updateAndDraw();

			ShowDoubleFloatArrays.showImageStackThree(stack_convolved, imp_convolved.getTitle() + "restored_rgb");
			return;
			/* ======================================================================== */
		} else if (label.equals("Test")) {
			IJ.showMessage("Error", "Nothing here, just a place holder");
			return;
			/* ======================================================================== */
		} else if (label.equals("Test Debayer")) {

			/** TODO - use stacks? Parameter? */

			ImagePlus imp_debayer = WindowManager.getCurrentImage();
			if (imp_debayer == null) {
				IJ.showMessage("Error", "Please select monochrome 32-bit image");
				return;
			}
			if (!showDeBayerDialog(DEBAYER_PARAMETERS, PROCESS_PARAMETERS))
				return;
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if ((imp_debayer.getWidth() < DEBAYER_PARAMETERS.size)
					|| (imp_debayer.getHeight() < DEBAYER_PARAMETERS.size)) {
				IJ.showMessage("Error", "Please select monochrome image at least " + DEBAYER_PARAMETERS.size + "x"
						+ DEBAYER_PARAMETERS.size + " pixels");
				return;
			}
			Roi roi_debayer = imp_debayer.getRoi();
			if (roi_debayer == null) {
				imp_debayer.setRoi(0, 0, imp_debayer.getWidth(), imp_debayer.getHeight());
				roi_debayer = imp_debayer.getRoi();
			}
			Rectangle rroi = roi_debayer.getBounds();
			/* center to the selection center */
			rroi.x = (rroi.x + rroi.width / 2) - (DEBAYER_PARAMETERS.size / 2);
			rroi.y = (rroi.y + rroi.height / 2) - (DEBAYER_PARAMETERS.size / 2);
			rroi.width = DEBAYER_PARAMETERS.size;
			rroi.height = DEBAYER_PARAMETERS.size;
			if (rroi.x < 0)
				rroi.x = 0;
			else if (rroi.x > (imp_debayer.getWidth() - DEBAYER_PARAMETERS.size))
				rroi.x = imp_debayer.getWidth() - DEBAYER_PARAMETERS.size;
			if (rroi.y < 0)
				rroi.y = 0;
			else if (rroi.y > (imp_debayer.getHeight() - DEBAYER_PARAMETERS.size))
				rroi.y = imp_debayer.getHeight() - DEBAYER_PARAMETERS.size;
			float[] fpixels_debayer = (float[]) imp_debayer.getProcessor().getPixels();
			double[][] pixels_debayer = new double[3][];
			pixels_debayer[0] = new double[DEBAYER_PARAMETERS.size * DEBAYER_PARAMETERS.size];
			int debayer_width = imp_debayer.getWidth();
			int debayer_base = rroi.y * debayer_width + rroi.x;
			;
			for (i = 0; i < DEBAYER_PARAMETERS.size; i++)
				for (j = 0; j < DEBAYER_PARAMETERS.size; j++)
					pixels_debayer[0][i * DEBAYER_PARAMETERS.size + j] = fpixels_debayer[debayer_base
							+ i * debayer_width + j];
			pixels_debayer[1] = pixels_debayer[0].clone();
			pixels_debayer[2] = pixels_debayer[0].clone();
			ShowDoubleFloatArrays.showArrays(pixels_debayer[0], "original");

			pixels_debayer = normalizeAndWindow(pixels_debayer,
					initWindowFunction((int) Math.sqrt(pixels_debayer[1].length)), false);
			pixels_debayer = extendFFTInputTo(pixels_debayer, DEBAYER_PARAMETERS.size);
			if (DEBUG_LEVEL > 2)
				ShowDoubleFloatArrays.showArrays(pixels_debayer[0], "wnd-orig");
			for (i = 0; i < DEBAYER_PARAMETERS.size; i++)
				for (j = 0; j < DEBAYER_PARAMETERS.size; j++) {
					pixels_debayer[1][i * DEBAYER_PARAMETERS.size
							+ j] *= ((((i % PSF_SUBPIXEL_SHOULD_BE_4) == 0) && ((j % PSF_SUBPIXEL_SHOULD_BE_4) == 0))
									|| (((i % PSF_SUBPIXEL_SHOULD_BE_4) == (PSF_SUBPIXEL_SHOULD_BE_4 / 2))
											&& ((j % PSF_SUBPIXEL_SHOULD_BE_4) == (PSF_SUBPIXEL_SHOULD_BE_4 / 2))))
													? 1.0
													: 0.0;
					pixels_debayer[0][i * DEBAYER_PARAMETERS.size + j] *= (((i % PSF_SUBPIXEL_SHOULD_BE_4) == 0)
							&& ((j % PSF_SUBPIXEL_SHOULD_BE_4) == (PSF_SUBPIXEL_SHOULD_BE_4 / 2))) ? 1.0 : 0.0;
					pixels_debayer[2][i * DEBAYER_PARAMETERS.size
							+ j] *= (((i % PSF_SUBPIXEL_SHOULD_BE_4) == (PSF_SUBPIXEL_SHOULD_BE_4 / 2))
									&& ((j % PSF_SUBPIXEL_SHOULD_BE_4) == 0)) ? 1.0 : 0.0;
				}
			if (DEBUG_LEVEL > 1)
				ShowDoubleFloatArrays.showArrays(pixels_debayer, DEBAYER_PARAMETERS.debayerStacks, "mosaic");
			/* swap quadrants and perform FHT */
			double[][] amps = null;
			if (DEBUG_LEVEL > 1)
				amps = new double[3][];
			for (i = 0; i < 3; i++) {
				FHT_INSTANCE.swapQuadrants(pixels_debayer[i]);
				FHT_INSTANCE.transform(pixels_debayer[i]);
				if (amps != null)
					amps[i] = FHT_INSTANCE.calculateAmplitude(pixels_debayer[i]);

			}
			DeBayerScissors debayer_instance = new DeBayerScissors(DEBAYER_PARAMETERS.size, // size of the square array,
																							// center is at size/2,
																							// size/2, only top
																							// half+line will be used
					DEBAYER_PARAMETERS.polarStep, // maximal step in pixels on the maxRadius for 1 angular step (i.e.
													// 0.5)
					DEBAYER_PARAMETERS.debayerRelativeWidthGreen, // result green mask mpy by scaled default (diamond)
					DEBAYER_PARAMETERS.debayerRelativeWidthRedblue, // result red/blue mask mpy by scaled default
																	// (square)
					DEBAYER_PARAMETERS.debayerRelativeWidthRedblueMain, // green mask when applied to red/blue, main
																		// (center)
					DEBAYER_PARAMETERS.debayerRelativeWidthRedblueClones); // green mask when applied to red/blue,
																			// clones

			double[][] both_masks = debayer_instance.aliasScissors(pixels_debayer[1], // fht array for green, will be
																						// masked in-place
					DEBAYER_PARAMETERS.debayerThreshold, // no high frequencies - use default uniform filter
					DEBAYER_PARAMETERS.debayerGamma, // power function applied to the amplitudes before generating
														// spectral masks
					DEBAYER_PARAMETERS.debayerBonus, // for green mask - rays start radius from center, relative to
														// distance to the first alias
					DEBAYER_PARAMETERS.mainToAlias, // relative main/alias amplitudes to enable lixels (i.e. 0.5 means
													// that if alias is >0.5*main, the pixel will be masked out)
					DEBAYER_PARAMETERS.debayerMaskBlur, // for both masks sigma for gaussian blur of the binary masks
														// (<0 -do not use "scissors")
					DEBAYER_PARAMETERS.debayerUseScissors, // use "scissors", if false - just apply "diamond" ands
															// "square" with
															// DEBAYER_PARAMETERS.debayerRelativeWidthGreen and
															// DEBAYER_PARAMETERS.debayerRelativeWidthRedblue
					DEBUG_LEVEL); // internal debug level

			double[] green_mask = both_masks[0];
			double[] red_blue_mask = both_masks[1];

			pixels_debayer[1] = FHT_INSTANCE.multiply(pixels_debayer[1], green_mask, false);
			pixels_debayer[0] = FHT_INSTANCE.multiply(pixels_debayer[0], red_blue_mask, false);
			pixels_debayer[2] = FHT_INSTANCE.multiply(pixels_debayer[2], red_blue_mask, false);

			FHT_INSTANCE.inverseTransform(pixels_debayer[1]);
			FHT_INSTANCE.swapQuadrants(pixels_debayer[1]);

			FHT_INSTANCE.inverseTransform(pixels_debayer[0]);
			FHT_INSTANCE.swapQuadrants(pixels_debayer[0]);

			FHT_INSTANCE.inverseTransform(pixels_debayer[2]);
			FHT_INSTANCE.swapQuadrants(pixels_debayer[2]);

			ShowDoubleFloatArrays.showArrays(pixels_debayer, DEBAYER_PARAMETERS.debayerStacks, "filtered");

			/* Swap quadrants in masks and display them */
			if (DEBUG_LEVEL > 1) {
				FHT_INSTANCE.swapQuadrants(green_mask);
				FHT_INSTANCE.swapQuadrants(red_blue_mask);
				ShowDoubleFloatArrays.showArrays(green_mask, "G-mask");
				ShowDoubleFloatArrays.showArrays(red_blue_mask, "RB-mask");
				if (amps != null) {
					/** normalize amplitudes, apply gamma */
					double dmax = 0.0;
					for (i = 0; i < amps.length; i++) {
						for (j = 0; j < amps[i].length; j++)
							if (amps[i][j] > dmax)
								dmax = amps[i][j];
						dmax = 1.0 / dmax;
						for (j = 0; j < amps[i].length; j++)
							amps[i][j] = Math.pow(amps[i][j] * dmax, DEBAYER_PARAMETERS.debayerGamma);
					}
					ShowDoubleFloatArrays.showArrays(amps, DEBAYER_PARAMETERS.debayerStacks, "PWR");
					for (i = 0; i < amps[1].length; i++) {
						amps[1][i] *= green_mask[i];
						amps[0][i] *= red_blue_mask[i];
						amps[2][i] *= red_blue_mask[i];
					}
					ShowDoubleFloatArrays.showArrays(amps, DEBAYER_PARAMETERS.debayerStacks, "PWR-MSK");
				}
			}
			return;
			/* ======================================================================== */

		} else if (label.equals("Save") || label.equals("Save Clean")) {
			// If saved just after restore, QUAD_CLT==null and QUAD_CLT_AUX==null and are
			// not saved
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			if (QUAD_CLT_AUX == null) {
				if (EYESIS_CORRECTIONS_AUX == null) {
					EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
							CORRECTION_PARAMETERS.getAux());
				}
				QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
						CORRECTION_PARAMETERS.getAux());
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels for aux camera");
				}
			}
			if (label.equals("Save Clean")) {
				PROPERTIES = new Properties();
			}
			saveProperties(
					null,
					CORRECTION_PARAMETERS.resultsDirectory,
					true,
					PROPERTIES,
					DEBUG_LEVEL);
			return;
			/* ======================================================================== */
		} else if (label.equals("Save offset")) {

			saveInfinityOffsets(null, CORRECTION_PARAMETERS.resultsDirectory);
			return;
			/* ======================================================================== */
		} else if (label.equals("Restore")) {
			String path = loadProperties(null, CORRECTION_PARAMETERS.resultsDirectory, true, PROPERTIES);
			if (path != null) {
				getAllProperties(PROPERTIES);
				if (DEBUG_LEVEL > -3)
					System.out.println("Configuration parameters are restored from " + path);
			} else {
				if (DEBUG_LEVEL > -10)
					System.out.println("Failed to restore configuration parameters");
			}
			return;
			/* ======================================================================== */
		} else if (label.equals("RGB")) {
			ImagePlus imp_colorStack = WindowManager.getCurrentImage();
			if ((imp_colorStack == null) || (imp_colorStack.getStackSize() < 3)) {
				IJ.showMessage("Error", "Please select image stack of 3 (float) slices (r,g,b)");
				return;
			}

			if (!showRGBProcessDialog(RGB_PARAMETERS))
				return;

			ImageStack stack = imp_colorStack.getStack();
			fixSliceSequence(stack);
			ImageStack stack_crop = cropStack32(stack, SPLIT_PARAMETERS);
			ImageStack stack_rot = rotateStack32CW(stack_crop);
			ImageStack stack16 = convertRGB32toRGB16Stack(stack_rot, RGB_PARAMETERS);
			ImagePlus imp_stack16 = new ImagePlus(imp_colorStack.getTitle() + "-16b", stack16);
			CompositeImage compositeImage = convertToComposite(imp_stack16);
			compositeImage.show();
			ImagePlus imp_RGB24 = convertRGB48toRGB24(stack16, imp_colorStack.getTitle() + "-RGB24", 0, 65536, // r
																												// range
																												// 0->0,
																												// 65536->256
					0, 65536, // g range
					0, 65536, // b range
					0, 65536);// alpha range
			imp_RGB24.setTitle(imp_colorStack.getTitle() + "rgb24");
			imp_RGB24.show();
			return;
			/* ======================================================================== */
		} else if (label.equals("Configure correction")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CORRECTION_PARAMETERS.showJDialog("Correction parameters");
			return;

			/* ======================================================================== */
		} else if (label.equals("Channel gains/colors")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CHANNEL_GAINS_PARAMETERS.showDialog(CHANNEL_GAINS_PARAMETERS_AUX); // will only show
																				// CHANNEL_GAINS_PARAMETERS_AUX tab if
																				// used (non-null)
			return;
			/* ======================================================================== */

		} else if (label.equals("Configure warping")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (!EQUIRECTANGULAR_PARAMETERS.showDialog())
				return;
			if (EQUIRECTANGULAR_PARAMETERS.isNeedRebuildSet()) {
				System.out.println("rebuilding equirectangular map");
				EYESIS_CORRECTIONS.rebuildEquirectangularMaps(EQUIRECTANGULAR_PARAMETERS, THREADS_MAX, // int
																										// threadsMax,
																										// // maximal
																										// number of
																										// threads to
																										// launch
						UPDATE_STATUS, // boolean updateStatus,
						DEBUG_LEVEL); // int globalDebugLevel){
			}
			return;
			/* ======================================================================== */
		} else if (label.equals("Select source files")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CORRECTION_PARAMETERS.selectSourceFiles(false, // boolean allFiles,
					DEBUG_LEVEL); // int debugLevel
			return;
			/* ======================================================================== */
		} else if (label.equals("Select source sets")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CORRECTION_PARAMETERS.selectSourceSets(
//    			false, // boolean allFiles,
					DEBUG_LEVEL); // int debugLevel
			return;

//
			/* ======================================================================== */
		} else if (label.equals("Process files")) {
			processFiles();
			return;
			/* ======================================================================== */
		} else if (label.equals("Tiff Writer")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
//			EyesisTiff eyesisTiff = new EyesisTiff();
			ImagePlus imp_sel = WindowManager.getCurrentImage();
			if (imp_sel == null) {
				IJ.showMessage("Error", "No images selected");
				return;
			}
			String path = CORRECTION_PARAMETERS.resultsDirectory + Prefs.getFileSeparator() + imp_sel.getTitle()
					+ ".eyesis-tiff";
			if (CORRECTION_PARAMETERS.equirectangularFormat > 3) {
				IJ.showMessage("Error", "For ImageJ stack use \"Save As Tiff\" menu command");
				return;
			}
			try {
				try {
					EyesisTiff.saveTiff(imp_sel, path, CORRECTION_PARAMETERS.equirectangularFormat,
							(CORRECTION_PARAMETERS.equirectangularFormat == 3) ? CORRECTION_PARAMETERS.outputRangeFP
									: CORRECTION_PARAMETERS.outputRangeInt,
							CORRECTION_PARAMETERS.imageJTags, DEBUG_LEVEL);
				} catch (ServiceException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (DependencyException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} catch (IOException e) {
				e.printStackTrace();
			} catch (FormatException e) {
				e.printStackTrace();
			}
			return;
			/* ======================================================================== */
		} else if (label.equals("Tiff Properties")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_sel = WindowManager.getCurrentImage();
			if (imp_sel == null) {
				IJ.showMessage("Error", "No images selected");
				return;
			}
			String orig_path = imp_sel.getOriginalFileInfo().getFilePath();
			try {
				DumpImageMetadata.processFile(new File(orig_path));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
//			EyesisTiff.propertiesTiff(imp_sel);
			return;
			/* ======================================================================== */
		} else if (label.equals("Add TIFF resolution")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_sel = WindowManager.getCurrentImage();
			if (imp_sel == null) {
				IJ.showMessage("Error", "No images selected");
				return;
			}
			String orig_path = imp_sel.getOriginalFileInfo().getFilePath();
			// Testing change image resolution
			File inputFile = new File(orig_path);
			IIOMetadata inMetadata = null;
			try {
				inMetadata = DumpImageMetadata.getFileMetadata(inputFile);
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
				return;
			}
			
			double resolutionDPI = 600;
			File outputFile = new File(orig_path+"_"+((int)resolutionDPI)+"-01.tiff");
			BufferedImage image = null;
			try {
				image = ChangeImageResolution.readImage(inputFile); // wrong imageType 0 (should be 2)
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return;
			}
			IIOMetadataNode newMetadata = ChangeImageResolution.createResolutionMetadata(resolutionDPI);
//			IIOMetadataNode addMetadata = ChangeImageResolution.createResolutionMetadata0(resolutionDPI);
//			inMetaData.appendChild()
			try {
				inMetadata.mergeTree(IIOMetadataFormatImpl.standardMetadataFormatName, newMetadata);
			} catch (IIOInvalidTreeException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
				return;
			}
			
			
			
			Node metadata_as_tree = inMetadata.getAsTree("javax_imageio_tiff_image_1.0");
		    BufferedImage bufferedImageAlpha = new BufferedImage(
		    		image.getWidth(),
		    		image.getHeight(),
		    		BufferedImage.TYPE_INT_ARGB
		    		);
		    ImageWriter tiffWriter = ImageIO.getImageWritersByFormatName("tiff").next();
		    ImageWriteParam tiffWriteParam = tiffWriter.getDefaultWriteParam();
		    tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
		    ImageTypeSpecifier imageType = ImageTypeSpecifier.createFromRenderedImage(bufferedImageAlpha);            
		    IIOMetadata imageMetadata = tiffWriter.getDefaultImageMetadata(imageType, tiffWriteParam);
		    TIFFDirectory directory = null;
		    try {
				directory = TIFFDirectory.createFromMetadata(imageMetadata);
			} catch (IIOInvalidTreeException e2) {
				// TODO Auto-generated catch block
				e2.printStackTrace();
			}
///		    TIFFTagSet tag_set = ExifGPSTagSet.getInstance();	    
///		    directory.addTagSet(tag_set); // ExifGPSTagSet.getInstance());
////	    imageMetadata = directory.getAsMetadata();
		    
		    System.out.println("\n=== metadata_as_tree ===:");
		    DumpImageMetadata.displayMetadataNode(metadata_as_tree, 1);
		    System.out.println("\n=== imageMetadata as javax_imageio_tiff_image_1.0 ===:");
		    DumpImageMetadata.displayMetadataNode(imageMetadata.getAsTree("javax_imageio_tiff_image_1.0"), 1);
		    
			try {
//				imageMetadata.mergeTree(IIOMetadataFormatImpl.standardMetadataFormatName, metadata_as_tree);
				imageMetadata.mergeTree("javax_imageio_tiff_image_1.0", metadata_as_tree);
			} catch (IIOInvalidTreeException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
				return;
			}
			
		    System.out.println("\n=== imageMetadata merged as javax_imageio_tiff_image_1.0 ===:");
		    DumpImageMetadata.displayMetadataNode(imageMetadata.getAsTree("javax_imageio_tiff_image_1.0"), 1);
			
			// works with inMetadata. Will it with imageMetadata?
			try {
				ChangeImageResolution.writeImage(outputFile, image, imageMetadata); // inMetadata); // newMetadata);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return;
			/* ======================================================================== */
		} else if (label.equals("Configure PP")) {
			POST_PROCESSING.postProcessingParameters.showDialog("Configure processing parameters");
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			return;
			/* ======================================================================== */
		} else if (label.equals("Source PP")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			POST_PROCESSING.postProcessingParameters.selectSourceFiles(false, // boolean allFiles,
					DEBUG_LEVEL); // int debugLevel
			return;
			/* ======================================================================== */
		} else if (label.equals("ConvertPP")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			POST_PROCESSING.convertSourceFiles(DEBUG_LEVEL); // int debugLevel
			return;
			/* ======================================================================== */
		} else if (label.equals("Linear Features")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (!POST_PROCESSING.linearFeaturesParameters.showDialog("Configure linear features parameters"))
				return;
			POST_PROCESSING.linearFeatures(THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					DEBUG_LEVEL); // int debugLevel

			return;
			/* ======================================================================== */
		} else if (label.equals("Intercam correlations")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (POST_PROCESSING.interSensor == null) {
				System.out.println("POST_PROCESSING.interSensor==null");
				return;
			}
			double[][] disparityScales = POST_PROCESSING.interSensor.getDisparityScales();
			if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure correlation parameters",
					disparityScales, 1 + 4 + 8 + 16 + 32, POST_PROCESSING.interSensor.mapWidth,
					POST_PROCESSING.interSensor.mapHeight))
				return;
			// read external data
			String externalTitle = null;
			double[][] externalData = null;
			if (POST_PROCESSING.disparityCorrelationParameters.useFileData) {
				String[] patterns = { ".lin-tiff", ".tiff", ".tif" };
				if (DEBUG_LEVEL > 1)
					System.out.println("Suggesting " + POST_PROCESSING.disparityCorrelationParameters.dataPathName);

				String path = selectFile(true, // smart
						false, // save
						"External file with per-image slice (i.e. linear features)", // title
						"Select external image file", // button
						new MultipleExtensionsFileFilter(patterns, patterns[0] + " files"), // filter
						POST_PROCESSING.disparityCorrelationParameters.dataPathName);
				if ((path != null) && (path.length() > 0)) {
					Opener opener = new Opener();
					ImagePlus imp = opener.openImage("", path);
					if (imp == null) {
						String msg = "Failed to read external image file " + path;
						IJ.showMessage("Error", msg);
						System.out.println(msg);
						throw new IllegalArgumentException(msg);
					}
					POST_PROCESSING.disparityCorrelationParameters.dataPathName = path;
					externalTitle = imp.getTitle(); // TODO - get rid of extension?
					// Prefs.getFileSeparator()
					int firstIndex = externalTitle.lastIndexOf(Prefs.getFileSeparator()) + 1;
					int dotIndex = externalTitle.lastIndexOf(".");
					if (dotIndex < 0)
						dotIndex = externalTitle.length();
					externalTitle = externalTitle.substring(firstIndex, dotIndex);
					float[][] fpixels = new float[imp.getStack().getSize()][];
					for (int si = 0; si < fpixels.length; si++)
						fpixels[si] = (float[]) imp.getStack().getPixels(si + 1);
					externalData = new double[fpixels.length][];
					for (int si = 0; si < fpixels.length; si++) {
						externalData[si] = new double[fpixels[si].length];
						for (int pi = 0; pi < fpixels[si].length; pi++)
							externalData[si][pi] = fpixels[si][pi];
					}
				}
			}
			boolean doubleSizeOutput = true;
			POST_PROCESSING.matchTest(POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineTilePeriod,
					POST_PROCESSING.disparityCorrelationParameters.refinePhaseCoeff,
					POST_PROCESSING.disparityCorrelationParameters.refineHighPassSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineLowPassSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineCorrMaxDistance,
					POST_PROCESSING.disparityCorrelationParameters.refineCorrThreshold,
					POST_PROCESSING.disparityCorrelationParameters.refineSubPixel, externalData,
					POST_PROCESSING.disparityCorrelationParameters.corrShift,
					POST_PROCESSING.disparityCorrelationParameters.corrXC,
					POST_PROCESSING.disparityCorrelationParameters.corrYC,
					POST_PROCESSING.disparityCorrelationParameters.channelMask,
					POST_PROCESSING.disparityCorrelationParameters.firstImage,
					POST_PROCESSING.disparityCorrelationParameters.secondImage, doubleSizeOutput,
					POST_PROCESSING.disparityCorrelationParameters.corrFFTSize
							/ POST_PROCESSING.disparityCorrelationParameters.tileOverlapFraction,
					DEBUG_LEVEL); // int debugLevel
			return;
			/* ======================================================================== */
		} else if (label.equals("Intercam correlations OLD")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (POST_PROCESSING.interSensor == null) {
				System.out.println("POST_PROCESSING.interSensor==null");
				return;
			}
			double[][] disparityScales = POST_PROCESSING.interSensor.getDisparityScales();
			if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure correlation parameters",
					disparityScales, 1, POST_PROCESSING.interSensor.mapWidth, POST_PROCESSING.interSensor.mapHeight))
				return;
//    	POST_PROCESSING.linearFeatures(
//        		THREADS_MAX, //final int          threadsMax,  // maximal number of threads to launch
//    			DEBUG_LEVEL); //int debugLevel
			double[][] correlation = POST_PROCESSING.correlationTest(
					POST_PROCESSING.disparityCorrelationParameters.corrShift,
					POST_PROCESSING.disparityCorrelationParameters.corrXC,
					POST_PROCESSING.disparityCorrelationParameters.corrYC, THREADS_MAX, // final int threadsMax, //
																						// maximal number of threads to
																						// launch
					DEBUG_LEVEL); // int debugLevel
			int numSensors = POST_PROCESSING.interSensor.channel.length;
			int numLayers = POST_PROCESSING.interSensor.overlapImages.length / numSensors;
			int numPairs = correlation.length / numLayers;
			String[] titles1 = { "Combo", "Y", "Cb", "Cr" };
			String[] titles = new String[correlation.length];
			for (int nPair = 0; nPair < numPairs; nPair++) {
				for (int nLayer = 0; nLayer < numLayers; nLayer++) {
					titles[nPair * numLayers + nLayer] = titles1[nLayer] + "_" + nPair;
				}

			}
			ShowDoubleFloatArrays.showArrays(correlation, POST_PROCESSING.disparityCorrelationParameters.corrFFTSize,
					POST_PROCESSING.disparityCorrelationParameters.corrFFTSize, true,
					"corr-x" + POST_PROCESSING.disparityCorrelationParameters.corrXC + "-y"
							+ POST_PROCESSING.disparityCorrelationParameters.corrYC + "-SHFT"
							+ POST_PROCESSING.disparityCorrelationParameters.corrShift + "-PC"
							+ (("" + POST_PROCESSING.disparityCorrelationParameters.corrPhaseFraction).replace('.',
									'_')),
					titles);
			int size = POST_PROCESSING.disparityCorrelationParameters.corrFFTSize;
			int length = size * size;
			double[][] corr_products = new double[numLayers][length];
			// int numPairs=correlation.length/numLayers;
			double pow = 1.0 / numLayers;
			for (int numLayer = 0; numLayer < numLayers; numLayer++) {
				for (int index = 0; index < length; index++) {
					corr_products[numLayer][index] = 1.0;
					for (int nPair = 0; nPair < numPairs; nPair++) {
						corr_products[numLayer][index] *= correlation[nPair * numLayers + numLayer][index];
					}
					corr_products[numLayer][index] = (corr_products[numLayer][index] > 0.0)
							? Math.pow(corr_products[numLayer][index], pow)
							: 0.0;
				}
			}
			ShowDoubleFloatArrays.showArrays(corr_products, POST_PROCESSING.disparityCorrelationParameters.corrFFTSize,
					POST_PROCESSING.disparityCorrelationParameters.corrFFTSize, true,
					"Prod" + numLayers + "-x" + POST_PROCESSING.disparityCorrelationParameters.corrXC + "-y"
							+ POST_PROCESSING.disparityCorrelationParameters.corrYC + "-SHFT"
							+ POST_PROCESSING.disparityCorrelationParameters.corrShift + "-PC"
							+ (("" + POST_PROCESSING.disparityCorrelationParameters.corrPhaseFraction).replace('.',
									'_')),
					titles1);

//		double [][] corr_average=new double [numLayers][length];

			for (int numLayer = 0; numLayer < numLayers; numLayer++) {
				for (int index = 0; index < length; index++) {
					corr_products[numLayer][index] = 0.0;
					for (int nPair = 0; nPair < numPairs; nPair++) {
						corr_products[numLayer][index] += correlation[nPair * numLayers + numLayer][index];
					}
					corr_products[numLayer][index] /= numLayers;
				}
			}
			ShowDoubleFloatArrays.showArrays(corr_products, POST_PROCESSING.disparityCorrelationParameters.corrFFTSize,
					POST_PROCESSING.disparityCorrelationParameters.corrFFTSize, true,
					"Average" + numLayers + "-x" + POST_PROCESSING.disparityCorrelationParameters.corrXC + "-y"
							+ POST_PROCESSING.disparityCorrelationParameters.corrYC + "-SHFT"
							+ POST_PROCESSING.disparityCorrelationParameters.corrShift + "-PC"
							+ (("" + POST_PROCESSING.disparityCorrelationParameters.corrPhaseFraction).replace('.',
									'_')),
					titles1);
			return;
			/* ======================================================================== */
		} else if (label.equals("Tile correlations")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (POST_PROCESSING.interSensor == null) {
				System.out.println("POST_PROCESSING.interSensor==null");
				return;
			}
			double[][] disparityScales = POST_PROCESSING.interSensor.getDisparityScales();
			if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure tile correlation parameters",
					disparityScales, 2 + 4 + 16, POST_PROCESSING.interSensor.mapWidth,
					POST_PROCESSING.interSensor.mapHeight))
				return;
			if (POST_PROCESSING.disparityCorrelationParameters.saveCorrelationToImage) {
				String configPath = POST_PROCESSING.postProcessingParameters.selectResultsDirectory(true, true);
				if (configPath == null) {
					String msg = "No results directory selected, command aborted";
					System.out.println("Warning: " + msg);
					IJ.showMessage("Warning", msg);
					return;
				}
				configPath += Prefs.getFileSeparator() + "autoconfig";
				try {
					saveTimestampedProperties(configPath, // full path or null
							null, // use as default directory if path==null
							true, PROPERTIES);

				} catch (Exception e) {
					String msg = "Failed to save configuration to " + configPath + ", command aborted";
					System.out.println("Error: " + msg);
					IJ.showMessage("Error", msg);
					return;
				}
			}
			String externalTitle = null;
			double[][] externalData = null;
			if (POST_PROCESSING.disparityCorrelationParameters.useFileData) {
				String[] patterns = { ".lin-tiff", ".tiff", ".tif" };
				String path = selectFile(true, // smart
						false, // save
						"External file with per-image slice (i.e. linear features)", // title
						"Select external image file", // button
						new MultipleExtensionsFileFilter(patterns, patterns[0] + " files"), // filter
						POST_PROCESSING.disparityCorrelationParameters.dataPathName);
				if ((path != null) && (path.length() > 0)) {
					Opener opener = new Opener();
					ImagePlus imp = opener.openImage("", path);
					if (imp == null) {
						String msg = "Failed to read external image file " + path;
						IJ.showMessage("Error", msg);
						System.out.println(msg);
						throw new IllegalArgumentException(msg);
					}
					POST_PROCESSING.disparityCorrelationParameters.dataPathName = path;
					externalTitle = imp.getTitle(); // TODO - get rid of extension?
					// Prefs.getFileSeparator()
					int firstIndex = externalTitle.lastIndexOf(Prefs.getFileSeparator()) + 1;
					int dotIndex = externalTitle.lastIndexOf(".");
					if (dotIndex < 0)
						dotIndex = externalTitle.length();
					externalTitle = externalTitle.substring(firstIndex, dotIndex);
					float[][] fpixels = new float[imp.getStack().getSize()][];
					for (int si = 0; si < fpixels.length; si++)
						fpixels[si] = (float[]) imp.getStack().getPixels(si + 1);
					externalData = new double[fpixels.length][];
					for (int si = 0; si < fpixels.length; si++) {
						externalData[si] = new double[fpixels[si].length];
						for (int pi = 0; pi < fpixels[si].length; pi++)
							externalData[si][pi] = fpixels[si][pi];
					}
				}
			}

			DISPARITY_TILES = POST_PROCESSING.getDisparityTiles(externalTitle, externalData,
					POST_PROCESSING.disparityCorrelationParameters.channelMask,
					POST_PROCESSING.disparityCorrelationParameters.disparitySteps,
					POST_PROCESSING.disparityCorrelationParameters.resultWindow, THREADS_MAX, // final int threadsMax,
																								// // maximal number of
																								// threads to launch
					UPDATE_STATUS, DEBUG_LEVEL); // int debugLevel

			if ((DISPARITY_TILES != null) && POST_PROCESSING.disparityCorrelationParameters.saveCorrelationToImage) {
				String path = POST_PROCESSING.postProcessingParameters.selectResultsDirectory(true, true);
				if (path == null) {
					String msg = "No results directory selected, command aborted";
					System.out.println("Warning: " + msg);
					IJ.showMessage("Warning", msg);
					return;
				}
				path += Prefs.getFileSeparator() + DISPARITY_TILES.impDisparity.getTitle();
				FileSaver fs = new FileSaver(DISPARITY_TILES.impDisparity);
				fs.saveAsTiffStack(path);

			}
			/* ======================================================================== */
		} else if (label.equals("Load correlations")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (POST_PROCESSING.interSensor == null) {
				System.out.println("POST_PROCESSING.interSensor==null");
				return;
			}
			String[] patterns = { ".corr-tiff", ".tiff", ".tif" };
			String path = selectFile(false, // save
					"Disparity correlation file selection", // title
					"Select disparity correlation file", // button
					new MultipleExtensionsFileFilter(patterns, patterns[0] + " files"), // filter
					"");
			if ((path != null) && (path.length() > 0)) {
				DISPARITY_TILES = POST_PROCESSING.interSensor.new DisparityTiles(path);
			}

			return;
			/* ======================================================================== */
		} else if (label.equals("Section correlations")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			GenericDialog gd = new GenericDialog("Correlation section parameters");
			gd.addCheckbox("Horizontal section (unchecked - vertical)", false);
			gd.addNumericField("Section X/Y", 1280, 0, 4, "pix");
			gd.addNumericField("Fat zero", 0.05, 3, 5, "");
			gd.showDialog();
			if (gd.wasCanceled())
				return;
			boolean horizontal = gd.getNextBoolean();
			int section = (int) gd.getNextNumber();
			double fatZero = gd.getNextNumber();
			if (DISPARITY_TILES == null) {
				if (!loadCorrelations())
					return;
			}
			int sourceWidth = POST_PROCESSING.interSensor.mapWidth;
			int soureHeight = POST_PROCESSING.interSensor.mapHeight;
			int resultHeight = horizontal ? sourceWidth : soureHeight;
			int resultWidth = DISPARITY_TILES.getDisparityPoints();

			int[][] pairIndices = DISPARITY_TILES.imagePairIndices();
			// int [][] pairNumbers=DISPARITY_TILES.imagePairNumbers();
			int numImages = 0;
			int numSecondImages = 0;
			for (int np = 0; np < pairIndices.length; np++) {
				if (DEBUG_LEVEL > 2)
					System.out.println("pairIndices[" + np + "][0]=" + pairIndices[np][0] + " pairIndices[" + np
							+ "][1]=" + pairIndices[np][1]);
				if (pairIndices[np][0] > numImages)
					numImages = pairIndices[np][0];
				if (pairIndices[np][1] > numSecondImages)
					numSecondImages = pairIndices[np][1];
			}
			numImages++;
			numSecondImages++;
			if (DEBUG_LEVEL > 2)
				System.out.println("numImages=" + numImages + " numSecondImages=" + numSecondImages);
			boolean[][] pairMap = new boolean[numImages][numSecondImages];
			for (int ni = 0; ni < numImages; ni++)
				for (int ns = 0; ns < numSecondImages; ns++)
					pairMap[ni][ns] = false;
			for (int np = 0; np < pairIndices.length; np++)
				pairMap[pairIndices[np][0]][pairIndices[np][1]] = true;
			int numPairs = 0;
			for (int ni = 0; ni < numImages; ni++)
				for (int ns = 0; ns < numSecondImages; ns++)
					if (pairMap[ni][ns])
						numPairs++;
			double[][] pixels = new double[numPairs + numImages + 1 + numImages][resultWidth * resultHeight];
			String[] titles = new String[pixels.length];
			int index = 0;
			int x0 = horizontal ? 0 : section;
			int y0 = horizontal ? section : 0;
			int dx = horizontal ? 1 : 0;
			int dy = horizontal ? 0 : 1;
			for (int ni = 0; ni < numImages; ni++) {
				for (int ns = 0; ns < pairMap[ni].length; ns++)
					if (pairMap[ni][ns]) {
						int si = (ns >= ni) ? (ns + 1) : ns;
						titles[index] = String.format("%03d-%03d", ni, si);
						for (int rY = 0; rY < resultHeight; rY++) {
							double[] row = DISPARITY_TILES.getCorrelationArray(ni, ns, x0 + dx * rY, y0 + dy * rY,
									THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
									UPDATE_STATUS, DEBUG_LEVEL); // int debugLevel
							for (int rX = 0; rX < row.length; rX++)
								pixels[index][resultWidth * rY + rX] = row[rX];
						}
						index++;
					}
				titles[index] = String.format("%03d-combo", ni);
				for (int rY = 0; rY < resultHeight; rY++) {
					double[] row = DISPARITY_TILES.getCorrelationArray(ni, fatZero, x0 + dx * rY, y0 + dy * rY,
							THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
							UPDATE_STATUS, DEBUG_LEVEL); // int debugLevel

					for (int rX = 0; rX < row.length; rX++)
						pixels[index][resultWidth * rY + rX] = row[rX];
				}
				index++;
				index++; // skip for synthetic
			}
			titles[index] = "Center";
			for (int rY = 0; rY < resultHeight; rY++) {
				double[] row = DISPARITY_TILES.getCorrelationArray((double) (x0 + dx * rY), (double) (y0 + dy * rY),
						fatZero, THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
						UPDATE_STATUS, DEBUG_LEVEL); // int debugLevel
				for (int rX = 0; rX < row.length; rX++)
					pixels[index][resultWidth * rY + rX] = row[rX];
			}
			index = 0;
			for (int ni = 0; ni < numImages; ni++) {
				for (int ns = 0; ns < pairMap[ni].length; ns++)
					if (pairMap[ni][ns])
						index++;
				index++; // skip combo
				titles[index] = String.format("%03d-synth", ni);
				for (int rY = 0; rY < resultHeight; rY++) {
					double[] row = DISPARITY_TILES.getCorrelationArray( // synthetic pixels
							ni, x0 + dx * rY, y0 + dy * rY, THREADS_MAX, // final int threadsMax, // maximal number of
																			// threads to launch
							UPDATE_STATUS, DEBUG_LEVEL); // int debugLevel
					for (int rX = 0; rX < row.length; rX++)
						pixels[index][resultWidth * rY + rX] = row[rX];
				}
				index++;
			}

			ShowDoubleFloatArrays
					.showArrays(
							pixels, resultWidth, resultHeight, true, "sect-" + (horizontal ? "HOR" : "VERT") + "-"
									+ section + "_" + DISPARITY_TILES.corrFFTSize + "-" + DISPARITY_TILES.overlapStep,
							titles);
			return;
			/* ======================================================================== */
		} else if (label.equals("Setup Z-map")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			// centerPixels
			double[][] disparityScales = POST_PROCESSING.interSensor.getDisparityScales();
			if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure tile correlation parameters",
					disparityScales, 64 + 128, POST_PROCESSING.interSensor.mapWidth,
					POST_PROCESSING.interSensor.mapHeight))
				return;
			if (DISPARITY_TILES == null) {
				if (!loadCorrelations())
					return;
			}

			if (!DISPARITY_TILES.isCenterPixelsDefined()) {
				DISPARITY_TILES.combineDisparityToCenter(POST_PROCESSING.disparityCorrelationParameters.zMapFatZero,
						THREADS_MAX, // int threadsMax,
						UPDATE_STATUS, // boolean showProgress,
						DEBUG_LEVEL); // int debugLevel)
			}
			Rectangle tileWOI = DISPARITY_TILES
					.pixelsToTilesWOI(POST_PROCESSING.disparityCorrelationParameters.zMapWOI);
			DISPARITY_TILES.setupZMap(tileWOI, // Rectangle woi, // in tiles - may be
					POST_PROCESSING.disparityCorrelationParameters.zMapMaxNumber, // int maxNumber,
					POST_PROCESSING.disparityCorrelationParameters.zMapMinFirst, // double minFirst,
					POST_PROCESSING.disparityCorrelationParameters.zMapMinAbsolute, // double minAbsolute,
					POST_PROCESSING.disparityCorrelationParameters.zMapMinRelative, // double minRelative,
					POST_PROCESSING.disparityCorrelationParameters.zMapMergeMax, // double mergeMax,
					POST_PROCESSING.disparityCorrelationParameters.zMapOverlap, // final int overlap,
					POST_PROCESSING.disparityCorrelationParameters.zMapMinForeground, THREADS_MAX, // int threadsMax,
					UPDATE_STATUS, // boolean showProgress,
					DEBUG_LEVEL); // int debugLevel)
			float[][] pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI,
					false);
			String[] zMapTitles = new String[pixels.length];
			for (int nImg = 0; nImg < zMapTitles.length; nImg++)
				zMapTitles[nImg] = "img-" + nImg;
			ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
					POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
					"zMap-A" + POST_PROCESSING.disparityCorrelationParameters.zMapMinAbsolute + "-R"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapMinRelative + "-F"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapMinFirst + "-X"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapWOI.x + "-Y"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapWOI.y + "-W"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width + "-H"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height,
					zMapTitles);
			return;
			/* ======================================================================== */

		} else if (label.equals("Fill FG gaps")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			double[][] disparityScales = POST_PROCESSING.interSensor.getDisparityScales();
			if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure tile correlation parameters",
					disparityScales, 256, POST_PROCESSING.interSensor.mapWidth, POST_PROCESSING.interSensor.mapHeight))
				return;

			if (DISPARITY_TILES == null) {
				if (!loadCorrelations())
					return;
			}

			if (!DISPARITY_TILES.isCenterPixelsDefined()) {
				DISPARITY_TILES.combineDisparityToCenter(POST_PROCESSING.disparityCorrelationParameters.zMapFatZero,
						THREADS_MAX, // int threadsMax,
						UPDATE_STATUS, // boolean showProgress,
						DEBUG_LEVEL); // int debugLevel)
			}
			// setup ZMap if it is not yet done
			if (!DISPARITY_TILES.isZMapDefined()) {
				if (!DISPARITY_TILES.isCenterPixelsDefined()) {
					DISPARITY_TILES.combineDisparityToCenter(POST_PROCESSING.disparityCorrelationParameters.zMapFatZero,
							THREADS_MAX, // int threadsMax,
							UPDATE_STATUS, // boolean showProgress,
							DEBUG_LEVEL); // int debugLevel)
				}
				Rectangle tileWOI = DISPARITY_TILES
						.pixelsToTilesWOI(POST_PROCESSING.disparityCorrelationParameters.zMapWOI);
				DISPARITY_TILES.setupZMap(tileWOI, // Rectangle woi, // in tiles - may be
						POST_PROCESSING.disparityCorrelationParameters.zMapMaxNumber, // int maxNumber,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinFirst, // double minFirst,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinAbsolute, // double minAbsolute,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinRelative, // double minRelative,
						POST_PROCESSING.disparityCorrelationParameters.zMapMergeMax, // double mergeMax,
						POST_PROCESSING.disparityCorrelationParameters.zMapOverlap, // final int overlap,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinForeground, THREADS_MAX, // int
																										// threadsMax,
						UPDATE_STATUS, // boolean showProgress,
						DEBUG_LEVEL); // int debugLevel)
			}

			int numberFGfilled = 0;
			do {
				numberFGfilled = DISPARITY_TILES.fillForegroundGapsStep(null, // Rectangle woi, // in tiles - may be
						POST_PROCESSING.disparityCorrelationParameters.fillFgGapNeib, // int minNeib,
						POST_PROCESSING.disparityCorrelationParameters.fillFgGapDiff, // double maxDifference,
						POST_PROCESSING.disparityCorrelationParameters.fillFgGapMin, // double foregroundThreshold,
						THREADS_MAX, // int threadsMax,
						UPDATE_STATUS, // boolean showProgress,
						DEBUG_LEVEL); // int debugLevel)
				if (DEBUG_LEVEL > 1)
					System.out.println("Filled foreground gaps: " + numberFGfilled + " tiles");
			} while (numberFGfilled > 0);

			float[][] pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI,
					false);
			String[] zMapTitles = new String[pixels.length];
			for (int nImg = 0; nImg < zMapTitles.length; nImg++)
				zMapTitles[nImg] = "img-" + nImg;
			ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
					POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
					"zMap-A" + POST_PROCESSING.disparityCorrelationParameters.zMapMinAbsolute + "-R"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapMinRelative + "-F"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapMinFirst + "-X"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapWOI.x + "-Y"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapWOI.y + "-W"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width + "-H"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height + "-fgN"
							+ POST_PROCESSING.disparityCorrelationParameters.fillFgGapNeib + // int minNeib,
							"-fgD" + POST_PROCESSING.disparityCorrelationParameters.fillFgGapDiff + // double
																									// maxDifference,
							"-fgM" + POST_PROCESSING.disparityCorrelationParameters.fillFgGapMin // double
																									// foregroundThreshold,

					, zMapTitles);
			return;
//"Fill FG gaps"
			/* ======================================================================== */
		} else if (label.equals("Filter Z-map")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			// centerPixels
			double[][] disparityScales = POST_PROCESSING.interSensor.getDisparityScales();
			if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure tile correlation parameters",
					disparityScales, 4 + 32 + 64 + 128, POST_PROCESSING.interSensor.mapWidth,
					POST_PROCESSING.interSensor.mapHeight))
				return;
			if (DISPARITY_TILES == null) {
				if (!loadCorrelations())
					return;
			}

			String externalTitle = null;
			double[][] externalData = null;
			if (POST_PROCESSING.disparityCorrelationParameters.useFileData) {
				String[] patterns = { ".lin-tiff", ".tiff", ".tif" };
				if (DEBUG_LEVEL > 1)
					System.out.println("Suggesting " + POST_PROCESSING.disparityCorrelationParameters.dataPathName);

				String path = selectFile(true, // smart
						false, // save
						"External file with per-image slice (i.e. linear features)", // title
						"Select external image file", // button
						new MultipleExtensionsFileFilter(patterns, patterns[0] + " files"), // filter
						POST_PROCESSING.disparityCorrelationParameters.dataPathName);
				if ((path != null) && (path.length() > 0)) {
					Opener opener = new Opener();
					ImagePlus imp = opener.openImage("", path);
					if (imp == null) {
						String msg = "Failed to read external image file " + path;
						IJ.showMessage("Error", msg);
						System.out.println(msg);
						throw new IllegalArgumentException(msg);
					}
					POST_PROCESSING.disparityCorrelationParameters.dataPathName = path;
					externalTitle = imp.getTitle(); // TODO - get rid of extension?
					// Prefs.getFileSeparator()
					int firstIndex = externalTitle.lastIndexOf(Prefs.getFileSeparator()) + 1;
					int dotIndex = externalTitle.lastIndexOf(".");
					if (dotIndex < 0)
						dotIndex = externalTitle.length();
					externalTitle = externalTitle.substring(firstIndex, dotIndex);
					float[][] fpixels = new float[imp.getStack().getSize()][];
					for (int si = 0; si < fpixels.length; si++)
						fpixels[si] = (float[]) imp.getStack().getPixels(si + 1);
					externalData = new double[fpixels.length][];
					for (int si = 0; si < fpixels.length; si++) {
						externalData[si] = new double[fpixels[si].length];
						for (int pi = 0; pi < fpixels[si].length; pi++)
							externalData[si][pi] = fpixels[si][pi];
					}
				}
			}

			if (!DISPARITY_TILES.isZMapDefined()) {
				if (!DISPARITY_TILES.isCenterPixelsDefined()) {
					DISPARITY_TILES.combineDisparityToCenter(POST_PROCESSING.disparityCorrelationParameters.zMapFatZero,
							THREADS_MAX, // int threadsMax,
							UPDATE_STATUS, // boolean showProgress,
							DEBUG_LEVEL); // int debugLevel)
				}
				Rectangle tileWOI = DISPARITY_TILES
						.pixelsToTilesWOI(POST_PROCESSING.disparityCorrelationParameters.zMapWOI);
				DISPARITY_TILES.setupZMap(tileWOI, // Rectangle woi, // in tiles - may be
						POST_PROCESSING.disparityCorrelationParameters.zMapMaxNumber, // int maxNumber,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinFirst, // double minFirst,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinAbsolute, // double minAbsolute,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinRelative, // double minRelative,
						POST_PROCESSING.disparityCorrelationParameters.zMapMergeMax, // double mergeMax,
						POST_PROCESSING.disparityCorrelationParameters.zMapOverlap, // final int overlap,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinForeground, THREADS_MAX, // int
																										// threadsMax,
						UPDATE_STATUS, // boolean showProgress,
						DEBUG_LEVEL); // int debugLevel)
			}
			int numImages = DISPARITY_TILES.disparityScales.length;
			int numLayers = 4; // alpha,Y,Cb,Cr,Aux
			double[][][] imageData = new double[numImages][numLayers + 1][];
			for (int nImg = 0; nImg < imageData.length; nImg++) {
				for (int nLayer = 0; nLayer < numLayers; nLayer++)
					imageData[nImg][nLayer] = POST_PROCESSING.interSensor.overlapImages[numLayers * nImg + nLayer];
				imageData[nImg][numLayers] = (externalData != null) ? externalData[nImg] : null;
			}
			DISPARITY_TILES.filterForegroundZMap(imageData, // double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}.
															// Alpha may be null - will handle later?
					POST_PROCESSING.interSensor.mapWidth, // int imageFullWidth,
					POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma, // double blurVarianceSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineTilePeriod, // int refineTilePeriod,
					POST_PROCESSING.disparityCorrelationParameters.refinePhaseCoeff, // double refinePhaseCoeff,
					POST_PROCESSING.disparityCorrelationParameters.refineHighPassSigma, // double refineHighPassSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineLowPassSigma, // double refineLowPassSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineCorrMaxDistance, // double
																							// refineCorrMaxDistance,
					POST_PROCESSING.disparityCorrelationParameters.refineCorrThreshold, // double refineCorrThreshold,
					POST_PROCESSING.disparityCorrelationParameters.refineSubPixel, // int refineSubPixel,
					POST_PROCESSING.disparityCorrelationParameters.zMapMinForeground, // double zMapMinForeground,
					POST_PROCESSING.disparityCorrelationParameters.zMapVarMask, // int zMapVarMask,
					POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds, // double [] zMapVarThresholds,
					POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights,
					POST_PROCESSING.disparityCorrelationParameters.auxVarMode,
					POST_PROCESSING.disparityCorrelationParameters.normalizeCorrelation, // normalize correlation by the
																							// total energy
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask, // int zMapCorrMask,
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds, // double [] zMapCorrThresholds,
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights,
					POST_PROCESSING.disparityCorrelationParameters.debugRow,
					POST_PROCESSING.disparityCorrelationParameters.debugColumn, THREADS_MAX, // int threadsMax,
					UPDATE_STATUS, // boolean showProgress,
					DEBUG_LEVEL); // int debugLevel)

			float[][] pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI,
					false);
			String[] zMapTitles = new String[pixels.length];
			for (int nImg = 0; nImg < zMapTitles.length; nImg++)
				zMapTitles[nImg] = "img-" + nImg;
			ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
					POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
					"zF-MV" + POST_PROCESSING.disparityCorrelationParameters.zMapVarMask + "-VY"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[0] + "-VCb"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[1] + "-VCr"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[2] + "-VAux"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[3] + "-WVY"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[0] + "-WVCb"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[1] + "-WVCr"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[2] + "-WVAux"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[3] + "-MC"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask + "-CY"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[0] + "-CCb"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[1] + "-CCr"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[2] + "-CAux"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[3] + "-WCY"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[0] + "-WCCb"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[1] + "-WCCr"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[2] + "-WCAux"
							+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[3],
					zMapTitles);
			return;
			/* ======================================================================== */
		} else if (label.equals("Occluding FG")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			// centerPixels
			double[][] disparityScales = POST_PROCESSING.interSensor.getDisparityScales();
			if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure tile correlation parameters",
					disparityScales, 4 + 32 + 64 + 128, POST_PROCESSING.interSensor.mapWidth,
					POST_PROCESSING.interSensor.mapHeight))
				return;
			if (DISPARITY_TILES == null) {
				if (!loadCorrelations())
					return;
			}
			String externalTitle = null;
			double[][] externalData = null;
			if (POST_PROCESSING.disparityCorrelationParameters.useFileData) {
				String[] patterns = { ".lin-tiff", ".tiff", ".tif" };
				if (DEBUG_LEVEL > 1)
					System.out.println("Suggesting " + POST_PROCESSING.disparityCorrelationParameters.dataPathName);

				String path = selectFile(true, // smart
						false, // save
						"External file with per-image slice (i.e. linear features)", // title
						"Select external image file", // button
						new MultipleExtensionsFileFilter(patterns, patterns[0] + " files"), // filter
						POST_PROCESSING.disparityCorrelationParameters.dataPathName);
				if ((path != null) && (path.length() > 0)) {
					Opener opener = new Opener();
					ImagePlus imp = opener.openImage("", path);
					if (imp == null) {
						String msg = "Failed to read external image file " + path;
						IJ.showMessage("Error", msg);
						System.out.println(msg);
						throw new IllegalArgumentException(msg);
					}
					POST_PROCESSING.disparityCorrelationParameters.dataPathName = path;
					externalTitle = imp.getTitle(); // TODO - get rid of extension?
					// Prefs.getFileSeparator()
					int firstIndex = externalTitle.lastIndexOf(Prefs.getFileSeparator()) + 1;
					int dotIndex = externalTitle.lastIndexOf(".");
					if (dotIndex < 0)
						dotIndex = externalTitle.length();
					externalTitle = externalTitle.substring(firstIndex, dotIndex);
					float[][] fpixels = new float[imp.getStack().getSize()][];
					for (int si = 0; si < fpixels.length; si++)
						fpixels[si] = (float[]) imp.getStack().getPixels(si + 1);
					externalData = new double[fpixels.length][];
					for (int si = 0; si < fpixels.length; si++) {
						externalData[si] = new double[fpixels[si].length];
						for (int pi = 0; pi < fpixels[si].length; pi++)
							externalData[si][pi] = fpixels[si][pi];
					}
				}
			}

			if (!DISPARITY_TILES.isZMapDefined()) {
				if (!DISPARITY_TILES.isCenterPixelsDefined()) {
					DISPARITY_TILES.combineDisparityToCenter(POST_PROCESSING.disparityCorrelationParameters.zMapFatZero,
							THREADS_MAX, // int threadsMax,
							UPDATE_STATUS, // boolean showProgress,
							DEBUG_LEVEL); // int debugLevel)
				}
				Rectangle tileWOI = DISPARITY_TILES
						.pixelsToTilesWOI(POST_PROCESSING.disparityCorrelationParameters.zMapWOI);
				DISPARITY_TILES.setupZMap(tileWOI, // Rectangle woi, // in tiles - may be
						POST_PROCESSING.disparityCorrelationParameters.zMapMaxNumber, // int maxNumber,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinFirst, // double minFirst,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinAbsolute, // double minAbsolute,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinRelative, // double minRelative,
						POST_PROCESSING.disparityCorrelationParameters.zMapMergeMax, // double mergeMax,
						POST_PROCESSING.disparityCorrelationParameters.zMapOverlap, // final int overlap,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinForeground, THREADS_MAX, // int
																										// threadsMax,
						UPDATE_STATUS, // boolean showProgress,
						DEBUG_LEVEL); // int debugLevel)
			}
			int numImages = DISPARITY_TILES.disparityScales.length;
			int numLayers = 4; // alpha,Y,Cb,Cr,Aux
			double[][][] imageData = new double[numImages][numLayers + 1][];
			for (int nImg = 0; nImg < imageData.length; nImg++) {
				for (int nLayer = 0; nLayer < numLayers; nLayer++)
					imageData[nImg][nLayer] = POST_PROCESSING.interSensor.overlapImages[numLayers * nImg + nLayer];
				imageData[nImg][numLayers] = (externalData != null) ? externalData[nImg] : null;
			}
			DISPARITY_TILES.foregroundByOcclusion(imageData, // double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}.
																// Alpha may be null - will handle later?
					POST_PROCESSING.interSensor.mapWidth, // int imageFullWidth,
					POST_PROCESSING.disparityCorrelationParameters.bgFraction, // double bgFraction,
					POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma, // double blurVarianceSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineTilePeriod, // int refineTilePeriod,
					POST_PROCESSING.disparityCorrelationParameters.refinePhaseCoeff, // double refinePhaseCoeff,
					POST_PROCESSING.disparityCorrelationParameters.refineHighPassSigma, // double refineHighPassSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineLowPassSigma, // double refineLowPassSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineCorrMaxDistance, // double
																							// refineCorrMaxDistance,
					POST_PROCESSING.disparityCorrelationParameters.refineCorrThreshold, // double refineCorrThreshold,
					POST_PROCESSING.disparityCorrelationParameters.refineSubPixel, // int refineSubPixel,
					POST_PROCESSING.disparityCorrelationParameters.zMapMinForeground, // double zMapMinForeground,
					POST_PROCESSING.disparityCorrelationParameters.zMapVarMask, // int zMapVarMask,
					POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds, // double [] zMapVarThresholds,
					POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights,
					POST_PROCESSING.disparityCorrelationParameters.normalizeCorrelation, // normalize correlation by the
																							// total energy
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask, // int zMapCorrMask,
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds, // double [] zMapCorrThresholds,
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights,
					POST_PROCESSING.disparityCorrelationParameters.debugRow,
					POST_PROCESSING.disparityCorrelationParameters.debugColumn, THREADS_MAX, // int threadsMax,
					UPDATE_STATUS, // boolean showProgress,
					DEBUG_LEVEL); // int debugLevel)
			if ((POST_PROCESSING.disparityCorrelationParameters.zMapVarMask != 0)
					|| (POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask != 0)) {
				float[][] pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 2,
						false);
				String[] zMapTitles = new String[pixels.length];
				for (int nImg = 0; nImg < zMapTitles.length; nImg++)
					zMapTitles[nImg] = "img-" + nImg;
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"DISPAR-OF-Bg" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-VS"
								+ POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma + "-MV"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarMask + "-VY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[0] + "-VCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[1] + "-VCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[2] + "-VAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[3] + "-WVY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[0] + "-WVCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[1] + "-WVCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[2] + "-WVAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[3],
						zMapTitles);
			}
			if (POST_PROCESSING.disparityCorrelationParameters.zMapVarMask != 0) {
				float[][] pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 0,
						false);
				String[] zMapTitles = new String[pixels.length];
				for (int nImg = 0; nImg < zMapTitles.length; nImg++)
					zMapTitles[nImg] = "img-" + nImg;
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"OF-Bg" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-VS"
								+ POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma + "-MV"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarMask + "-VY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[0] + "-VCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[1] + "-VCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[2] + "-VAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[3] + "-WVY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[0] + "-WVCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[1] + "-WVCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[2] + "-WVAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[3],
						zMapTitles);
// debug
				pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 3, false);
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"VAR-OF-Bg" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-VS"
								+ POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma + "-MV"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarMask + "-VY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[0] + "-VCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[1] + "-VCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[2] + "-VAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[3] + "-WVY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[0] + "-WVCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[1] + "-WVCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[2] + "-WVAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[3],
						zMapTitles);

				pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 4, false);
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"PAIR-OF-Bg" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-VS"
								+ POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma + "-MV"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarMask + "-VY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[0] + "-VCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[1] + "-VCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[2] + "-VAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[3] + "-WVY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[0] + "-WVCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[1] + "-WVCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[2] + "-WVAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[3],
						zMapTitles);

				pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 5, false);
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"THIS-OF-Bg" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-VS"
								+ POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma + "-MV"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarMask + "-VY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[0] + "-VCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[1] + "-VCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[2] + "-VAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[3] + "-WVY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[0] + "-WVCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[1] + "-WVCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[2] + "-WVAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[3],
						zMapTitles);

				pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 6, false);
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"D-VAR-Bg" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-VS"
								+ POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma + "-MV"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarMask + "-VY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[0] + "-VCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[1] + "-VCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[2] + "-VAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[3] + "-WVY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[0] + "-WVCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[1] + "-WVCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[2] + "-WVAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[3],
						zMapTitles);

				/*
				 * aux2[i]=(float) disparity; aux3[i]=(float) pairVarDbgBest; aux4[i]=(float)
				 * pairDiffDbgBest; aux5[i]=(float) thisDiffDbgBest;
				 * 
				 */

			}
			if (POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask != 0) {
				float[][] pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 1,
						true);
				String[] zMapTitles = new String[pixels.length];
				for (int nImg = 0; nImg < zMapTitles.length; nImg++)
					zMapTitles[nImg] = "img-" + nImg;
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"OF-Bg" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-MC"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask + "-CY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[0] + "-CCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[1] + "-CCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[2] + "-CAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[3] + "-WCY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[0] + "-WCCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[1] + "-WCCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[2] + "-WCAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[3],
						zMapTitles);

				// debug
				pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 7, true);
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"COR-PAIR" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-MC"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask + "-CY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[0] + "-CCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[1] + "-CCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[2] + "-CAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[3] + "-WCY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[0] + "-WCCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[1] + "-WCCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[2] + "-WCAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[3],
						zMapTitles);

				pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 8, true);
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"COR-AVER" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-MC"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask + "-CY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[0] + "-CCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[1] + "-CCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[2] + "-CAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[3] + "-WCY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[0] + "-WCCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[1] + "-WCCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[2] + "-WCAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[3],
						zMapTitles);

				pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 9, true);
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"COR-FIRST" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-MC"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask + "-CY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[0] + "-CCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[1] + "-CCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[2] + "-CAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[3] + "-WCY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[0] + "-WCCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[1] + "-WCCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[2] + "-WCAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[3],
						zMapTitles);

				pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 10, true);
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true,
						"COR-SECOND" + POST_PROCESSING.disparityCorrelationParameters.bgFraction + "-MC"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask + "-CY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[0] + "-CCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[1] + "-CCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[2] + "-CAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds[3] + "-WCY"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[0] + "-WCCb"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[1] + "-WCCr"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[2] + "-WCAux"
								+ POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights[3],
						zMapTitles);

			}

			return;
			/* ======================================================================== */
		} else if (label.equals("Refine Disparities")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			// centerPixels
			double[][] disparityScales = POST_PROCESSING.interSensor.getDisparityScales();
			if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure tile correlation parameters",
					disparityScales,
//    			4+32+64+128,
					4 + 32 + 128 + 512, POST_PROCESSING.interSensor.mapWidth, POST_PROCESSING.interSensor.mapHeight))
				return;
			if (DISPARITY_TILES == null) {
				if (!loadCorrelations())
					return;
			}
			String externalTitle = null;
			double[][] externalData = null;
			if (POST_PROCESSING.disparityCorrelationParameters.useFileData) {
				String[] patterns = { ".lin-tiff", ".tiff", ".tif" };
				if (DEBUG_LEVEL > 1)
					System.out.println("Suggesting " + POST_PROCESSING.disparityCorrelationParameters.dataPathName);

				String path = selectFile(true, // smart
						false, // save
						"External file with per-image slice (i.e. linear features)", // title
						"Select external image file", // button
						new MultipleExtensionsFileFilter(patterns, patterns[0] + " files"), // filter
						POST_PROCESSING.disparityCorrelationParameters.dataPathName);
				if ((path != null) && (path.length() > 0)) {
					Opener opener = new Opener();
					ImagePlus imp = opener.openImage("", path);
					if (imp == null) {
						String msg = "Failed to read external image file " + path;
						IJ.showMessage("Error", msg);
						System.out.println(msg);
						throw new IllegalArgumentException(msg);
					}
					POST_PROCESSING.disparityCorrelationParameters.dataPathName = path;
					externalTitle = imp.getTitle(); // TODO - get rid of extension?
					// Prefs.getFileSeparator()
					int firstIndex = externalTitle.lastIndexOf(Prefs.getFileSeparator()) + 1;
					int dotIndex = externalTitle.lastIndexOf(".");
					if (dotIndex < 0)
						dotIndex = externalTitle.length();
					externalTitle = externalTitle.substring(firstIndex, dotIndex);
					float[][] fpixels = new float[imp.getStack().getSize()][];
					for (int si = 0; si < fpixels.length; si++)
						fpixels[si] = (float[]) imp.getStack().getPixels(si + 1);
					externalData = new double[fpixels.length][];
					for (int si = 0; si < fpixels.length; si++) {
						externalData[si] = new double[fpixels[si].length];
						for (int pi = 0; pi < fpixels[si].length; pi++)
							externalData[si][pi] = fpixels[si][pi];
					}
				}
			}

			if (!DISPARITY_TILES.isZMapDefined()) {
				if (!DISPARITY_TILES.isCenterPixelsDefined()) {
					DISPARITY_TILES.combineDisparityToCenter(POST_PROCESSING.disparityCorrelationParameters.zMapFatZero,
							THREADS_MAX, // int threadsMax,
							UPDATE_STATUS, // boolean showProgress,
							DEBUG_LEVEL); // int debugLevel)
				}
				Rectangle tileWOI = DISPARITY_TILES
						.pixelsToTilesWOI(POST_PROCESSING.disparityCorrelationParameters.zMapWOI);
				DISPARITY_TILES.setupZMap(tileWOI, // Rectangle woi, // in tiles - may be
						POST_PROCESSING.disparityCorrelationParameters.zMapMaxNumber, // int maxNumber,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinFirst, // double minFirst,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinAbsolute, // double minAbsolute,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinRelative, // double minRelative,
						POST_PROCESSING.disparityCorrelationParameters.zMapMergeMax, // double mergeMax,
						POST_PROCESSING.disparityCorrelationParameters.zMapOverlap, // final int overlap,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinForeground, THREADS_MAX, // int
																										// threadsMax,
						UPDATE_STATUS, // boolean showProgress,
						DEBUG_LEVEL); // int debugLevel)
			}
			int numImages = DISPARITY_TILES.disparityScales.length;
			int numLayers = 4; // alpha,Y,Cb,Cr,Aux
			double[][][] imageData = new double[numImages][numLayers + 1][];
			for (int nImg = 0; nImg < imageData.length; nImg++) {
				for (int nLayer = 0; nLayer < numLayers; nLayer++)
					imageData[nImg][nLayer] = POST_PROCESSING.interSensor.overlapImages[numLayers * nImg + nLayer];
				imageData[nImg][numLayers] = (externalData != null) ? externalData[nImg] : null;
			}
			DISPARITY_TILES.refinePlaneDisparity(imageData, // double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}.
															// Alpha may be null - will handle later?
					POST_PROCESSING.interSensor.mapWidth, // int imageFullWidth,
					POST_PROCESSING.disparityCorrelationParameters.refinePhaseCoeff, // double refinePhaseCoeff,
					POST_PROCESSING.disparityCorrelationParameters.refineHighPassSigma, // double refineHighPassSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineLowPassSigma, // double refineLowPassSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineCorrMaxDistance, // double
																							// refineCorrMaxDistance,
					POST_PROCESSING.disparityCorrelationParameters.refineCorrThreshold, // double refineCorrThreshold,
					POST_PROCESSING.disparityCorrelationParameters.refineSubPixel, // int refineSubPixel,
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask, // int zMapCorrMask,
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds, // double [] zMapCorrThresholds,
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights,
					POST_PROCESSING.disparityCorrelationParameters.debugRow,
					POST_PROCESSING.disparityCorrelationParameters.debugColumn,
					POST_PROCESSING.disparityCorrelationParameters.filter2DisparityMax, // double disparityMax,
					POST_PROCESSING.disparityCorrelationParameters.filter2disparityMin, // double disparityMin,
					POST_PROCESSING.disparityCorrelationParameters.filter2UpdateMax
							? POST_PROCESSING.disparityCorrelationParameters.filter2MinAbsolute
							: Double.NaN, // double minAbsolute, // or NaN - will use enabled/disabled state of the tile
					POST_PROCESSING.disparityCorrelationParameters.filter2UpdateMax
							? POST_PROCESSING.disparityCorrelationParameters.filter2MinRelative
							: Double.NaN, // double minRelative,
					POST_PROCESSING.disparityCorrelationParameters.filter2ByForeground, // boolean filterByForeground,
																						// // apply known certain masks
					POST_PROCESSING.disparityCorrelationParameters.filter2ByForegroundMargin, // double
																								// filterByForegroundMargin,
					POST_PROCESSING.disparityCorrelationParameters.filter2ByDisabled, // boolean filterByDisabled,
					POST_PROCESSING.disparityCorrelationParameters.filter2DisparityTolearnce, // double
																								// disparityTolearnce,
					POST_PROCESSING.disparityCorrelationParameters.filter2MaskBlurSigma, // double maskBlurSigma,
					POST_PROCESSING.disparityCorrelationParameters.filter2corrHighPassSigma, // double
																								// corrHighPassSigma, //
																								// subtract blurred
																								// version to minimize
																								// correlation caused by
																								// masks
					THREADS_MAX, // int threadsMax,
					UPDATE_STATUS, // boolean showProgress,
					DEBUG_LEVEL); // int debugLevel)
			return;

			/* ======================================================================== */
		} else if (label.equals("Init Photometry")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			// centerPixels
			double[][] disparityScales = POST_PROCESSING.interSensor.getDisparityScales();
			if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure tile correlation parameters",
					disparityScales,
//    			4+32+64+128,
					4 + 1024, POST_PROCESSING.interSensor.mapWidth, POST_PROCESSING.interSensor.mapHeight))
				return;
			if (DISPARITY_TILES == null) {
				if (!loadCorrelations())
					return;
			}
			String externalTitle = null;
			double[][] externalData = null;
			if (POST_PROCESSING.disparityCorrelationParameters.useFileData) {
				String[] patterns = { ".lin-tiff", ".tiff", ".tif" };
				if (DEBUG_LEVEL > 1)
					System.out.println("Suggesting " + POST_PROCESSING.disparityCorrelationParameters.dataPathName);

				String path = selectFile(true, // smart
						false, // save
						"External file with per-image slice (i.e. linear features)", // title
						"Select external image file", // button
						new MultipleExtensionsFileFilter(patterns, patterns[0] + " files"), // filter
						POST_PROCESSING.disparityCorrelationParameters.dataPathName);
				if ((path != null) && (path.length() > 0)) {
					Opener opener = new Opener();
					ImagePlus imp = opener.openImage("", path);
					if (imp == null) {
						String msg = "Failed to read external image file " + path;
						IJ.showMessage("Error", msg);
						System.out.println(msg);
						throw new IllegalArgumentException(msg);
					}
					POST_PROCESSING.disparityCorrelationParameters.dataPathName = path;
					externalTitle = imp.getTitle(); // TODO - get rid of extension?
					// Prefs.getFileSeparator()
					int firstIndex = externalTitle.lastIndexOf(Prefs.getFileSeparator()) + 1;
					int dotIndex = externalTitle.lastIndexOf(".");
					if (dotIndex < 0)
						dotIndex = externalTitle.length();
					externalTitle = externalTitle.substring(firstIndex, dotIndex);
					float[][] fpixels = new float[imp.getStack().getSize()][];
					for (int si = 0; si < fpixels.length; si++)
						fpixels[si] = (float[]) imp.getStack().getPixels(si + 1);
					externalData = new double[fpixels.length][];
					for (int si = 0; si < fpixels.length; si++) {
						externalData[si] = new double[fpixels[si].length];
						for (int pi = 0; pi < fpixels[si].length; pi++)
							externalData[si][pi] = fpixels[si][pi];
					}
				}
			}
			int numImages = DISPARITY_TILES.disparityScales.length;
			int numLayers = 4; // alpha,Y,Cb,Cr,Aux
			double[][][] imageData = new double[numImages][numLayers][];
			for (int nImg = 0; nImg < imageData.length; nImg++) {
				for (int nLayer = 0; nLayer < (numLayers - 1); nLayer++)
					imageData[nImg][nLayer] = POST_PROCESSING.interSensor.overlapImages[numLayers * nImg + nLayer + 1];
				imageData[nImg][numLayers - 1] = (externalData != null) ? externalData[nImg] : null;
			}
			DISPARITY_TILES.setPhotometric(imageData, POST_PROCESSING.interSensor.mapWidth, // int imageWidth,
					200, // int margins,
					POST_PROCESSING.disparityCorrelationParameters.photometricIgnoreFraction, // double ignoreFraction,
					POST_PROCESSING.disparityCorrelationParameters.photometricSubdivAverage, // int subdivAverage,
					POST_PROCESSING.disparityCorrelationParameters.photometricSubdivHalfDifference, // int
																									// subdivHalfDifference,
					POST_PROCESSING.disparityCorrelationParameters.photometricSmoothVarianceSigma, // double
																									// smoothVarianceSigma,
					POST_PROCESSING.disparityCorrelationParameters.photometricScaleVariance, // double scaleVariance,
					DEBUG_LEVEL); // int debugLevel)
			DISPARITY_TILES.showPhotometric();
			return;
			/* ======================================================================== */
		} else if (label.equals("Plane Likely")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			// centerPixels
			double[][] disparityScales = POST_PROCESSING.interSensor.getDisparityScales();
			if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure tile correlation parameters",
					disparityScales,
//    			4+32+64+128,
					4 + 32 + 128 + 512 + 2048, POST_PROCESSING.interSensor.mapWidth,
					POST_PROCESSING.interSensor.mapHeight))
				return;
			if (DISPARITY_TILES == null) {
				if (!loadCorrelations())
					return;
			}
			String externalTitle = null;
			double[][] externalData = null;
			if (POST_PROCESSING.disparityCorrelationParameters.useFileData) {
				String[] patterns = { ".lin-tiff", ".tiff", ".tif" };
				if (DEBUG_LEVEL > 1)
					System.out.println("Suggesting " + POST_PROCESSING.disparityCorrelationParameters.dataPathName);

				String path = selectFile(true, // smart
						false, // save
						"External file with per-image slice (i.e. linear features)", // title
						"Select external image file", // button
						new MultipleExtensionsFileFilter(patterns, patterns[0] + " files"), // filter
						POST_PROCESSING.disparityCorrelationParameters.dataPathName);
				if ((path != null) && (path.length() > 0)) {
					Opener opener = new Opener();
					ImagePlus imp = opener.openImage("", path);
					if (imp == null) {
						String msg = "Failed to read external image file " + path;
						IJ.showMessage("Error", msg);
						System.out.println(msg);
						throw new IllegalArgumentException(msg);
					}
					POST_PROCESSING.disparityCorrelationParameters.dataPathName = path;
					externalTitle = imp.getTitle(); // TODO - get rid of extension?
					// Prefs.getFileSeparator()
					int firstIndex = externalTitle.lastIndexOf(Prefs.getFileSeparator()) + 1;
					int dotIndex = externalTitle.lastIndexOf(".");
					if (dotIndex < 0)
						dotIndex = externalTitle.length();
					externalTitle = externalTitle.substring(firstIndex, dotIndex);
					float[][] fpixels = new float[imp.getStack().getSize()][];
					for (int si = 0; si < fpixels.length; si++)
						fpixels[si] = (float[]) imp.getStack().getPixels(si + 1);
					externalData = new double[fpixels.length][];
					for (int si = 0; si < fpixels.length; si++) {
						externalData[si] = new double[fpixels[si].length];
						for (int pi = 0; pi < fpixels[si].length; pi++)
							externalData[si][pi] = fpixels[si][pi];
					}
				}
			}

			if (!DISPARITY_TILES.isZMapDefined()) {
				if (!DISPARITY_TILES.isCenterPixelsDefined()) {
					DISPARITY_TILES.combineDisparityToCenter(POST_PROCESSING.disparityCorrelationParameters.zMapFatZero,
							THREADS_MAX, // int threadsMax,
							UPDATE_STATUS, // boolean showProgress,
							DEBUG_LEVEL); // int debugLevel)
				}
				Rectangle tileWOI = DISPARITY_TILES
						.pixelsToTilesWOI(POST_PROCESSING.disparityCorrelationParameters.zMapWOI);
				DISPARITY_TILES.setupZMap(tileWOI, // Rectangle woi, // in tiles - may be
						POST_PROCESSING.disparityCorrelationParameters.zMapMaxNumber, // int maxNumber,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinFirst, // double minFirst,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinAbsolute, // double minAbsolute,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinRelative, // double minRelative,
						POST_PROCESSING.disparityCorrelationParameters.zMapMergeMax, // double mergeMax,
						POST_PROCESSING.disparityCorrelationParameters.zMapOverlap, // final int overlap,
						POST_PROCESSING.disparityCorrelationParameters.zMapMinForeground, THREADS_MAX, // int
																										// threadsMax,
						UPDATE_STATUS, // boolean showProgress,
						DEBUG_LEVEL); // int debugLevel)
			}
			int numImages = DISPARITY_TILES.disparityScales.length;
			int numLayers = 4; // alpha,Y,Cb,Cr,Aux
			double[][][] imageData = new double[numImages][numLayers + 1][];
			for (int nImg = 0; nImg < imageData.length; nImg++) {
				for (int nLayer = 0; nLayer < numLayers; nLayer++)
					imageData[nImg][nLayer] = POST_PROCESSING.interSensor.overlapImages[numLayers * nImg + nLayer];
				imageData[nImg][numLayers] = (externalData != null) ? externalData[nImg] : null;
			}
			if (DISPARITY_TILES.photometric == null) {
				if (!POST_PROCESSING.disparityCorrelationParameters.showDialog("Configure tile correlation parameters",
						disparityScales, 1024, POST_PROCESSING.interSensor.mapWidth,
						POST_PROCESSING.interSensor.mapHeight))
					return;
				DISPARITY_TILES.setPhotometric(imageData, POST_PROCESSING.interSensor.mapWidth, // int imageWidth,
						200, // int margins,
						POST_PROCESSING.disparityCorrelationParameters.photometricIgnoreFraction, // double
																									// ignoreFraction,
						POST_PROCESSING.disparityCorrelationParameters.photometricSubdivAverage, // int subdivAverage,
						POST_PROCESSING.disparityCorrelationParameters.photometricSubdivHalfDifference, // int
																										// subdivHalfDifference,
						POST_PROCESSING.disparityCorrelationParameters.photometricSmoothVarianceSigma, // double
																										// smoothVarianceSigma,
						POST_PROCESSING.disparityCorrelationParameters.photometricScaleVariance, // double
																									// scaleVariance,
						DEBUG_LEVEL); // int debugLevel)
				DISPARITY_TILES.showPhotometric();
			}
			DISPARITY_TILES.planeLikely(imageData, // double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may
													// be null - will handle later?
					POST_PROCESSING.interSensor.mapWidth, // int imageFullWidth,
					POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineTilePeriod,
					POST_PROCESSING.disparityCorrelationParameters.refinePhaseCoeff, // double refinePhaseCoeff,
					POST_PROCESSING.disparityCorrelationParameters.refineHighPassSigma, // double refineHighPassSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineLowPassSigma, // double refineLowPassSigma,
					POST_PROCESSING.disparityCorrelationParameters.refineSubPixel,
					POST_PROCESSING.disparityCorrelationParameters.zMapMinForeground, // double zMapMinForeground,
					POST_PROCESSING.disparityCorrelationParameters.zMapVarMask, // int zMapVarMask,
					POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds, // double [] zMapVarThresholds,
					POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights,
					POST_PROCESSING.disparityCorrelationParameters.auxVarMode,
					POST_PROCESSING.disparityCorrelationParameters.normalizeCorrelation, // normalize correlation by the
																							// total energy
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrMask, // int zMapCorrMask,
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholds, // double [] zMapCorrThresholds,
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrThresholdsRel, // double []
																							// zMapCorrThresholdsRel,
					POST_PROCESSING.disparityCorrelationParameters.zMapCorrWeights,
					POST_PROCESSING.disparityCorrelationParameters.debugRow,
					POST_PROCESSING.disparityCorrelationParameters.debugColumn, 0, // int combineMode, // different
																					// image pairs - 0
					POST_PROCESSING.disparityCorrelationParameters.filter2DisparityMax, // double disparityMax,
					POST_PROCESSING.disparityCorrelationParameters.filter2disparityMin, // double disparityMin,
					(POST_PROCESSING.disparityCorrelationParameters.filter2UpdateMax
							? POST_PROCESSING.disparityCorrelationParameters.filter2MinAbsolute
							: Double.NaN), // double minAbsolute, // or NaN - will use enabled/disabled state of the
											// tile
					(POST_PROCESSING.disparityCorrelationParameters.filter2UpdateMax
							? POST_PROCESSING.disparityCorrelationParameters.filter2MinRelative
							: Double.NaN), // double minRelative,
					POST_PROCESSING.disparityCorrelationParameters.filter2ByForeground, // boolean filterByForeground,
																						// // apply known certain masks
					POST_PROCESSING.disparityCorrelationParameters.filter2ByForegroundMargin, // double
																								// filterByForegroundMargin,
					POST_PROCESSING.disparityCorrelationParameters.filter2ByDisabled, // boolean filterByDisabled,
					POST_PROCESSING.disparityCorrelationParameters.filter2DisparityTolearnce, // double
																								// disparityTolearnce,
					POST_PROCESSING.disparityCorrelationParameters.filter2MaskBlurSigma, // double maskBlurSigma,
					POST_PROCESSING.disparityCorrelationParameters.filter2corrHighPassSigma, // double
																								// corrHighPassSigma, //
																								// subtract blurred
																								// version to minimize
																								// correlation caused by
																								// masks
					POST_PROCESSING.disparityCorrelationParameters.matchStatVarianceBlurScale,
					POST_PROCESSING.disparityCorrelationParameters.matchStatKLocal,
					POST_PROCESSING.disparityCorrelationParameters.matchStatMode, THREADS_MAX, // int threadsMax,
					UPDATE_STATUS, // boolean showProgress,
					DEBUG_LEVEL); // int debugLevel)

			if (POST_PROCESSING.disparityCorrelationParameters.zMapVarMask != 0) {
				float[][] pixels = DISPARITY_TILES.renderZMap(POST_PROCESSING.disparityCorrelationParameters.zMapWOI, 0,
						false);
				String[] zMapTitles = new String[pixels.length];
				for (int nImg = 0; nImg < zMapTitles.length; nImg++)
					zMapTitles[nImg] = "img-" + nImg;
				ShowDoubleFloatArrays.showArrays(pixels, POST_PROCESSING.disparityCorrelationParameters.zMapWOI.width,
						POST_PROCESSING.disparityCorrelationParameters.zMapWOI.height, true, "Likely" +
						// "OF-Bg"+POST_PROCESSING.disparityCorrelationParameters.bgFraction+
						// "-VS"+POST_PROCESSING.disparityCorrelationParameters.blurVarianceSigma+
						// "-MV"+POST_PROCESSING.disparityCorrelationParameters.zMapVarMask+
						// "-VY"+POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[0]+
						// "-VCb"+POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[1]+
						// "-VCr"+POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[2]+
						// "-VAux"+POST_PROCESSING.disparityCorrelationParameters.zMapVarThresholds[3]+
						// "-WVY"+POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[0]+
						// "-WVCb"+POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[1]+
						// "-WVCr"+POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[2]+
						// "-WVAux"+POST_PROCESSING.disparityCorrelationParameters.zMapVarWeights[3],
								"",
						zMapTitles);
			}

			return;

			/* ======================================================================== */
		} else if (label.equals("DCT test 1")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
//    	IJ.showMessage("DCT test 1");
			int n = 32;
			DttRad2 dtt = new DttRad2(n);
			double[] x = new double[n];
			double[] y = new double[n];
			@SuppressWarnings("unused")
			double[] xr = new double[n];
			double[] yc = new double[n];
			double[] yr1 = new double[n];
			double[] dindex = new double[n];
			double[] shiftXY = { 0.0 };
			if (!getDCTShiftwDialog(shiftXY))
				return;
			double[] cos_shift_x = new double[n];
			double[] sin_shift_x = new double[n];
			for (int ii = 0; ii < n; ii++) {
				cos_shift_x[ii] = Math.cos(Math.PI * (ii + 0.5) * shiftXY[0] / n);
				sin_shift_x[ii] = Math.sin(Math.PI * (ii + 0.5) * shiftXY[0] / n);
			}
			double[] ys;
			double[] ys1;
			double[] y_shifted = new double[n];

			for (int ii = 0; ii < n; ii++) {
				dindex[ii] = ii;
				x[ii] = 0.0;
			}
//    	x[1] = 1.0;
			x[n - 2] = 1.0;
			y = dtt.dctiv_direct(x);
			xr = dtt.dctiv_direct(y);
			yc = dtt.dct_iv(x);
			ys = dtt.dst_iv(x);
			ys1 = dtt.dstiv_direct(x);

//    	xr1= dtt.dct_iv(yc);
			for (int ii = 0; ii < n; ii++) {
				y_shifted[ii] = cos_shift_x[ii] * yc[ii] - sin_shift_x[ii] * ys[ii];
			}
			yr1 = dtt.dct_iv(y_shifted);

			PlotWindow.noGridLines = false; // draw grid lines
			Plot plot = new Plot("Example Plot", "X Axis", "Y Axis", dindex, x);
			plot.setLimits(0, n - 1, -2, 2);
			plot.setLineWidth(1);

			plot.setColor(Color.red);
			plot.addPoints(dindex, yc, PlotWindow.X);
			plot.addPoints(dindex, yc, PlotWindow.LINE);

			plot.setColor(Color.black);
			plot.addPoints(dindex, ys, PlotWindow.X);
			plot.addPoints(dindex, ys1, PlotWindow.LINE);

			plot.setColor(Color.magenta);
			plot.addPoints(dindex, y_shifted, PlotWindow.X);
			plot.addPoints(dindex, y_shifted, PlotWindow.LINE);

			plot.setColor(Color.cyan);
			plot.addPoints(dindex, yr1, PlotWindow.X);
			plot.addPoints(dindex, yr1, PlotWindow.LINE);

			plot.setColor(Color.blue);

			plot.show();
			return;
			/* ======================================================================== */
			// public ImagePlus DBG_IMP = null;
		} else if (label.equals("select MDCT image")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
//       	IJ.showMessage("DCT test 1");
			if (!DCT_PARAMETERS.showDialog())
				return;
			// process selected image stack
			ImagePlus imp_src = WindowManager.getCurrentImage();
			if (imp_src == null) {
				IJ.showMessage("Error", "JP4 image or Bayer image stack required");
				return;
			}
			if (imp_src.getStackSize() < 3) { // convert JP4 to image stack

				EyesisCorrectionParameters.SplitParameters split_parameters = new EyesisCorrectionParameters.SplitParameters(
						1, // oversample;
						// Add just for mdct (N/2)
						DCT_PARAMETERS.dct_size / 2, // addLeft
						DCT_PARAMETERS.dct_size / 2, // addTop
						DCT_PARAMETERS.dct_size / 2, // addRight
						DCT_PARAMETERS.dct_size / 2 // addBottom
				);

				ImageStack sourceStack = bayerToStack(imp_src, // source Bayer image, linearized, 32-bit (float))
						split_parameters);
				DBG_IMP = new ImagePlus(imp_src.getTitle() + "-SPIT", sourceStack);
				if (DEBUG_LEVEL > 1) {
					DBG_IMP.getProcessor().resetMinAndMax();
					DBG_IMP.show();
				}
			} else {
				DBG_IMP = imp_src;
			}
			/* ======================================================================== */
		} else if (label.equals("MDCT scale")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
//       	IJ.showMessage("DCT test 1");
			if (!DCT_PARAMETERS.showDialog())
				return;
			// process selected image stack
			if (DBG_IMP == null) {
				ImagePlus imp_src = WindowManager.getCurrentImage();
				if (imp_src == null) {
					IJ.showMessage("Error", "JP4 image or Bayer image stack required");
					return;
				}
				// ImagePlus imp2;
				if (imp_src.getStackSize() < 3) { // convert JP4 to image stack

					EyesisCorrectionParameters.SplitParameters split_parameters = new EyesisCorrectionParameters.SplitParameters(
							1, // oversample;
							// Add just for mdct (N/2)
							DCT_PARAMETERS.dct_size / 2, // addLeft
							DCT_PARAMETERS.dct_size / 2, // addTop
							DCT_PARAMETERS.dct_size / 2, // addRight
							DCT_PARAMETERS.dct_size / 2 // addBottom
					);

					ImageStack sourceStack = bayerToStack(imp_src, // source Bayer image, linearized, 32-bit (float))
							split_parameters);
					DBG_IMP = new ImagePlus(imp_src.getTitle() + "-SPIT", sourceStack);
					DBG_IMP.getProcessor().resetMinAndMax();
					DBG_IMP.show();
				} else {
					DBG_IMP = imp_src;
				}

			}
			ImageDtt image_dtt = new ImageDtt(4, // number of sensors - not used here ?
					DCT_PARAMETERS.dct_size, null, // FIXME: needs ImageDttParameters (clt_parameters.img_dtt),
					false, // aux
					false, // mono
					false, // lwir
					1.0); // Bayer( not monochrome), scale correlation strengths
			double[][][][] dctdc_data = image_dtt.mdctScale(DBG_IMP.getStack(), DCT_PARAMETERS.kernel_chn,
					DCT_PARAMETERS, THREADS_MAX, DEBUG_LEVEL, UPDATE_STATUS);

			for (int chn = 0; chn < dctdc_data.length; chn++) {
				image_dtt.dct_scale(DCT_PARAMETERS.dbg_scale, // DCT_PARAMETERS.dct_size / DCT_PARAMETERS.dbg_src_size ,
																// //final double scale_hor, // < 1.0 - enlarge in dct
																// domain (shrink in time/space)
						DCT_PARAMETERS.dbg_scale, // DCT_PARAMETERS.dct_size / DCT_PARAMETERS.dbg_src_size, // final
													// double scale_vert, // < 1.0 - enlarge in dct domain (shrink in
													// time/space)
						DCT_PARAMETERS.normalize, // final boolean normalize, // preserve weighted dct values
						dctdc_data[chn], // final double [][][] dct_data,
						DCT_PARAMETERS.tileX, DCT_PARAMETERS.tileY, THREADS_MAX, DEBUG_LEVEL);
			}

			for (int chn = 0; chn < dctdc_data.length; chn++) {
				image_dtt.dct_lpf(DCT_PARAMETERS.dbg_sigma, dctdc_data[chn], THREADS_MAX, DEBUG_LEVEL);
			}

//           int tilesY = DBG_IMP.getHeight()/DCT_PARAMETERS.dct_size - 1;
//           int tilesX = DBG_IMP.getWidth()/DCT_PARAMETERS.dct_size - 1;
			int tilesY = dctdc_data[0].length;
			int tilesX = dctdc_data[0][0].length;
			System.out.println("tilesX=" + tilesX);
			System.out.println("tilesY=" + tilesY);
			double[][] dct = new double[dctdc_data.length][];
			for (int chn = 0; chn < dct.length; chn++) {
				dct[chn] = image_dtt.lapped_dct_dbg(dctdc_data[chn], THREADS_MAX, DEBUG_LEVEL);
			}
//           System.out.println("dct_dc.length="+dct_dc.length+" dct_ac.length="+dct_ac.length);
			if (DEBUG_LEVEL > 0) {
				ShowDoubleFloatArrays.showArrays(dct, tilesX * DCT_PARAMETERS.dct_size, tilesY * DCT_PARAMETERS.dct_size, true,
						DBG_IMP.getTitle() + "-DCT");
			}
			double[][] idct_data = new double[dctdc_data.length][];
			for (int chn = 0; chn < idct_data.length; chn++) {
				idct_data[chn] = image_dtt.lapped_idct(dctdc_data[chn], // scanline representation of dcd data,
																		// organized as dct_size x dct_size tiles
						DCT_PARAMETERS.dct_size, // final int
						DCT_PARAMETERS.dct_window, // window_type
						THREADS_MAX, // maximal number of threads to launch
						DEBUG_LEVEL); // globalDebugLevel)
			}
			ShowDoubleFloatArrays.showArrays(idct_data, (tilesX + 1) * DCT_PARAMETERS.dct_size,
					(tilesY + 1) * DCT_PARAMETERS.dct_size, true, DBG_IMP.getTitle() + "-IDCTDC");
			return;

			/* ======================================================================== */
		} else if (label.equals("MDCT stack")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
//    	IJ.showMessage("DCT test 1");
			if (!DCT_PARAMETERS.showDialog())
				return;
// process selected image stack
			if (DBG_IMP == null) {
				ImagePlus imp_src = WindowManager.getCurrentImage();
				if (imp_src == null) {
					IJ.showMessage("Error", "JP4 image or Bayer image stack required");
					return;
				}
				// ImagePlus imp2;
				if (imp_src.getStackSize() < 3) { // convert JP4 to image stack

					EyesisCorrectionParameters.SplitParameters split_parameters = new EyesisCorrectionParameters.SplitParameters(
							1, // oversample;
							// Add just for mdct (N/2)
							DCT_PARAMETERS.dct_size / 2, // addLeft
							DCT_PARAMETERS.dct_size / 2, // addTop
							DCT_PARAMETERS.dct_size / 2, // addRight
							DCT_PARAMETERS.dct_size / 2 // addBottom
					);

					ImageStack sourceStack = bayerToStack(imp_src, // source Bayer image, linearized, 32-bit (float))
							split_parameters);
					DBG_IMP = new ImagePlus(imp_src.getTitle() + "-SPIT", sourceStack);
					DBG_IMP.getProcessor().resetMinAndMax();
					DBG_IMP.show();
				} else {
					DBG_IMP = imp_src;
				}

			}
			ImageDtt image_dtt = new ImageDtt(4, // number of sensors - not used here ?
					DCT_PARAMETERS.dct_size, null, // FIXME: needs ImageDttParameters (clt_parameters.img_dtt),
					false, // aux
					false, // mono
					false, // lwir
					1.0); // Bayer( not monochrome), scale correlation strengths
			double[][][][] dctdc_data = image_dtt.mdctStack(DBG_IMP.getStack(), DCT_PARAMETERS.kernel_chn,
					DCT_PARAMETERS, EYESIS_DCT, THREADS_MAX, DEBUG_LEVEL, UPDATE_STATUS);

			for (int chn = 0; chn < dctdc_data.length; chn++) {
				image_dtt.dct_lpf(DCT_PARAMETERS.dbg_sigma, dctdc_data[chn], THREADS_MAX, DEBUG_LEVEL);
			}

			int tilesY = DBG_IMP.getHeight() / DCT_PARAMETERS.dct_size - 1;
			int tilesX = DBG_IMP.getWidth() / DCT_PARAMETERS.dct_size - 1;
			System.out.println("tilesX=" + tilesX);
			System.out.println("tilesY=" + tilesY);
			double[][] dct = new double[dctdc_data.length][];
			for (int chn = 0; chn < dct.length; chn++) {
				dct[chn] = image_dtt.lapped_dct_dbg(dctdc_data[chn], THREADS_MAX, DEBUG_LEVEL);
			}
//        System.out.println("dct_dc.length="+dct_dc.length+" dct_ac.length="+dct_ac.length);
			if (DEBUG_LEVEL > 0) {
				ShowDoubleFloatArrays.showArrays(dct, tilesX * DCT_PARAMETERS.dct_size, tilesY * DCT_PARAMETERS.dct_size, true,
						DBG_IMP.getTitle() + "-DCT");
			}
			double[][] idct_data = new double[dctdc_data.length][];
			for (int chn = 0; chn < idct_data.length; chn++) {
				idct_data[chn] = image_dtt.lapped_idct(dctdc_data[chn], // scanline representation of dcd data,
																		// organized as dct_size x dct_size tiles
						DCT_PARAMETERS.dct_size, // final int
						DCT_PARAMETERS.dct_window, // window_type
						THREADS_MAX, // maximal number of threads to launch
						DEBUG_LEVEL); // globalDebugLevel)
			}
			ShowDoubleFloatArrays.showArrays(idct_data, (tilesX + 1) * DCT_PARAMETERS.dct_size,
					(tilesY + 1) * DCT_PARAMETERS.dct_size, true, DBG_IMP.getTitle() + "-IDCTDC");
			return;
			/* ======================================================================== */

		} else if (label.equals("Setup DCT parameters")) {
			DCT_PARAMETERS.showDialog();
			return;

			/* ======================================================================== */

		} else if (label.equals("DCT process files")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (EYESIS_DCT == null) {
				EYESIS_DCT = new EyesisDCT(PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS, DCT_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new EyesisDCT instance, will need to read DCT kernels");
				}
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;
			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);
			int numChannels = EYESIS_CORRECTIONS.getNumChannels();
			NONLIN_PARAMETERS.modifyNumChannels(numChannels);
			CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);
			if (!EYESIS_DCT.DCTKernelsAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Reading/converting DCT kernels");
				}
				EYESIS_DCT.readDCTKernels(DCT_PARAMETERS, CONVOLVE_FFT_SIZE / 2, THREADS_MAX, UPDATE_STATUS, // update
																												// status
																												// info
						DEBUG_LEVEL);
				if (DEBUG_LEVEL > 1) {
					EYESIS_DCT.showKernels(); // show restored kernels
				}
			}
			EYESIS_DCT.processDCTChannelImages( // hard-wired 4 sensors!
					DCT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					DEBAYER_PARAMETERS, // EyesisCorrectionParameters.DebayerParameters debayerParameters,
					NONLIN_PARAMETERS, // EyesisCorrectionParameters.NonlinParameters nonlinParameters,
					COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					CONVOLVE_FFT_SIZE, // int convolveFFTSize, // 128 - fft size, kernel size should be size/2
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL); // final int debugLevel);

			if (configPath != null) {
				saveTimestampedProperties( // save config again
						configPath, // full path or null
						null, // use as default directory if path==null
						true, PROPERTIES);
			}

			return;

			/* ======================================================================== */

		} else if (label.equals("DCT test 3")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			int n = 32;
			int n2 = 2 * n;
			int window_type = 1; // 0;
			int tilesY = 4;
			int tilesX = 4;
			double blurSigma = 0.8;
			DoubleGaussianBlur gb = null;
			if (blurSigma > 0)
				gb = new DoubleGaussianBlur();

			int iw = (tilesX + 1) * n;
			int ih = (tilesY + 1) * n;
			DttRad2 dtt4 = new DttRad2(4);
			dtt4.set_window(window_type);
			DttRad2 dtt8 = new DttRad2(8);
			dtt8.set_window(window_type);

			DttRad2 dtt = new DttRad2(n);
			dtt.set_window(window_type);

			double[] p = new double[n * n];
			for (int ii = 0; ii < p.length; ii++)
				p[ii] = 0;
			for (int ii = 0; ii < 10; ii++) {
				p[(ii / 3) * n + (ii)] = 1.0; // shifted delta;
//    		p[(n-1-ii/3)*n  + (n-1-ii)] = 1.0; // shifted delta;

			}

			if (blurSigma > 0) {
				gb.blurDouble(p, n, n, blurSigma, blurSigma, 0.01);
			}
			ShowDoubleFloatArrays.showArrays(p, n, n, "p " + n + "x" + n);
			double[] Fciip = dtt.dttt_ii(p, n);
			ShowDoubleFloatArrays.showArrays(Fciip, n, n, "p " + n + "x" + n);

			double[] x = new double[n * n];
			for (int ii = 0; ii < x.length; ii++)
				x[ii] = 0;
			/*
			 * for (int ii = 0; ii<10;ii++){ x[(5+ii)*n + (11+ii)] = 1.0; // shifted delta;
			 * }
			 * 
			 * if (blurSigma>0){ gb.blurDouble(x, n, n, blurSigma, blurSigma, 0.01); }
			 */

//    	x[5*n  + 11] = 1.0; // shifted delta;
//    	x[17*n + 15] = 1.0; // shifted delta;
			x[10 * n + 8] = 1.0; // shifted delta;
			x[26 * n + 27] = 1.0; // shifted delta;
			/*
			 * for (int ii=0;ii<n;ii++){ for (int jj=0;jj<n;jj++){ x[ii*n+jj] =
			 * Math.cos(2*Math.PI/(n*Math.sqrt(n))*(ii*ii+jj*jj)); } }
			 */
			double[] shiftXY = { 0.0, 0.0 };
			if (!getDCTShiftwDialog(shiftXY))
				return;
			double[] cos_shift_x = new double[n];
			double[] sin_shift_x = new double[n];
			double[] cos_shift_y = new double[n];
			double[] sin_shift_y = new double[n];
			for (int ii = 0; ii < n; ii++) {
				cos_shift_x[ii] = Math.cos(Math.PI * (ii + 0.5) * shiftXY[0] / n);
				sin_shift_x[ii] = Math.sin(Math.PI * (ii + 0.5) * shiftXY[0] / n);
				cos_shift_y[ii] = Math.cos(Math.PI * (ii + 0.5) * shiftXY[1] / n);
				sin_shift_y[ii] = Math.sin(Math.PI * (ii + 0.5) * shiftXY[1] / n);
			}

			ShowDoubleFloatArrays.showArrays(x, n, n, "x " + n + "x" + n);
			double[][] yy = new double[4][];
			double[][] yr = new double[3][];
			yy[0] = dtt.dttt_iv(x, 0, n);
			yy[1] = dtt.dttt_iv(x, 1, n);
			yy[2] = dtt.dttt_iv(x, 2, n);
			yy[3] = dtt.dttt_iv(x, 3, n);
			ShowDoubleFloatArrays.showArrays(yy, n, n, true, "y " + n + "x" + n);

//        double [] y = dtt.dttt_iv(x, 0, n);
//        ShowDoubleFloatArrays.showArrays(y, n, n, "y "+n+"x"+n);
			yr[0] = dtt.dttt_iv(yy[0], 0, n);
//        System.out.println("cos_shift_x.length="+cos_shift_x.length+" sin_shift_x.length="+sin_shift_x.length);
//        System.out.println("yy[0].length="+yy[0].length+" yy[1].length="+yy[0].length);
			double[] y = new double[n * n];
			for (int iy = 0; iy < n; iy++) {
				for (int ix = 0; ix < n; ix++) {
//    			y[n*iy+ix]=cos_shift_x[ix]*yy[0][n*iy+ix]-sin_shift_x[ix]*yy[1][n*iy+ix];
					y[n * iy + ix] = cos_shift_x[ix] * yy[0][n * iy + ix] - sin_shift_x[ix] * yy[2][n * iy + ix];
				}
			}
			yr[1] = dtt.dttt_iv(y, 0, n);
			double[] yconv = new double[n * n];
			for (int ii = 0; ii < y.length; ii++) {
				yconv[ii] = (yy[0][ii] + yy[3][ii]) * Fciip[ii] * 50;

			}
			yr[2] = dtt.dttt_iv(yconv, 0, n);

			ShowDoubleFloatArrays.showArrays(yr, n, n, true, "yr " + n + "x" + n);

			if (n < 64)
				return;

			double[] mx = new double[iw * ih];
			double[][] mxt = new double[tilesY * tilesX][];
			double[][] mxtf = new double[tilesY * tilesX][]; // folded
			double[][] mxtfu = new double[tilesY * tilesX][]; // folded/unfolded
			double[][] mycc = new double[tilesY * tilesX][]; // dct for x and y
			double[][] mysc = new double[tilesY * tilesX][]; // dst for x, dct for y
			double[][] myccx = new double[tilesY * tilesX][]; // dct shifted in x direction
			double[][] myt = new double[tilesY * tilesX][];
			double[][] imyt = new double[tilesY * tilesX][];
			double[] imy = new double[iw * ih];

			for (int ii = 0; ii < mx.length; ii++)
				mx[ii] = 0;
			for (int ii = 0; ii < imy.length; ii++)
				imy[ii] = 0;
			for (int iy = 0; iy < n; iy++)
				for (int ix = 0; ix < n; ix++)
					mx[iw * (iy + n) + (ix + n)] = x[n * iy + ix];
			for (int iy = 0; iy < n; iy++)
				for (int ix = 0; ix < n; ix++)
					mx[iw * (iy + 2 * n) + (ix + n)] = x[n * (n - 1 - iy) + ix];
			for (int iy = 0; iy < n; iy++)
				for (int ix = 0; ix < n; ix++)
					mx[iw * (iy + 3 * n) + (ix + n)] = x[n * iy + (n - 1 - ix)];
			for (int iy = 0; iy < n; iy++)
				for (int ix = 0; ix < n; ix++)
					mx[iw * (iy + n) + (ix + 2 * n)] = x[n * (n - 1 - iy) + (n - 1 - ix)];
			for (int iy = 0; iy < n; iy++)
				for (int ix = 0; ix < n; ix++)
					mx[iw * (iy + 2 * n) + (ix + 2 * n)] = x[n * iy + ix];
			for (int iy = 0; iy < n; iy++)
				for (int ix = 0; ix < n; ix++)
					mx[iw * (iy + 3 * n) + (ix + 2 * n)] = x[n * (n - 1 - iy) + ix];
			for (int iy = 0; iy < n; iy++)
				for (int ix = 0; ix < n; ix++)
					mx[iw * (iy + n) + (ix + 3 * n)] = x[n * iy + (n - 1 - ix)];
			for (int iy = 0; iy < n; iy++)
				for (int ix = 0; ix < n; ix++)
					mx[iw * (iy + 2 * n) + (ix + 3 * n)] = x[n * (n - 1 - iy) + (n - 1 - ix)];
			for (int iy = 0; iy < n; iy++)
				for (int ix = 0; ix < n; ix++)
					mx[iw * (iy + 3 * n) + (ix + 3 * n)] = x[n * iy + ix];
			for (int ii = 0; ii < mx.length; ii++) {
//    		if ((((ii % iw) ^ (ii / iw)) & 1) !=0) mx[ii] = 0;
			}
			ShowDoubleFloatArrays.showArrays(mx, iw, ih, "mx " + iw + "x" + ih);

			for (int tileY = 0; tileY < tilesY; tileY++) {
				for (int tileX = 0; tileX < tilesX; tileX++) {
					double[] tile = new double[4 * n * n];
					int tileN = tileY * tilesX + tileX;
					for (int iy = 0; iy < n2; iy++) {
						for (int ix = 0; ix < n2; ix++) {
							tile[n2 * iy + ix] = mx[iw * (iy + n * tileY) + (ix + n * tileX)];
						}
					}
					mxt[tileN] = tile.clone();
					mxtf[tileN] = dtt.fold_tile(tile, n, 0); // DCCT
					mxtfu[tileN] = dtt.unfold_tile(mxtf[tileN], n, 0); // DCCT

					mycc[tileN] = dtt.dttt_iv(mxtf[tileN], 0, n);
//            	mysc[tileN] =   dtt.dttt_iv     (mxtf[tileN], 1, n); // x - sin, y - cos
					mysc[tileN] = dtt.dttt_iv(mxtf[tileN], 2, n); // x - sin, y - cos

					myccx[tileN] = new double[n * n];
					int indx = 0;
					for (int iy = 0; iy < n; iy++) {
						for (int ix = 0; ix < n; ix++) {
							myccx[tileN][indx] = cos_shift_x[ix] * mycc[tileN][indx]
									- sin_shift_x[ix] * mysc[tileN][indx];
							indx++;
						}
					}
					myt[tileN] = dtt.dttt_iv(myccx[tileN], 0, n);
					imyt[tileN] = dtt.unfold_tile(myt[tileN], n, 0); // DCCT, each tile - imdct

					for (int iy = 0; iy < n2; iy++) {
						for (int ix = 0; ix < n2; ix++) {
							imy[iw * (iy + n * tileY) + (ix + n * tileX)] += imyt[tileN][n2 * iy + ix];
//            			imy[iw*(iy + n * tileY)+ (ix+ n*tileX)] += mxtfu[tileN][n2*iy + ix] ;
						}
					}
				}
			}
			ShowDoubleFloatArrays.showArrays(mxt, n2, n2, true, "mxt " + n2 + "x" + n2);
			ShowDoubleFloatArrays.showArrays(mxtf, n, n, true, "mxtf " + n + "x" + n);
			ShowDoubleFloatArrays.showArrays(mxtfu, n2, n2, true, "mxtfu " + n2 + "x" + n2);
			ShowDoubleFloatArrays.showArrays(mycc, n, n, true, "mycc" + n + "x" + n);
			ShowDoubleFloatArrays.showArrays(mysc, n, n, true, "mysc" + n + "x" + n);
			ShowDoubleFloatArrays.showArrays(myccx, n, n, true, "myccx" + n + "x" + n);
			ShowDoubleFloatArrays.showArrays(myt, n, n, true, "myt " + n + "x" + n);
			ShowDoubleFloatArrays.showArrays(imyt, n2, n2, true, "imyt " + n2 + "x" + n2);
			ShowDoubleFloatArrays.showArrays(imy, iw, ih, "imy " + iw + "x" + ih);

			return;
		} else if (label.equals("Test Kernel Factorization")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (!DCT_PARAMETERS.showDialog())
				return;
			FactorConvKernel factorConvKernel = new FactorConvKernel();
			factorConvKernel.setDebugLevel(DEBUG_LEVEL);
//        factorConvKernel.setTargetWindowMode(DCT_PARAMETERS.dbg_window_mode, DCT_PARAMETERS.centerWindowToTarget);
			factorConvKernel.setTargetWindowMode(DCT_PARAMETERS.centerWindowToTarget);
			factorConvKernel.numIterations = DCT_PARAMETERS.LMA_steps;
			factorConvKernel.setAsymCompactness(DCT_PARAMETERS.compactness, DCT_PARAMETERS.asym_tax_free);
			factorConvKernel.setSymCompactness(DCT_PARAMETERS.sym_compactness);
			factorConvKernel.setDCWeight(DCT_PARAMETERS.dc_weight);

			int target_kernel_size = 2 * DCT_PARAMETERS.dct_size - 1;
//    	int target_expanded_size = 2*DCT_PARAMETERS.dct_size + DCT_PARAMETERS.asym_size -2;

			int target_expanded_size = 4 * DCT_PARAMETERS.dct_size;
			int target_antiperiodic_size = 2 * DCT_PARAMETERS.dct_size;

			double[] target_expanded = null;
			double[] target_antiperiodic = null;
			if ((EYESIS_DCT != null) && EYESIS_DCT.kernelImageSet() && (DCT_PARAMETERS.color_channel >= 0)) {
				System.out.println("Using extracted target kernel");
				double[] src_kernel = EYESIS_DCT.extractOneKernelFromStack(CONVOLVE_FFT_SIZE / 2, // 64
						DCT_PARAMETERS.color_channel, // 0..2
						DCT_PARAMETERS.tileX, // horizontal number of kernel to extract
						DCT_PARAMETERS.tileY); // vertical number of kernel to extract

				if ((DCT_PARAMETERS.decimation == 2) && (DCT_PARAMETERS.decimateSigma < 0)) {
					target_expanded = EYESIS_DCT.reformatKernel2(src_kernel, // will be blured in-place
							CONVOLVE_FFT_SIZE / 2, // typical 64
							target_expanded_size); // typical 15

				} else {
					target_expanded = EYESIS_DCT.reformatKernel(src_kernel, // will be blured in-place
							CONVOLVE_FFT_SIZE / 2, // typical 64
							target_expanded_size, // typical 15
							DCT_PARAMETERS.decimation, // typical 2
							DCT_PARAMETERS.decimateSigma);
				}
				if (DCT_PARAMETERS.normalize) {
					double s = 0.0;
					for (int ii = 0; ii < target_expanded.length; ii++) {
						s += target_expanded[ii];
					}
					for (int ii = 0; ii < target_expanded.length; ii++) {
						target_expanded[ii] /= s;
					}
				}
//        	target_antiperiodic

			} else {
				System.out.println("Using synthesized target kernel");

				double[] target_kernel = new double[target_kernel_size * target_kernel_size];

				for (int ii = 0; ii < target_kernel.length; ii++)
					target_kernel[ii] = 0.0;
				double dist = Math.sqrt(
						(DCT_PARAMETERS.dbg_x1 - DCT_PARAMETERS.dbg_x) * (DCT_PARAMETERS.dbg_x1 - DCT_PARAMETERS.dbg_x)
								+ (DCT_PARAMETERS.dbg_y1 - DCT_PARAMETERS.dbg_y)
										* (DCT_PARAMETERS.dbg_y1 - DCT_PARAMETERS.dbg_y));
				int num_steps = (int) Math.round(dist + 0.5);
				dist = num_steps;
				for (int ii = 0; ii <= num_steps; ii++) {
					int dbg_x = (int) Math
							.round((DCT_PARAMETERS.dbg_x1 - DCT_PARAMETERS.dbg_x) * ii / dist + DCT_PARAMETERS.dbg_x);
					int dbg_y = (int) Math
							.round((DCT_PARAMETERS.dbg_y1 - DCT_PARAMETERS.dbg_y) * ii / dist + DCT_PARAMETERS.dbg_y);
					target_kernel[(target_kernel_size / 2 + dbg_y) * target_kernel_size
							+ (target_kernel_size / 2 + dbg_x)] = 1.0;
					if (MASTER_DEBUG_LEVEL > 2) {
						System.out.println(ii + ": "
								+ ((DCT_PARAMETERS.dbg_x1 - DCT_PARAMETERS.dbg_x) * ii / dist + DCT_PARAMETERS.dbg_x)
								+ " / "
								+ ((DCT_PARAMETERS.dbg_y1 - DCT_PARAMETERS.dbg_y) * ii / dist + DCT_PARAMETERS.dbg_y)
								+ " (" + dbg_x + ":" + dbg_y + ")");
					}

				}

				double blurSigma = DCT_PARAMETERS.dbg_sigma;
				DoubleGaussianBlur gb = null;
				if (blurSigma > 0)
					gb = new DoubleGaussianBlur();
				if (blurSigma > 0)
					gb.blurDouble(target_kernel, target_kernel_size, target_kernel_size, blurSigma, blurSigma, 0.01);
				// ShowDoubleFloatArrays.showArrays(target_kernel, target_kernel_size,
				// target_kernel_size, "target_kernel");

				if ((DCT_PARAMETERS.dbg_x == 0.0) && (DCT_PARAMETERS.dbg_x1 == 0.0) && (DCT_PARAMETERS.dbg_y == 0.0)
						&& (DCT_PARAMETERS.dbg_y1 == 0.0)) {
					System.out.println("Making sure target kernel is center symmetrical (copying top-left quater)");
					for (int ii = 0; ii < target_kernel_size; ii++)
						for (int jj = 0; jj < target_kernel_size; jj++) {
							int ii1 = (ii >= DCT_PARAMETERS.dct_size) ? (2 * DCT_PARAMETERS.dct_size - ii - 2) : ii;
							int jj1 = (jj >= DCT_PARAMETERS.dct_size) ? (2 * DCT_PARAMETERS.dct_size - jj - 2) : jj;
							target_kernel[target_kernel_size * ii + jj] = target_kernel[target_kernel_size * ii1 + jj1];
						}
				}
				target_expanded = new double[target_expanded_size * target_expanded_size];

				for (int ii = 0; ii < target_expanded.length; ii++)
					target_expanded[ii] = 0.0;
				int left_top_margin = (target_expanded_size - target_kernel_size) / 2; // ((DCT_PARAMETERS.asym_size-1)/2);
				for (int ii = 0; ii < target_kernel_size; ii++) {
					for (int jj = 0; jj < target_kernel_size; jj++) {
						target_expanded[(ii + left_top_margin) * target_expanded_size
								+ (jj + left_top_margin)] = target_kernel[ii * target_kernel_size + jj];
					}
				}
				ShowDoubleFloatArrays.showArrays(target_kernel, target_kernel_size, target_kernel_size, "target_kernel");
				ShowDoubleFloatArrays.showArrays(target_expanded, target_expanded_size, target_expanded_size,
						"target_expanded");

			}
			if (EYESIS_DCT == null) {
				EYESIS_DCT = new EyesisDCT(PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS, DCT_PARAMETERS);
			}
			target_antiperiodic = EYESIS_DCT.makeAntiperiodic(DCT_PARAMETERS.dct_size, target_expanded);
			ShowDoubleFloatArrays.showArrays(target_antiperiodic, target_antiperiodic_size, target_antiperiodic_size,
					"target_antiperiodic");

			boolean[] mask = null;
			if (!DCT_PARAMETERS.dbg_mask.equals("")) {
				mask = new boolean[DCT_PARAMETERS.asym_size * DCT_PARAMETERS.asym_size];
				for (int ii = 0; ii < mask.length; ii++) {
					mask[ii] = ((ii <= DCT_PARAMETERS.dbg_mask.length())
							&& (DCT_PARAMETERS.dbg_mask.charAt(ii) == '*'));
				}

			}
			long startTime = System.nanoTime();
			boolean result = factorConvKernel.calcKernels(target_antiperiodic, DCT_PARAMETERS.asym_size,
					DCT_PARAMETERS.dct_size, DCT_PARAMETERS.fact_precision, mask, DCT_PARAMETERS.seed_size,
					DCT_PARAMETERS.asym_random);
			System.out.println("factorConvKernel.calcKernels() returned: >>> " + result + " <<<");
			System.out
					.println("RMS = " + factorConvKernel.getRMSes()[0] + ", RMSPure = " + factorConvKernel.getRMSes()[1]
							+ ", relRMSPure = " + (factorConvKernel.getRMSes()[1] / factorConvKernel.getTargetRMS())
							+ ", spent " + IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec");
			double[] sym_kernel = factorConvKernel.getSymKernel();
			double[] asym_kernel = factorConvKernel.getAsymKernel();
			double[] convolved = factorConvKernel.getConvolved();
//        double [] target_weights = factorConvKernel.getTargetWeights();

			double[] diff100 = new double[convolved.length];
//        double [] weighted_diff100 = new double [convolved.length];
			for (int ii = 0; ii < convolved.length; ii++) {
				diff100[ii] = 100.0 * (target_antiperiodic[ii] - convolved[ii]);
//        	weighted_diff100[ii] = diff100[ii]* target_weights[ii];
			}
//        double [][] compare_kernels = {target_expanded, convolved, weighted_diff100,target_weights, diff100};
			double[][] compare_kernels = { target_antiperiodic, convolved, diff100 };
			if (DEBUG_LEVEL > 0) {
				System.out.println("DCT_PARAMETERS.dct_size=" + DCT_PARAMETERS.dct_size + " DCT_PARAMETERS.asym_size="
						+ DCT_PARAMETERS.asym_size);
				System.out.println("sym_kernel.length=" + sym_kernel.length);
				System.out.println("asym_kernel.length=" + asym_kernel.length);
				System.out.println("convolved.length=" + convolved.length);
			}
			ShowDoubleFloatArrays.showArrays(sym_kernel, DCT_PARAMETERS.dct_size, DCT_PARAMETERS.dct_size, "sym_kernel");
			ShowDoubleFloatArrays.showArrays(asym_kernel, DCT_PARAMETERS.asym_size, DCT_PARAMETERS.asym_size, "asym_kernel");
//        ShowDoubleFloatArrays.showArrays(compare_kernels,  target_expanded_size, target_expanded_size, true, "compare_kernels");
			ShowDoubleFloatArrays.showArrays(compare_kernels, target_antiperiodic_size, target_antiperiodic_size, true,
					"compare_kernels");
//
//        ShowDoubleFloatArrays.showArrays(convolved,  target_kernel_size, target_kernel_size,   "convolved");
			return;
		} else if (label.equals("Min Kernel Factorization")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (!DCT_PARAMETERS.showDialog())
				return;
			FactorConvKernel factorConvKernel = new FactorConvKernel();
			factorConvKernel.setDebugLevel(DEBUG_LEVEL);
//        factorConvKernel.setTargetWindowMode(DCT_PARAMETERS.dbg_window_mode, DCT_PARAMETERS.centerWindowToTarget);
			factorConvKernel.setTargetWindowMode(DCT_PARAMETERS.centerWindowToTarget);
			factorConvKernel.numIterations = DCT_PARAMETERS.LMA_steps;
			factorConvKernel.setAsymCompactness(DCT_PARAMETERS.compactness, DCT_PARAMETERS.asym_tax_free);
			factorConvKernel.setSymCompactness(DCT_PARAMETERS.sym_compactness);
			factorConvKernel.setDCWeight(DCT_PARAMETERS.dc_weight);
			int target_kernel_size = 2 * DCT_PARAMETERS.dct_size - 1;
//    	int target_expanded_size = 2*DCT_PARAMETERS.dct_size + DCT_PARAMETERS.asym_size -2;

			int target_expanded_size = 4 * DCT_PARAMETERS.dct_size;
			int target_antiperiodic_size = 2 * DCT_PARAMETERS.dct_size;
			double[] target_expanded = null;
			double[] target_antiperiodic = null;

			if ((EYESIS_DCT != null) && EYESIS_DCT.kernelImageSet() && (DCT_PARAMETERS.color_channel >= 0)) {
				System.out.println("Using extracted target kernel");
				double[] src_kernel = EYESIS_DCT.extractOneKernelFromStack(CONVOLVE_FFT_SIZE / 2, // 64
						DCT_PARAMETERS.color_channel, // 0..2
						DCT_PARAMETERS.tileX, // horizontal number of kernel to extract
						DCT_PARAMETERS.tileY); // vertical number of kernel to extract
				if ((DCT_PARAMETERS.decimation == 2) && (DCT_PARAMETERS.decimateSigma < 0)) {
					target_expanded = EYESIS_DCT.reformatKernel2(src_kernel, // will be blured in-place
							CONVOLVE_FFT_SIZE / 2, // typical 64
							target_expanded_size); // typical 15

				} else {
					target_expanded = EYESIS_DCT.reformatKernel(src_kernel, // will be blured in-place
							CONVOLVE_FFT_SIZE / 2, // typical 64
							target_expanded_size, // typical 15
							DCT_PARAMETERS.decimation, // typical 2
							DCT_PARAMETERS.decimateSigma);
				}
				if (DCT_PARAMETERS.normalize) {
					double s = 0.0;
					for (int ii = 0; ii < target_expanded.length; ii++) {
						s += target_expanded[ii];
					}
					for (int ii = 0; ii < target_expanded.length; ii++) {
						target_expanded[ii] /= s;
					}
				}
			} else {
				System.out.println("Using synthesized target kernel");
				double[] target_kernel = new double[target_kernel_size * target_kernel_size];
				for (int ii = 0; ii < target_kernel.length; ii++)
					target_kernel[ii] = 0.0;
				double dist = Math.sqrt(
						(DCT_PARAMETERS.dbg_x1 - DCT_PARAMETERS.dbg_x) * (DCT_PARAMETERS.dbg_x1 - DCT_PARAMETERS.dbg_x)
								+ (DCT_PARAMETERS.dbg_y1 - DCT_PARAMETERS.dbg_y)
										* (DCT_PARAMETERS.dbg_y1 - DCT_PARAMETERS.dbg_y));
				int num_steps = (int) Math.round(dist + 0.5);
				dist = num_steps;
				for (int ii = 0; ii <= num_steps; ii++) {
					int dbg_x = (int) Math
							.round((DCT_PARAMETERS.dbg_x1 - DCT_PARAMETERS.dbg_x) * ii / dist + DCT_PARAMETERS.dbg_x);
					int dbg_y = (int) Math
							.round((DCT_PARAMETERS.dbg_y1 - DCT_PARAMETERS.dbg_y) * ii / dist + DCT_PARAMETERS.dbg_y);
					target_kernel[(target_kernel_size / 2 + dbg_y) * target_kernel_size
							+ (target_kernel_size / 2 + dbg_x)] = 1.0;
					if (MASTER_DEBUG_LEVEL > 2) {
						System.out.println(ii + ": "
								+ ((DCT_PARAMETERS.dbg_x1 - DCT_PARAMETERS.dbg_x) * ii / dist + DCT_PARAMETERS.dbg_x)
								+ " / "
								+ ((DCT_PARAMETERS.dbg_y1 - DCT_PARAMETERS.dbg_y) * ii / dist + DCT_PARAMETERS.dbg_y)
								+ " (" + dbg_x + ":" + dbg_y + ")");
					}
				}

				double blurSigma = DCT_PARAMETERS.dbg_sigma;
				DoubleGaussianBlur gb = null;
				if (blurSigma > 0)
					gb = new DoubleGaussianBlur();
				if (blurSigma > 0)
					gb.blurDouble(target_kernel, target_kernel_size, target_kernel_size, blurSigma, blurSigma, 0.01);
				target_expanded = new double[target_expanded_size * target_expanded_size];
				for (int ii = 0; ii < target_expanded.length; ii++)
					target_expanded[ii] = 0.0;
				int left_top_margin = ((DCT_PARAMETERS.asym_size - 1) / 2);
				for (int ii = 0; ii < target_kernel_size; ii++) {
					for (int jj = 0; jj < target_kernel_size; jj++) {
						target_expanded[(ii + left_top_margin) * target_expanded_size
								+ (jj + left_top_margin)] = target_kernel[ii * target_kernel_size + jj];
					}
				}
			}
			if (EYESIS_DCT == null) {
				EYESIS_DCT = new EyesisDCT(PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS, DCT_PARAMETERS);
			}
			target_antiperiodic = EYESIS_DCT.makeAntiperiodic(DCT_PARAMETERS.dct_size, target_expanded);
			long startTime = System.nanoTime();
			int numPixels = factorConvKernel.calcKernels(target_antiperiodic, DCT_PARAMETERS.asym_size,
					DCT_PARAMETERS.dct_size, DCT_PARAMETERS.fact_precision, DCT_PARAMETERS.asym_pixels,
					DCT_PARAMETERS.asym_distance, DCT_PARAMETERS.seed_size);
			System.out.println("factorConvKernel.getRMSes().length=" + factorConvKernel.getRMSes().length);
			System.out.println("calcKernels() number of asym pixels = " + numPixels + " RMS = "
					+ factorConvKernel.getRMSes()[0] + ", RMSPure = " + factorConvKernel.getRMSes()[1] + ", RMSP_DC = "
					+ factorConvKernel.getRMSes()[2] + ", relRMSPure = "
					+ (factorConvKernel.getRMSes()[1] / factorConvKernel.getTargetRMS()) + ", relRMS_DC = "
					+ (factorConvKernel.getRMSes()[2] / factorConvKernel.getTargetRMS()) + ", number of LMA runs = "
					+ factorConvKernel.getLMARuns() + ", spent "
					+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec");

			double[] sym_kernel = factorConvKernel.getSymKernel();
			double[] asym_kernel = factorConvKernel.getAsymKernel();
			double[] convolved = factorConvKernel.getConvolved();
//        double [] target_weights = factorConvKernel.getTargetWeights();
			double[] diff100 = new double[convolved.length];
//        double [] weighted_diff100 = new double [convolved.length];
			double /* s0=0,s1 = 0, */ s2 = 0.0, s3 = 0.0;
			for (int ii = 0; ii < convolved.length; ii++) {
				diff100[ii] = 100.0 * (target_antiperiodic[ii] - convolved[ii]);
//        	weighted_diff100[ii] = diff100[ii]* target_weights[ii];
//        	s0+=target_weights[ii];
//        	s1+=target_expanded[ii]*target_weights[ii];
				s2 += convolved[ii];
				s3 += target_antiperiodic[ii];
				if (DEBUG_LEVEL > 3) {
					System.out.println(
							ii + ": t=" + target_antiperiodic[ii] + " c=" + convolved[ii] + " s2=" + s2 + " s3=" + s3);
				}
			}
			double sum_asym = 0.0, sum_sym = 0.0;
			for (int ii = 0; ii < asym_kernel.length; ii++) {
				sum_asym += asym_kernel[ii];
			}
			for (int ii = 0; ii < DCT_PARAMETERS.dct_size; ii++) {
				for (int jj = 0; jj < DCT_PARAMETERS.dct_size; jj++) {
					double d = sym_kernel[ii * DCT_PARAMETERS.dct_size + jj];
					if (ii > 0)
						d *= 2;
					if (jj > 0)
						d *= 2;
					sum_sym += d;
				}
			}

//        double [][] compare_kernels = {target_expanded, convolved, weighted_diff100,target_weights, diff100};
			double[][] compare_kernels = { target_antiperiodic, convolved, diff100 };
			if (DEBUG_LEVEL > 0) {
				System.out.println("DCT_PARAMETERS.dct_size=" + DCT_PARAMETERS.dct_size + " DCT_PARAMETERS.asym_size="
						+ DCT_PARAMETERS.asym_size);
				System.out.println("sym_kernel.length=" + sym_kernel.length);
				System.out.println("asym_kernel.length=" + asym_kernel.length);
				System.out.println("convolved.length=" + convolved.length);
//        	System.out.println("weights s0="+s0+" target s1/s0="+(s1/s0)+ "convolved s2/s0="+(s2/s0)+ " s2/s1="+(s2/s1));
				System.out.println("convolved s2=" + s2 + "target s3=" + s3);
				System.out.println("sum_asym = " + sum_asym + " sum_sym=" + sum_sym + " sum_asym*sum_sym="
						+ (sum_asym * sum_sym) + " sum(target) = " + s3);
			}

			DttRad2 dtt = new DttRad2(DCT_PARAMETERS.dct_size);
			double[] sym_dct_iii = dtt.dttt_iii(sym_kernel);
			double[] sym_dct_iii_ii = dtt.dttt_ii(sym_dct_iii);
			double[][] sym_kernels = { sym_kernel, sym_dct_iii, sym_dct_iii_ii };

			ShowDoubleFloatArrays.showArrays(sym_kernels, DCT_PARAMETERS.dct_size, DCT_PARAMETERS.dct_size, true,
					"sym_kernel_iii_ii");

////        ShowDoubleFloatArrays.showArrays(sym_kernel,    DCT_PARAMETERS.dct_size,       DCT_PARAMETERS.dct_size,   "sym_kernel");
			ShowDoubleFloatArrays.showArrays(asym_kernel, DCT_PARAMETERS.asym_size, DCT_PARAMETERS.asym_size, "asym_kernel");
			ShowDoubleFloatArrays.showArrays(compare_kernels, target_antiperiodic_size, target_antiperiodic_size, true,
					"compare_kernels");
//        ShowDoubleFloatArrays.showArrays(convolved,  target_kernel_size, target_kernel_size,   "convolved");
			return;

		} else if (label.equals("DCT test 4")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;
			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);

		} else if (label.equals("Select kernels image")) {
			if (!DCT_PARAMETERS.showDialog())
				return;
			if (EYESIS_DCT == null) {
				EYESIS_DCT = new EyesisDCT(PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS, DCT_PARAMETERS);
			}
			ImagePlus imp_src = WindowManager.getCurrentImage();
			if (imp_src == null) {
				IJ.showMessage("Error", "3-layer files of deconvolution kernels is required");
				return;
			}
			EYESIS_DCT.setKernelImageFile(imp_src);
// set CLT image file too
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
			}
			QUAD_CLT.setKernelImageFile(imp_src);

		} else if (label.equals("Create DCT kernels")) {
			if (!DCT_PARAMETERS.showDialog())
				return;
			if (EYESIS_DCT == null) {
				EYESIS_DCT = new EyesisDCT(PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS, DCT_PARAMETERS);
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;

			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);

			EYESIS_DCT.createDCTKernels(DCT_PARAMETERS, CONVOLVE_FFT_SIZE / 2, THREADS_MAX, UPDATE_STATUS, // update
																											// status
																											// info
					DEBUG_LEVEL);
//"Reset DCT kernels"
		} else if (label.equals("Reset DCT kernels")) {
			if (EYESIS_DCT != null) {
				EYESIS_DCT.resetDCTKernels();
			}

		} else if (label.equals("Read DCT kernels")) {
			if (!DCT_PARAMETERS.showDialog())
				return;
			if (EYESIS_DCT == null) {
				EYESIS_DCT = new EyesisDCT(PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS, DCT_PARAMETERS);
			}

			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;
			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);
			EYESIS_DCT.readDCTKernels(DCT_PARAMETERS, CONVOLVE_FFT_SIZE / 2, THREADS_MAX, UPDATE_STATUS, // update
																											// status
																											// info
					DEBUG_LEVEL);
//		EyesisCorrectionParameters.DCTParameters dCTParameters,
//		int srcKernelSize,
			EYESIS_DCT.showKernels(); // show restored kernels
			/* ======================================================================== */
		} else if (label.equals("Setup CLT Batch parameters")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CORRECTION_PARAMETERS.showCLTBatchDialog("CLT Batch parameters", CLT_PARAMETERS, true, this.commandFromMcp); // codex 2026-01-27
			return;

			/* ======================================================================== */
		} else if (label.equals("Setup CLT parameters")) {
			CLT_PARAMETERS.showJDialog(true, this.commandFromMcp); // codex 2026-01-27
			return;
			/* ======================================================================== */
		} else if (label.equals("Setup CLT")) {
			CLT_PARAMETERS.showJDialog(true, this.commandFromMcp); // codex 2026-01-27
			return;
			/* ======================================================================== */
		} else if (label.equals("Infinity offset")) {
			while (true) {
				if (!CLT_PARAMETERS.modifyZCorr("Modify infinity per image set disparity corrections"))
					break;
			}
			return;
			/* ======================================================================== */
		} else if (label.equals("Rig offset")) {
			while (true) {
				if (!CLT_PARAMETERS.modifyInfCorr("Modify infinity offset per image set disparity corrections"))
					break;
			}
			return;
			/* ======================================================================== */
		} else if (label.equals("Select CLT image")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_rslt = selectCLTImage();
			if (imp_rslt != null)
				DBG_IMP = imp_rslt;
			return;
			/* ======================================================================== */
		} else if (label.equals("Select second CLT image")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_rslt = selectCLTImage();
			if (imp_rslt != null)
				CORRELATE_IMP = imp_rslt;
			return;
			/* ======================================================================== */
		} else if (label.equals("CLT stack")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLTStack();
			return;
			/* ======================================================================== */
		} else if (label.equals("CLT correlate")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			runtime.gc();
			System.out.println("--- Free memory=" + runtime.freeMemory() + " (of " + runtime.totalMemory() + ")");
			CLTCorrelate();
			return;
//==============================================================================
		} else if (label.equals("Reset Geometry")) {
			if (!CLT_PARAMETERS.showJDialog())
				return;
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;
			String cltPath = EYESIS_CORRECTIONS.correctionsParameters.selectCLTKernelDirectory( // create if it does not
																								// exist
					true, true);
			if (cltPath == null) {
				String msg = "No CLT kernels (results) directory selected, command aborted";
				System.out.println("Warning: " + msg);
				IJ.showMessage("Warning", msg);
				return;
			}
			QUAD_CLT.resetGeometryCorrection();
			QUAD_CLT.initGeometryCorrection(DEBUG_LEVEL + 2);

			// ==============================================================================
		} else if (label.equals("Reset AUX Geometry")) {
			if (!CLT_PARAMETERS.showJDialog())
				return;
			if (EYESIS_CORRECTIONS_AUX == null) {
				EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
						CORRECTION_PARAMETERS.getAux());
			}
			if ((QUAD_CLT_AUX == null) || (QUAD_CLT_AUX.eyesisCorrections == null)) {
				QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
						CORRECTION_PARAMETERS.getAux());
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;
			String cltPath = EYESIS_CORRECTIONS.correctionsParameters.selectCLTKernelDirectory( // create if it does not
																								// exist
					true, true);
			if (cltPath == null) {
				String msg = "No CLT kernels (results) directory selected, command aborted";
				System.out.println("Warning: " + msg);
				IJ.showMessage("Warning", msg);
				return;
			}

			EYESIS_CORRECTIONS_AUX.initSensorFiles(DEBUG_LEVEL, true, // true - ignore missing files
					true, // boolean all_sensors,
					COLOR_PROC_PARAMETERS_AUX.correct_vignetting); // boolean correct_vignetting

			QUAD_CLT_AUX.resetGeometryCorrection();
			QUAD_CLT_AUX.initGeometryCorrection(DEBUG_LEVEL + 2);

//==============================================================================
		} else if (label.equals("Create CLT kernels")) {
			if (!CLT_PARAMETERS.showJDialog())
				return;
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;
			String cltPath = EYESIS_CORRECTIONS.correctionsParameters.selectCLTKernelDirectory( // create if it does not
																								// exist
					true, true);
			if (cltPath == null) {
				String msg = "No CLT kernels (results) directory selected, command aborted";
				System.out.println("Warning: " + msg);
				IJ.showMessage("Warning", msg);
				return;
			}

			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL, true, // true - ignore missing files
					true, // boolean all_sensors,
					COLOR_PROC_PARAMETERS.correct_vignetting); // boolean correct_vignetting

			QUAD_CLT.createCLTKernels(CLT_PARAMETERS, CONVOLVE_FFT_SIZE / 2, THREADS_MAX, UPDATE_STATUS, // update
																											// status
																											// info
					DEBUG_LEVEL);
//==============================================================================
		} else if (label.equals("Create AUX CLT kernels")) {
			if (!CLT_PARAMETERS.showJDialog())
				return;
			if (EYESIS_CORRECTIONS_AUX == null) {
				EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
						CORRECTION_PARAMETERS.getAux());
			}
			if ((QUAD_CLT_AUX == null) || (QUAD_CLT_AUX.eyesisCorrections == null)) {
				QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
						CORRECTION_PARAMETERS.getAux());
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;
			String cltPath = EYESIS_CORRECTIONS.correctionsParameters.selectCLTKernelDirectory( // create if it does not
																								// exist
					true, true);
			if (cltPath == null) {
				String msg = "No CLT kernels (results) directory selected, command aborted";
				System.out.println("Warning: " + msg);
				IJ.showMessage("Warning", msg);
				return;
			}

			EYESIS_CORRECTIONS_AUX.initSensorFiles(DEBUG_LEVEL, true, // true - ignore missing files
					true, // boolean all_sensors,
					COLOR_PROC_PARAMETERS_AUX.correct_vignetting); // boolean correct_vignetting

			QUAD_CLT_AUX.createCLTKernels(CLT_PARAMETERS, CONVOLVE_FFT_SIZE / 2, THREADS_MAX, UPDATE_STATUS, // update
																												// status
																												// info
					DEBUG_LEVEL);

			// "Reset DCT kernels"
		} else if (label.equals("Reset CLT kernels")) {
			if (QUAD_CLT != null) {
				QUAD_CLT.resetCLTKernels();
			}
			if (QUAD_CLT_AUX != null) {
				QUAD_CLT.resetCLTKernels();
			}

		} else if (label.equals("Read CLT kernels")) {
			if (!CLT_PARAMETERS.showJDialog())
				return;
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;
			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);

			QUAD_CLT.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
					DEBUG_LEVEL);
			if (DEBUG_LEVEL > -1) {
				QUAD_CLT.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);
			}

			return;
		} else if (label.equals("CLT process files")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}

			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;
			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);
			int numChannels = EYESIS_CORRECTIONS.getNumChannels();
			CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);

			if (!QUAD_CLT.CLTKernelsAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Reading CLT kernels");
				}
				QUAD_CLT.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);
				if (DEBUG_LEVEL > 1) {
					QUAD_CLT.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
							DEBUG_LEVEL);
				}
			}

///========================================

			QUAD_CLT.processCLTChannelImages(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL); // final int debugLevel);

			if (configPath != null) {
				saveTimestampedProperties( // save config again
						configPath, // full path or null
						null, // use as default directory if path==null
						true, PROPERTIES);
			}
			return;

		} else if (label.equals("CLT process sets")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;

			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);
			int numChannels = EYESIS_CORRECTIONS.getNumChannels();
//      NONLIN_PARAMETERS.modifyNumChannels(numChannels); // not used
			CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);

			if (!QUAD_CLT.CLTKernelsAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Reading CLT kernels");
				}
				QUAD_CLT.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);

				if (DEBUG_LEVEL > 1) {
					QUAD_CLT.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
							DEBUG_LEVEL);
				}
			}

			if (!QUAD_CLT.geometryCorrectionAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Calculating geometryCorrection");
				}
				if (!QUAD_CLT.initGeometryCorrection(DEBUG_LEVEL + 2)) {
					return;
				}
			}

///========================================

			QUAD_CLT.processCLTSets(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL); // final int debugLevel);

			if (configPath != null) {
				saveTimestampedProperties( // save config again
						configPath, // full path or null
						null, // use as default directory if path==null
						true, PROPERTIES);
			}
			return;

		} else if (label.equals("CLT 4 images") || label.equals("CLT apply fine corr")
				|| label.equals("CLT infinity corr") || label.equals("CORR TEST") || label.equals("ERS main")) {
			boolean apply_corr = label.equals("CLT apply fine corr");
			boolean infinity_corr = label.equals("CLT infinity corr");
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;

			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL, false, // boolean missing_ok,
					true, // boolean all_sensors, Otherwise - Eyesis
					COLOR_PROC_PARAMETERS.correct_vignetting); // boolean correct_vignetting

			int numChannels = EYESIS_CORRECTIONS.getNumChannels();
//        NONLIN_PARAMETERS.modifyNumChannels(numChannels);
			CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);

			if (!QUAD_CLT.CLTKernelsAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Reading CLT kernels");
				}
				QUAD_CLT.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);

				if (DEBUG_LEVEL > 1) {
					QUAD_CLT.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
							DEBUG_LEVEL);
				}
			}

			if (!QUAD_CLT.geometryCorrectionAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Calculating geometryCorrection");
				}
				if (!QUAD_CLT.initGeometryCorrection(DEBUG_LEVEL + 2)) {
					return;
				}
			}
			// After kernels and GeometryCorrection
			if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
				if (GPU_TILE_PROCESSOR == null) {
					try {
						GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
					} catch (Exception e) {
						System.out.println("Failed to initialize GPU class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return;
					} // final int debugLevel);
				}
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}
			}

///========================================
			int num_infinity_corr = infinity_corr ? CLT_PARAMETERS.inf_repeat : 1;
			if (num_infinity_corr < 1)
				num_infinity_corr = 1;
			for (int i_infinity_corr = 0; i_infinity_corr < num_infinity_corr; i_infinity_corr++) {
				if (label.equals("ERS main")) {
					QUAD_CLT.processCLTQuadCorrsTestERS(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters
							COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters
													// colorProcParameters,
							CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
							RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
							apply_corr, infinity_corr, // calculate and apply geometry correction at infinity
							THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
							UPDATE_STATUS, // final boolean updateStatus,
							DEBUG_LEVEL); // final int debugLevel);
				} else if (label.equals("CORR TEST")) {
					QUAD_CLT.processCLTQuadCorrsTest(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters
							COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters
													// colorProcParameters,
							CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
							RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
							apply_corr, infinity_corr, // calculate and apply geometry correction at infinity
							THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
							UPDATE_STATUS, // final boolean updateStatus,
							DEBUG_LEVEL); // final int debugLevel);

				} else {
					QUAD_CLT.processCLTQuadCorrs(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters
							COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters
													// colorProcParameters,
							CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
							RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
							apply_corr, infinity_corr, // calculate and apply geometry correction at infinity
							THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
							UPDATE_STATUS, // final boolean updateStatus,
							DEBUG_LEVEL); // final int debugLevel);
					/*
					 * String x3d_path = QUAD_CLT.getX3dDirectory(); String file_name =
					 * QUAD_CLT.getImageName(); // + suffix; String file_path = x3d_path +
					 * Prefs.getFileSeparator() + file_name + ".tiff"; if ((QUAD_CLT.getGPU() !=
					 * null) && (QUAD_CLT.getGPU().getQuadCLT() != QUAD_CLT)) {
					 * QUAD_CLT.getGPU().updateQuadCLT(QUAD_CLT); // to re-load new set of Bayer
					 * images to the GPU }
					 * 
					 * ImagePlus img_quad = QUAD_CLT.processCLTQuadCorrGPU( null, // ImagePlus []
					 * imp_quad, //null will be OK null, // boolean [][] saturation_imp, // (near)
					 * saturated pixels or null // Not needed use this.saturation_imp
					 * CLT_PARAMETERS, // CLTParameters clt_parameters, DEBAYER_PARAMETERS, //
					 * EyesisCorrectionParameters.DebayerParameters debayerParameters,
					 * COLOR_PROC_PARAMETERS, // ColorProcParameters colorProcParameters,
					 * CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters
					 * channelGainParameters, RGB_PARAMETERS, //
					 * EyesisCorrectionParameters.RGBParameters rgbParameters, null, // double []
					 * scaleExposures, // probably not needed here - restores brightness of the
					 * final image true, // boolean only4slice, THREADS_MAX, // final int
					 * threadsMax, // maximal number of threads to launch UPDATE_STATUS, // final
					 * boolean updateStatus, DEBUG_LEVEL); // final int debugLevel); FileSaver
					 * fs=new FileSaver(img_quad); // is null, will be saved inside to
					 * /home/elphel/lwir16-proc/proc1/results_cuda/1626032208_613623-AUX-SHIFTED-D0.
					 * 0 fs.saveAsTiff(file_path);
					 */
				}
			}

			if (configPath != null) {
				saveTimestampedProperties( // save config again
						configPath, // full path or null
						null, // use as default directory if path==null
						true, PROPERTIES);
			}
			return;

//-------------------------------------------------------
		} else if (label.equals("AUX 4 images") || label.equals("AUX apply fine corr")
				|| label.equals("AUX infinity corr")) {
			boolean apply_corr = label.equals("AUX apply fine corr");
			boolean infinity_corr = label.equals("AUX infinity corr");
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (EYESIS_CORRECTIONS_AUX == null) {
				EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
						CORRECTION_PARAMETERS.getAux());
			}
			if (QUAD_CLT_AUX == null) {
				QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
						CORRECTION_PARAMETERS.getAux());
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT AUX instance, will need to read CLT kernels");
				}
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;

			EYESIS_CORRECTIONS_AUX.initSensorFiles(DEBUG_LEVEL, false, // boolean missing_ok,
					true, // boolean all_sensors, Otherwise - Eyesis
					COLOR_PROC_PARAMETERS_AUX.correct_vignetting); // boolean correct_vignetting

			int numChannels = EYESIS_CORRECTIONS_AUX.getNumChannels();
//        NONLIN_PARAMETERS.modifyNumChannels(numChannels);
			CHANNEL_GAINS_PARAMETERS_AUX.modifyNumChannels(numChannels);

			if (!QUAD_CLT_AUX.CLTKernelsAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Reading AUX CLT kernels");
				}
				QUAD_CLT_AUX.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);

				if (DEBUG_LEVEL > 1) {
					QUAD_CLT_AUX.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
							DEBUG_LEVEL);
				}
			}

			if (!QUAD_CLT_AUX.geometryCorrectionAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Calculating geometryCorrection");
				}
				if (!QUAD_CLT_AUX.initGeometryCorrection(DEBUG_LEVEL + 2)) {
					return;
				}
			}
			// After kernels and GeometryCorrection
			if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
				if (GPU_TILE_PROCESSOR == null) {
					try {
						GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
					} catch (Exception e) {
						System.out.println("Failed to initialize GPU class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return;
					} // final int debugLevel);
				}
				/*
				 * if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null))
				 * { // if GPU main is needed try { GPU_QUAD = new GpuQuad( GPU_TILE_PROCESSOR,
				 * QUAD_CLT); } catch (Exception e) {
				 * System.out.println("Failed to initialize GpuQuad class"); // TODO
				 * Auto-generated catch block e.printStackTrace(); return; // false; } //final
				 * int debugLevel); QUAD_CLT.setGPU(GPU_QUAD); }
				 */
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return; // false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			}
			@SuppressWarnings("unused")
			QuadCLT dbg_qc_main = QUAD_CLT;
			@SuppressWarnings("unused")
			QuadCLT dbg_qc_aux = QUAD_CLT_AUX;

///========================================
			int num_infinity_corr = infinity_corr ? CLT_PARAMETERS.inf_repeat : 1;
			if (num_infinity_corr < 1)
				num_infinity_corr = 1;
			for (int i_infinity_corr = 0; i_infinity_corr < num_infinity_corr; i_infinity_corr++) {
				QUAD_CLT_AUX.processCLTQuadCorrs(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters
						COLOR_PROC_PARAMETERS_AUX, // EyesisCorrectionParameters.ColorProcParameters
													// colorProcParameters,
						CHANNEL_GAINS_PARAMETERS_AUX, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
						RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
						apply_corr, infinity_corr, // calculate and apply geometry correction at infinity
						THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
						UPDATE_STATUS, // final boolean updateStatus,
						DEBUG_LEVEL); // final int debugLevel);
			}
			if (configPath != null) {
				saveTimestampedProperties( // save config again
						configPath, // full path or null
						null, // use as default directory if path==null
						true, PROPERTIES);
			}
			return;

		} else if (label.equals("CLT reset fine corr")) {
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			QUAD_CLT.reset_fine_corr();
			return;
		} else if (label.equals("CLT reset extrinsic corr")) {
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			QUAD_CLT.resetExtrinsicCorr(CLT_PARAMETERS);
			return;
		} else if (label.equals("ERS reset")) {
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			QUAD_CLT.resetExtrinsicCorr(CLT_PARAMETERS);
			return;

		} else if (label.equals("CLT show geometry")) {
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			QUAD_CLT.listGeometryCorrection(true);
			if (QUAD_CLT_AUX != null) { // Only show auxiliary camera geometry if it is initialized
				System.out.println("\n === Auxiliary camera ===");
				QUAD_CLT_AUX.listGeometryCorrection(true);
			}

			return;
		} else if (label.equals("CLT show fine corr")) {
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			QUAD_CLT.show_fine_corr();
			return;

		} else if (label.equals("CLT process fine corr") || label.equals("CLT test fine corr")) {
			boolean dry_run = label.equals("CLT test fine corr");
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}

			QUAD_CLT.processLazyEye(dry_run, // boolean dry_run
					CLT_PARAMETERS, DEBUG_LEVEL);

			return;

		} else if (label.equals("CLT ext infinity corr")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			QUAD_CLT.process_infinity_corr(CLT_PARAMETERS, DEBUG_LEVEL);
			return;

		} else if (label.equals("CLT disparity scan")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;

			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);
			int numChannels = EYESIS_CORRECTIONS.getNumChannels();
//        NONLIN_PARAMETERS.modifyNumChannels(numChannels);
			CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);

			if (!QUAD_CLT.CLTKernelsAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Reading CLT kernels");
				}
				QUAD_CLT.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);

				if (DEBUG_LEVEL > 1) {
					QUAD_CLT.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
							DEBUG_LEVEL);
				}
			}

			if (!QUAD_CLT.geometryCorrectionAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Calculating geometryCorrection");
				}
				if (!QUAD_CLT.initGeometryCorrection(DEBUG_LEVEL + 2)) {
					return;
				}
			}

///========================================
			System.out.println("***** QUAD_CLT.cltDisparityScans() is removed");
			/*
			 * QUAD_CLT.cltDisparityScans( CLT_PARAMETERS, //
			 * EyesisCorrectionParameters.DCTParameters dct_parameters, DEBAYER_PARAMETERS,
			 * COLOR_PROC_PARAMETERS, //EyesisCorrectionParameters.ColorProcParameters
			 * colorProcParameters, CHANNEL_GAINS_PARAMETERS,
			 * //CorrectionColorProc.ColorGainsParameters channelGainParameters,
			 * RGB_PARAMETERS, //EyesisCorrectionParameters.RGBParameters rgbParameters,
			 * EQUIRECTANGULAR_PARAMETERS, //
			 * EyesisCorrectionParameters.EquirectangularParameters
			 * equirectangularParameters, THREADS_MAX, //final int threadsMax, // maximal
			 * number of threads to launch UPDATE_STATUS, //final boolean updateStatus,
			 * DEBUG_LEVEL); //final int debugLevel);
			 */
			if (configPath != null) {
				saveTimestampedProperties( // save config again
						configPath, // full path or null
						null, // use as default directory if path==null
						true, PROPERTIES);
			}
			return;

		} else if (label.equals("CLT reset 3D")) {
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			QUAD_CLT.tp.resetCLTPasses();
			QUAD_CLT.tp.setTrustedCorrelation(CLT_PARAMETERS.grow_disp_trust);

			return;

/// ============================================

		} else if (label.equals("CLT 3D") || label.equals("MAIN extrinsics") || label.equals("CLT Poly corr")
				|| label.equals("DRY RUN")) {

			boolean adjust_extrinsics = label.equals("MAIN extrinsics") || label.equals("CLT Poly corr");
			boolean adjust_poly = label.equals("CLT Poly corr");
			boolean dry_run = label.equals("DRY RUN");// init kernel/geometry only

			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;

			clt3d(adjust_extrinsics, adjust_poly, dry_run);
			return;
		} else if (label.equals("AUX 3D") || label.equals("AUX extrinsics") || label.equals("AUX Poly corr")) {
			boolean adjust_extrinsics = label.equals("AUX extrinsics") || label.equals("AUX Poly corr");
			boolean adjust_poly = label.equals("AUX Poly corr");
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			clt3d_aux(adjust_extrinsics, adjust_poly);
			return;
		} else if (label.equals("Main to AUX") || label.equals("Main img AUX")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			mainToAux(label.equals("Main img AUX"));
			return;
//
		} else if (label.equals("CLT planes")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT == null) {
				System.out.println("QUAD_CLT is null, nothing to show");
				return;
			}
			QUAD_CLT.showCLTPlanes(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL); // final int debugLevel);
			return;

		} else if (label.equals("Periodic")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT == null) {
				System.out.println("QUAD_CLT is null, nothing to show");
				return;
			}
			QUAD_CLT.showPeriodic(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL); // final int debugLevel);
			return;

		} else if (label.equals("CLT ASSIGN")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT == null) {
				System.out.println("QUAD_CLT is null, nothing to show");
				return;
			}

			if (!CLT_PARAMETERS.showTsDialog())
				return;

			double[][] assign_dbg = QUAD_CLT.assignCLTPlanes(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters
																				// dct_parameters,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL); // final int debugLevel);
			if (assign_dbg == null) {
				System.out.println("Could not assign tiles to surfaces, probably \"CLT planes\" command did not run");
				return;
			}
			return;

		} else if (label.equals("CLT OUT 3D")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT == null) {
				System.out.println("QUAD_CLT is null, nothing to show (will add previous steps)");
				return;
			}

			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;
			boolean OK = QUAD_CLT.output3d(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL); // final int debugLevel);
			if (!OK) {
				String msg = "Image data not initialized, run 'CLT 3D' command first";
				System.out.println("Error: " + msg);
				IJ.showMessage("Error", msg);
			}

		} else if (label.equals("CLT batch process")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return;

			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);
			int numChannels = EYESIS_CORRECTIONS.getNumChannels();
//        NONLIN_PARAMETERS.modifyNumChannels(numChannels);
			CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);

			if (!QUAD_CLT.CLTKernelsAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Reading CLT kernels");
				}
				QUAD_CLT.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);

				if (DEBUG_LEVEL > 1) {
					QUAD_CLT.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
							DEBUG_LEVEL);
				}
			}

			if (!QUAD_CLT.geometryCorrectionAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Calculating geometryCorrection");
				}
				if (!QUAD_CLT.initGeometryCorrection(DEBUG_LEVEL + 2)) {
					return;
				}
			}

///======================================== Main camera, same can be done for aAUX one! =====
			QUAD_CLT.batchCLT3d(TWO_QUAD_CLT, // TwoQuadCLT twoQuadCLT, //maybe null in no-rig mode, otherwise may
												// contain rig measurements to be used as infinity ground truth
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL); // final int debugLevel);

			if (configPath != null) {
				saveTimestampedProperties( // save config again
						configPath, // full path or null
						null, // use as default directory if path==null
						true, PROPERTIES);
			}
			return;
			/* ======================================================================== */
		} else if (label.equals("CM Test")) {
			cm_test();
			return;
			/* ======================================================================== */
		} else if (label.equals("Show scan")) {
			showScan();
			return;
			/* ======================================================================== */
		} else if (label.equals("Show all scans")) {
			showAllScans();
			return;
			/* ======================================================================== */

		} else if (label.equals("Import Aux")) {
			importAux();
			return;
			/* ======================================================================== */
		} else if (label.equals("CLT 2*4 images")) { // never used
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			getPairImages();
			return;
			/* ======================================================================== */
		} else if (label.equals("Rig8 images")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			getPairImages2();
			return;
			/* ======================================================================== */
		} else if (label.equals("GPU simulate")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			generateGPUDebugFiles();
			return;
			/* ======================================================================== */
		} else if (label.equals("GPU RUN")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			getPairImages2Gpu();
			return;

			/* ======================================================================== */
		} else if (label.equals("ShowGPU")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			showImageFromGPU();
			return;
//
			/* ======================================================================== */
		} else if (label.equals("RIG extrinsics")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			infinityRig();
			return;
			/* ======================================================================== */
		} else if (label.equals("SAVE extrinsics")) {
			saveExtrinsics();
			return;

			/* ======================================================================== */
		} else if (label.equals("Set Infinity")) {
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			QUAD_CLT.showExtrinsicCorr("main");// show_fine_corr("main");
			if (QUAD_CLT_AUX == null) {
				if (EYESIS_CORRECTIONS_AUX == null) {
					EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
							CORRECTION_PARAMETERS.getAux());
				}
				QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
						CORRECTION_PARAMETERS.getAux());
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels for aux camera");
				}
			}
			QUAD_CLT_AUX.showExtrinsicCorr("aux BEFORE");// show_fine_corr("aux");
			
			GenericJTabbedDialog gd = new GenericJTabbedDialog("UAS Log");
			double           inf_disp_ref = -.138;
			gd.addNumericField("Disparity at Infinity",  inf_disp_ref, 3, 6, "pix","Disparity at Infinity");
			gd.showDialog();
			if (gd.wasCanceled())
				return;
			inf_disp_ref =    gd.getNextNumber();

			OpticalFlow.correctInfinityMain(
					CLT_PARAMETERS, // CLTParameters    clt_parameters,
					QUAD_CLT_AUX,   // QuadCLT          quadCLT_main,
					inf_disp_ref,   // double           inf_disp_ref, 
					DEBUG_LEVEL);   //final int        debugLevel)
			QUAD_CLT_AUX.showExtrinsicCorr("aux AFTER");// show_fine_corr("aux");
			/*
			QUAD_CLT_AUX.geometryCorrection.showRig();// show_fine_corr("aux");
			System.out.println("=== IMS ===");
			QUAD_CLT_AUX.showQuatCorr();
			System.out.println("=== IMU ===");
			QUAD_CLT_AUX.showPimuOffsets(CLT_PARAMETERS); //
			@SuppressWarnings("unused")
			QuadCLT dbg_QUAD_CLT = QUAD_CLT;
			@SuppressWarnings("unused")
			QuadCLT dbg_QUAD_CLT_AUX = QUAD_CLT_AUX;
			*/
			return;
			
		} else if (label.equals("SHOW extrinsics")) {
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			QUAD_CLT.showExtrinsicCorr("main");// show_fine_corr("main");
			if (QUAD_CLT_AUX == null) {
				if (EYESIS_CORRECTIONS_AUX == null) {
					EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
							CORRECTION_PARAMETERS.getAux());
				}
				QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
						CORRECTION_PARAMETERS.getAux());
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels for aux camera");
				}
			}
			QUAD_CLT_AUX.showExtrinsicCorr("aux");// show_fine_corr("aux");
			QUAD_CLT_AUX.geometryCorrection.showRig();// show_fine_corr("aux");
			System.out.println("=== IMS ===");
			QUAD_CLT_AUX.showQuatCorr();
			System.out.println("=== IMU ===");
			QUAD_CLT_AUX.showPimuOffsets(CLT_PARAMETERS); //
			@SuppressWarnings("unused")
			QuadCLT dbg_QUAD_CLT = QUAD_CLT;
			@SuppressWarnings("unused")
			QuadCLT dbg_QUAD_CLT_AUX = QUAD_CLT_AUX;
			return;
			
			
			
			/* ======================================================================== */
		} else if (label.equals("Remove source paths")) {
			CORRECTION_PARAMETERS.clearSourcePaths();
			System.out.println("Cleared list of source paths for the MAIN camera.");
			if (CORRECTION_PARAMETERS.getAux() != null ) {
				CORRECTION_PARAMETERS.getAux().clearSourcePaths();
				System.out.println("Cleared list of source paths for the AUX camera.");
			}
			return;
			/* ======================================================================== */
		} else if (label.equals("IMU main")) {
			editIMU(false);
			/* ======================================================================== */
		} else if (label.equals("IMU aux")) {
			editIMU(true);
			/* ======================================================================== */
		} else if (label.equals("LIST extrinsics")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (EYESIS_CORRECTIONS_AUX == null) {
				EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
						CORRECTION_PARAMETERS.getAux());
			}
			listExtrinsics();
			return;
			/* ======================================================================== */
		} else if (label.equals("ML recalc")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			mlRecalc();
			return;
//
			/* ======================================================================== */
		} else if (label.equals("DSI histogram")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			dsiHistogram();
			return;

			/* ======================================================================== */
		} else if (label.equals("Reset GT")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			resetGroundTruth();

			return;
			/* ======================================================================== */
		} else if (label.equals("Ground truth")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			groundTruth();
			return;
			/* ======================================================================== */
		} else if (label.equals("RIG DSI")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			rigDSI();
			return;

			/* ======================================================================== */
		} else if (label.equals("Show biscan")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			showBiScan();
			return;
			/* ======================================================================== */
		} else if (label.equals("Poles GT")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			processPoles();
			return;
			/* ======================================================================== */
		} else if (label.equals("CLT planes")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			rigPlanes();
			return;

			/* ======================================================================== */
		} else if (label.equals("ML export")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			exportMLData();
			return;
			/* ======================================================================== */
		} else if (label.equals("JP4 copy")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			copyJP4src();
			return;
			/* ======================================================================== */
		} else if (label.equals("Rig batch")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			batchRig();
			return;

			/* ======================================================================== */
		} else if (label.equals("LWIR batch")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			batchLwir();
			return;
			/* ======================================================================== */
		} else if (label.equals("Inter Test")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			testInterScene(false);
			return;
			/* ======================================================================== */
		} else if (label.equals("Aux Build Series")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			buildSeries(
					true,
					0); // int     cuas_proc_mode); // 0 - old, 1 combine scene series) {);
			return;
			/* ======================================================================== */
		} else if (label.equals("CUAS Combine")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			buildSeries(
					true,
					1); // int     cuas_proc_mode); // 0 - old, 1 combine scene series) {);
			return;
			//"CUAS Combine" "CUAS Video"
			/* ======================================================================== */
		} else if (label.equals("CUAS Video")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			buildSeries(
					true,
					2); // int     cuas_proc_mode); // 0 - old, 1 combine scene series) {);
			return;
			/* ======================================================================== */
		} else if (label.equals("CUAS RT")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			buildSeries(
					true,
					3); // int     cuas_proc_mode); // 0 - old, 1 combine scene series) {);
			return;
			/* ======================================================================== */
		} else if (label.equals("Aux Inter Test")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			testInterScene(true);
			return;
			/* ======================================================================== */
		} else if (label.equals("Inter Pairs")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			interPairsLMA(false);
			return;
			/* ======================================================================== */
		} else if (label.equals("Aux Inter Pairs")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			interPairsLMA(true);
			return;
			/* ======================================================================== */
		} else if (label.equals("Inter Series")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			interSeriesLMA(false);
			return;
			/* ======================================================================== */
		} else if (label.equals("Aux Inter Series")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			interSeriesLMA(true);
			return;
			/* ======================================================================== */
		} else if (label.equals("Inter Accumulate")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			intersceneAccumulate(false);
			return;
			/* ======================================================================== */
		} else if (label.equals("Aux Inter Accumulate")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			intersceneAccumulate(true);
			return;

			/* ======================================================================== */
		} else if (label.equals("Inter Noise")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			intersceneNoise(false, false, false); // boolean bayer_artifacts_debug);
			return;
			/* ======================================================================== */
		} else if (label.equals("Inter Noise Aux")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			intersceneNoise(true, false, false); // boolean bayer_artifacts_debug);
			return;
			/* ======================================================================== */
		} else if (label.equals("Inter Intra ML")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			inter_intra_export(true);
			return;
			/* ======================================================================== */
		} else if (label.equals("Batch Noise Aux")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			intersceneNoise(true, true, false); // boolean bayer_artifacts_debug);
			return;

			/* ======================================================================== */
		} else if (label.equals("Inter Debug Noise")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			intersceneNoise(false, false, true); // boolean bayer_artifacts_debug);
			return;

			/* ======================================================================== */
		} else if (label.equals("Noise Stats")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			intersceneNoiseStats(false);
			return;

			/* ======================================================================== */
		} else if (label.equals("Noise Stats Aux")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			intersceneNoiseStats(true);
			return;

			/* ======================================================================== */
		} else if (label.equals("Colorize Depth")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			coloriseDepthMap();
			return;
			/* ======================================================================== */
		} else if (label.equals("Test 1D")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			test1d();
			return;

			/* ======================================================================== */
		} else if (label.equals("Inter LMA")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			testInterLMA(false);
			return;
			/* ======================================================================== */
		} else if (label.equals("Aux Inter LMA")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			testInterLMA(true);
			return;
			/* ======================================================================== */
		} else if (label.equals("Main LY series")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			adjustLYSeries(false);
			return;
			/* ======================================================================== */
		} else if (label.equals("Aux LY series")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			adjustLYSeries(true);
			return;
			
			/* ======================================================================== */
		} else if (label.equals("CLT rig edit")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (QUAD_CLT_AUX == null) {
				if (EYESIS_CORRECTIONS_AUX == null) {
					EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
							CORRECTION_PARAMETERS.getAux());
				}
				QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
						CORRECTION_PARAMETERS.getAux());
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels for aux camera");
				}
			}
			QUAD_CLT_AUX.editRig();
			return;
			/* ======================================================================== */
		} else if (label.equals("DSI show")) {
			showDSI();
			return;

			/* ======================================================================== */
		} else if (label.equals("JCUDA TEST")) {

			ImagePlus impl = WindowManager.getCurrentImage();
			if (impl != null) {

				ImageProcessor impr = impl.getProcessor();

				JCuda_ImageJ_Example_Plugin jcuda = new JCuda_ImageJ_Example_Plugin();

				/*
				 * In a standalone test JCUDA plugin it's unknown where .setup() is called
				 * from... As well as .run(). But it works like this.
				 */
				jcuda.setup(null, impl);
				jcuda.run(impr);

			} else {
				System.out.println("Missing the current image. Open one.");
			}

			return;
			/* ======================================================================== */
		} else if (label.equals("TF TEST")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			boolean keep_empty = true;
			if (TENSORFLOW_INFER_MODEL == null) {
				TENSORFLOW_INFER_MODEL = new TensorflowInferModel(324, // int tilesX,
						242, // int tilesY,
						9, // int corr_side,
						4 // int num_layers
				);
			}
			TENSORFLOW_INFER_MODEL.test_tensorflow(keep_empty);
			return;
			/* ======================================================================== */
		} else if (label.equals("LWIR_TEST")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			// public static LwirReader LWIR_READER = null;
			if (LWIR_READER == null) {
				LWIR_READER = new LwirReader(CLT_PARAMETERS.lwir);
			}
			ImagePlus[][] imps = LWIR_READER.readAllMultiple(10, // final int num_frames,
//    			true, // use LWIR telemetry
					true, // final boolean show,
					false); // true); // final boolean scale)
			for (ImagePlus imp : imps[0]) {
				imp.show();
			}

			System.out.println("LWIR_TEST: got " + imps.length + " image sets");
			ImagePlus[][] imps_sync = LWIR_READER.matchSets(imps, 0.001, 3); // double max_mismatch)
			if (imps_sync != null) {
				ImagePlus[] imps_avg = LWIR_READER.averageMultiFrames(imps_sync);
				for (ImagePlus imp : imps_avg) {
					imp.show();
				}
			}
			/* ======================================================================== */
		} else if (label.equals("LWIR_ACQUIRE")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			// public static LwirReader LWIR_READER = null;
			if (LWIR_READER == null) {
				LWIR_READER = new LwirReader(CLT_PARAMETERS.lwir);
			}
			ImagePlus[] imps = LWIR_READER.acquire("attic/lwir_test_images"); // directory to save
			if (imps != null) {
///			for (ImagePlus imp: imps) {
///				imp.show();
///			}
			}
			/* ======================================================================== */
		} else if (label.equals("Rotations_test")) {
			ErsCorrection.test_rotations();

		} else if (label.equals("Generate Sym Vectors")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ErsCorrection.test_rotations();
			boolean full_type1 = false;
			boolean full_type2 = false;
			int num_cameras = 4; // will try other values
			SYMM_VECTOR_EO = new SymmVector(num_cameras, // int num_cameras,
					full_type1, // boolean full_type1, // false - all R or all T, true - mixed
					full_type2, // boolean full_type2) {// false - quarter 3 is negated quarter 1, true -
								// independent
					DEBUG_LEVEL);
			num_cameras = 8; // will try other values
			SYMM_VECTOR_LWIR = new SymmVector(num_cameras, // int num_cameras,
					full_type1, // boolean full_type1, // false - all R or all T, true - mixed
					full_type2, // boolean full_type2) {// false - quarter 3 is negated quarter 1, true -
								// independent
					DEBUG_LEVEL);
			num_cameras = 12; // will try other values
			SYMM_VECTOR_LWIR = new SymmVector(num_cameras, // int num_cameras,
					full_type1, // boolean full_type1, // false - all R or all T, true - mixed
					full_type2, // boolean full_type2) {// false - quarter 3 is negated quarter 1, true -
								// independent
					DEBUG_LEVEL);
			num_cameras = 16;
			SYMM_VECTOR_LWIR = new SymmVector(num_cameras, // int num_cameras,
					full_type1, // boolean full_type1, // false - all R or all T, true - mixed
					full_type2, // boolean full_type2) {// false - quarter 3 is negated quarter 1, true -
								// independent
					DEBUG_LEVEL);
			num_cameras = 20;
			SYMM_VECTOR_LWIR = new SymmVector(num_cameras, // int num_cameras,
					full_type1, // boolean full_type1, // false - all R or all T, true - mixed
					full_type2, // boolean full_type2) {// false - quarter 3 is negated quarter 1, true -
								// independent
					DEBUG_LEVEL);
			num_cameras = 24;
			SYMM_VECTOR_LWIR = new SymmVector(num_cameras, // int num_cameras,
					full_type1, // boolean full_type1, // false - all R or all T, true - mixed
					full_type2, // boolean full_type2) {// false - quarter 3 is negated quarter 1, true -
								// independent
					DEBUG_LEVEL);
			num_cameras = 28;
			SYMM_VECTOR_LWIR = new SymmVector(num_cameras, // int num_cameras,
					full_type1, // boolean full_type1, // false - all R or all T, true - mixed
					full_type2, // boolean full_type2) {// false - quarter 3 is negated quarter 1, true -
								// independent
					DEBUG_LEVEL);
			num_cameras = 32;
			SYMM_VECTOR_LWIR = new SymmVector(num_cameras, // int num_cameras,
					full_type1, // boolean full_type1, // false - all R or all T, true - mixed
					full_type2, // boolean full_type2) {// false - quarter 3 is negated quarter 1, true -
								// independent
					DEBUG_LEVEL);
		} else if (label.equals("Illustrations Configure")) {
			CALIBRATION_ILLUSTRATION_PARAMETERS.showJDialog();
			return;
		} else if (label.equals("Footage Organize")) {
			if (CALIBRATION_ILLUSTRATION == null) { // LWIR_PARAMETERS
				CALIBRATION_ILLUSTRATION = new CalibrationIllustration(CLT_PARAMETERS.lwir, // LWIR_PARAMETERS, //
																							// LwirReaderParameters
																							// lwirReaderParameters,
						CALIBRATION_ILLUSTRATION_PARAMETERS, // CalibrationIllustrationParameters
																// illustrationParameters,
						null, // EYESIS_ABERRATIONS, // EyesisAberrations eyesisAberrations,
						null, // LENS_DISTORTIONS, // Distortions distortions,
						SYNC_COMMAND.stopRequested, // AtomicInteger stopRequested,
						MASTER_DEBUG_LEVEL); // int debug_level);
			}
			FootageOrganize.OrganizeSeries(
					CLT_PARAMETERS,
					CALIBRATION_ILLUSTRATION);
		} else if (label.equals("Super batch")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			String [] configs = loadMultiProperties(
					CORRECTION_PARAMETERS.resultsDirectory, // String directory,
					true);// boolean useXML,
			if (configs == null) {
				return;
			}
			long startTime = System.nanoTime();
			for (int nc = 0; nc < configs.length; nc++) {
				long startTimeRun = System.nanoTime();
				String config = configs[nc];
				String path = loadProperties(config, CORRECTION_PARAMETERS.resultsDirectory, true, PROPERTIES);
				System.out.println(path);
				if (path != null) {
					getAllProperties(PROPERTIES);
					if (DEBUG_LEVEL > -3)
						System.out.println("Configuration parameters are restored from " + path);
				} else {
					if (DEBUG_LEVEL > -10)
						System.out.println("Failed to restore configuration parameters");
					return;
				}
				batchLwir();
				System.out.println("Super batch: processing of run "+ nc+" (of "+configs.length+"): "+config+ " finished in "
						+ IJ.d2s(0.000000001 * (System.nanoTime() - startTimeRun), 3) + " sec, --- Free memory="
						+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
			}
			System.out.println("Super batch: all (of "+configs.length+"): finished at "
					+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
					+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
			return;
			
			
		} else if (label.equals("Image Properties")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_sel = WindowManager.getCurrentImage();
			if (imp_sel == null) {
				IJ.showMessage("Error", "There are no images open\nProcess canceled");
				return;
			}
			JP46_Reader_camera.decodeProperiesFromInfo(imp_sel);
			ArrayList<String> prop_kv = new ArrayList<String>();
			Set<Object> jp4_set;
			Properties jp4_prop;
			Iterator<Object> itr;
			String str;
			jp4_prop = imp_sel.getProperties();
			if (jp4_prop != null) {
				jp4_set = jp4_prop.keySet();
				itr = jp4_set.iterator();
				while (itr.hasNext()) {
					str = (String) itr.next();
					// if (!str.equals("Info"))
					// info+="<"+str+">\""+jp4_prop.getProperty(str)+"\"</"+str+">";
					if (!str.equals("Info")) {
						prop_kv.add(str + "|" + jp4_prop.getProperty(str));
					}
				}
			}
			Collections.sort(prop_kv);
			prop_kv.add("|"); // empty item to add new property
			GenericDialog gd = new GenericDialog("Edit properties");
			gd.addMessage("Use empty string to remove property, quoted empty - to set zero length, "
					+ "name|value for value to set a new property");
			String[] names = new String[prop_kv.size()];
			for (int ii = 0; ii < prop_kv.size(); ii++) {
				String s = (String) prop_kv.get(ii);
				int sep = s.indexOf("|");
				if (sep < 0) {
					sep = s.length();
				}
				String name = s.substring(0, sep);
				names[ii] = name;
				String val = (sep == s.length()) ? "" : s.substring(sep + 1);
				gd.addStringField(ii + ": " + name, val, 20);
			}
			WindowTools.addScrollBars(gd);
			gd.showDialog();
			if (gd.wasCanceled())
				return;
			for (int ii = 0; ii < prop_kv.size(); ii++) {
				String name = names[ii];
				String val = gd.getNextString();
				int sep = val.indexOf("|");
				if (sep > 0) { // ignore existing key, set a key/value pair
					name = val.substring(0, sep);
					val = val.substring(sep + 1);
				} else if (val.length() == 0) {
					val = null;
				} else {
					val = val.replaceAll("^\"+|\"+$", ""); // remove leading/trailing "
				}
				if (name.length() > 0) {
					imp_sel.setProperty(name, val);
				}
			}
			JP46_Reader_camera.encodeProperiesToInfo(imp_sel);
			return;
		} else if (label.equals("Build World")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			buildLWIRWorld(true);
			return;
		} else if (label.equals("Test IMX5")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			CLT_PARAMETERS.batch_run = true;
			String imx_logs = CORRECTION_PARAMETERS.getImsSourceDirectory(); // sourceImsDirectory;
			String debug_path = CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"debug_local_master";
			if ((imx_logs == null) || imx_logs.equals("")) {
				System.out.println("sourceImsDirectory is not set. Use Setup CLT Batch parameters to define it.");
				return;
			}
			EventLogger eventLogger = EventLogger.getEventLogger(
					CLT_PARAMETERS, //CLTParameters clt_parameters,
					imx_logs,
					debug_path);
			if (eventLogger == null) {
				System.out.println("Failed to create eventLogger instance.");
				return;
			}
			double [][] quats_all = eventLogger.getQuats(
					CLT_PARAMETERS.imp.imsv_ts0, //  1763232236.0, // double ts_start, or NaN
					CLT_PARAMETERS.imp.imsv_ts1); // 1763234000.0); // double ts_end)
			double [] vert_xyz = null;
			if (quats_all != null) {
				vert_xyz = QuatVertLMA.getVertAndRms(
						CLT_PARAMETERS, // CLTParameters   clt_parameters,
						quats_all,      // double [][] quats,
						DEBUG_LEVEL);   // int             debugLevel)
			}
			
			eventLogger.print2Pps(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"rec_1pps");
			eventLogger.printStrobeInTime(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"did_strobe_in_time");
			eventLogger.printPimu(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"did_pimu");
			eventLogger.printGps(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"did_gps", 7);
			eventLogger.printDidIns1(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"did_ins1",
					vert_xyz); // may be null. 4-element - ignore last (it is RMS)
			
			eventLogger.printDidIns2(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"did_ins2");
 /*
			if (EVENT_LOGGER == null) {
				if ((imx_logs != null) && !imx_logs.equals("")) {
					EVENT_LOGGER = new EventLogger (
							imx_logs,
							debug_path);
				} else {
					System.out.println("sourceImsDirectory is not set. Use Setup CLT Batch parameters to define it.");
				}
			}
			if (EVENT_LOGGER != null) {
				//print2Pps
				EVENT_LOGGER.print2Pps(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"rec_1pps");
				EVENT_LOGGER.printStrobeInTime(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"did_strobe_in_time");
				EVENT_LOGGER.printPimu(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"did_pimu");
				EVENT_LOGGER.printGps(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"did_gps", 7);
				EVENT_LOGGER.printDidIns1(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"did_ins1");
				EVENT_LOGGER.printDidIns2(CORRECTION_PARAMETERS.getImsPrintDirectory()+Prefs.getFileSeparator()+"did_ins2");
			} else {
				System.out.println("EVENT_LOGGEER is null.");
			}
*/			
			return;
//JTabbedTest
// End of buttons code
		} else if (label.equals("Show mice")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_src = WindowManager.getCurrentImage();
			ImagePlus imp_mice = subtractAverage(imp_src);
			imp_mice.show();
		} else if (label.equals("Set pair")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ComboMatch.openTestPair();
		} else if (label.equals("Warp pair")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ComboMatch.testPair();
		} else if (label.equals("Read Tiff")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			ImagePlus imp_sel = WindowManager.getCurrentImage();
			if (imp_sel == null) {
				IJ.showMessage("Error", "No images selected");
				return;
			}
			String orig_path = imp_sel.getOriginalFileInfo().getFilePath();
			try {
				ElphelTiffReader.getTiffMeta(orig_path);				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
//			ComboMatch.testReadTiff();
		} else if (label.equals("Ortho") || label.equals("Ortho Pairs") || label.equals("Extract Objects") || label.equals("Manual pair")) {
			DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return;
				} // final int debugLevel);
			}
			
			boolean init_scene = false; // true;
			if (init_scene) {
				debugInitOneScene();
			}
			ComboMatch.openTestPairGps(
					CLT_PARAMETERS,     // CLTParameters    clt_parameters,
					GPU_TILE_PROCESSOR,
					label.equals("Ortho"),// 			 boolean          dflt_options,
					label.equals("Extract Objects"), // boolean    extract_mines,
					label.equals("Manual pair"), // boolean          manual_pair,
					DEBUG_LEVEL);
		} else if (label.equals("Test video")) {
			ImagePlus imp_sel = WindowManager.getCurrentImage();
			if (imp_sel == null) {
				IJ.showMessage("Error", "No images selected");
				return;
			}
			OrthoMap.testVideo(imp_sel);
		} else if (label.equals("Mismatched resolutions")) {
			ImagePlus imp_sel = WindowManager.getCurrentImage();
			if (imp_sel == null) {
				IJ.showMessage("Error", "No images selected");
				return;
			}
			OrthoMap.createMismatchedResolutionKernel(
					imp_sel,          // ImagePlus imp,
					512,              // int [] slices,
					new int [] {1,2}, // int [] slices, deconvolve slice 2 with slice1
					6, // 4,                // int kernel_radius,
					true,             // boolean hor_sym,
					true,             // boolean vert_sym,
					true, // false,            // boolean all_sym,
					DEBUG_LEVEL);     // int debugLevel) { // >0
		} else if (label.equals("Generate DATI")) {
			ImagePlus imp_sel = WindowManager.getCurrentImage();
			if (imp_sel == null) {
				IJ.showMessage("Error", "No images selected");
				return;
			}
			OrthoMap.generateDATI(
					imp_sel,          // ImagePlus imp,
					512,              // int [] slices,
					new int [] {1,2}, // int [] slices, deconvolve slice 2 with slice1
					DEBUG_LEVEL);     // int debugLevel) { // >0
		} else if (label.equals("Create mine")) {
			OrthoMap.testPatternGenerate();
		} else if (label.equals("Pattern correlate")) {
			ImagePlus imp_sel = WindowManager.getCurrentImage();
			if (imp_sel == null) {
				IJ.showMessage("Error", "No images selected");
				return;
			}
			OrthoMap.testPatternCorrelate(imp_sel);
		} else if (label.equals("Properties compare")) {
			QuadCLTCPU.compareProperties();
		} else if (label.equals("Test Orange")) {
			OrangeTest.testOrange();			
		} else if (label.equals("Process Merged")) {
			OrangeTest.processMerged();			
		} else if (label.equals("Vegetation LMA")) {
			VegetationModel.processVegetation(
					SYNC_COMMAND, // // SyncCommand  SYNC_COMMAND,
					CLT_PARAMETERS, //CLTParameters    clt_parameters,
					false); //boolean combine_segments);			
		} else if (label.equals("Combine LMA Segments")) {
			VegetationModel.processVegetation(
					SYNC_COMMAND, // // SyncCommand  SYNC_COMMAND,
					CLT_PARAMETERS, //CLTParameters    clt_parameters,
					true); //boolean combine_segments);	
		} else if (label.equals("Render synthetic")) {
			VegetationSynthesis.testSynthetic(
					SYNC_COMMAND, // // SyncCommand  SYNC_COMMAND,
					CLT_PARAMETERS, // CLTParameters    clt_parameters,
					DEBUG_LEVEL);   // final int debugLevel
		} else if (label.equals("Test LLT Cholesky")) {
			ImagePlus imp_sel = WindowManager.getCurrentImage();
			if (imp_sel == null) {
				IJ.showMessage("Error", "No images selected");
				return;
			}
			CholeskyBlockTest.testCholesky(imp_sel); // ImagePlus imp.);	 
		} else if (label.equals("UAS log")) {
			if (QUAD_CLT_AUX == null) {
				if (EYESIS_CORRECTIONS_AUX == null) {
					EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
							CORRECTION_PARAMETERS.getAux());
				}
				QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
						CORRECTION_PARAMETERS.getAux());
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels for aux camera");
				}
			}
			testUasLog();
			return;
		} else if (label.equals("DJI SRT")) {
			testDjiSrt();
		} else if (label.equals("SRT to KML")) {
			djiSrtToKml();
			return;
		} else if (label.equals("Target Analyze")) {
			CuasMotion.targetsAnalyze();
			return;
		}
		} finally {
			MCP_COMMAND_CONTEXT = false;
		}
	}
	
	public static boolean testUasLog() {
		String uas_path= "/home/elphel/lwir16-proc/eagle_mountain/flight-logs/01-05-2025210705.json";
		GenericJTabbedDialog gd = new GenericJTabbedDialog("UAS Log");
		gd.addStringField("File path of the UAS log", uas_path, 100,
				"Provide full path to the UAS JSON flight log");
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		uas_path = gd.getNextString();
		{
			setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}

			EYESIS_CORRECTIONS_AUX.initSensorFiles(DEBUG_LEVEL); // null pointer
			if (!QUAD_CLT_AUX.CLTKernelsAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Reading AUX CLT kernels");
				}
				QUAD_CLT_AUX.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);

				if (DEBUG_LEVEL > 1) {
					QUAD_CLT_AUX.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
							DEBUG_LEVEL);
				}
			}

			if (!QUAD_CLT_AUX.geometryCorrectionAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Calculating geometryCorrection");
				}
				if (!QUAD_CLT_AUX.initGeometryCorrection(DEBUG_LEVEL + 2)) {
					return false;
				}
			}
			if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
				// needed
				try {
					GPU_QUAD_AUX = new GpuQuad(//
							GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
				} catch (Exception e) {
					System.out.println("Failed to initialize GpuQuad class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
				QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
			}
			
		}
		
		
		
		try {
//			UasLogReader uasLogReader = new UasLogReader(uas_path);
			UasLogReader.testUasLogReader(uas_path,QUAD_CLT_AUX);
		} catch (JSONException e) {
			System.out.println("Error reading/parsing "+uas_path);
			System.out.println();
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		return true;
	}
	public static boolean testDjiSrt() {
		String srt_path= "/home/elphel/Documents/dji/images/DJI_001-02/DJI_20250515122524_0007_D.SRT";
		GenericJTabbedDialog gd = new GenericJTabbedDialog("DJI SRT");
		gd.addStringField("File path of the DJI SRT", srt_path, 100,
				"Provide full path to the DJI SRT");
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		srt_path = gd.getNextString();
		try {
			ArrayList<DjiSrt> drt_list=DjiSrt.parseDjiSrt(srt_path);
		} catch (Exception e) {
			System.out.println("Error reading/parsing "+srt_path);
			System.out.println();
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		return true;
	}
	/*
	public static boolean motion_cuas(
			ImagePlus imp_sel,
			int       mode) {
//		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
		if (GPU_TILE_PROCESSOR == null) {
			try {
				GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
			} catch (Exception e) {
				System.out.println("Failed to initialize GPU class");
				// TODO Auto-generated catch block
				e.printStackTrace();
				return false;
			} // final int debugLevel);
		}
		EYESIS_CORRECTIONS_AUX.initSensorFiles(DEBUG_LEVEL);
		if (!QUAD_CLT_AUX.CLTKernelsAvailable()) {
			if (DEBUG_LEVEL > 0) {
				System.out.println("Reading AUX CLT kernels");
			}
			QUAD_CLT_AUX.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
					DEBUG_LEVEL);

			if (DEBUG_LEVEL > 1) {
				QUAD_CLT_AUX.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);
			}
		}

		if (!QUAD_CLT_AUX.geometryCorrectionAvailable()) {
			if (DEBUG_LEVEL > 0) {
				System.out.println("Calculating geometryCorrection");
			}
			if (!QUAD_CLT_AUX.initGeometryCorrection(DEBUG_LEVEL + 2)) {
				return false;
			}
		}
		if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
			// needed
			try {
				GPU_QUAD_AUX = new GpuQuad(//
						GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
			} catch (Exception e) {
				System.out.println("Failed to initialize GpuQuad class");
				// TODO Auto-generated catch block
				e.printStackTrace();
				return false;
			} // final int debugLevel);
			QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
		}
		switch (mode) {
		case 0: 
			CuasMotion.testCuasMotion(
					imp_sel,         // ImagePlus         imp_sel,
					CLT_PARAMETERS, // CLTParameters     clt_parameters,
					QUAD_CLT_AUX,   // QuadCLT           parentCLT)
					DEBUG_LEVEL);   // //int               debugLevel)
			break;
		case 1: 
			CuasMotion.testCuasScanMotion(
					imp_sel,         // ImagePlus         imp_sel,
					CLT_PARAMETERS, // CLTParameters     clt_parameters,
					QUAD_CLT_AUX,   // QuadCLT           parentCLT)
					DEBUG_LEVEL);   // //int               debugLevel)
			break;
			
		}
		
		return true;
	}
	*/
	
	public static boolean djiSrtToKml() {
		String srt_path=   "/home/elphel/Documents/dji/images/DJI_001-02/DJI_20250515122524_0007_D.SRT";
		String kml_path=   "/home/elphel/Documents/dji/images/DJI_001-02/DJI_20250515122524_0007_D.kml";
		String icon_url=   "https://community.elphel.com/lwir16/test_kml/drone01.png";
		String name =      "Flight path from SRT";
		double alt_corr = 0;
		String time_zone = "MDT"; // "Americas/Boise"
		double time_step = 1.0; // sec
		GenericJTabbedDialog gd = new GenericJTabbedDialog("DJI SRT to KML");
		gd.addStringField("File path of the DJI SRT",      srt_path, 100, "Provide full path to the DJI SRT");
		gd.addStringField("File path of the output KML",   kml_path, 100, "Provide full path to the DJI SRT");
		gd.addStringField("UAS icon URL",                  icon_url, 100, "Provide url of the UAS icon file");
		gd.addStringField("KML document title",            name, 100, "Provide title of the KML file");
		gd.addStringField("Time Zone",                     time_zone, 100, "Provide url of the UAS icon file");
		gd.addNumericField("Interval between Placemarks",  time_step, 3, 6, "s","Interval between Placemarks");
		gd.addNumericField("Altitude correction",          alt_corr, 3, 6, "m","Add to log altitude");
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		srt_path =  gd.getNextString();
		kml_path =  gd.getNextString();
		icon_url =  gd.getNextString();
		name =      gd.getNextString();
		if (icon_url.trim().length()==0) {
			icon_url = null;
		}
		time_zone = gd.getNextString();
		time_step = gd.getNextNumber();
		alt_corr =  gd.getNextNumber();
		boolean ok = false;
		if (alt_corr != 0) {
// modify file name			
		}
		try {
			ok=DjiSrt.srtToKml(
					srt_path, // String srt_path,
					kml_path, // String kml_path,
					icon_url, //String icon_url,
					name+" "+srt_path,     // String name,
					time_zone, // String time_zone, // "MDT", "UTC"
					time_step, //double time_step // interval in seconds
					alt_corr);// double alt_corr // add to altitude

		} catch (Exception e) {
			System.out.println("Error reading/parsing "+srt_path);
			System.out.println();
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
// 
	public boolean debugInitOneScene() {
		DEBUG_LEVEL = MASTER_DEBUG_LEVEL;
		if (EYESIS_CORRECTIONS_AUX == null) {
			EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
					CORRECTION_PARAMETERS.getAux());
		}
		if (QUAD_CLT_AUX == null) {
			QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
					CORRECTION_PARAMETERS.getAux());
			if (DEBUG_LEVEL > 0) {
				System.out.println("Created new QuadCLT AUX instance, will need to read CLT kernels");
			}
		}
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		EYESIS_CORRECTIONS_AUX.initSensorFiles(DEBUG_LEVEL, false, // boolean missing_ok,
				true, // boolean all_sensors, Otherwise - Eyesis
				COLOR_PROC_PARAMETERS_AUX.correct_vignetting); // boolean correct_vignetting

		int numChannels = EYESIS_CORRECTIONS_AUX.getNumChannels();
//    NONLIN_PARAMETERS.modifyNumChannels(numChannels);
		CHANNEL_GAINS_PARAMETERS_AUX.modifyNumChannels(numChannels);

		if (!QUAD_CLT_AUX.CLTKernelsAvailable()) {
			if (DEBUG_LEVEL > 0) {
				System.out.println("Reading AUX CLT kernels");
			}
			QUAD_CLT_AUX.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
					DEBUG_LEVEL);

			if (DEBUG_LEVEL > 1) {
				QUAD_CLT_AUX.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);
			}
		}

		if (!QUAD_CLT_AUX.geometryCorrectionAvailable()) {
			if (DEBUG_LEVEL > 0) {
				System.out.println("Calculating geometryCorrection");
			}
			if (!QUAD_CLT_AUX.initGeometryCorrection(DEBUG_LEVEL + 2)) {
				return false;
			}
		}
		// After kernels and GeometryCorrection
		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			/*
			 * if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null))
			 * { // if GPU main is needed try { GPU_QUAD = new GpuQuad( GPU_TILE_PROCESSOR,
			 * QUAD_CLT); } catch (Exception e) {
			 * System.out.println("Failed to initialize GpuQuad class"); // TODO
			 * Auto-generated catch block e.printStackTrace(); return; // false; } //final
			 * int debugLevel); QUAD_CLT.setGPU(GPU_QUAD); }
			 */
			if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
				try {
					GPU_QUAD_AUX = new GpuQuad(//
							GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
				} catch (Exception e) {
					System.out.println("Failed to initialize GpuQuad class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false; // false;
				} // final int debugLevel);
				QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
			}
		}
		
		
		return true;
	}
	
	
	/* ======================================================================== */
	public boolean editIMU(boolean aux) {
		if (aux) {
			if (QUAD_CLT_AUX == null) {
				if (EYESIS_CORRECTIONS_AUX == null) {
					EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
							CORRECTION_PARAMETERS.getAux());
				}
				QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
						CORRECTION_PARAMETERS.getAux());
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels for aux camera");
				}
			}
			return QUAD_CLT_AUX.editExtrinsicCorr();
		} else {
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			return QUAD_CLT.editExtrinsicCorr();
		}

	}

	public boolean mainToAux(boolean use_img) {
		if (QUAD_CLT == null) {
			QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
			if (DEBUG_LEVEL > 0) {
				System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
			}
		}
		if (QUAD_CLT_AUX == null) {
			if (EYESIS_CORRECTIONS_AUX == null) {
				EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
						CORRECTION_PARAMETERS.getAux());
			}
			QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
					CORRECTION_PARAMETERS.getAux());
			if (DEBUG_LEVEL > 0) {
				System.out.println("Created new QuadCLT instance, will need to read CLT kernels for aux camera");
			}
		}
		@SuppressWarnings("unused")
		QuadCLT dbg_QUAD_CLT = QUAD_CLT;
		@SuppressWarnings("unused")
		QuadCLT dbg_QUAD_CLT_AUX = QUAD_CLT_AUX;

		ImagePlus imp_sel = WindowManager.getCurrentImage();
		if (imp_sel == null) {
			IJ.showMessage("Error", "There are no images open\nProcess canceled");
			return false;
		}
		ImageStack stack_sel = imp_sel.getStack();
		String[] labels = stack_sel.getSliceLabels();
		int indx = 0;
		for (int i = 0; i < labels.length; i++) {
			if (labels[i] != null)
				indx++;
		}
		boolean just2 = (indx == 2) && (labels[0] != null) && (labels[1] != null);

		String[] choices = { "---", "disparity", "strength" };
//		double min_strength =  0.18; // use some configurable parameters
//		boolean use_wnd =      true;
		boolean split_fg_bg = true;
//		double split_fbg_rms = 0.2; // split small source samples tp FG/BG if all aux tile RMS exceeds this value
		boolean for_adjust = false;

		GenericDialog gd = new GenericDialog("Select disparity and strength slices");
		indx = 0;
		for (int i = 0; i < labels.length; i++) {
			if (labels[i] != null) {
				gd.addChoice(((indx++) + 1) + ": " + labels[i], choices, choices[just2 ? (i + 1) : 0]);
			}
		}
		gd.addMessage("--- main-to-aux depth map parameters ---");
		gd.addNumericField("Minimal EO correlation strength", CLT_PARAMETERS.ly_gt_strength, 3, 6, "");
		gd.addCheckbox("Use window for AUX tiles to reduce weight of the hi-res tiles near low-res tile boundaries",
				CLT_PARAMETERS.ly_gt_use_wnd);
		gd.addCheckbox("Split FG and BG if hi-res disparity varies for the same low-res tile", split_fg_bg);
		gd.addNumericField("Aux disparity thershold to split FG and BG", CLT_PARAMETERS.ly_gt_rms, 3, 6, "");
		gd.addCheckbox("Data for Lazy Eye correction (remove ambiguous tiles)", for_adjust);

		WindowTools.addScrollBars(gd);
		gd.showDialog();
		if (gd.wasCanceled())
			return false;

		int[] selections = new int[indx];
		indx = 0;
		for (int i = 0; i < selections.length; i++) {
			selections[i] = gd.getNextChoiceIndex();
		}
		CLT_PARAMETERS.ly_gt_strength = gd.getNextNumber();
		CLT_PARAMETERS.ly_gt_use_wnd = gd.getNextBoolean();
		split_fg_bg = gd.getNextBoolean();
		CLT_PARAMETERS.ly_gt_rms = gd.getNextNumber();
		for_adjust = gd.getNextBoolean();

		int index_disparity = -1, index_strength = -1;
		indx = 0;
		for (int i = 0; i < labels.length; i++)
			if (labels[i] != null) {
				if ((index_disparity < 0) && (selections[indx] == 1)) {
					index_disparity = i;
				}
				if ((index_strength < 0) && (selections[indx] == 2)) {
					index_strength = i;
				}
				if ((index_disparity >= 0) && (index_strength >= 0)) {
					break;
				}
				indx++;
			}

		int width = imp_sel.getWidth();
		int height = imp_sel.getHeight();
		String title = imp_sel.getTitle() + "-DS";
		float[][] f_ds = new float[2][];
		f_ds[0] = (float[]) stack_sel.getPixels(index_disparity + 1);
		f_ds[1] = (float[]) stack_sel.getPixels(index_strength + 1);
		double[][] ds = new double[2][width * height];
		for (int l = 0; l < ds.length; l++) {
			for (int i = 0; i < ds[l].length; i++) {
				ds[l][i] = f_ds[l][i];
			}
		}
		String[] rslt_titles = split_fg_bg ? QuadCLT.FGBG_TITLES_AUX : QuadCLT.FGBG_TITLES_ADJ; // last 2 will be 0;

		ShowDoubleFloatArrays.showArrays(ds, width, height, true, title, QuadCLT.FGBG_TITLES_ADJ);

		int tile_size = CLT_PARAMETERS.transform_size;
		int[] wh_aux = QUAD_CLT_AUX.getGeometryCorrection().getSensorWH();
		int tilesX_aux = wh_aux[0] / tile_size;
		int tilesY_aux = wh_aux[1] / tile_size;

//		int num_slices = split_fg_bg? 7:2;

		double[][] ds_aux = QUAD_CLT_AUX.depthMapMainToAux(ds, // double [][] ds,
				QUAD_CLT.getGeometryCorrection(), // GeometryCorrection geometryCorrection_main,
				QUAD_CLT_AUX.getGeometryCorrection(), // GeometryCorrection geometryCorrection_aux,
				CLT_PARAMETERS, split_fg_bg, for_adjust, DEBUG_LEVEL); // int debug_level
		ShowDoubleFloatArrays.showArrays(ds_aux, tilesX_aux, tilesY_aux, true, title + "_TOAUX", rslt_titles);
		if (ds_aux.length == 2) {
			QUAD_CLT_AUX.ds_from_main = ds_aux;
		} else {
			QUAD_CLT_AUX.ds_from_main = null;
		}
		return true;

	}

//getGeometryCorrection
///data_ssd/lwir3d/results/saved/1562390490_233403/v11/
	/* ======================================================================== */

	public String getSaveCongigPath() {
		String configPath = null;
		if (EYESIS_CORRECTIONS.correctionsParameters.saveSettings) {
			configPath = EYESIS_CORRECTIONS.correctionsParameters.selectResultsDirectory(true, true);
			if (configPath == null) {
				String msg = "No results directory selected, command aborted";
				System.out.println("Warning: " + msg);
				IJ.showMessage("Warning", msg);
				return "ABORT";
			}
			configPath += Prefs.getFileSeparator() + "autoconfig" + Prefs.getFileSeparator() + "autoconfig";
			try {
				saveTimestampedProperties(configPath, // full path or null
						null, // use as default directory if path==null
						true, PROPERTIES);

			} catch (Exception e) {
				String msg = "Failed to save configuration to " + configPath + ", command aborted";
				System.out.println("Error: " + msg);
				IJ.showMessage("Error", msg);
				return "ABORT";
			}
		}
		return configPath;
	}

	public boolean importAux() {
		Properties aux_properties = new Properties();
		String path = loadProperties(null, CORRECTION_PARAMETERS.resultsDirectory, true, aux_properties);
		if (path != null) {

			EyesisCorrectionParameters.CorrectionParameters auxCorrectionParameters = new EyesisCorrectionParameters.CorrectionParameters();
			auxCorrectionParameters.getProperties("CORRECTION_PARAMETERS.", aux_properties);
			CORRECTION_PARAMETERS.auxFromExternal(auxCorrectionParameters);
			if (QUAD_CLT_AUX == null) {
				if (EYESIS_CORRECTIONS_AUX == null) {
					EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
							CORRECTION_PARAMETERS.getAux());
				}
				QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX, // _AUX, - not used
						PROPERTIES, // aux_properties,
						EYESIS_CORRECTIONS_AUX, CORRECTION_PARAMETERS.getAux());
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			// import main properties.
			QUAD_CLT_AUX.copyPropertiesFrom(aux_properties, QuadCLT.PREFIX, QuadCLT.PREFIX_AUX);
			// copy color gains for channels (from main other file to this aux)
			if (CHANNEL_GAINS_PARAMETERS_AUX == null) {
				CHANNEL_GAINS_PARAMETERS_AUX = new CorrectionColorProc.ColorGainsParameters();
			}
			CHANNEL_GAINS_PARAMETERS_AUX.getProperties("CHANNEL_GAINS_PARAMETERS.", aux_properties);
			if (DEBUG_LEVEL > -1)
				System.out.println(
						"Importing auxiliary camera parameters for its configuration (where it is main): " + path);
		} else {
			if (DEBUG_LEVEL > -10)
				System.out.println("Failed to import auxiliary configuration parameters");
		}
		return true;
	}

	public boolean getPairImages() { // Never Used
		if (QUAD_CLT == null) {
			QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
			if (DEBUG_LEVEL > -2) {
				System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
			}
		}
		if (QUAD_CLT_AUX == null) {
			if (EYESIS_CORRECTIONS_AUX == null) {
				EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
						CORRECTION_PARAMETERS.getAux());
			}
			QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
					CORRECTION_PARAMETERS.getAux());
			if (DEBUG_LEVEL > -2) {
				System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
			}
		}
//        QuadCLT dbg_QUAD_CLT = QUAD_CLT;
//        QuadCLT dbg_QUAD_CLT_AUX = QUAD_CLT_AUX;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Running initSensorFiles for the main camera ++++++++++++++");
		}
		EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL + 2, true, false, COLOR_PROC_PARAMETERS.correct_vignetting); // boolean
																													// correct_vignetting
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Running initSensorFiles for the auxiliary camera ++++++++++++++");
		}
		EYESIS_CORRECTIONS_AUX.initSensorFiles(DEBUG_LEVEL + 2, true, false,
				COLOR_PROC_PARAMETERS_AUX.correct_vignetting); // boolean correct_vignetting

		int numChannels = EYESIS_CORRECTIONS.getNumChannels();
		int numChannelsAux = EYESIS_CORRECTIONS_AUX.getNumChannels();
		if (DEBUG_LEVEL > -2) {
			System.out.println("numChannels=" + numChannels + ", numChannelsAux=" + numChannelsAux);
		}

		CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);
		if (CHANNEL_GAINS_PARAMETERS_AUX == null) {
			CHANNEL_GAINS_PARAMETERS_AUX = new CorrectionColorProc.ColorGainsParameters();
		}
		CHANNEL_GAINS_PARAMETERS_AUX.modifyNumChannels(numChannelsAux);
		if (!QUAD_CLT.CLTKernelsAvailable()) {
			if (DEBUG_LEVEL > -2) {
				System.out.println("++++++++++++++ Reading CLT kernels for the main camera ++++++++++++++");
			}
			QUAD_CLT.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
					DEBUG_LEVEL);

			if (DEBUG_LEVEL > 1) {
				QUAD_CLT.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);
			}
		}
		if (!QUAD_CLT_AUX.CLTKernelsAvailable()) {
			if (DEBUG_LEVEL > -2) {
				System.out.println("++++++++++++++ Reading CLT kernels for the auxiliary camera ++++++++++++++ ");
			}
			QUAD_CLT_AUX.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
					DEBUG_LEVEL);

			if (DEBUG_LEVEL > 1) {
				QUAD_CLT_AUX.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);
			}
		}
		if (!QUAD_CLT.geometryCorrectionAvailable()) {
			if (DEBUG_LEVEL > -2) {
				System.out.println("++++++++++++++ Calculating geometryCorrection for the main camera ++++++++++++++");
			}
			if (!QUAD_CLT.initGeometryCorrection(DEBUG_LEVEL + 2)) {
				return false;
			}
		}
		if (!QUAD_CLT_AUX.geometryCorrectionAvailable()) {
			if (DEBUG_LEVEL > -2) {
				System.out.println(
						"++++++++++++++ Calculating geometryCorrection for the auxiliary camera ++++++++++++++");
			}
			if (!QUAD_CLT_AUX.initGeometryCorrection(DEBUG_LEVEL + 2)) {
				return false;
			}
		}

		QUAD_CLT.processCLTQuadCorrs(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
				COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
				CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
				RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
				false, // apply_corr,
				false, // infinity_corr, // calculate and apply geometry correction at infinity
				THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
				UPDATE_STATUS, // final boolean updateStatus,
				DEBUG_LEVEL); // final int debugLevel);
		QUAD_CLT_AUX.processCLTQuadCorrs(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
				COLOR_PROC_PARAMETERS_AUX, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
				CHANNEL_GAINS_PARAMETERS_AUX, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
				RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
				false, // apply_corr,
				false, // infinity_corr, // calculate and apply geometry correction at infinity
				THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
				UPDATE_STATUS, // final boolean updateStatus,
				DEBUG_LEVEL); // final int debugLevel);

		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}

		return true;
	}

// inverted false/true in initSensorFiles(), should work with LWIR also
	public boolean prepareRigImages() {
		if (QUAD_CLT == null) {
			QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
			if (DEBUG_LEVEL > -2) {
				System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
			}
		}
		if (QUAD_CLT_AUX == null) {
			if (EYESIS_CORRECTIONS_AUX == null) {
				EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
						CORRECTION_PARAMETERS.getAux());
			}
			QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
					CORRECTION_PARAMETERS.getAux());
			if (DEBUG_LEVEL > -2) {
				System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
			}
		}
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Running initSensorFiles for the main camera ++++++++++++++");
		}
		EYESIS_CORRECTIONS.initSensorFiles( // long
				DEBUG_LEVEL + 2, false, // true,
				true, // false,
				COLOR_PROC_PARAMETERS.correct_vignetting); // boolean correct_vignetting
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Running initSensorFiles for the auxiliary camera ++++++++++++++");
		}
		EYESIS_CORRECTIONS_AUX.initSensorFiles(DEBUG_LEVEL + 2, false, // true, // ***** here reads all files *****
				true, // false,
				COLOR_PROC_PARAMETERS_AUX.correct_vignetting); // boolean correct_vignetting

		int numChannels = EYESIS_CORRECTIONS.getNumChannels();
		int numChannelsAux = EYESIS_CORRECTIONS_AUX.getNumChannels();
		if (DEBUG_LEVEL > -2) {
			System.out.println("numChannels=" + numChannels + ", numChannelsAux=" + numChannelsAux);
		}

		CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);
		if (CHANNEL_GAINS_PARAMETERS_AUX == null) {
			CHANNEL_GAINS_PARAMETERS_AUX = new CorrectionColorProc.ColorGainsParameters();
		}
		CHANNEL_GAINS_PARAMETERS_AUX.modifyNumChannels(numChannelsAux);
		if (!QUAD_CLT.CLTKernelsAvailable()) {
			if (DEBUG_LEVEL > -2) {
				System.out.println("++++++++++++++ Reading CLT kernels for the main camera ++++++++++++++");
			}
			QUAD_CLT.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
					DEBUG_LEVEL);

			if (DEBUG_LEVEL > 1) {
				QUAD_CLT.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);
			}
		}
		if (!QUAD_CLT_AUX.CLTKernelsAvailable()) {
			if (DEBUG_LEVEL > -2) {
				System.out.println("++++++++++++++ Reading CLT kernels for the auxiliary camera ++++++++++++++ ");
			}
			QUAD_CLT_AUX.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
					DEBUG_LEVEL);

			if (DEBUG_LEVEL > 1) {
				QUAD_CLT_AUX.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);
			}
		}
		if (!QUAD_CLT.geometryCorrectionAvailable()) {
			if (DEBUG_LEVEL > -2) {
				System.out.println("++++++++++++++ Calculating geometryCorrection for the main camera ++++++++++++++");
			}
			if (!QUAD_CLT.initGeometryCorrection(DEBUG_LEVEL + 2)) {
				return false;
			}
		}
		if (!QUAD_CLT_AUX.geometryCorrectionAvailable()) {
			if (DEBUG_LEVEL > -2) {
				System.out.println(
						"++++++++++++++ Calculating geometryCorrection for the auxiliary camera ++++++++++++++");
			}
			if (!QUAD_CLT_AUX.initGeometryCorrection(DEBUG_LEVEL + 2)) {// java.lang.NullPointerException
				return false;
			}
		}

		if (TWO_QUAD_CLT == null) {
			TWO_QUAD_CLT = new TwoQuadCLT(QUAD_CLT, QUAD_CLT_AUX);
		} else {
			// is it needed to update main/aux? Or it never changes?
			TWO_QUAD_CLT.quadCLT_main = QUAD_CLT;
			TWO_QUAD_CLT.quadCLT_aux = QUAD_CLT_AUX;
			// will need to make sure that miScan image is not from the previous scene

		}
		return true;
	}

	public boolean getPairImages2() {
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Calculating combined correlations ++++++++++++++");
		}
		// reset if ran after 3d model to save memory
		if (QUAD_CLT.tp != null) {
			QUAD_CLT.tp.clt_3d_passes = null; // resetCLTPasses();
		}
		if (QUAD_CLT_AUX.tp != null) {
			QUAD_CLT_AUX.tp.clt_3d_passes = null; // resetCLTPasses();
		}
		if (COLOR_PROC_PARAMETERS_AUX == null) {
			COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
		}
		try {
			TWO_QUAD_CLT.processCLTQuadCorrPairs(QUAD_CLT, // QuadCLT quadCLT_main,
					QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					COLOR_PROC_PARAMETERS_AUX, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					// CHANNEL_GAINS_PARAMETERS, //CorrectionColorProc.ColorGainsParameters
					// channelGainParameters,
					// CHANNEL_GAINS_PARAMETERS_AUX, //CorrectionColorProc.ColorGainsParameters
					// channelGainParameters_aux,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		QUAD_CLT.tp.clt_3d_passes = null; // resetCLTPasses(); // so running "Ground truth" after would be OK
		QUAD_CLT_AUX.tp.clt_3d_passes = null; // .resetCLTPasses();

		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		return true;
	}

	public boolean showImageFromGPU() {
//		TWO_QUAD_CLT.showImageFromGPU();
		TwoQuadCLT.showImageFromGPU();
		return true;
	}

	public boolean generateGPUDebugFiles() {
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
//		if ((CORRECTION_PARAMETERS.tile_processor_gpu != null) &&

		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Calculating combined correlations ++++++++++++++");
		}
		// reset if ran after 3d model to save memory
		if (QUAD_CLT.tp != null) {
			QUAD_CLT.tp.clt_3d_passes = null; // resetCLTPasses();
		}
		if (QUAD_CLT_AUX.tp != null) {
			QUAD_CLT_AUX.tp.clt_3d_passes = null; // resetCLTPasses();
		}
		if (COLOR_PROC_PARAMETERS_AUX == null) {
			COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
		}

		try {
			TWO_QUAD_CLT.prepareFilesForGPUDebug(CORRECTION_PARAMETERS.tile_processor_gpu, // String save_prefix, //
																							// absolute path to the cuda
																							// project root
					QUAD_CLT, // QuadCLT quadCLT_main,
					QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					COLOR_PROC_PARAMETERS_AUX, // EyesisCorrectionParameters.ColorProcParameters
												// colorProcParameters_aux,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (QUAD_CLT.tp != null) {
			QUAD_CLT.tp.clt_3d_passes = null; // resetCLTPasses();
		}
		if (QUAD_CLT_AUX.tp != null) {
			QUAD_CLT_AUX.tp.clt_3d_passes = null; // resetCLTPasses();
		}

		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		return true;
	}

	public boolean getPairImages2Gpu() {
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Calculating combined correlations ++++++++++++++");
		}
		// reset if ran after 3d model to save memory
		if (QUAD_CLT.tp != null) {
			QUAD_CLT.tp.clt_3d_passes = null; // resetCLTPasses();
		}
		if (QUAD_CLT_AUX.tp != null) {
			QUAD_CLT_AUX.tp.clt_3d_passes = null; // resetCLTPasses();
		}
		if (GPU_TILE_PROCESSOR == null) {
			try {
				GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
			} catch (Exception e) {
				System.out.println("Failed to initialize GPU class");
				// TODO Auto-generated catch block
				e.printStackTrace();
				return false;
			} // final int debugLevel);

		}

		if (GPU_QUAD == null) {
			try {
				GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
			} catch (Exception e) {
				System.out.println("Failed to initialize GpuQuad class");
				// TODO Auto-generated catch block
				e.printStackTrace();
				return false;
			} // final int debugLevel);
			QUAD_CLT.setGPU(GPU_QUAD);
		}
		// For now keep GPU_QUAD_AUX==null

		if (COLOR_PROC_PARAMETERS_AUX == null) {
			COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
		}
		try {
			TWO_QUAD_CLT.processCLTQuadCorrPairsGpu(
//					GPU_TILE_PROCESSOR,
					GPU_QUAD, // GPUTileProcessor.GpuQuad gpuQuad_main,
					GPU_QUAD_AUX, // GPUTileProcessor.GpuQuad gpuQuad_aux,
					QUAD_CLT, // QuadCLT quadCLT_main,
					QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					CORRECTION_PARAMETERS, // EyesisCorrectionParameters ecp,
					COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					COLOR_PROC_PARAMETERS_AUX, // EyesisCorrectionParameters.ColorProcParameters
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		} // final int debugLevel);
		QUAD_CLT.tp.clt_3d_passes = null; // resetCLTPasses(); // so running "Ground truth" after would be OK
		QUAD_CLT_AUX.tp.clt_3d_passes = null; // .resetCLTPasses();

		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		return true;
	}

	public boolean rigPlanes() {
		if ((QUAD_CLT == null) || (QUAD_CLT.tp == null) || (QUAD_CLT.tp.clt_3d_passes == null)
				|| (QUAD_CLT.tp.clt_3d_passes.size() == 0)) {
			String msg = "DSI data is not available. Please run \"CLT 3D\" first";
			IJ.showMessage("Error", msg);
			System.out.println(msg);
			return false;
		}
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		if (DEBUG_LEVEL > -2) {
			System.out
					.println("++++++++++++++ Extracting  planes using a dual-quad camera rig DSI data ++++++++++++++");
		}
		QUAD_CLT.showCLTPlanes(CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
				THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
				UPDATE_STATUS, // final boolean updateStatus,
				DEBUG_LEVEL); // final int debugLevel);
		return true;

	}

	public boolean showBiScan() {
		if ((QUAD_CLT == null) || (QUAD_CLT.tp == null) || (QUAD_CLT.tp.clt_3d_passes == null) || (TWO_QUAD_CLT == null)
				|| (TWO_QUAD_CLT.biCamDSI_persistent == null) || (TWO_QUAD_CLT.biCamDSI_persistent.biScans == null)) {
			String msg = "Data is not available. Please run \"Ground truth\" first";
			IJ.showMessage("Error", msg);
			System.out.println(msg);
			return false;
		}

		TWO_QUAD_CLT.showBiScan(QUAD_CLT, // QuadCLT quadCLT_main, // tiles should be set
				QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
				CLT_PARAMETERS, // EyesisCorrectionParameters.CLTParameters clt_parameters,
				THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
				UPDATE_STATUS, // final boolean updateStatus,
				DEBUG_LEVEL); // final int debugLevel);
		return true;
	}

	public boolean processPoles() {
		long startTime = System.nanoTime();
		if ((QUAD_CLT == null) || (QUAD_CLT.tp == null) || (QUAD_CLT.tp.clt_3d_passes == null) || (TWO_QUAD_CLT == null)
				|| (TWO_QUAD_CLT.biCamDSI_persistent == null) || (TWO_QUAD_CLT.biCamDSI_persistent.biScans == null)) {
			String msg = "Data is not available. Please run \"Ground truth\" first";
			IJ.showMessage("Error", msg);
			System.out.println(msg);
			return false;
		}

		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Generating GT data for street poles ++++++++++++++");
		}
		TWO_QUAD_CLT.processPoles( // actually there is no sense to process multiple image sets. Combine with other
									// processing?
				QUAD_CLT, // QuadCLT quadCLT_main,
				QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
				TWO_QUAD_CLT.biCamDSI_persistent, // BiCamDSI biCamDSI,
				CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
				THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
				UPDATE_STATUS, // final boolean updateStatus,
				DEBUG_LEVEL);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("processPoles(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");

		return true;
	}

//resetGroundTruthByRig()
	public boolean resetGroundTruth() {
		if ((QUAD_CLT == null) || (QUAD_CLT.tp == null))
			return false;
		QUAD_CLT.resetGroundTruthByRig();
		return true;
	}

//	boolean adjust_extrinsics = label.equals("MAIN extrinsics") || label.equals("CLT Poly corr");
//	boolean adjust_poly = label.equals("CLT Poly corr");

	public boolean clt3d_aux(boolean adjust_extrinsics, boolean adjust_poly) {
		if (QUAD_CLT_AUX == null) {
			if (EYESIS_CORRECTIONS_AUX == null) {
				EYESIS_CORRECTIONS_AUX = new EyesisCorrections(SYNC_COMMAND.stopRequested,
						CORRECTION_PARAMETERS.getAux());
			}
			QUAD_CLT_AUX = new QuadCLT(QuadCLT.PREFIX_AUX, PROPERTIES, EYESIS_CORRECTIONS_AUX,
					CORRECTION_PARAMETERS.getAux());
			if (DEBUG_LEVEL > 0) {
				System.out.println(
						"Created new QuadCLT instance for AUX camera, will need to read CLT kernels for aux camera");
			}
		}
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		EYESIS_CORRECTIONS_AUX.initSensorFiles(DEBUG_LEVEL);
		int numChannelsAux = EYESIS_CORRECTIONS_AUX.getNumChannels();
		CHANNEL_GAINS_PARAMETERS_AUX.modifyNumChannels(numChannelsAux);

		if (!QUAD_CLT_AUX.CLTKernelsAvailable()) {
			if (DEBUG_LEVEL > 0) {
				System.out.println("Reading AUX CLT kernels");
			}
			QUAD_CLT_AUX.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
					DEBUG_LEVEL);

			if (DEBUG_LEVEL > 1) {
				QUAD_CLT_AUX.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);
			}
		}

		if (!QUAD_CLT_AUX.geometryCorrectionAvailable()) {
			if (DEBUG_LEVEL > 0) {
				System.out.println("Calculating geometryCorrection for AUX camera");
			}
			if (!QUAD_CLT_AUX.initGeometryCorrection(DEBUG_LEVEL + 2)) {
				return false;
			}
		}

		QUAD_CLT_AUX.processCLTQuads3d(adjust_extrinsics, // boolean adjust_extrinsics,
				adjust_poly, // boolean adjust_poly,
				TWO_QUAD_CLT, // TwoQuadCLT twoQuadCLT, //maybe null in no-rig mode, otherwise may contain rig
								// measurements to be used as infinity ground truth
				CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
				COLOR_PROC_PARAMETERS_AUX, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
				CHANNEL_GAINS_PARAMETERS_AUX, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
				RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
				EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
											// equirectangularParameters,
				THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
				UPDATE_STATUS, // final boolean updateStatus,
				DEBUG_LEVEL); // final int debugLevel);

		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		return true;
	}

	public boolean clt3d(boolean adjust_extrinsics, boolean adjust_poly, boolean dry_run // init kernel/geometry only
	) {
		if (QUAD_CLT == null) {
			QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
			if (DEBUG_LEVEL > 0) {
				System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
			}
		}
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);
		int numChannels = EYESIS_CORRECTIONS.getNumChannels();
		CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);

		if (!QUAD_CLT.CLTKernelsAvailable()) {
			if (DEBUG_LEVEL > 0) {
				System.out.println("Reading CLT kernels");
			}
			QUAD_CLT.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
					DEBUG_LEVEL);

			if (DEBUG_LEVEL > 1) {
				QUAD_CLT.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);
			}
		}

		if (!QUAD_CLT.geometryCorrectionAvailable()) {
			if (DEBUG_LEVEL > 0) {
				System.out.println("Calculating geometryCorrection");
			}
			if (!QUAD_CLT.initGeometryCorrection(DEBUG_LEVEL + 2)) {
				return false;
			}
		}
		if (dry_run) {
			return true;
		}
		QUAD_CLT.processCLTQuads3d(adjust_extrinsics, // boolean adjust_extrinsics,
				adjust_poly, // boolean adjust_poly,
				TWO_QUAD_CLT, // TwoQuadCLT twoQuadCLT, //maybe null in no-rig mode, otherwise may contain rig
								// measurements to be used as infinity ground truth
				CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
				COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
				CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
				RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
				EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
											// equirectangularParameters,
				// CONVOLVE_FFT_SIZE, //int convolveFFTSize, // 128 - fft size, kernel size
				// should be size/2
				THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
				UPDATE_STATUS, // final boolean updateStatus,
				DEBUG_LEVEL); // final int debugLevel);

		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		return true;
	}

	public boolean groundTruth() {
		long startTime = System.nanoTime();
		if ((QUAD_CLT == null) || (QUAD_CLT.tp == null) || (QUAD_CLT.tp.clt_3d_passes == null)
				|| (QUAD_CLT.tp.clt_3d_passes.size() == 0)) {
			boolean OK = clt3d(false, // boolean adjust_extrinsics,
					false, // boolean adjust_poly);
					false); // boolean dry_run // init kernel/geometry only

			if (!OK) {
				String msg = "DSI data is not available and \"CLT 3D\" failed";
				IJ.showMessage("Error", msg);
				System.out.println(msg);
				return false;
			}
		}
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		if (DEBUG_LEVEL > -2) {
			System.out.println(
					"++++++++++++++ Enhancing single-camera DSI by the dual-camera rig using planes ++++++++++++++");
		}
		if (COLOR_PROC_PARAMETERS_AUX == null) {
			COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
		}
		TWO_QUAD_CLT.groundTruth( // actually there is no sense to process multiple image sets. Combine with other
									// processing?
				QUAD_CLT, // QuadCLT quadCLT_main,
				QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
				CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
				COLOR_PROC_PARAMETERS, COLOR_PROC_PARAMETERS_AUX, THREADS_MAX, // final int threadsMax, // maximal
																				// number of threads to launch
				UPDATE_STATUS, // final boolean updateStatus,
				DEBUG_LEVEL - 2);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("groundTruth(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");

		return true;
	}

	public boolean rigDSI() {
		long startTime = System.nanoTime();
		if ((QUAD_CLT == null) || (QUAD_CLT.tp == null) || (QUAD_CLT.tp.clt_3d_passes == null)
				|| (QUAD_CLT.tp.clt_3d_passes.size() == 0)) {
			boolean OK = clt3d(false, // boolean adjust_extrinsics,
					false, // boolean adjust_poly);
					false); // boolean dry_run // init kernel/geometry only
			if (!OK) {
				String msg = "DSI data is not available and \"CLT 3D\" failed";
				IJ.showMessage("Error", msg);
				System.out.println(msg);
				return false;
			}
		}
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Creating a dual camera rig DSI from a single camera DSI ++++++++++++++");
		}
		if (COLOR_PROC_PARAMETERS_AUX == null) {
			COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
		}

		boolean OK = (TWO_QUAD_CLT.rigInitialScan( // actually there is no sense to process multiple image sets. Combine
													// with other processing?
				QUAD_CLT, // QuadCLT quadCLT_main,
				QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
				CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
				COLOR_PROC_PARAMETERS, COLOR_PROC_PARAMETERS_AUX, THREADS_MAX, // final int threadsMax, // maximal
																				// number of threads to launch
				UPDATE_STATUS, // final boolean updateStatus,
				DEBUG_LEVEL - 2) != null);
		if (!OK) {
			System.out.println("rigDSI(): Processing FAILED at "
					+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
					+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
			return false;
		}
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("rigDSI(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");

		return true;
	}

	public boolean batchRig() {
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Running batch processing of dual-quad camera rig ++++++++++++++");
		}
		if (COLOR_PROC_PARAMETERS_AUX == null) {
			COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
		}
		try {
			TWO_QUAD_CLT.batchRig(QUAD_CLT, // QuadCLT quadCLT_main,
					QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					COLOR_PROC_PARAMETERS_AUX, // EyesisCorrectionParameters.ColorProcParameters
												// colorProcParameters_aux,
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					PROPERTIES, // Properties properties,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}

	public boolean batchLwir() {
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out
					.println("++++++++++++++ Running batch processing of dual-quad EO/LWIR camera rig ++++++++++++++");
		}
		// parameters are different
///		if (COLOR_PROC_PARAMETERS_AUX == null) {
///			COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
///		}

		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
				try {
					GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
				} catch (Exception e) {
					System.out.println("Failed to initialize GpuQuad class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
				QUAD_CLT.setGPU(GPU_QUAD);
			}
			if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																									// needed
				try {
					GPU_QUAD_AUX = new GpuQuad(//
							GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
				} catch (Exception e) {
					System.out.println("Failed to initialize GpuQuad class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
				QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
			}
		}
		CLT_PARAMETERS.setColorProcParameters(COLOR_PROC_PARAMETERS,     false);
		CLT_PARAMETERS.setColorProcParameters(COLOR_PROC_PARAMETERS_AUX, true);
		CLT_PARAMETERS.setRGBParameters(RGB_PARAMETERS);

		try {
			TWO_QUAD_CLT.batchLwirRig(
					SYNC_COMMAND,  // SyncCommand sync_command,
					QUAD_CLT, // QuadCLT quadCLT_main,
					QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					COLOR_PROC_PARAMETERS_AUX, // EyesisCorrectionParameters.ColorProcParameters
												// colorProcParameters_aux,
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					PROPERTIES, // Properties properties,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}

	public boolean testInterScene(boolean use_aux) {
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Testing Interscene processing ++++++++++++++");
		}
		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}
			}

		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;
		try {
			TWO_QUAD_CLT.TestInterScene(
					SYNC_COMMAND,  // SyncCommand sync_command,
					// use_aux, // boolean is_aux,
					quadCLT, // QUAD_CLT, // QuadCLT quadCLT_main,
					// QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					colorProcParameters, // COLOR_PROC_PARAMETERS, //EyesisCorrectionParameters.ColorProcParameters
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					PROPERTIES, // Properties properties,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}
	
	
	public boolean buildSeries(
			boolean use_aux,
			int     cuas_proc_mode) { // 0 - old, 1 combine scene series, 2 - combine video, 3 - RT tests) {
		int lwir_level = CLT_PARAMETERS.lwir.getDebugLevel();
		String slev = EyesisCorrections.stringLogLevel(lwir_level);
		if (slev != null) {
			System.out.println("Setting log level to "+slev);
			loci.common.DebugTools.setRootLevel(slev);
		}
		
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Building series from scratch ++++++++++++++");
		}
		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}
			}

		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;
		CLT_PARAMETERS.setColorProcParameters(COLOR_PROC_PARAMETERS,     false);
		CLT_PARAMETERS.setColorProcParameters(COLOR_PROC_PARAMETERS_AUX, true);
		CLT_PARAMETERS.setRGBParameters(RGB_PARAMETERS);
		
		try {
			TwoQuadCLT.buildSeriesTQ(
					SYNC_COMMAND,               // SyncCommand sync_command,
					cuas_proc_mode,             // int  cuas_proc_mode, // 0 - old, 1 combine scene series
					quadCLT,                    // QUAD_CLT, // QuadCLT quadCLT_main,
					-1,                         // int  ref_index,
					0,                          // int  ref_step, 
					CLT_PARAMETERS,             // EyesisCorrectionParameters.DCTParameters dct_parameters,
					colorProcParameters,        // COLOR_PROC_PARAMETERS, //EyesisCorrectionParameters.ColorProcParameters
					CHANNEL_GAINS_PARAMETERS,   // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS,             // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					PROPERTIES,                 // Properties properties,
					true,                       // false, // boolean reset_from_extrinsics,
					THREADS_MAX,                // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS,              // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}
	
	public boolean buildLWIRWorld(boolean use_aux) {
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Building series from scratch ++++++++++++++");
		}
		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}
			}

		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;
		CLT_PARAMETERS.setColorProcParameters(COLOR_PROC_PARAMETERS,     false);
		CLT_PARAMETERS.setColorProcParameters(COLOR_PROC_PARAMETERS_AUX, true);
		CLT_PARAMETERS.setRGBParameters(RGB_PARAMETERS);
		
		try {
			LwirWorld.buildWorld(
					quadCLT, // QUAD_CLT, // QuadCLT quadCLT_main,
					-1, // int  ref_index,
					0,  // int  ref_step, 
					// QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					colorProcParameters, // COLOR_PROC_PARAMETERS, //EyesisCorrectionParameters.ColorProcParameters
											// colorProcParameters,
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					PROPERTIES, // Properties properties,
					true, // false, // boolean reset_from_extrinsics,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		saveTimestampedProperties( // save config again
				configPath, // full path or null
				null, // use as default directory if path==null
				true, PROPERTIES);
		System.out.println("buildLWIRWorld(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}

	
	
	public boolean testInterLMA(boolean use_aux) {
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Testing Interscene processing ++++++++++++++");
		}

		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}
			}
		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;
		try {
			TWO_QUAD_CLT.TestInterLMA(
					SYNC_COMMAND,  // SyncCommand sync_command,
					quadCLT, // QUAD_CLT, // QuadCLT quadCLT_main,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					colorProcParameters, // COLOR_PROC_PARAMETERS, //EyesisCorrectionParameters.ColorProcParameters
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					PROPERTIES, // Properties properties,
					true, // false, // boolean reset_from_extrinsics,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}

	public boolean interPairsLMA(boolean use_aux) {
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Testing Interscene processing ++++++++++++++");
		}

		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}

			}
		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;
		try {
			TWO_QUAD_CLT.interPairsLMA(
					SYNC_COMMAND,  // SyncCommand sync_command,
					quadCLT, // QUAD_CLT, // QuadCLT quadCLT_main,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					colorProcParameters, // COLOR_PROC_PARAMETERS, //EyesisCorrectionParameters.ColorProcParameters
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					PROPERTIES, // Properties properties,
					true, // false, // boolean reset_from_extrinsics,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}

	public boolean interSeriesLMA(boolean use_aux) {
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Testing Interscene processing ++++++++++++++");
		}

		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}
			}
		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;
		try {
			TWO_QUAD_CLT.interSeriesLMA(
					SYNC_COMMAND,  // SyncCommand sync_command,
					quadCLT, // QUAD_CLT, // QuadCLT quadCLT_main,
					-1, // int  ref_index,
					0,  // int  ref_step, 
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					colorProcParameters, // COLOR_PROC_PARAMETERS, //EyesisCorrectionParameters.ColorProcParameters
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
												// equirectangularParameters,
					PROPERTIES, // Properties properties,
					true, // false, // boolean reset_from_extrinsics,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}

	public boolean intersceneAccumulate(boolean use_aux) {
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Testing Interscene processing ++++++++++++++");
		}

		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}

			}
		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;
		try {
			TWO_QUAD_CLT.intersceneAccumulate(
					SYNC_COMMAND,  // SyncCommand sync_command,
					quadCLT, // QUAD_CLT, // QuadCLT quadCLT_main,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					colorProcParameters, // COLOR_PROC_PARAMETERS, //EyesisCorrectionParameters.ColorProcParameters
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
					PROPERTIES, // Properties properties,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}

	public boolean inter_intra_export(boolean use_aux) {

		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Testing Interscene processing ++++++++++++++");
		}
		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}

			}
		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;
		try {
			TWO_QUAD_CLT.interIntraExportML(
					SYNC_COMMAND,              // SyncCommand sync_command,
					quadCLT, // QuadCLT quadCLT_main,
					-1, // use last // int  indx_ref,     // = num_scenes - 1; 
					0,  // int                                                  ref_step, 
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					colorProcParameters, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
					PROPERTIES, // Properties properties,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}

	public boolean intersceneNoise(boolean use_aux, boolean batch_noise, boolean bayer_artifacts_debug) {
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Testing Interscene processing ++++++++++++++");
		}
		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}

			}
		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;

		try {
			if (batch_noise) {
				TWO_QUAD_CLT.intersceneNoiseBatch(
						SYNC_COMMAND,  // SyncCommand sync_command,
						quadCLT, // QuadCLT quadCLT_main,
						CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
						colorProcParameters, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
						CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
						RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
						EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
						PROPERTIES, // Properties properties,
						bayer_artifacts_debug, // boolean bayer_artifacts_debug
						THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
						UPDATE_STATUS, // final boolean updateStatus,
						DEBUG_LEVEL);
			} else {
				TWO_QUAD_CLT.intersceneNoise(
						SYNC_COMMAND,  // SyncCommand sync_command,
						quadCLT, // QuadCLT quadCLT_main,
						CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
						colorProcParameters, // EyesisCorrectionParameters.ColorProcParameters colorProcParameters,
						CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
						RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
						EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
						PROPERTIES, // Properties properties,
						bayer_artifacts_debug, // boolean bayer_artifacts_debug
						-1, // int noise_variant, // <0 - no-variants, compatible with old code
						THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
						UPDATE_STATUS, // final boolean updateStatus,
						DEBUG_LEVEL);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}

	public boolean intersceneNoiseStats(boolean use_aux) {
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Testing Interscene processing ++++++++++++++");
		}
		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}

			}
		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;

		try {
			TWO_QUAD_CLT.intersceneNoiseStats(quadCLT, // QUAD_CLT, // QuadCLT quadCLT_main,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					colorProcParameters, // COLOR_PROC_PARAMETERS, //EyesisCorrectionParameters.ColorProcParameters
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
					PROPERTIES, // Properties properties,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("batchRig(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}

	/**
	 * Adjust LY using consecutive series of scenes
	 * @param use_aux
	 * @return
	 */
	public boolean adjustLYSeries(boolean use_aux) {
		MultisceneLY.MSLY_MODE adjust_mode = MultisceneLY.MSLY_MODE.INF_NOINF;
		if (CLT_PARAMETERS.ofp.pattern_mode) {
			adjust_mode = MultisceneLY.MSLY_MODE.NOINF_ONLY;	
		}
		long startTime = System.nanoTime();
		// load needed sensor and kernels files
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;
		setAllProperties(PROPERTIES); // batchRig may save properties with the model. Extrinsics will be updated,
										// others should be set here
		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ adjustLYSeries ++++++++++++++");
		}

		if (CLT_PARAMETERS.useGPU()) { // only init GPU instances if it is used
			if (GPU_TILE_PROCESSOR == null) {
				try {
					GPU_TILE_PROCESSOR = new GPUTileProcessor(CORRECTION_PARAMETERS.tile_processor_gpu);
				} catch (Exception e) {
					System.out.println("Failed to initialize GPU class");
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				} // final int debugLevel);
			}
			if (use_aux) {
				if (CLT_PARAMETERS.useGPU(true) && (QUAD_CLT_AUX != null) && (GPU_QUAD_AUX == null)) { // if GPU AUX is
																										// needed
					try {
						GPU_QUAD_AUX = new GpuQuad(//
								GPU_TILE_PROCESSOR, QUAD_CLT_AUX, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT_AUX.setGPU(GPU_QUAD_AUX);
				}
			} else {
				if (CLT_PARAMETERS.useGPU(false) && (QUAD_CLT != null) && (GPU_QUAD == null)) { // if GPU main is needed
					try {
						GPU_QUAD = new GpuQuad(GPU_TILE_PROCESSOR, QUAD_CLT, CLT_PARAMETERS.gpu_debug_level);
					} catch (Exception e) {
						System.out.println("Failed to initialize GpuQuad class");
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					} // final int debugLevel);
					QUAD_CLT.setGPU(GPU_QUAD);
				}
			}
		}
		QuadCLT quadCLT = use_aux ? QUAD_CLT_AUX : QUAD_CLT;
		ColorProcParameters colorProcParameters = use_aux ? COLOR_PROC_PARAMETERS_AUX : COLOR_PROC_PARAMETERS;
		try {
			TWO_QUAD_CLT.adjustLYSeries(
					quadCLT, // QUAD_CLT, // QuadCLT quadCLT_main,
					CLT_PARAMETERS,       // EyesisCorrectionParameters.DCTParameters dct_parameters,
					adjust_mode,          // MultisceneLY.MSLY_MODE                               adjust_mode,
					colorProcParameters,  // COLOR_PROC_PARAMETERS, //EyesisCorrectionParameters.ColorProcParameters
					CHANNEL_GAINS_PARAMETERS, // CorrectionColorProc.ColorGainsParameters channelGainParameters,
					RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
					EQUIRECTANGULAR_PARAMETERS, // EyesisCorrectionParameters.EquirectangularParameters
					PROPERTIES, // Properties properties,
					true, // false, // boolean reset_from_extrinsics,
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);
		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("adjustLYSeries(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");
		return true;
	}
	
	
	
	
	public boolean test1d() {
		Clt1d clt1d = new Clt1d(CLT_PARAMETERS.transform_size); // CLT_PARAMETERS
		clt1d.test1d(CLT_PARAMETERS);
		return true;
	}

	public boolean coloriseDepthMap() {
		ImagePlus imp_sel = WindowManager.getCurrentImage();
		if (imp_sel == null) {
			IJ.showMessage("Error", "No images selected");
			return false;
		}
		String sky_mask = "/home/elphel/lwir16-proc/results-cuda/ERS-debug1/models3/1626032208_613623/v01/1626032208_613623-sky_mask.tiff";
		double disp_inf = -0.2; // -1.0; // 0.0;
		double disparity0 = 0.75; // 2; // 0.75;
		double disparity_max = 23; // 75.0; // pix
		boolean show_dialog2 = false;
		boolean linear_disparity = false;
		int palette = 4;

		int legend_width = 2; // 0;
		int legend_gap = 2; // 5;
		GenericJTabbedDialog gd0 = new GenericJTabbedDialog("Ln mode");
		gd0.addStringField("File path of the sky mask", sky_mask, 80,
				"optional file with 0.0 - keep, >0.0 - replace with NaN");
		gd0.addNumericField("Disparity at infinity", disp_inf, 5, 8, "pix", "Disparity at infinity ");
		gd0.addNumericField("disparity0", disparity0, 5, 8, "pix",
				"Disparity to switch from linear to log. ) - skip log mode");
		gd0.addNumericField("Maximal disparity", disparity_max, 5, 8, "pix", "Fixed maximal disparity (0 - auto");
		gd0.addCheckbox("Linear disparity legend", linear_disparity);
		gd0.addNumericField("Legend width", legend_width, 0, 3, "", "Optional disparity legend vertical bar width");
		gd0.addNumericField("Legend gap", legend_gap, 0, 3, "", "Optional disparity legend vertical bar gap");
		gd0.addNumericField("palette", palette, 0, 3, "", "Palette index");
		gd0.addCheckbox("Show second dialog", show_dialog2);

		gd0.showDialog();
		if (gd0.wasCanceled())
			return false;
		sky_mask = gd0.getNextString();
		disp_inf = gd0.getNextNumber();
		disparity0 = gd0.getNextNumber();
		disparity_max = gd0.getNextNumber();
		linear_disparity = gd0.getNextBoolean();
		legend_width = (int) gd0.getNextNumber();
		legend_gap = (int) gd0.getNextNumber();
		palette = (int) gd0.getNextNumber();
		show_dialog2 = gd0.getNextBoolean();

		boolean log_mode = disparity0 > 0.00;
		float[] fsky_mask = null;
		if (sky_mask.length() > 0) {
			ImagePlus imp_sky_mask = new ImagePlus(sky_mask);
			fsky_mask = (float[]) imp_sky_mask.getProcessor().getPixels();
		}
//		float [] pixels=(float []) imp_srcs[srcChannel].getProcessor().getPixels();		
		int current_slice = imp_sel.getCurrentSlice();
		ImageStack imageStack = imp_sel.getStack();
		float[] fpixels0 = (float[]) imageStack.getPixels(current_slice);
		float[] fpixels = fpixels0.clone();
		for (int i = 0; i < fpixels.length; i++) {
			fpixels[i] -= (float) disp_inf;
			if ((fsky_mask != null) && (fsky_mask[i] > 0.0f)) {
				fpixels[i] = Float.NaN;
			}
		}
		int width = imp_sel.getWidth();
		int height = imp_sel.getHeight();
		String title = imp_sel.getShortTitle(); // getTitle();
		double[] dpixels;
		int width0 = width;
		if (legend_width <= 0) {
			dpixels = new double[fpixels.length];
			for (int i = 0; i < fpixels.length; i++) {
				double d = fpixels[i];
				dpixels[i] = d;
			}
		} else {
			width += legend_width + legend_gap;
			dpixels = new double[height * width];
			Arrays.fill(dpixels, Double.NaN);
			double mx = disparity_max;
			for (int y = 0; y < height; y++) {
				for (int x = 0; x < width0; x++) {
					double d = fpixels[x + y * width0];
					dpixels[x + y * width] = d;
					if (d > mx) {
						mx = d;
					}
				}
			}
			if (disparity_max > 0) {
				mx = disparity_max;
			}
			if (linear_disparity) {
				for (int y = 0; y < height; y++) {
					double d = (mx * y) / height;
					for (int i = 0; i < legend_width; i++) {
						dpixels[width0 + legend_gap + i + y * width] = d;
					}
				}
			}
		}
		double mn = dpixels[0];
		double mx = mn;
		double pwr = 1.0;
		for (int i = 0; i < dpixels.length; i++) {
			double d = dpixels[i];
			if (log_mode) {
				if (!Double.isNaN(d)) {
					if (d < 0.0) { //
						d = 0.0;
					} else if (d < disparity0) {
						d = d / disparity0;
					} else {
						d = Math.log(d / disparity0) + 1.0;
					}
				}
			}
			int px = i % width;
			if ((px < width0) && !Double.isNaN(d)) { // do not use legend
				if (!(d <= mx))
					mx = d;
				if (!(d >= mn))
					mn = d;
			}
			dpixels[i] = d;
		}
		if (log_mode && (disparity_max > 0)) {
			mn = 0.0;
			double d = disparity_max;
			if (d < 0.0) { //
				d = 0.0;
			} else if (d < disparity0) {
				d = d / disparity0;
			} else {
				d = Math.log(d / disparity0) + 1.0;
			}
			mx = d;

		}

		if (show_dialog2) {
			GenericJTabbedDialog gd = new GenericJTabbedDialog("Colorization" + (log_mode ? " {log mode)" : ""));
			gd.addNumericField("min", mn, 5, 8, "", "Minimal value to map");
			gd.addNumericField("max", mx, 5, 8, "", "Maximal value to map");
			gd.addNumericField("pwr", pwr, 5, 8, "", "Exponent power");
			gd.showDialog();
			if (gd.wasCanceled())
				return false;
			mn = gd.getNextNumber();
			mx = gd.getNextNumber();
			pwr = gd.getNextNumber();
		}
		if (pwr != 1.0) {
			if (mn < 0) {
				mn = 0.0;
			}
			mn = Math.pow(mn, pwr);
			mx = Math.pow(mx, pwr);
			for (int i = 0; i < dpixels.length; i++) {
				if (dpixels[i] < 0) {
					dpixels[i] = 0.0;

				} else {
					dpixels[i] = Math.pow(dpixels[i], pwr);
				}
			}
		}

		if (!linear_disparity && (legend_width > 0)) {
			for (int y = 0; y < height; y++) {
				double d = ((mx - mn) * y) / height;
				for (int i = 0; i < legend_width; i++) {
					dpixels[width0 + legend_gap + i + y * width] = d;
				}
			}
		}

		System.out.println("mn=" + mn + ", mx=" + mx);
//		ShowDoubleFloatArrays .showArrays(dpixels,  width, height, "test_depth_mn="+mn+"_mx="+mx);

		double[][] pseudo_pixels = new double[3][dpixels.length];
		ThermalColor tc = new ThermalColor(palette, // public int lwir_palette = 0; // 0 - white - hot, 1 - black - hot,
													// 2+ - colored
				mn, mx, 255.0);
		for (int i = 0; i < dpixels.length; i++) {
			double[] rgb = tc.getRGB(dpixels[i]);
			pseudo_pixels[0][i] = rgb[0]; // red
			pseudo_pixels[1][i] = rgb[1]; // green
			pseudo_pixels[2][i] = rgb[2]; // blue
		}
		String[] rgb_titles = { "red", "green", "blue" };

		ImageStack stack = ShowDoubleFloatArrays.makeStack(pseudo_pixels, // iclt_data,
				width, // (tilesX + 0) * clt_parameters.transform_size,
				height, // (tilesY + 0) * clt_parameters.transform_size,
				rgb_titles, // or use null to get chn-nn slice names
				true); // replace NaN with 0.0
		ImagePlus imp_pseudo = EyesisCorrections.convertRGBAFloatToRGBA32(stack, // ImageStack stackFloat, //r,g,b,a
				// name+"ARGB"+suffix, // String title,
				title + "-pseudo", // String title,
				0.0, // double r_min,
				255.0, // double r_max,
				0.0, // double g_min,
				255.0, // double g_max,
				0.0, // double b_min,
				255.0, // double b_max,
				0.0, // double alpha_min,
				1.0); // double alpha_max)
		imp_pseudo.getProcessor().resetMinAndMax();
		imp_pseudo.show();
		return true;
	}

	public boolean exportMLData() {
		long startTime = System.nanoTime();
		if ((QUAD_CLT == null) || (QUAD_CLT.tp == null) || (QUAD_CLT.tp.rig_pre_poles_ds == null)) {
			String msg = "DSI data is not available. Please run \"Ground truth\" first";
			IJ.showMessage("Error", msg);
			System.out.println(msg);
			return false;
		}
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Generating ML datasets ++++++++++++++");
		}
		try {
			TWO_QUAD_CLT.outputMLData( // actually there is no sense to process multiple image sets. Combine with other
										// processing?
					QUAD_CLT, // QuadCLT quadCLT_main,
					QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					null, // String ml_directory, // full path or null (will use config one)
					THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);

		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		System.out.println("exportMLData(): Processing finished at "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " sec, --- Free memory="
				+ Runtime.getRuntime().freeMemory() + " (of " + Runtime.getRuntime().totalMemory() + ")");

		return true;
	}

	public boolean copyJP4src() {
		if ((QUAD_CLT == null) || (QUAD_CLT.tp == null) || (QUAD_CLT.tp.clt_3d_passes == null)) {
			if (QUAD_CLT == null) {
				QUAD_CLT = new QuadCLT(QuadCLT.PREFIX, PROPERTIES, EYESIS_CORRECTIONS, CORRECTION_PARAMETERS);
				if (DEBUG_LEVEL > 0) {
					System.out.println("Created new QuadCLT instance, will need to read CLT kernels");
				}
			}
			String configPath = getSaveCongigPath();
			if (configPath.equals("ABORT"))
				return false;

			EYESIS_CORRECTIONS.initSensorFiles(DEBUG_LEVEL);
			int numChannels = EYESIS_CORRECTIONS.getNumChannels();
			CHANNEL_GAINS_PARAMETERS.modifyNumChannels(numChannels);

			if (!QUAD_CLT.CLTKernelsAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Reading CLT kernels");
				}
				QUAD_CLT.readCLTKernels(CLT_PARAMETERS, THREADS_MAX, UPDATE_STATUS, // update status info
						DEBUG_LEVEL);

				if (DEBUG_LEVEL > 1) {
					QUAD_CLT.showCLTKernels(THREADS_MAX, UPDATE_STATUS, // update status info
							DEBUG_LEVEL);
				}
			}

			if (!QUAD_CLT.geometryCorrectionAvailable()) {
				if (DEBUG_LEVEL > 0) {
					System.out.println("Calculating geometryCorrection");
				}
				if (!QUAD_CLT.initGeometryCorrection(DEBUG_LEVEL + 2)) {
					return false;
				}
			}
			/*
			 * boolean OK = clt3d( false, // boolean adjust_extrinsics, false); // boolean
			 * adjust_poly); if (! OK) { String msg =
			 * "DSI data is not available and \"CLT 3D\" failed";
			 * IJ.showMessage("Error",msg); System.out.println(msg); return false; }
			 */
		}
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Copying JP4 source files ++++++++++++++");
		}
		try {
			TwoQuadCLT.copyJP4src( // actually there is no sense to process multiple image sets. Combine with other
										// processing?
					null, // String set_name
					QUAD_CLT, // QuadCLT quadCLT_main,
					QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
					null, // QuadCLT quadCLT_this,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					true, // boolean                                  skip_existing,
					true, // boolean                                  search_KML,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);

		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}

		return true;
	}

	public boolean saveExtrinsics() {
		String[] patterns = { ".corr-xml", ".xml" };
		String path = selectFile(true, // save
				"Save configuration selection", // title
				"Select configuration file", // button
				new MultipleExtensionsFileFilter(patterns, "XML Configuration files (*.corr-xml)"), // filter
				CORRECTION_PARAMETERS.resultsDirectory); // may be ""
		if (path == null)
			return false;
		if (TWO_QUAD_CLT == null) {
			return false;
		}
		TWO_QUAD_CLT.saveProperties(path, // String path, // full name with extension or w/o path to use x3d directory
				null, // Properties properties, // if null - will only save extrinsics)
				DEBUG_LEVEL); // int debugLevel)
		return true;
	}

//    	listExtrinsics();
	public boolean listExtrinsics() {
		String dir = CalibrationFileManagement.selectDirectory(false, // true, // smart,
				false, // newAllowed, // save
				"Model directories to scan", // title
				"Select", // button
				null, // filter
				CORRECTION_PARAMETERS.x3dDirectory); // this.sourceDirectory);
		if (dir != null) {
			System.out.println("top directory = " + dir);
		}
		return MLStats.listExtrinsics(dir, // ); // , mask);
				EYESIS_CORRECTIONS, EYESIS_CORRECTIONS_AUX);
	}

	public boolean mlRecalc() {
		String dir = CalibrationFileManagement.selectDirectory(false, // true, // smart,
				false, // newAllowed, // save
				"Model directories to scan", // title
				"Select", // button
				null, // filter
				CORRECTION_PARAMETERS.x3dDirectory); // this.sourceDirectory);
		if (dir != null) {
			System.out.println("top directory = " + dir);
		}
//		return MLStats.mlRecalc(dir); // , mask);
		return mlRecalc(dir); // , mask);
	}

	public boolean mlRecalc(String dir)

	{
		Runtime runtime = Runtime.getRuntime();
		long startTime = System.nanoTime();

		String mask = ".*EXTRINSICS\\.corr-xml";
		String full_conf_suffix = ".corr-xml";
//		String dsi_suffix = "-DSI_COMBO.tiff";
		String dsi_suffix = TwoQuadCLT.DSI_COMBO_SUFFIX + ".tiff";

		String correction_parameters_prefix = "CORRECTION_PARAMETERS.";

		System.out.println("File mask = " + mask);

		final List<Path> files = new ArrayList<>();
		final String fMask = mask;
		Path path = Paths.get(dir);
		try {
			Files.walkFileTree(path, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
					new SimpleFileVisitor<Path>() {
						@Override
						public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
							if (!attrs.isDirectory()) {
								if (file.toString().matches(fMask)) {
									files.add(file);
								}
							}
							return FileVisitResult.CONTINUE;
						}
					});
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		int indx = 0;
///		String [] used_models =getUsedModels();
		for (Path p : files) {

			int count = p.getNameCount();
			if (count >= 3) {
				String model = p.getName(count - 3).toString();
				String version = p.getName(count - 2).toString();
				String name = p.getName(count - 1).toString();
				Path parent = p.getParent();
				Path full_conf_path = p.resolveSibling(model + full_conf_suffix);
				Path dsi_combo_path = p.resolveSibling(model + dsi_suffix);
				System.out.println(indx + ": model:" + model + ", version:" + version + ", name: " + name + ", parent="
						+ parent + ", full_conf_path=" + full_conf_path);
				// See if there is a full configuration file and DSI combo
				if (!full_conf_path.toFile().exists()) {
					System.out.println("Full configuration file for the model:" + model + ", version:" + version + ": "
							+ full_conf_path + " does not exist");
					continue;
				}
				if (!dsi_combo_path.toFile().exists()) {
					System.out.println("DSI combo file (GT data) for the model:" + model + ", version:" + version + ": "
							+ dsi_combo_path + " does not exist");
					continue;
				}
				// overwrite extrinsics in current properties from the model file
				MLStats.loadProperties(full_conf_path.toString(), // String path,
						PROPERTIES); // Properties properties)

				Properties full_properties = MLStats.loadProperties(full_conf_path.toString(), // String path,
						null); // Properties properties)
				ArrayList<String> source_list = new ArrayList<String>();
				if (full_properties.getProperty(correction_parameters_prefix + "sourcePaths") != null) {
					int numFiles = Integer
							.parseInt(full_properties.getProperty(correction_parameters_prefix + "sourcePaths"));
					for (int i = 0; i < numFiles; i++) {
						String src = full_properties.getProperty(correction_parameters_prefix + "sourcePath" + i);
						Path src_path = Paths.get(src);
						if (src_path.getName(src_path.getNameCount() - 1).toString().contains(model)) {
							source_list.add(src);
						}
					}
				} else {
					System.out.println("Source files are not povided for the model:" + model + ", version:" + version
							+ " in " + full_conf_path);
					continue;
				}
				Collections.sort(source_list, new Comparator<String>() {
					@Override
					public int compare(String lhs, String rhs) {
						// -1 - less than, 1 - greater than, 0 - equal, not inverted for ascending
						// disparity
						return lhs.compareTo(rhs);
					}
				});

				CORRECTION_PARAMETERS.sourcePaths = source_list.toArray(new String[0]);
				System.out.println("Got " + source_list.size() + " source files");
				QUAD_CLT = null;
				QUAD_CLT_AUX = null; // reset images

				if (!prepareRigImages()) { // will use extrinsics properties
					return false;
				}
				String mldir = CORRECTION_PARAMETERS.mlDirectory;
				if (mldir.equals("ml")) {
					mldir = "ml"; // not to overwrite original data
				}
				if (COLOR_PROC_PARAMETERS_AUX == null) {
					COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
				}
				try {
					TWO_QUAD_CLT.regenerateML(dsi_combo_path.toString(), // String path_DSI, // Combo DSI path
							parent.toString(), // String model_dir, // model/version directory
							mldir, // "ml1", // String ml_subdir, // new ML subdir (or null to use configuartion
									// one)
							QUAD_CLT, // QuadCLT quadCLT_main,
							QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
							CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
							COLOR_PROC_PARAMETERS, // EyesisCorrectionParameters.ColorProcParameters
													// colorProcParameters,
							COLOR_PROC_PARAMETERS_AUX, // EyesisCorrectionParameters.ColorProcParameters
														// colorProcParameters_aux,
							RGB_PARAMETERS, // EyesisCorrectionParameters.RGBParameters rgbParameters,
							THREADS_MAX, // final int threadsMax, // maximal number of threads to launch
							UPDATE_STATUS, // final boolean updateStatus,
							DEBUG_LEVEL);

				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				indx++;
				if (DEBUG_LEVEL > 0)
					System.out.println("Finished scene " + indx + " of " + files.size() + " at "
							+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " seconds  Free memory="
							+ IJ.d2s(runtime.freeMemory() / (1024.0 * 1024.0 * 1024.0), 3) + " GB (of "
							+ IJ.d2s(runtime.totalMemory() / (1024.0 * 1024.0 * 1024.0), 3) + " GB), used "
							+ IJ.d2s((runtime.totalMemory() - runtime.freeMemory()) / (1024.0 * 1024.0 * 1024.0), 3)
							+ " GB");

			}
		}
		System.out.println("Re-processing ML files (" + files.size() + ") done in "
				+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " seconds");
		return true;
	}

	String[] getUsedModels() {
		String[] models = { "1527257933_150165", "1527257933_150165", "1527257939_550165", "1527257939_550165",
				"1527257947_950165", "1527257947_950165", "1527257932_350165", "1527257932_350165", "1527257941_950165",
				"1527257941_950165", "1527257932_550165", "1527257932_550165", "1527257947_750165", "1527257947_750165",
				"1527257935_350165", "1527257935_350165", "1527257927_750165", "1527257946_550165", "1527257946_550165",
				"1527257949_950165", "1527257949_950165", "1527257944_750165", "1527257944_750165", "1527257938_750165",
				"1527257938_750165", "1527257940_550165", "1527257940_550165", "1527257937_150165", "1527257937_150165",
				"1527257924_950165", "1527257924_950165", "1527257924_950165", "1527257941_350165", "1527257941_350165",
				"1527257944_350165", "1527257944_350165", "1527257945_950165", "1527257945_950165", "1527257941_550165",
				"1527257941_550165", "1527257943_550165", "1527257943_550165", "1527257939_150165", "1527257939_150165",
				"1527257933_950165", "1527257933_950165", "1527257938_350165", "1527257938_350165", "1527257936_750165",
				"1527257936_750165", "1527257928_750165", "1527257940_950165", "1527257940_950165", "1527257943_950165",
				"1527257943_950165", "1527257937_750165", "1527257937_750165", "1527257943_750165", "1527257943_750165",
				"1527257941_750165", "1527257941_750165", "1527257936_550165", "1527257936_550165", "1527257942_750165",
				"1527257942_750165", "1527257931_550165", "1527257949_350165", "1527257949_350165", "1527257924_550165",
				"1527257924_550165", "1527257924_550165", "1527257940_350165", "1527257940_350165", "1527257948_550165",
				"1527257948_550165", "1527257930_950165", "1527257947_550165", "1527257947_550165", "1527257925_750165",
				"1527257925_750165", "1527257931_750165", "1527257944_950165", "1527257944_950165", "1527257929_550165",
				"1527257948_750165", "1527257948_750165", "1527257930_150165", "1527257930_350165", "1527257931_150165",
				"1527257942_550165", "1527257942_550165", "1527257937_350165", "1527257937_350165", "1527257941_150165",
				"1527257941_150165", "1527257938_150165", "1527257938_150165", "1527257928_350165", "1527257948_350165",
				"1527257948_350165", "1527257932_750165", "1527257932_750165", "1527257946_750165", "1527257946_750165",
				"1527257926_950165", "1527257928_550165", "1527257925_350165", "1527257925_350165", "1527257947_150165",
				"1527257947_150165", "1527257930_750165", "1527257927_350165", "1527257929_750165", "1527257937_550165",
				"1527257937_550165", "1527257933_350165", "1527257933_350165", "1527257944_150165", "1527257944_150165",
				"1527257931_950165", "1527257936_950165", "1527257936_950165", "1527257928_950165", "1527257925_150165",
				"1527257925_150165", "1527257924_750165", "1527257924_750165", "1527257924_750165", "1527257937_950165",
				"1527257937_950165", "1527257946_150165", "1527257946_150165", "1527257949_750165", "1527257949_750165",
				"1527257942_150165", "1527257942_150165", "1527257926_150165", "1527257931_350165", "1527257942_950165",
				"1527257942_950165", "1527257939_350165", "1527257939_350165", "1527257934_550165", "1527257934_550165",
				"1527257945_550165", "1527257945_550165", "1527257935_150165", "1527257935_150165", "1527257945_150165",
				"1527257945_150165", "1527257942_350165", "1527257942_350165", "1527257930_550165", "1527257945_350165",
				"1527257945_350165", "1527257947_350165", "1527257947_350165", "1527257938_550165", "1527257938_550165",
				"1527257927_150165", "1527257927_950165", "1527257933_750165", "1527257933_750165", "1527257934_750165",
				"1527257934_750165", "1527257949_150165", "1527257949_150165", "1527257944_550165", "1527257944_550165",
				"1527257933_550165", "1527257933_550165", "1527257936_350165", "1527257936_350165", "1527257940_750165",
				"1527257940_750165", "1527257935_550165", "1527257935_550165", "1527257928_150165", "1527257943_150165",
				"1527257943_150165", "1527257948_150165", "1527257948_150165", "1527257932_950165", "1527257932_950165",
				"1527257949_550165", "1527257949_550165", "1527257926_750165", "1527257948_950165", "1527257948_950165",
				"1527257934_950165", "1527257934_950165", "1527257926_350165", "1527257926_550165", "1527257929_150165",
				"1527257925_550165", "1527257925_550165", "1527257934_150165", "1527257934_150165", "1527257924_350165",
				"1527257924_350165", "1527257924_350165", "1527257934_350165", "1527257934_350165", "1527257943_350165",
				"1527257943_350165", "1527257946_950165", "1527257946_950165", "1527257938_950165", "1527257938_950165",
				"1527257935_950165", "1527257935_950165", "1527257917_550165", "1527257917_550165", "1527257917_550165",
				"1527257917_550165", "1527257939_950165", "1527257939_950165", "1527257924_150165", "1527257924_150165",
				"1527257924_150165", "1527257939_750165", "1527257939_750165", "1527257946_350165", "1527257946_350165",
				"1527257935_750165", "1527257935_750165", "1527257925_950165", "1527257925_950165", "1527257945_750165",
				"1527257945_750165", "1527257929_350165", "1527257927_550165", "1527257940_150165", "1527257940_150165",
				"1527257936_150165", "1527257936_150165", "1527257929_950165", "1527257932_150165", "1527257932_150165",
				"1527182809_496892", "1527182811_296892", "1527182807_696892", "1527182807_896892", "1527182811_696892",
				"1527182801_896892", "1527182801_296892", "1527182809_896892", "1527182810_896892", "1527182803_496892",
				"1527182809_696892", "1527182807_296892", "1527182807_096892", "1527182806_096892", "1527182808_696892",
				"1527182801_696892", "1527182804_696892", "1527182803_696892", "1527182810_496892", "1527182811_496892",
				"1527182802_296892", "1527182802_496892", "1527182808_296892", "1527182810_296892", "1527182808_496892",
				"1527182811_896892", "1527182804_096892", "1527182810_096892", "1527182802_896892", "1527182802_096892",
				"1527182805_496892", "1527182804_496892", "1527182804_296892", "1527182810_696892", "1527182808_896892",
				"1527182803_096892", "1527182802_696892", "1527182806_696892", "1527182801_096892", "1527182808_096892",
				"1527182805_296892", "1527182809_096892", "1527182801_496892", "1527182811_096892", "1527182809_296892",
				"1527182806_496892", "1527182805_096892", "1527182807_496892", "1527182806_296892", "1527182804_896892",
				"1527182803_296892", "1527182806_896892", "1527182803_896892", "1527182805_896892", "1527182805_696892",
				"1527256857_750165", "1527256877_350165", "1527256858_150165", "1527256818_750165", "1527256818_750165",
				"1527256856_350165", "1527256837_150165", "1527256818_550165", "1527256818_550165", "1527256875_350165",
				"1527256878_350165", "1527256878_950165", "1527256856_750165", "1527256877_550165", "1527256858_950165",
				"1527256836_350165", "1527256816_550165", "1527256816_550165", "1527256837_550165", "1527256855_950165",
				"1527256838_550165", "1527256855_350165", "1527256836_750165", "1527256875_550165", "1527256818_350165",
				"1527256818_350165", "1527256835_750165", "1527256837_950165", "1527256818_950165",
				"1527256818_950165" };
		return models;

	}

	public boolean dsiHistogram() {
		String dir = CalibrationFileManagement.selectDirectory(false, // true, // smart,
				false, // newAllowed, // save
				"Model directories to scan", // title
				"Select", // button
				null, // filter
				CORRECTION_PARAMETERS.x3dDirectory); // this.sourceDirectory);
		if (dir != null) {
			System.out.println("top directory = " + dir);
		}
		return MLStats.dsiHistogram(dir); // , mask);
	}

	public boolean showDSI() {
		if (TWO_QUAD_CLT == null) {
			System.out.println("TWO_QUAD_CLT is not initialized");
			return false;
		}
		TWO_QUAD_CLT.showDSI();
		return true;
	}

	public boolean infinityRig() {
		if (!prepareRigImages())
			return false;
		String configPath = getSaveCongigPath();
		if (configPath.equals("ABORT"))
			return false;

		if (DEBUG_LEVEL > -2) {
			System.out.println("++++++++++++++ Processing infinity rig calibration ++++++++++++++");
		}
		if (COLOR_PROC_PARAMETERS_AUX == null) {
			COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
		}
		try {
			TWO_QUAD_CLT.processInfinityRigs( // actually there is no sense to process multiple image sets. Combine with
												// other processing?
					QUAD_CLT, // QuadCLT quadCLT_main,
					QUAD_CLT_AUX, // QuadCLT quadCLT_aux,
					CLT_PARAMETERS, // EyesisCorrectionParameters.DCTParameters dct_parameters,
					COLOR_PROC_PARAMETERS, COLOR_PROC_PARAMETERS_AUX, THREADS_MAX, // final int threadsMax, // maximal
																					// number of threads to launch
					UPDATE_STATUS, // final boolean updateStatus,
					DEBUG_LEVEL);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} // final int debugLevel);

		if (configPath != null) {
			saveTimestampedProperties( // save config again
					configPath, // full path or null
					null, // use as default directory if path==null
					true, PROPERTIES);
		}
		return true;
	}
	public static ImagePlus subtractAverage(ImagePlus imp_src) {
		ImagePlus imp_rslt = null;
		int num_img = imp_src.getStackSize();
		int width =  imp_src.getWidth();
		int height = imp_src.getHeight();
		ImageStack stack = imp_src.getStack();
		// Simple version - global average
		int [] num_pix = new int [width*height];
		double [] sum_pix = new double [width*height];
		double [][] out_pix = new double [num_img][sum_pix.length];
		for (int nslice = 0; nslice < num_img; nslice++) {
			float [] fpix =  (float[]) stack.getPixels(nslice + 1);
			Arrays.fill(out_pix[nslice], Double.NaN);
			for (int i = 0; i < fpix.length; i++) if (!Float.isNaN(fpix[i])) {
				double d = fpix[i];
				out_pix[nslice][i] = d;
				sum_pix[i]+= d;
				num_pix[i]++;
			}
		}
		for (int i = 0; i < sum_pix.length; i++) {
			if (num_pix[i] > 0) {
				sum_pix[i] /= num_pix[i];
			} else {
				sum_pix[i] = Double.NaN;
			}
		}
		for (int nslice = 0; nslice < num_img; nslice++) {
			for (int i = 0; i < sum_pix.length; i++) {
				out_pix[nslice][i] -= sum_pix[i]; 
			}
		}
		ImageStack out_stack = ShowDoubleFloatArrays.makeStack(out_pix, width, height);
		imp_rslt = new ImagePlus(imp_src.getTitle() + "-motion", out_stack);
		return imp_rslt;
	}
	public ImagePlus selectCLTImage() {
		if (!CLT_PARAMETERS.showJDialog())
			return null;
		// process selected image stack
		ImagePlus imp_src = WindowManager.getCurrentImage();
		ImagePlus imp_rslt = null;
		if (imp_src == null) {
			IJ.showMessage("Error", "JP4 image or Bayer image stack required");
			return null;
		}
		if (imp_src.getStackSize() < 3) { // convert JP4 to image stack

			EyesisCorrectionParameters.SplitParameters split_parameters = new EyesisCorrectionParameters.SplitParameters(
					1, // oversample;
					// Add just for mdct (N/2)
					CLT_PARAMETERS.transform_size / 2, // addLeft
					CLT_PARAMETERS.transform_size / 2, // addTop
					CLT_PARAMETERS.transform_size / 2, // addRight
					CLT_PARAMETERS.transform_size / 2 // addBottom
			);

			ImageStack sourceStack = bayerToStack(imp_src, // source Bayer image, linearized, 32-bit (float))
					split_parameters);
			imp_rslt = new ImagePlus(imp_src.getTitle() + "-SPIT", sourceStack);
			if (DEBUG_LEVEL > 1) {
				imp_rslt.getProcessor().resetMinAndMax();
				imp_rslt.show();
			}
		} else {
			imp_rslt = imp_src;
		}
		return imp_rslt;
	}

	public void CLTStack() {
		// IJ.showMessage("DCT test 1");
		if (!CLT_PARAMETERS.showJDialog())
			return;
		// process selected image stack
		if (DBG_IMP == null) {
			ImagePlus imp_src = WindowManager.getCurrentImage();
			if (imp_src == null) {
				IJ.showMessage("Error", "JP4 image or Bayer image stack required");
				return;
			}
			// ImagePlus imp2;
			if (imp_src.getStackSize() < 3) { // convert JP4 to image stack

				EyesisCorrectionParameters.SplitParameters split_parameters = new EyesisCorrectionParameters.SplitParameters(
						1, // oversample;
						CLT_PARAMETERS.transform_size / 2, // addLeft
						CLT_PARAMETERS.transform_size / 2, // addTop
						CLT_PARAMETERS.transform_size / 2, // addRight
						CLT_PARAMETERS.transform_size / 2 // addBottom
				);

				ImageStack sourceStack = bayerToStack(imp_src, // source Bayer image, linearized, 32-bit (float))
						split_parameters);
				DBG_IMP = new ImagePlus(imp_src.getTitle() + "-SPIT", sourceStack);
				DBG_IMP.getProcessor().resetMinAndMax();
				DBG_IMP.show();
			} else {
				DBG_IMP = imp_src;
			}
		}

		ImageDtt image_dtt = new ImageDtt(4, // number of sensors - not used here ?
				CLT_PARAMETERS.transform_size, CLT_PARAMETERS.img_dtt, false, // aux
				false, // mono
				false, // lwir
				1.0); // Bayer( not monochrome), scale correlation strengths
		double[][][][][] clt_data = image_dtt.cltStack(DBG_IMP.getStack(), 0, // CLT_PARAMETERS.kernel_chn,
				CLT_PARAMETERS, CLT_PARAMETERS.ishift_x, // final int shiftX, // shift image horizontally (positive -
															// right)
				CLT_PARAMETERS.ishift_y, // final int shiftY, // shift image vertically (positive - down)
				THREADS_MAX, DEBUG_LEVEL, UPDATE_STATUS);

		int tilesY = DBG_IMP.getHeight() / CLT_PARAMETERS.transform_size - 1;
		int tilesX = DBG_IMP.getWidth() / CLT_PARAMETERS.transform_size - 1;
		System.out.println("'CLT stack': tilesX=" + tilesX);
		System.out.println("'CLT stack': tilesY=" + tilesY);
		double[][] clt = new double[clt_data.length * 4][];
		for (int chn = 0; chn < clt_data.length; chn++) {
			double[][] clt_set = image_dtt.clt_dbg(clt_data[chn], THREADS_MAX, DEBUG_LEVEL);
			for (int ii = 0; ii < clt_set.length; ii++)
				clt[chn * 4 + ii] = clt_set[ii];
		}
		// System.out.println("dct_dc.length="+dct_dc.length+"
		// dct_ac.length="+dct_ac.length);
		if (DEBUG_LEVEL > 0) {
			ShowDoubleFloatArrays.showArrays(clt, tilesX * CLT_PARAMETERS.transform_size,
					tilesY * CLT_PARAMETERS.transform_size, true,
					DBG_IMP.getTitle() + "-CLT+" + CLT_PARAMETERS.iclt_mask);
		}

		if ((CLT_PARAMETERS.shift_x != 0) || (CLT_PARAMETERS.shift_y != 0)) {
			for (int chn = 0; chn < clt_data.length; chn++) {
				clt_data[chn] = image_dtt.clt_shiftXY(clt_data[chn], // final double [][][][] dct_data, // array
																		// [tilesY][tilesX][4][dct_size*dct_size]
///						CLT_PARAMETERS.transform_size,  // final int             dct_size,
						CLT_PARAMETERS.shift_x, // final double shiftX,
						CLT_PARAMETERS.shift_y, // final double shiftY,
						(CLT_PARAMETERS.dbg_mode >> 2) & 3, // swap order hor/vert
						THREADS_MAX, // maximal number of threads to launch
						DEBUG_LEVEL); // globalDebugLevel)
			}
		}

		double[][] iclt_data = new double[clt_data.length][];
		for (int chn = 0; chn < iclt_data.length; chn++) {
			iclt_data[chn] = image_dtt.iclt_2d(clt_data[chn], // scanline representation of dcd data, organized as
																// dct_size x dct_size tiles
///					CLT_PARAMETERS.transform_size,  // final int
					CLT_PARAMETERS.clt_window, // window_type
					CLT_PARAMETERS.iclt_mask, // which of 4 to transform back
					CLT_PARAMETERS.dbg_mode, // which of 4 to transform back
					THREADS_MAX, // maximal number of threads to launch
					DEBUG_LEVEL); // globalDebugLevel)
		}
		ShowDoubleFloatArrays.showArrays(iclt_data, (tilesX + 1) * CLT_PARAMETERS.transform_size,
				(tilesY + 1) * CLT_PARAMETERS.transform_size, true,
				DBG_IMP.getTitle() + "-ICLT-" + CLT_PARAMETERS.iclt_mask);
		return;
	}

	/* ======================================================================== */

	public void CLTCorrelate() {

//    	IJ.showMessage("DCT test 1");
		if (!CLT_PARAMETERS.showJDialog())
			return;
// process selected image stack
		if (DBG_IMP == null) {
			ImagePlus imp_src = WindowManager.getCurrentImage();
			if (imp_src == null) {
				IJ.showMessage("Error", "JP4 image or Bayer image stack required");
				return;
			}
			// ImagePlus imp2;
			if (imp_src.getStackSize() < 3) { // convert JP4 to image stack

				EyesisCorrectionParameters.SplitParameters split_parameters = new EyesisCorrectionParameters.SplitParameters(
						1, // oversample;
						CLT_PARAMETERS.transform_size / 2, // addLeft
						CLT_PARAMETERS.transform_size / 2, // addTop
						CLT_PARAMETERS.transform_size / 2, // addRight
						CLT_PARAMETERS.transform_size / 2 // addBottom
				);

				ImageStack sourceStack = bayerToStack(imp_src, // source Bayer image, linearized, 32-bit (float))
						split_parameters);
				DBG_IMP = new ImagePlus(imp_src.getTitle() + "-SPIT", sourceStack);
				DBG_IMP.getProcessor().resetMinAndMax();
				DBG_IMP.show();
			} else {
				DBG_IMP = imp_src;
			}
		}

		if (CORRELATE_IMP == null) {
			ImagePlus imp_src = WindowManager.getCurrentImage();
			if (imp_src == null) {
				IJ.showMessage("Error", "JP4 image or Bayer image stack required");
				return;
			}
			if (imp_src.getStackSize() < 3) { // convert JP4 to image stack

				EyesisCorrectionParameters.SplitParameters split_parameters = new EyesisCorrectionParameters.SplitParameters(
						1, // oversample;
						// Add just for mdct (N/2)
						CLT_PARAMETERS.transform_size / 2, // addLeft
						CLT_PARAMETERS.transform_size / 2, // addTop
						CLT_PARAMETERS.transform_size / 2, // addRight
						CLT_PARAMETERS.transform_size / 2 // addBottom
				);

				ImageStack sourceStack = bayerToStack(imp_src, // source Bayer image, linearized, 32-bit (float))
						split_parameters);
				CORRELATE_IMP = new ImagePlus(imp_src.getTitle() + "-SPIT_CORRELATE" + "", sourceStack);
				if (DEBUG_LEVEL > 1) {
					CORRELATE_IMP.getProcessor().resetMinAndMax();
					CORRELATE_IMP.show();
				}
			} else {
				CORRELATE_IMP = imp_src;
			}

		}
		String suffix = "-dx_" + (CLT_PARAMETERS.ishift_x + CLT_PARAMETERS.shift_x) + "_dy_"
				+ (CLT_PARAMETERS.ishift_y + CLT_PARAMETERS.shift_y);
		ImageDtt image_dtt = new ImageDtt(4, // number of sensors
				CLT_PARAMETERS.transform_size, CLT_PARAMETERS.img_dtt, false, // aux
				COLOR_PROC_PARAMETERS.isMonochrome(), COLOR_PROC_PARAMETERS.isLwir(),
				CLT_PARAMETERS.getScaleStrength(false)); // Bayer, not monochrome
		String[] titles = { "redCC", "redSC", "redCS", "redSS", "blueCC", "blueSC", "blueCS", "blueSS", "greenCC",
				"greenSC", "greenCS", "greenSS" };
		double[][][][][] clt_data = image_dtt.cltStack(DBG_IMP.getStack(), 0, // CLT_PARAMETERS.kernel_chn,
				CLT_PARAMETERS, 0, // final int shiftX, // shift image horizontally (positive - right)
				0, // final int shiftY, // shift image vertically (positive - down)
				THREADS_MAX, DEBUG_LEVEL, UPDATE_STATUS);

		int tilesY = DBG_IMP.getHeight() / CLT_PARAMETERS.transform_size - 1;
		int tilesX = DBG_IMP.getWidth() / CLT_PARAMETERS.transform_size - 1;
		System.out.println("'CLT stack': tilesX=" + tilesX);
		System.out.println("'CLT stack': tilesY=" + tilesY);
		double[][] clt = new double[clt_data.length * 4][];
		for (int chn = 0; chn < clt_data.length; chn++) {
			double[][] clt_set = image_dtt.clt_dbg(clt_data[chn], THREADS_MAX, DEBUG_LEVEL);
			for (int ii = 0; ii < clt_set.length; ii++)
				clt[chn * 4 + ii] = clt_set[ii];
		}
//        System.out.println("dct_dc.length="+dct_dc.length+" dct_ac.length="+dct_ac.length);
		if (DEBUG_LEVEL > 0) {
			ShowDoubleFloatArrays.showArrays(clt, tilesX * CLT_PARAMETERS.transform_size,
					tilesY * CLT_PARAMETERS.transform_size, true, DBG_IMP.getTitle() + "-CLT+" + suffix, titles);
		}
		// convert second image (should be the same size)
// apply integer shift to the second image
		double[][][][][] clt_data2 = image_dtt.cltStack(CORRELATE_IMP.getStack(), 0, // CLT_PARAMETERS.kernel_chn,
				CLT_PARAMETERS, CLT_PARAMETERS.ishift_x, // final int shiftX, // shift image horizontally (positive -
															// right)
				CLT_PARAMETERS.ishift_y, // final int shiftY, // shift image vertically (positive - down)
				THREADS_MAX, DEBUG_LEVEL, UPDATE_STATUS);
		int tilesY2 = DBG_IMP.getHeight() / CLT_PARAMETERS.transform_size - 1;
		int tilesX2 = DBG_IMP.getWidth() / CLT_PARAMETERS.transform_size - 1;
		System.out.println("'CLT stack 2': tilesX=" + tilesX2);
		System.out.println("'CLT stack 2': tilesY=" + tilesY2);

		if ((tilesX2 != tilesX) || (tilesY2 != tilesY)) {
			System.out.println(
					"Imges for correlation mismatch: " + tilesX + "x" + tilesY + " != " + tilesX2 + "x" + tilesY2);
			return;
		}

// optionally apply fractional shift to the second image
		if ((CLT_PARAMETERS.shift_x != 0) || (CLT_PARAMETERS.shift_y != 0)) {
			for (int chn = 0; chn < clt_data.length; chn++) {
				clt_data2[chn] = image_dtt.clt_shiftXY(clt_data2[chn], // final double [][][][] dct_data, // array
																		// [tilesY][tilesX][4][dct_size*dct_size]
///        			CLT_PARAMETERS.transform_size,  // final int             dct_size,
						CLT_PARAMETERS.shift_x, // final double shiftX,
						CLT_PARAMETERS.shift_y, // final double shiftY,
						(CLT_PARAMETERS.dbg_mode >> 2) & 3, // swap order hor/vert
						THREADS_MAX, // maximal number of threads to launch
						DEBUG_LEVEL); // globalDebugLevel)
			}
		}

		clt = new double[clt_data2.length * 4][];
		for (int chn = 0; chn < clt_data.length; chn++) {
			double[][] clt_set = image_dtt.clt_dbg(clt_data2[chn], THREADS_MAX, DEBUG_LEVEL);
			for (int ii = 0; ii < clt_set.length; ii++)
				clt[chn * 4 + ii] = clt_set[ii];
		}
//        System.out.println("dct_dc.length="+dct_dc.length+" dct_ac.length="+dct_ac.length);
		if (DEBUG_LEVEL > 0) {
			ShowDoubleFloatArrays.showArrays(clt, tilesX * CLT_PARAMETERS.transform_size,
					tilesY * CLT_PARAMETERS.transform_size, true, DBG_IMP.getTitle() + "-CLT2+" + suffix, titles);
		}

		double[][][][][] clt_corr = new double[clt_data.length][][][][];
		for (int chn = 0; chn < clt_data.length; chn++) {
			clt_corr[chn] = image_dtt.clt_correlate(clt_data[chn], // final double [][][][] data1, // array
																	// [tilesY][tilesX][4][dct_size*dct_size]
					clt_data2[chn], // final double [][][][] data2, // array [tilesY][tilesX][4][dct_size*dct_size]
///        			CLT_PARAMETERS.transform_size,  // final int             dct_size,
					CLT_PARAMETERS.getFatZero(image_dtt.isMonochrome()), // final double fat_zero, // add to denominator
																			// to modify phase correlation (same units
																			// as data1, data2)
					CLT_PARAMETERS.tileX, // final int debug_tileX
					CLT_PARAMETERS.tileY, // final int debug_tileY

					THREADS_MAX, // maximal number of threads to launch
					DEBUG_LEVEL); // globalDebugLevel)
		}

		clt = new double[clt_data.length * 4][];
		for (int chn = 0; chn < clt_data.length; chn++) {
			double[][] clt_set = image_dtt.clt_dbg(clt_corr[chn], THREADS_MAX, DEBUG_LEVEL);
			for (int ii = 0; ii < clt_set.length; ii++)
				clt[chn * 4 + ii] = clt_set[ii];
		}
		if (DEBUG_LEVEL > 0) {
			ShowDoubleFloatArrays.showArrays(clt, tilesX * CLT_PARAMETERS.transform_size,
					tilesY * CLT_PARAMETERS.transform_size, true, DBG_IMP.getTitle() + "-CORR+" + suffix, titles);
		}

		if (CLT_PARAMETERS.getCorrSigma(image_dtt.isMonochrome()) > 0.0) {
			for (int chn = 0; chn < clt_data.length; chn++) {
				image_dtt.clt_lpf( // filter in-place
						CLT_PARAMETERS.getCorrSigma(image_dtt.isMonochrome()), // final double sigma,
						clt_corr[chn], // final double [][][][] clt_data,
///        				CLT_PARAMETERS.transform_size,
						THREADS_MAX, // maximal number of threads to launch
						DEBUG_LEVEL); // globalDebugLevel)
			}
			clt = new double[clt_data.length * 4][];
			for (int chn = 0; chn < clt_corr.length; chn++) {
				double[][] clt_set = image_dtt.clt_dbg(clt_corr[chn], THREADS_MAX, DEBUG_LEVEL);
				for (int ii = 0; ii < clt_set.length; ii++)
					clt[chn * 4 + ii] = clt_set[ii];
			}
			if (DEBUG_LEVEL > 0) {
				ShowDoubleFloatArrays.showArrays(clt, tilesX * CLT_PARAMETERS.transform_size,
						tilesY * CLT_PARAMETERS.transform_size, true, DBG_IMP.getTitle() + "-LPF_CORR+" + suffix,
						titles);
			}

		}
		for (int chn = 0; chn < clt_corr.length; chn++) {
			image_dtt.clt_dtt2( // DCCT2, DSCT2, DCST2, DSST2 - in-place
					clt_corr[chn], // final double [][][][] clt_data,
					true, // final boolean transpose, // when doing inverse transform, the data comes in
							// transposed form, so CS <->SC
					THREADS_MAX, // maximal number of threads to launch
					DEBUG_LEVEL); // globalDebugLevel)
		}
		clt = new double[clt_corr.length * 4][];
		for (int chn = 0; chn < clt_data.length; chn++) {
			double[][] clt_set = image_dtt.clt_dbg(clt_corr[chn], THREADS_MAX, DEBUG_LEVEL);
			for (int ii = 0; ii < clt_set.length; ii++)
				clt[chn * 4 + ii] = clt_set[ii];
		}
		if (DEBUG_LEVEL > -1) {
			ShowDoubleFloatArrays.showArrays(clt, tilesX * CLT_PARAMETERS.transform_size,
					tilesY * CLT_PARAMETERS.transform_size, true, DBG_IMP.getTitle() + "-ICORR+" + suffix, titles);
		}

		double[][][][] corr_tiles = new double[clt_corr.length][][][];
		for (int chn = 0; chn < clt_corr.length; chn++) {
			corr_tiles[chn] = image_dtt.clt_corr_quad(clt_corr[chn], THREADS_MAX, DEBUG_LEVEL);
		}
		if (DEBUG_LEVEL > 0) { // ============== -1 =================
			double[][] corr_rslt = new double[clt_corr.length][];
			for (int chn = 0; chn < clt_corr.length; chn++) {
				corr_rslt[chn] = image_dtt.corr_dbg(corr_tiles[chn], 2 * CLT_PARAMETERS.transform_size - 1,
						CLT_PARAMETERS.corr_border_contrast, THREADS_MAX, DEBUG_LEVEL);

			}
			String[] titles_rbg = { "red", "blue", "green" };
			ShowDoubleFloatArrays.showArrays(corr_rslt, tilesX * (2 * CLT_PARAMETERS.transform_size),
					tilesY * (2 * CLT_PARAMETERS.transform_size), true, DBG_IMP.getTitle() + "-C" + suffix, titles_rbg);
		}
	}

	public boolean getDCTShiftwDialog(double[] shiftXY) {
		GenericDialog gd = new GenericDialog("Set DCT shift");
		gd.addNumericField("X-shift", shiftXY[0], 2); // 2
		if (shiftXY.length > 1) {
			gd.addNumericField("Y-shift", shiftXY[1], 2); // 2
		}
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		shiftXY[0] = gd.getNextNumber();
		if (shiftXY.length > 1) {
			shiftXY[1] = gd.getNextNumber();
		}
		return true;
	}

	public boolean showScan() {
		if ((QUAD_CLT == null) || (QUAD_CLT.tp == null) || (QUAD_CLT.tp.clt_3d_passes == null)) {
			String msg = "DSI data is not available. Please run \"CLT 3D\" first";
			IJ.showMessage("Error", msg);
			System.out.println(msg);
			return false;
		}
		int scan_index = QUAD_CLT.tp.clt_3d_passes.size() - 1;
		GenericJTabbedDialog gd = new GenericJTabbedDialog("Select scan to show", 400, 100);
		gd.addNumericField("Scan index (0..." + (QUAD_CLT.tp.clt_3d_passes.size() - 1), scan_index, 0, 2, "",
				"Display scan by index");

		gd.showDialog();
		if (gd.wasCanceled())
			return false;

		scan_index = (int) gd.getNextNumber();
		QUAD_CLT.tp.showScan(QUAD_CLT.tp.clt_3d_passes.get(scan_index), "Scan-" + scan_index);
		return true;

	}

	public boolean showAllScans() {
		if ((QUAD_CLT == null) || (QUAD_CLT.tp == null) || (QUAD_CLT.tp.clt_3d_passes == null)) {
			String msg = "DSI data is not available. Please run \"CLT 3D\" first";
			IJ.showMessage("Error", msg);
			System.out.println(msg);
			return false;
		}
///		int scan_index = 0;
		String[] titles = QUAD_CLT.tp.getScanTitles();

		GenericJTabbedDialog gd = new GenericJTabbedDialog("Select scan slice to show", 400, 800);
		gd.addMessage("Select scan types to show");
		gd.addCheckbox("All scans", true, "Include all types of scans");
		gd.addCheckbox("Measured scans", false, "Include measured scans");
		gd.addCheckbox("Processed scans", false, "Include processed scans");
		gd.addCheckbox("Combo scans", false, "Include combo scans");
		gd.addMessage("Select slice to show (only first selected will be shown)");
		for (int i = 0; i < titles.length; i++) {
			gd.addCheckbox(i + ": " + titles[i], false); // 0
		}

		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		boolean all = gd.getNextBoolean();
		boolean measured = gd.getNextBoolean();
		boolean processed = gd.getNextBoolean();
		boolean combo = gd.getNextBoolean();
		boolean[] bslice = new boolean[titles.length];
		for (int i = 0; i < titles.length; i++) {
			bslice[i] = gd.getNextBoolean();
		}
		int slice = -1;
		for (int i = 0; i < bslice.length; i++)
			if (bslice[i]) {
				slice = i;
				break;
			}

		if (slice < 0) {
			System.out.println("Slice not selected");
			return false;
		}
		QUAD_CLT.tp.showAllScans("SCANS", // String title,
				slice, // int slice,
				all, // boolean all,
				measured, // boolean measured,
				processed, // boolean processed,
				combo); // boolean combo);
		return true;
	}

	public boolean cm_test() {
		double hsize_x = 1.5;
		double hsize_y = 1.5;
		int steps = 20;
		double sigma = 0.0;
		boolean separable = true; // false;
		double pwr = 1.0;
		GenericJTabbedDialog gd = new GenericJTabbedDialog("Set CLT parameters", 400, 300);
		gd.addNumericField("Half size X", hsize_x, 6, 8, "pix",
				"Correlation maximum half width in disparity direction");
		gd.addNumericField("Half size Y", hsize_y, 6, 8, "pix",
				"Correlation maximum half width in orthogonal direction");
		gd.addNumericField("Number of steps", steps, 0, 6, "", "Number of steps to subdivide [0,1) half-interval");
		gd.addNumericField("Low pass sigma", sigma, 3, 6, "pix",
				"Correlation maximum half width in orthogonal direction");
		gd.addCheckbox("X/Y separable", separable);
		gd.addNumericField("Value power", pwr, 6, 8, "", "Raise values to this power before calculating CM");

		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		DoubleGaussianBlur gb = new DoubleGaussianBlur();

		hsize_x = gd.getNextNumber();
		hsize_y = gd.getNextNumber();

		steps = (int) gd.getNextNumber();
		sigma = gd.getNextNumber();
		separable = gd.getNextBoolean();
		pwr = gd.getNextNumber();

		int size_x = 2 * ((int) Math.round(hsize_x + 2 * sigma + 1)) + 1;
		int size_y = 2 * ((int) Math.round(hsize_y + 2 * sigma + 1)) + 1;

		System.out.println("size_x=" + size_x + ", size_y=" + size_y + " sigma = " + sigma + " separable " + separable
				+ " pwr=" + pwr);
		String[] titles = new String[steps];
		double[][] data = new double[steps][size_x * size_y];
		for (int i = 0; i < steps; i++) {
			double dx = 1.0 * i / steps;
			titles[i] = IJ.d2s(dx, 3);
			for (int iy = 0; iy < size_y; iy++) {
				double y = (iy - (size_y - 1) / 2) / hsize_y;
				if ((y >= -1.0) && (y <= 1.0)) {
					double ay = separable ? (0.5 * (Math.cos(y * Math.PI) + 1.0)) : 1.0;
					for (int ix = 0; ix < size_x; ix++) {

						double x = (ix - (size_x - 1) / 2 - dx) / hsize_x;
						double r = separable ? Math.abs(x) : Math.sqrt(x * x + y * y);
//						if ((r >= -hsize_x) && (x <= hsize_x)) {
						if (r <= 1.0) {
							double ax = 0.5 * (Math.cos(r * Math.PI) + 1.0);
							data[i][iy * size_x + ix] = ax * ay;
						}
					}
				}
			}
		}
		ShowDoubleFloatArrays.showArrays(data, size_x, size_y, true, "pre-gauss", titles);
		if (sigma > 0.0) {
			for (int i = 0; i < steps; i++) {
				gb.blurDouble(data[i], size_x, size_y, sigma, sigma, 0.01);
			}
			ShowDoubleFloatArrays.showArrays(data, size_x, size_y, true, "blured", titles);
		}

		double[] cm_x = new double[steps];
		for (int i = 0; i < steps; i++) {
			double s0 = 0, sx = 0;
			for (int iy = 0; iy < size_y; iy++) {
				for (int ix = 0; ix < size_x; ix++) {
					double d = Math.pow(data[i][iy * size_y + ix], pwr);
					s0 += d;
					sx += ix * d;
				}
			}
			cm_x[i] = sx / s0 - (size_x - 1) / 2;
		}
		for (int i = 0; i <= steps / 2; i++) {
			double dx = 1.0 * i / steps;
			System.out.println(String.format("%3d %8.5f %8.5f %8.5f %8.5f %8.5f", i, dx, cm_x[i], cm_x[i] - dx,
					cm_x[i] / (dx + 0.00000001), dx / (cm_x[i] + 0.00000001)));
		}

		return true;

	}

	private boolean loadCorrelations() {
		String[] patterns = { ".corr-tiff", ".tiff", ".tif" };
		String path = selectFile(true, // smart
				false, // save
				"Select inter-image correlation vs. diparity file ", // title
				"Select inter-image correlation file", // button
				new MultipleExtensionsFileFilter(patterns, patterns[0] + " files"), // filter
				POST_PROCESSING.disparityCorrelationParameters.corrPathName);
		if ((path != null) && (path.length() > 0)) {
			DISPARITY_TILES = POST_PROCESSING.interSensor.new DisparityTiles(path);
			POST_PROCESSING.disparityCorrelationParameters.corrPathName = path;
			return true;
		}
		return false;
	}
	/* ======================================================================== */

	public String[] selectSourceFiles(String[] defaultPaths) {
		String[] patterns = { ".jp4", ".jp46", ".tiff", ".tif" };
		return selectFiles(false, // save
				"Source file(s) selection", // title
				"Select source file", // button
				new MultipleExtensionsFileFilter(patterns, "JP4/TIFF files"), // filter
				defaultPaths);
	}

	public String selectSourceFile(String defaultPath) {
		String[] patterns = { ".jp4", ".jp46", ".tiff", ".tif" };
		return selectFile(false, // save
				"Source file(s) selection", // title
				"Select source file", // button
				new MultipleExtensionsFileFilter(patterns, "JP4/TIFF files"), // filter
				defaultPath);
	}

	private String selectResultsDirectory(String defaultPath) {
		return selectDirectory(true, // save
				"Results directory selection", // title
				"Select results directory", // button
				null, // FileFIlter
				defaultPath);
	}

	private String selectKernelsDirectory(String defaultPath) {
		return selectDirectory(false, // save
				"Convolution kernels directory selection", "Select kernels directory", null, // FileFIlter
				defaultPath);

	}

	public String selectDirectory(boolean save, String title, String button, FileFilter filter, String defaultPath) {
		return selectDirectoryOrFile(save, true, title, button, filter, defaultPath);
	}

	public String[] selectDirectories(boolean save, String title, String button, FileFilter filter,
			String[] defaultPaths) {
		return selectDirectoriesOrFiles(save, true, title, button, filter, defaultPaths);
	}

	public String selectFile(boolean smart, boolean save, String title, String button, FileFilter filter,
			String defaultPath) {
		if (smart && (defaultPath != null) && (defaultPath.length() > 0)) {
			File file = new File(defaultPath);
			if (file.exists() && !file.isDirectory())
				return defaultPath;
		}
		return selectDirectoryOrFile(save, false, title, button, filter, defaultPath);
	}

	public static String selectFile(
			boolean save,
			String title,
			String button,
			FileFilter filter,
			String defaultPath) {
		return selectDirectoryOrFile(
				save,
				false,
				title,
				button,
				filter,
				defaultPath);
	}

	public static String[] selectFiles(boolean save, String title, String button, FileFilter filter, String[] defaultPaths) {
		return selectDirectoriesOrFiles(save, false, title, button, filter, defaultPaths);
	}

	public static String[] selectDirectoriesOrFiles(boolean save, boolean directory, String title, String button,
			FileFilter filter, String[] defaultPaths) {
		if (GenericJTabbedDialogMcp.isDefaultMcpMode() && MCP_COMMAND_CONTEXT) {
			String defaultPath = null;
			if (defaultPaths != null && defaultPaths.length > 0) {
				defaultPath = defaultPaths[0];
			}
			String key = (title == null || title.isEmpty()) ? "default" : title;
			String initial = defaultPath;
			if (initial == null || initial.isEmpty()) {
				initial = MCP_LAST_PATHS.get(key);
			}
			if (initial == null || initial.isEmpty()) {
				initial = DEFAULT_DIRECTORY;
			}
			String[] selected = FileSelectMcp.selectMultiple(directory, title, initial);
			if (selected == null || selected.length == 0) {
				return null;
			}
			updateDefaultDirectoryFromPath(selected[0], directory);
			MCP_LAST_PATHS.put(key, selected[0]);
			return selected;
		}
		File dir = null;
		String defaultPath = null;
		File[] files = null;
		int fileNum;
		if ((defaultPaths != null) && (defaultPaths.length > 0)) {
			File[] tfiles = new File[defaultPaths.length];
			int nf = defaultPaths.length;
			for (fileNum = 0; fileNum < defaultPaths.length; fileNum++) {
				tfiles[fileNum] = new File(defaultPaths[fileNum]);
				if ((!tfiles[fileNum].exists()) || (!tfiles[fileNum].isFile())) {
					tfiles[fileNum] = null;
					nf--;
				}
			}
			files = new File[nf];
			nf = 0;
			for (fileNum = 0; fileNum < defaultPaths.length; fileNum++)
				if (tfiles[fileNum] != null) {
					files[nf++] = tfiles[fileNum];
				}
		}
		if ((defaultPaths != null) && (defaultPaths.length > 0) && (!defaultPaths[0].equals(""))) {
			defaultPath = defaultPaths[0];
			dir = new File(defaultPath);
		}
		if ((dir == null) || (!dir.exists())) {
			if (DEFAULT_DIRECTORY != null) {
				defaultPath = DEFAULT_DIRECTORY;
				dir = new File(defaultPath);
			}
		}
		if ((dir == null) || (!dir.exists())) {
			defaultPath = OpenDialog.getDefaultDirectory();
			if (defaultPath != null)
				dir = new File(defaultPath);
		}
		if ((dir != null) && (!dir.exists()))
			dir = null;
		if ((dir != null) && (!dir.isDirectory())) {
			dir = dir.getParentFile();
		}
//getSelectedFiles

		JFileChooser fc = new JFileChooser();
		fc.setPreferredSize(new Dimension(800, 1000));
		fc.setFileSelectionMode(directory ? JFileChooser.DIRECTORIES_ONLY : JFileChooser.FILES_ONLY);
		fc.setMultiSelectionEnabled(true);
		if ((title != null) && (title.length() > 0))
			fc.setDialogTitle(title);
		if ((button != null) && (button.length() > 0))
			fc.setApproveButtonText(button);
		if (filter != null)
			fc.setFileFilter(filter);
		if (dir != null)
			fc.setCurrentDirectory(dir);
		fc.setSelectedFiles(files);
		int returnVal = save ? (fc.showSaveDialog(IJ.getInstance())) : (fc.showOpenDialog(IJ.getInstance()));
		if (returnVal != JFileChooser.APPROVE_OPTION)
			return null;
		DEFAULT_DIRECTORY = fc.getCurrentDirectory().getPath();
		files = fc.getSelectedFiles();
		if (files.length < 1)
			return null;
		String[] filenames = new String[files.length];
//	  for (int nFile=0;nFile<files.length;nFile++) filenames[nFile]= files[nFile].getName();
		for (int nFile = 0; nFile < files.length; nFile++)
			filenames[nFile] = files[nFile].getPath();
		return filenames;
	}

	public static String selectDirectoryOrFile(boolean save, boolean directory, String title, String button, FileFilter filter,
			String defaultPath) {
		if (GenericJTabbedDialogMcp.isDefaultMcpMode() && MCP_COMMAND_CONTEXT) {
			return selectDirectoryOrFileMcp(directory, title, defaultPath);
		}
		File dir = null;
		if ((defaultPath != null) && (!defaultPath.equals(""))) {
			dir = new File(defaultPath);
		}
		if ((dir == null) || (!dir.exists())) {
			if (DEFAULT_DIRECTORY != null) {
				defaultPath = DEFAULT_DIRECTORY;
				dir = new File(defaultPath);
			}
		}
		if ((dir == null) || (!dir.exists())) {
			defaultPath = OpenDialog.getDefaultDirectory();
			if (defaultPath != null)
				dir = new File(defaultPath);
		}
		if ((dir != null) && (!dir.exists()))
			dir = null;
		if ((dir != null) && (!dir.isDirectory())) {
			dir = dir.getParentFile();
		}

		JFileChooser fc = new JFileChooser();
		fc.setPreferredSize(new Dimension(800, 1000));
		fc.setFileSelectionMode(directory ? JFileChooser.DIRECTORIES_ONLY : JFileChooser.FILES_ONLY);
		fc.setMultiSelectionEnabled(false);
		if ((title != null) && (title.length() > 0))
			fc.setDialogTitle(title);
		if ((button != null) && (button.length() > 0))
			fc.setApproveButtonText(button);
		if (filter != null)
			fc.setFileFilter(filter);
		if (dir != null)
			fc.setCurrentDirectory(dir);
		int returnVal = save ? (fc.showSaveDialog(IJ.getInstance())) : (fc.showOpenDialog(IJ.getInstance()));
		if (returnVal != JFileChooser.APPROVE_OPTION)
			return null;
		DEFAULT_DIRECTORY = fc.getCurrentDirectory().getPath();
		return fc.getSelectedFile().getPath();
	}

	// codex 2026-01-28: MCP file selection helper (single path)
	private static String selectDirectoryOrFileMcp(boolean directory, String title, String defaultPath) {
		String key = (title == null || title.isEmpty()) ? "default" : title;
		String initial = defaultPath;
		if (initial == null || initial.isEmpty()) {
			initial = MCP_LAST_PATHS.get(key);
		}
		if (initial == null || initial.isEmpty()) {
			initial = DEFAULT_DIRECTORY;
		}
		String value = FileSelectMcp.selectSingle(directory, title, initial);
		if (value == null) {
			return null;
		}
		updateDefaultDirectoryFromPath(value, directory);
		MCP_LAST_PATHS.put(key, value);
		return value;
	}

	// codex 2026-01-28: update DEFAULT_DIRECTORY from a selected path
	private static void updateDefaultDirectoryFromPath(String value, boolean directory) {
		if (value == null || value.isEmpty()) {
			return;
		}
		File selected = new File(value);
		if (directory && !selected.isDirectory()) {
			return;
		}
		if (selected.isDirectory()) {
			DEFAULT_DIRECTORY = selected.getPath();
		} else {
			File parent = selected.getParentFile();
			if (parent != null) {
				DEFAULT_DIRECTORY = parent.getPath();
			}
		}
	}
/*
	class MultipleExtensionsFileFilter extends FileFilter {
		protected String[] patterns;
		protected String description = "JP4 files";

		public MultipleExtensionsFileFilter(String[] patterns, String description) {
			this.description = description;
			this.patterns = patterns.clone();
		}

		public MultipleExtensionsFileFilter(String[] patterns) {
			this.patterns = patterns.clone();
		}

		@Override
		public boolean accept(File file) {
			int i;
			String name = file.getName();
			if (file.isDirectory())
				return true;
			for (i = 0; i < patterns.length; i++) {
				if (name.toLowerCase().endsWith(patterns[i].toLowerCase()))
					return true;
			}
			return false;
		}

		@Override
		public String getDescription() {
			return description;
		}
	}
*/
	/* ======================================================================== */

	private static boolean fixSliceSequence(ImageStack stack) {
		int i, j;
		int[] rgbNumbers = { 0, 0, 0 };
		for (j = 0; j < 3; j++) {
			for (i = 1; i <= 3; i++)
				if (stack.getSliceLabel(i).toLowerCase().equals(stackColorNames[j].toLowerCase())) {
// fix case (capitalized)
//			  System.out.println ( "stack.getSliceLabel("+i+")="+stack.getSliceLabel(i));
//			  System.out.println ( "stackColorNames["+j+"]="+stackColorNames[j]);
					stack.setSliceLabel(stackColorNames[j], i);
					rgbNumbers[j] = i;
//			  System.out.println ( "rgbNumbers["+j+"]="+rgbNumbers[j]);
					break;
				}
		}
		if (DEBUG_LEVEL > 2) {
			System.out.println("Input file color slice numbers:");
			System.out.println("  Red -   slice " + ((rgbNumbers[0] > 0) ? rgbNumbers[0] : "missing"));
			System.out.println("  Green - slice " + ((rgbNumbers[1] > 0) ? rgbNumbers[1] : "missing"));
			System.out.println("  Blue -  slice " + ((rgbNumbers[2] > 0) ? rgbNumbers[2] : "missing"));
		}

		for (i = 0; i < 3; i++)
			if (rgbNumbers[i] <= 0) {
				System.out
						.println(stackColorNames[i] + "  slice is missing in the input file. Please check slice names");
				return false;
			}
		while ((rgbNumbers[0] != 1) || (rgbNumbers[1] != 2) || (rgbNumbers[2] != 3)) {
			if (rgbNumbers[0] == 1)
				swapStackSlices(stack, 2, 3);
			else if (rgbNumbers[2] == 3)
				swapStackSlices(stack, 1, 2);
			else
				swapStackSlices(stack, 1, 3);
			for (j = 0; j < 3; j++) {
				for (i = 1; i <= 3; i++)
					if (stack.getSliceLabel(i).equals(stackColorNames[j])) {
						rgbNumbers[j] = i;
						break;
					}
			}
		}
		return true;
	}

	/* ======================================================================== */
	public static void swapStackSlices(ImageStack stack, int slice1, int slice2) {
		String label = stack.getSliceLabel(slice1);
		stack.setSliceLabel(stack.getSliceLabel(slice2), slice1);
		stack.setSliceLabel(label, slice2);
		Object pixels = stack.getPixels(slice1);
		stack.setPixels(stack.getPixels(slice2), slice1);
		stack.setPixels(pixels, slice2);
	}

	/* ======================================================================== */
	public static ImageStack cropStack32(ImageStack stack, EyesisCorrectionParameters.SplitParameters splitParameters) {
		int size = stack.getSize();
		int iWidth = stack.getWidth();
		int height = stack.getHeight() - splitParameters.addTop - splitParameters.addBottom;
		int width = stack.getWidth() - splitParameters.addLeft - splitParameters.addRight;
		int length = width * height;
		ImageStack stack_crop = new ImageStack(width, height);
		int i, x, y, index, base;
		float[] ipixels;
		float[] opixels;
		for (i = 0; i < size; i++) {
			ipixels = (float[]) stack.getPixels(i + 1);
			opixels = new float[length];
			index = 0;
			for (y = 0; y < height; y++) {
				base = iWidth * (y + splitParameters.addTop) + splitParameters.addLeft;
				for (x = 0; x < width; x++)
					opixels[index++] = ipixels[base++];
			}
			stack_crop.addSlice(stack.getSliceLabel(i + 1), opixels);
		}
		return stack_crop;
	}

	/* ======================================================================== */
	public static ImageStack rotateStack32CW(ImageStack stack) {
		int size = stack.getSize();
		int height = stack.getHeight();
		int width = stack.getWidth();
		int length = width * height;
		ImageStack stack_rot = new ImageStack(height, width);
		int i, x, y, index;
		float[] ipixels;
		float[] opixels;
		for (i = 0; i < size; i++) {
			ipixels = (float[]) stack.getPixels(i + 1);
			opixels = new float[length];
			index = 0;
			for (x = 0; x < width; x++)
				for (y = height - 1; y >= 0; y--)
					opixels[index++] = ipixels[y * width + x];
			stack_rot.addSlice(stack.getSliceLabel(i + 1), opixels);
		}
		return stack_rot;

	}

	/* ======================================================================== */
	public static ImagePlus cropImage32(ImagePlus imp, EyesisCorrectionParameters.SplitParameters splitParameters) {
		int iWidth = imp.getWidth();
		int height = imp.getHeight() - splitParameters.addTop - splitParameters.addBottom;
		int width = imp.getWidth() - splitParameters.addLeft - splitParameters.addRight;
		int length = width * height;
		int x, y, index, base;
		float[] ipixels = (float[]) imp.getProcessor().getPixels();
		float[] opixels = new float[length];
		index = 0;
		for (y = 0; y < height; y++) {
			base = iWidth * (y + splitParameters.addTop) + splitParameters.addLeft;
			for (x = 0; x < width; x++)
				opixels[index++] = ipixels[base++];
		}
		ImageProcessor ip_crop = new FloatProcessor(width, height, opixels, null);
		ImagePlus imp_crop = new ImagePlus(imp.getTitle(), ip_crop); // same title?
		return imp_crop;
	}

	/* ======================================================================== */
	public static ImagePlus rotateImage32CW(ImagePlus imp) {
		int width = imp.getWidth();
		int height = imp.getHeight();
		int length = width * height;
		int x, y, index;
		float[] ipixels = (float[]) imp.getProcessor().getPixels();
		float[] opixels = new float[length];
		index = 0;
		for (x = 0; x < width; x++)
			for (y = height - 1; y >= 0; y--)
				opixels[index++] = ipixels[y * width + x];
		ImageProcessor ip_rot = new FloatProcessor(height, width, opixels, null);
		ImagePlus imp_rot = new ImagePlus(imp.getTitle(), ip_rot); // same title?
		return imp_rot;
	}

	/* ======================================================================== */
	public static CompositeImage convertToComposite(ImagePlus imp) {
//	  if (imp.isComposite()) return imp;
		if (imp.isComposite())
			return null;
		if (imp.getNChannels() > 1) {
			return null; // number of channels should be just 1
		}
		int c = imp.getStackSize();
		imp.setDimensions(c, 1, 1);
		CompositeImage ci = new CompositeImage(imp, CompositeImage.COMPOSITE);
//	  ci.show();
//	  imp.hide();
		return ci;
	}

	/* ======================================================================== */
	public static ImageStack convertRGB32toRGB16Stack(ImageStack stack32,
			EyesisCorrectionParameters.RGBParameters rgbParameters) {
		ImageStack stack16 = new ImageStack(stack32.getWidth(), stack32.getHeight());
		int length = stack32.getWidth() * stack32.getHeight();
		int i, j;
		float[] fpixels;
		short[] spixels;
		int numSlices = stack32.getSize();
		if (numSlices > 4)
			numSlices = 4;
		if (numSlices < 3)
			return null;
		double[] mins = { rgbParameters.r_min, rgbParameters.g_min, rgbParameters.b_min, rgbParameters.alpha_min };
		double[] maxs = { rgbParameters.r_max, rgbParameters.g_max, rgbParameters.b_max, rgbParameters.alpha_max };
		double value;
		double scale;
		for (i = 0; i < numSlices; i++) {
			fpixels = (float[]) stack32.getPixels(i + 1);
			scale = 65535.0 / (maxs[i] - mins[i]);
			spixels = new short[length];
			for (j = 0; j < length; j++) {
				value = (fpixels[j] - mins[i]) * scale;
				if (value < 0.0)
					value = 0.0;
				else if (value > 65535.0)
					value = 65535.0;
				spixels[j] = (short) (value + 0.5);
			}
			stack16.addSlice(stack32.getSliceLabel(i + 1), spixels);
		}
		return stack16;

	}

	public static ImagePlus convertRGB48toRGB24(ImageStack stack16, String title, int r_min, int r_max, int g_min, int g_max,
			int b_min, int b_max, int alpha_min, int alpha_max) {
		int[] mins = { r_min, g_min, b_min, alpha_min };
		int[] maxs = { r_max, g_max, b_max, alpha_max };
		int i;
		int length = stack16.getWidth() * stack16.getHeight();
		int numSlices = stack16.getSize();
		if (numSlices > 4)
			numSlices = 4;
		short[][] spixels = new short[numSlices][];
		int[] pixels = new int[length];
		int c, d;
		double[] scale = new double[numSlices];
		for (c = 0; c < numSlices; c++) {
			scale[c] = 256.0 / (maxs[c] - mins[c]);
		}
		for (i = 0; i < numSlices; i++)
			spixels[i] = (short[]) stack16.getPixels(i + 1);
		for (i = 0; i < length; i++) {
			pixels[i] = 0;
			for (c = 0; c < numSlices; c++) {
				d = (int) (((spixels[c][i] & 0xffff) - mins[c]) * scale[c]);
				if (d > 255)
					d = 255;
				else if (d < 0)
					d = 0;
				pixels[i] = d | (pixels[i] << 8);
			}
		}
		ColorProcessor cp = new ColorProcessor(stack16.getWidth(), stack16.getHeight());
		cp.setPixels(pixels);
		ImagePlus imp = new ImagePlus(title, cp);
		return imp;
	}

	/* ======================================================================== */
	public static ImagePlus Image32toGreyRGB24(ImagePlus imp) {
		int width = imp.getWidth();
		int height = imp.getHeight();
		int length = width * height;
		int i;
		float[] ipixels = (float[]) imp.getProcessor().getPixels();
		int[] pixels = new int[length];
		float min = ipixels[0];
		float max = ipixels[0];
		for (i = 0; i < length; i++) {
			if (min > ipixels[i])
				min = ipixels[i];
			if (max < ipixels[i])
				max = ipixels[i];
		}
		double d = 256.0 / (max - min);
		int c;
		for (i = 0; i < length; i++) {
			c = (int) ((ipixels[i] - min) * d);
			if (c > 255)
				c = 255;
			pixels[i] = c | (c << 8) | (c << 16);
		}
		ColorProcessor cp = new ColorProcessor(width, height, pixels);
		ImagePlus imp_rgb = new ImagePlus(imp.getTitle(), cp);
		return imp_rgb;
	}

	/* ======================================================================== */
	public static void saveTimestampedProperties(String path, // full path or null
			String directory, // use as default directory if path==null
			boolean useXML, Properties properties) {
		if (path == null) {
			String msg = "path should not be null";
			IJ.showMessage("Error", msg);
			throw new IllegalArgumentException(msg);
		}
		saveProperties(path + "_" + IJ.d2s(0.000001 * (System.nanoTime() / 1000), 6).replace('.', '_'), // full path or
																										// null
				directory, // use as default directory if path==null
				useXML,
				properties,
				DEBUG_LEVEL);

	}

	public static void saveProperties(
			String path, // full path or null
			String     directory, // use as default directory if path==null
			boolean    useXML,
			Properties properties,
			int        debugLevel) {
		String[] XMLPatterns = { ".corr-xml", ".xml" };
		String[] confPatterns = { ".conf" };
		String[] patterns = useXML ? XMLPatterns : confPatterns;
		String ext = (useXML ? "XML " : "") + "Configuration files (" + (useXML ? "*.corr-xml" : "*.conf")
				+ ")";
		MultipleExtensionsFileFilter filter = new MultipleExtensionsFileFilter(patterns, ext); // filter

		if (path == null) {
			path = selectFile(true, // save
					"Save configuration selection", // title
					"Select configuration file", // button
					filter, // filter
					directory); // may be ""
		} else
			path += patterns[0];
		if (path == null)
			return;
		setAllProperties(properties);

		OutputStream os;
		try {
			os = new FileOutputStream(path);
		} catch (FileNotFoundException e1) {
			// missing config directory
			File dir = (new File(path)).getParentFile();
			if (!dir.exists()) {
				dir.mkdirs();
				try {
					os = new FileOutputStream(path);
				} catch (FileNotFoundException e2) {
					IJ.showMessage("Error",
							"Failed to create directory " + dir.getName() + " to save configuration file: " + path);
					return;
				}
			} else {
				IJ.showMessage("Error", "Failed to open configuration file: " + path);
				return;
			}
		}
		if (useXML) {
			try {
				properties.storeToXML(os, "last updated " + new java.util.Date(), "UTF8");

			} catch (IOException e) {
				IJ.showMessage("Error", "Failed to write XML configuration file: " + path);
				return;
			}
		} else {
			try {
				properties.store(os, "last updated " + new java.util.Date());
			} catch (IOException e) {
				IJ.showMessage("Error", "Failed to write configuration file: " + path);
				return;
			}
		}
		try {
			os.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (debugLevel > -3)
			System.out.println("Configuration parameters are saved to " + path);

	}

	public void saveInfinityOffsets(String path, // full path or null
			String directory) { // use as default directory if path==null
		String[] patterns = { ".corr-xml", ".xml" };
		if (path == null) {
			path = selectFile(true, // save
					"Save configuration selection", // title
					"Select configuration file", // button
					new MultipleExtensionsFileFilter(patterns, "XML Configuration files (*.corr-xml)"), // filter
					directory); // may be ""
		} else
			path += patterns[0];
		if (path == null)
			return;
		Properties properties = new Properties();

		CLT_PARAMETERS.setPropertiesInfinityDistance("CLT_PARAMETERS.", properties);
//		setAllProperties(properties);

		OutputStream os;
		try {
			os = new FileOutputStream(path);
		} catch (FileNotFoundException e1) {
			// missing config directory
			File dir = (new File(path)).getParentFile();
			if (!dir.exists()) {
				dir.mkdirs();
				try {
					os = new FileOutputStream(path);
				} catch (FileNotFoundException e2) {
					IJ.showMessage("Error",
							"Failed to create directory " + dir.getName() + " to save configuration file: " + path);
					return;
				}
			} else {
				IJ.showMessage("Error", "Failed to open configuration file: " + path);
				return;
			}
		}
		try {
			properties.storeToXML(os, "last updated " + new java.util.Date(), "UTF8");

		} catch (IOException e) {
			IJ.showMessage("Error", "Failed to write XML configuration file: " + path);
			return;
		}
		try {
			os.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (DEBUG_LEVEL > -3)
			System.out.println("Configuration parameters are saved to " + path);

	}

	/* ======================================================================== */
	public String loadProperties(String path, String directory, boolean useXML, Properties properties) {
		String[] XMLPatterns = { ".corr-xml", ".xml" };
		String[] confPatterns = { ".conf" };
		String[] patterns = useXML ? XMLPatterns : confPatterns;
		if (path == null) {
			path = selectFile(false, // save
					"Configuration file selection", // title
					"Read configuration file", // button
					new MultipleExtensionsFileFilter(patterns,
							(useXML ? "XML " : "") + "Configuration files (" + (useXML ? "*.corr-xml" : "*.conf")
									+ ")"), // filter
					directory); // may be ""
		} else
			if (!path.endsWith(patterns[0])) {
				path += patterns[0]; // where was it used???
			}
		if (path == null)
			return null;
		InputStream is;
		try {
			is = new FileInputStream(path);
		} catch (FileNotFoundException e) {
			IJ.showMessage("Error", "Failed to open configuration file: " + path);
			return null;
		}

		if (useXML) {
			try {
				properties.loadFromXML(is);

			} catch (IOException e) {
				IJ.showMessage("Error", "Failed to read XML configuration file: " + path);
				return null;
			}
		} else {
			try {
				properties.load(is);
			} catch (IOException e) {
				IJ.showMessage("Error", "Failed to read configuration file: " + path);
				return null;
			}
		}
		try {
			is.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return path;
	}

	
	public String [] loadMultiProperties(
			String directory,
			boolean useXML) {
		String[] XMLPatterns = { ".corr-xml", ".xml" };
		String[] confPatterns = { ".conf" };
		String[] patterns = useXML ? XMLPatterns : confPatterns;
		String [] paths = selectFiles(
				false, // boolean save,
				"Multiple configuration file selection", // title
				"Read configuration files", // button
				new MultipleExtensionsFileFilter(patterns,
						(useXML ? "XML " : "") + "Configuration files (" + (useXML ? "*.corr-xml" : "*.conf")
								+ ")"), // filter
				new String[] {directory}); // String[] defaultPaths)
		return paths;
	}
	
	
	/* ======================================================================== */
	public static void setAllProperties(Properties properties) {
		properties.setProperty("MASTER_DEBUG_LEVEL", MASTER_DEBUG_LEVEL + "");
		properties.setProperty("UPDATE_STATUS", UPDATE_STATUS + "");
		SPLIT_PARAMETERS.setProperties("SPLIT_PARAMETERS.", properties);
		DCT_PARAMETERS.setProperties("DCT_PARAMETERS.", properties);
		CLT_PARAMETERS.setProperties("CLT_PARAMETERS.", properties);
		CALIBRATION_ILLUSTRATION_PARAMETERS.setProperties("ILLUSTRATIONS.", properties);
		DEBAYER_PARAMETERS.setProperties("DEBAYER_PARAMETERS.", properties);
		NONLIN_PARAMETERS.setProperties("NONLIN_PARAMETERS.", properties); // keep for Eyesis, not used fro Quad CLT
		COLOR_PROC_PARAMETERS.setProperties("COLOR_PROC_PARAMETERS.", properties);
		if (COLOR_PROC_PARAMETERS_AUX != null) { // for saving settings
//    		COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
			COLOR_PROC_PARAMETERS_AUX.setProperties("COLOR_PROC_PARAMETERS." + ColorProcParameters.AUX_PREFIX,
					properties);
		}
		RGB_PARAMETERS.setProperties("RGB_PARAMETERS.", properties);
		PROCESS_PARAMETERS.setProperties("PROCESS_PARAMETERS.", properties);
		CORRECTION_PARAMETERS.setProperties("CORRECTION_PARAMETERS.", properties);
		CHANNEL_GAINS_PARAMETERS.setProperties("CHANNEL_GAINS_PARAMETERS.", properties);
		if (CHANNEL_GAINS_PARAMETERS_AUX != null) {
			CHANNEL_GAINS_PARAMETERS_AUX.setProperties(
					"CHANNEL_GAINS_PARAMETERS." + CorrectionColorProc.ColorGainsParameters.AUX_PREFIX, properties);
		}
		EQUIRECTANGULAR_PARAMETERS.setProperties("EQUIRECTANGULAR_PARAMETERS.", properties);
		POST_PROCESSING.setProperties("", properties);
		properties.setProperty("CONVOLVE_FFT_SIZE", CONVOLVE_FFT_SIZE + ""); // 128, FFT size for sliding convolution
																				// with kernel
		properties.setProperty("THREADS_MAX", THREADS_MAX + ""); // 100, testing multi-threading, limit maximal number
																	// of threads
		properties.setProperty("GAUSS_WIDTH", GAUSS_WIDTH + ""); // 0.4 (0 - use Hamming window)
		properties.setProperty("PSF_SUBPIXEL_SHOULD_BE_4", PSF_SUBPIXEL_SHOULD_BE_4 + ""); // 4, sub-pixel decimation
		// 3.27.2023 - added putAll to clean up old/unused parameters
		if (QUAD_CLT != null)
			properties.putAll(QUAD_CLT.setProperties(QuadCLT.PREFIX, new Properties())); // null));
		if (QUAD_CLT_AUX != null)
			properties.putAll(QUAD_CLT_AUX.setProperties(QuadCLT.PREFIX_AUX, new Properties())); //, null));
	}

	/* ======================================================================== */
	public static void getAllProperties(Properties properties) {
		MASTER_DEBUG_LEVEL = Integer.parseInt(properties.getProperty("MASTER_DEBUG_LEVEL"));
		UPDATE_STATUS = Boolean.parseBoolean(properties.getProperty("UPDATE_STATUS"));
		SPLIT_PARAMETERS.getProperties("SPLIT_PARAMETERS.", properties);
		DCT_PARAMETERS.getProperties("DCT_PARAMETERS.", properties);
		CLT_PARAMETERS.getProperties("CLT_PARAMETERS.", properties);
		CALIBRATION_ILLUSTRATION_PARAMETERS.getProperties("ILLUSTRATIONS.", properties);
		DEBAYER_PARAMETERS.getProperties("DEBAYER_PARAMETERS.", properties);
		NONLIN_PARAMETERS.getProperties("NONLIN_PARAMETERS.", properties); // keep for Eyesis, not used fro Quad CLT
		COLOR_PROC_PARAMETERS.getProperties("COLOR_PROC_PARAMETERS.", properties);
		// try AUX parameters, if none - remove
		if (COLOR_PROC_PARAMETERS_AUX == null) {
			COLOR_PROC_PARAMETERS_AUX = COLOR_PROC_PARAMETERS.clone();
			if (!COLOR_PROC_PARAMETERS_AUX.getProperties("COLOR_PROC_PARAMETERS." + ColorProcParameters.AUX_PREFIX,
					properties)) {
				CHANNEL_GAINS_PARAMETERS_AUX = null;
			}
		} else { // read on top, do not remove
			COLOR_PROC_PARAMETERS_AUX.getProperties("COLOR_PROC_PARAMETERS." + ColorProcParameters.AUX_PREFIX,
					properties);
		}
		RGB_PARAMETERS.getProperties("RGB_PARAMETERS.", properties);
		PROCESS_PARAMETERS.getProperties("PROCESS_PARAMETERS.", properties);
		CORRECTION_PARAMETERS.getProperties("CORRECTION_PARAMETERS.", properties);
		CHANNEL_GAINS_PARAMETERS.getProperties("CHANNEL_GAINS_PARAMETERS.", properties);
		// try AUX parameters, if none - remove
		if (CHANNEL_GAINS_PARAMETERS_AUX == null) {
			CHANNEL_GAINS_PARAMETERS_AUX = new CorrectionColorProc.ColorGainsParameters();
			if (!CHANNEL_GAINS_PARAMETERS_AUX.getProperties(
					"CHANNEL_GAINS_PARAMETERS." + CorrectionColorProc.ColorGainsParameters.AUX_PREFIX, properties)) {
				CHANNEL_GAINS_PARAMETERS_AUX = null;
			}
		} else { // read on top, do not remove
			CHANNEL_GAINS_PARAMETERS_AUX.getProperties(
					"CHANNEL_GAINS_PARAMETERS." + CorrectionColorProc.ColorGainsParameters.AUX_PREFIX, properties);
		}

		EQUIRECTANGULAR_PARAMETERS.getProperties("EQUIRECTANGULAR_PARAMETERS.", properties);
		POST_PROCESSING.getProperties("", properties);
		CONVOLVE_FFT_SIZE = Integer.parseInt(properties.getProperty("CONVOLVE_FFT_SIZE"));
		THREADS_MAX = Integer.parseInt(properties.getProperty("THREADS_MAX"));
		GAUSS_WIDTH = Double.parseDouble(properties.getProperty("GAUSS_WIDTH"));
		PSF_SUBPIXEL_SHOULD_BE_4 = Integer.parseInt(properties.getProperty("PSF_SUBPIXEL_SHOULD_BE_4"));
		if (QUAD_CLT != null)
			QUAD_CLT.getProperties(QuadCLT.PREFIX);
		if (QUAD_CLT_AUX != null)
			QUAD_CLT_AUX.getProperties(QuadCLT.PREFIX_AUX);
		return;
	}

	/* ======================================================================== */
	public ImagePlus[][][] processChannelFiles(boolean saveResult, // free memory, return empty results if false
			JP46_Reader_camera JP4_instance, String[] idirectories, String[] ifilenames,
			EyesisCorrectionParameters.ProcessParameters processParameters,
			EyesisCorrectionParameters.SplitParameters splitParameters,
			EyesisCorrectionParameters.DebayerParameters debayerParameters,
			EyesisCorrectionParameters.FilesParameters filesParameters,
			EyesisCorrectionParameters.NonlinParameters nonlinParameters, ColorProcParameters colorProcParameters,
			EyesisCorrectionParameters.ColorCalibParameters colorCalibParameters,
			EyesisCorrectionParameters.RGBParameters rgbParameters, int convolveFFTSize, // 128; // FFT size for sliding
																							// convolution with kernel
			int threadsMax, // 100; // testing multi-threading, limit maximal number of threads
			boolean updateStatus) {
		Runtime runtime = Runtime.getRuntime();
		long startTime = System.nanoTime();
		ImagePlus[][][] result = null;
		ImagePlus[][] imageNoiseGains = null;
		int nFile, nChn, nSubChn;
		int numFiles = ifilenames.length;
		if (numFiles == 0)
			return null;
		double[][] noiseMask = null;
		for (nFile = 0; nFile < ifilenames.length; nFile++) {
			if (DEBUG_LEVEL > 1)
				System.out.println(nFile + ": directory=" + idirectories[nFile] + " filename=" + ifilenames[nFile]);
		}
		boolean eyesisMode = processParameters.eyesisMode;
		// turn eyesisMode off, if all the selected files do not have the proper
		// signature (*_1-*, *_2-*, *_3-*)
		if (eyesisMode) {
			boolean allEyesis = true;
			boolean noneEyesis = true;
			boolean thisEyesis;
			for (nFile = 0; nFile < numFiles; nFile++) {
				thisEyesis = false;
				for (nChn = 0; nChn < processParameters.numEyesisChannels; nChn++)
					if (ifilenames[nFile].indexOf("_" + (nChn + 1) + "-") >= 0) {
						thisEyesis = true;
						break;
					}
				if (thisEyesis)
					noneEyesis = false;
				else
					allEyesis = false;
			}
			if (!noneEyesis && !allEyesis) {
				IJ.showMessage("Error",
						"Some of the selected files are Eyesis (with _1-, _2-, _3-), some - not\n Please select only one type or turn off the eysisMode");
				return null;
			}
			if (noneEyesis) {
				if (DEBUG_LEVEL > 0)
					System.out.println(
							">> eyesisMode was on, but non files have channel number, turning eyesisMode off.");
				eyesisMode = false;
			}
		}
		int numChannels = eyesisMode ? processParameters.numEyesisChannels : 1;
		int numSubChannels = eyesisMode ? processParameters.numEyesisSubChannels : 1;

		// Add same shot files (if enabled) - duplicates are possible
		String[][] filenames = new String[numFiles][numChannels];
		boolean[][][] channels = new boolean[ifilenames.length][numChannels][numSubChannels];
		String[] directories = idirectories.clone();
		int channel;
		int indexOfDot;
		String extension;
		String filename;
		String[] prefix = new String[numFiles];
		String[] suffix = new String[numFiles];
		for (nFile = 0; nFile < numFiles; nFile++) {
			indexOfDot = ifilenames[nFile].toLowerCase().lastIndexOf(".");
			if (indexOfDot < 0) {
				IJ.showMessage("Error", "Valid source filename should have extension");
				return null;
			}
			extension = ifilenames[nFile].substring(indexOfDot);
			if (eyesisMode) {
				filename = ifilenames[nFile].substring(0, indexOfDot);
				channel = -1;
				for (nChn = 1; nChn <= numChannels; nChn++)
					if (filename.indexOf("_" + nChn + "-") >= 0) {
						channel = nChn;
						break;
					}
				if (channel < 0) { // should not get here
					return null;
				}
				prefix[nFile] = filename.substring(0, filename.indexOf("_" + channel + "-"));
				suffix[nFile] = filename.substring(filename.indexOf("_" + channel + "-") + 3);
				if (DEBUG_LEVEL > 1) {
					System.out.println("channel=" + channel);
					System.out.println("prefix=" + prefix[nFile]);
					System.out.println("suffix=" + suffix[nFile]);
				}
				for (nChn = 0; nChn < numChannels; nChn++) {
					if ((!processParameters.thisFileOnly) || (nChn == (channel - 1))) {
						filenames[nFile][nChn] = prefix[nFile] + "_" + (nChn + 1) + "-" + suffix[nFile] + extension;
						for (nSubChn = 0; nSubChn < numSubChannels; nSubChn++) {
							if ((!processParameters.thisFileOnly)
									|| (nSubChn == (processParameters.subChannelToProcess - 1)))
								channels[nFile][nChn][nSubChn] = true;
							else
								channels[nFile][nChn][nSubChn] = false;
						}
					} else {
						filenames[nFile][nChn] = null;
						channels[nFile][nChn] = null;
					}
				}
			} else {
				filenames[nFile][0] = ifilenames[nFile];
				channels[nFile][0][0] = true;
				prefix[nFile] = ifilenames[nFile].substring(0, indexOfDot);
				suffix[nFile] = "";
			}
		}
		int nFile1;
		for (nFile = 1; nFile < numFiles; nFile++) {
			for (nChn = 0; (nChn < numChannels) && (filenames[nFile] != null); nChn++)
				if (filenames[nFile][nChn] != null) {
					for (nFile1 = 0; nFile1 < nFile; nFile1++)
						if ((filenames[nFile1] != null) && (filenames[nFile1][nChn] != null)
								&& (directories[nFile1].equals(directories[nFile]))
								&& (filenames[nFile1][nChn].equals(filenames[nFile][nChn]))) {
							filenames[nFile] = null;
							// nf--;
							break;
						}
				}

		}
		File file;
		result = new ImagePlus[numFiles][numChannels][numSubChannels];
		for (nFile = 1; nFile < numFiles; nFile++)
			for (nChn = 0; nChn < numChannels; nChn++)
				for (nSubChn = 0; nSubChn < numSubChannels; nSubChn++)
					result[nFile][nChn][nSubChn] = null;

		ImagePlus imp_composite = null;
		ImageStack stack, stack_d, stack_g;
		ImageStack[][] kernelsNoise = new ImageStack[numChannels][numSubChannels];
		String title, titleFull;
		String kernelPath;
		ImagePlus imp_kernels = null;
		ImagePlus imp_kernels2 = null;
		CompositeImage compositeImage = null;

		for (nChn = 0; nChn < numChannels; nChn++)
			for (nSubChn = 0; nSubChn < numSubChannels; nSubChn++)
				kernelsNoise[nChn][nSubChn] = null;

// == Calculate noise gains of convolution kernels ==
		// processParameters.frames[i][j]
		imageNoiseGains = new ImagePlus[numChannels][numSubChannels];

		if (processParameters.deconvolve && (nonlinParameters.noiseGainPower != 0))
			for (nChn = 0; nChn < numChannels; nChn++)
				for (nSubChn = 0; nSubChn < numSubChannels; nSubChn++)
					if (processParameters.frames[nChn][nSubChn]) {
						// Ask for the kernel directory if it is undefined
						if ((filesParameters.kernelDirectory == null)
								|| (filesParameters.kernelDirectory.length() == 0)) {
							kernelPath = selectKernelsDirectory(filesParameters.kernelDirectory);
							if (kernelPath != null)
								filesParameters.kernelDirectory = kernelPath;
						}
						// Read deconvolution kernels
						kernelPath = filesParameters.kernelDirectory + Prefs.getFileSeparator()
								+ filesParameters.rPSFNames[nChn][nSubChn];
						file = new File(kernelPath);
						if (!file.exists()) {
							System.out.println("Kernel stack file " + kernelPath + " does not exist");
							continue;
						}
						imp_kernels = new ImagePlus(kernelPath);
						if (imp_kernels.getStackSize() < 3) {
							System.out.println("Need a 3-layer stack with kernels");
							continue;
						}
						convolutionKernelStack = imp_kernels.getStack();
						if (DEBUG_LEVEL > 1)
							System.out.println("Using kernel stack " + kernelPath + " for noise gain calculation");
						if (nonlinParameters.useDiffNoiseGains) {
							// Read Gaussian kernels
							kernelPath = filesParameters.kernelDirectory + Prefs.getFileSeparator()
									+ filesParameters.gaussianNames[nChn][nSubChn];
							file = new File(kernelPath);
							if (!file.exists()) {
								System.out.println("Gaussian stack file " + kernelPath + " does not exist");
								continue;
							}
							imp_kernels2 = new ImagePlus(kernelPath);
							if (imp_kernels.getStackSize() < 3) {
								System.out.println("Need a 3-layer stack with gaussian kernels");
								continue;
							}
							convolutionKernelStack2 = imp_kernels2.getStack();
							if (DEBUG_LEVEL > 1)
								System.out.println(
										"Using second (lowpass) stack " + kernelPath + " for noise gain calculation");
						} else
							convolutionKernelStack2 = null;
						kernelsNoise[nChn][nSubChn] = EYESIS_CORRECTIONS.calculateKernelsNoiseGains(
								convolutionKernelStack, // first stack with 3 colors/slices convolution kernels
								convolutionKernelStack2, // second stack with 3 colors/slices convolution kernels (or
															// null)
								convolveFFTSize, // 128 - fft size, kernel size should be size/2
								nonlinParameters.blurSigma, threadsMax, updateStatus, // update status info
								MASTER_DEBUG_LEVEL);
						convolutionKernelStack = null;
						convolutionKernelStack2 = null;
						runtime.gc();
						/*
						 * imageNoiseGains is used , maybe change below to use just the array
						 * kernelsNoise[nChn][nSubChn]
						 */
						title = "noiseGains_" + (nonlinParameters.useDiffNoiseGains ? "diff_" : "") + (nChn + 1) + ""
								+ (nSubChn + 1);
						imageNoiseGains[nChn][nSubChn] = new ImagePlus(title, kernelsNoise[nChn][nSubChn]);
						if (processParameters.saveNoiseGains || processParameters.showNoiseGains) {
//			  title="noiseGains_"+(nonlinParameters.useDiffNoiseGains?"diff_":"")+(nChn+1)+""+(nSubChn+1);
//			  imageNoiseGains[nChn][nSubChn]= new ImagePlus(title, kernelsNoise[nChn][nSubChn]);
							saveAndShow(imageNoiseGains[nChn][nSubChn], filesParameters,
									processParameters.saveNoiseGains, processParameters.showNoiseGains);
//			  if (!processParameters.showNoiseGains) imageNoiseGains[nChn][nSubChn]=null; // it is needed!
						}
					}
		convolutionKernelStack = null;
		convolutionKernelStack2 = null;
		runtime.gc();

//==================================================
		// int i,nFile,nChn,nSubChn;
		for (nFile = 0; nFile < numFiles; nFile++)
			if (filenames[nFile] != null)
				for (nChn = 0; nChn < numChannels; nChn++)
					if (filenames[nFile][nChn] != null) {
						if (DEBUG_LEVEL > 1)
							System.out.println(">> using: " + directories[nFile] + filenames[nFile][nChn]
									+ ", eyesisMode=" + eyesisMode);
						file = new File(directories[nFile] + filenames[nFile][nChn]);
						if (!file.exists())
							continue;
						String thisExtension = filenames[nFile][nChn]
								.substring(filenames[nFile][nChn].toLowerCase().lastIndexOf("."));
						if (thisExtension.equals(".tiff") || thisExtension.equals(".tif")) {
							String fullPath = directories[nFile] + Prefs.getFileSeparator() + filenames[nFile][nChn];
							Opener opener = new Opener();
							imp_composite = opener.openImage("", fullPath);
							if (imp_composite == null) {
								String msg = "Failed to read source file " + fullPath;
								IJ.showMessage("Error", msg);
								throw new IllegalArgumentException(msg);
							}
							JP46_Reader_camera.decodeProperiesFromInfo(imp_composite);
							imp_composite.setProperty("isTiff", true);
						} else {
							imp_composite = JP4_instance.open(directories[nFile], // path,
									filenames[nFile][nChn], "", // arg - not used in JP46 reader
									true, // un-apply camera color gains
									null, // new window
									false); // do not show
							if (imp_composite == null)
								continue;
						}
						for (nSubChn = 0; nSubChn < numSubChannels; nSubChn++)
							if (channels[nFile][nChn][nSubChn])
								if (processParameters.frames[nChn][nSubChn]) {
									DENOISE_MASK = null; // color processing might try to use it, prevent using wrong
															// one
									// Extract sub-frame
									if (eyesisMode) {
										result[nFile][nChn][nSubChn] = JP4_instance.demuxImage(imp_composite, nSubChn);
										if (result[nFile][nChn][nSubChn] == null)
											continue;
										title = prefix[nFile] + "-" + suffix[nFile] + "__" + (nChn + 1) + (nSubChn + 1);
									} else {
										result[nFile][nChn][nSubChn] = imp_composite; // single channel
										title = prefix[nFile] + suffix[nFile];
									}
									if (DEBUG_LEVEL > 1)
										System.out.println("processing: " + title + ", eyesisMode=" + eyesisMode);
									if (result[nFile][nChn][nSubChn] == null)
										continue;
									result[nFile][nChn][nSubChn].setTitle(title + "RAW");
									if (!processParameters.split) {
										saveAndShow(result[nFile][nChn][nSubChn], processParameters, filesParameters);
										if (!saveResult) {
											result[nFile][nChn][nSubChn] = null; // erase results to save memory
											runtime.gc();
											if (DEBUG_LEVEL > 1)
												System.out.println("--- Free memory=" + runtime.freeMemory() + " (of "
														+ runtime.totalMemory() + ")");
										}
										continue;
									}
									// Split into Bayer components, oversample, increase canvas
									stack = bayerToStack(result[nFile][nChn][nSubChn], // source Bayer image,
																						// linearized, 32-bit (float))
											splitParameters);
									titleFull = title + "-SPLIT";
									if (!processParameters.debayer) {
										result[nFile][nChn][nSubChn] = new ImagePlus(titleFull, stack);
										saveAndShow(result[nFile][nChn][nSubChn], processParameters, filesParameters);
										if (!saveResult) {
											result[nFile][nChn][nSubChn] = null; // erase results to save memory
											runtime.gc();
											if (DEBUG_LEVEL > 1)
												System.out.println("--- Free memory=" + runtime.freeMemory() + " (of "
														+ runtime.totalMemory() + ")");
										}
										continue;
									}
									// Demosaic image
									stack = aliasScissorsStack(stack, // stack with 3 colors/slices with the image
											debayerParameters,
											(processParameters.saveDebayerEnergy
													|| processParameters.showDebayerEnergy),
											threadsMax, // number of image pixels/ sensor pixels (each direction) == 2
											updateStatus);// update status info
									if (processParameters.saveDebayerEnergy || processParameters.showDebayerEnergy) {
										if (DEBAYER_ENERGY != null) {
											ImagePlus debayerMask = ShowDoubleFloatArrays.makeArrays(DEBAYER_ENERGY,
													DEBAYER_ENERGY_WIDTH, DEBAYER_ENERGY.length / DEBAYER_ENERGY_WIDTH,
													title + "-DEBAYER-ENERGY");
											saveAndShow(debayerMask, filesParameters,
													processParameters.saveDebayerEnergy,
													processParameters.showDebayerEnergy);
										}
									}
									titleFull = title + "-DEMOSAIC";
									result[nFile][nChn][nSubChn] = new ImagePlus(titleFull, stack);
									if (processParameters.deconvolve) {
										// Ask for the kernel directory if it is undefined
										if ((filesParameters.kernelDirectory == null)
												|| (filesParameters.kernelDirectory.length() == 0)) {
											kernelPath = selectKernelsDirectory(filesParameters.kernelDirectory);
											if (kernelPath != null)
												filesParameters.kernelDirectory = kernelPath;
										}
										// Read deconvolution kernels
										kernelPath = filesParameters.kernelDirectory + Prefs.getFileSeparator()
												+ filesParameters.rPSFNames[nChn][nSubChn];
										file = new File(kernelPath);
										if (!file.exists()) {
											System.out.println("Kernel stack file " + kernelPath + " does not exist");
											if (!saveResult) {
												if (!saveResult) {
													result[nFile][nChn][nSubChn] = null; // erase results to save memory
													runtime.gc();
													if (DEBUG_LEVEL > 1)
														System.out.println("--- Free memory=" + runtime.freeMemory()
																+ " (of " + runtime.totalMemory() + ")");
												}
												continue;
											}
										}
										imp_kernels = new ImagePlus(kernelPath);
										if (imp_kernels.getStackSize() < 3) {
											System.out.println("Need a 3-layer stack with kernels");
											if (!saveResult) {
												result[nFile][nChn][nSubChn] = null; // erase results to save memory
												runtime.gc();
												if (DEBUG_LEVEL > 1)
													System.out.println("--- Free memory=" + runtime.freeMemory()
															+ " (of " + runtime.totalMemory() + ")");
											}
											continue;
										}
										convolutionKernelStack = imp_kernels.getStack();
										if (DEBUG_LEVEL > 1)
											System.out.println(
													"(1)Using kernel stack " + kernelPath + " for convolution with "
															+ result[nFile][nChn][nSubChn].getTitle());
										stack_d = convolveStackWithKernelStack(stack, // stack with 3 colors/slices with
																						// the image
												convolutionKernelStack, // stack with 3 colors/slices convolution
																		// kernels
												convolveFFTSize, // 128 - fft size, kernel size should be size/2
												threadsMax, updateStatus); // update status info
										titleFull = title + "-DECONV";
										if (processParameters.combine) {
											// Read Gaussian kernels
											kernelPath = filesParameters.kernelDirectory + Prefs.getFileSeparator()
													+ filesParameters.gaussianNames[nChn][nSubChn];
											file = new File(kernelPath);
											if (!file.exists()) {
												System.out.println(
														"Gaussian stack file " + kernelPath + " does not exist");
												if (!saveResult) {
													result[nFile][nChn][nSubChn] = null; // erase results to save memory
													runtime.gc();
													if (DEBUG_LEVEL > 1)
														System.out.println("--- Free memory=" + runtime.freeMemory()
																+ " (of " + runtime.totalMemory() + ")");
												}
												continue;
											}
											imp_kernels = new ImagePlus(kernelPath);
											if (imp_kernels.getStackSize() < 3) {
												System.out.println("Need a 3-layer stack with gaussian kernels");
												if (!saveResult)
													result[nFile][nChn][nSubChn] = null; // erase results to save memory
												continue;
											}
											convolutionKernelStack = imp_kernels.getStack();
											if (DEBUG_LEVEL > 1)
												System.out.println("(2)Using Gaussian stack " + kernelPath
														+ " for convolution with "
														+ result[nFile][nChn][nSubChn].getTitle());
											stack_g = convolveStackWithKernelStack(stack, // stack with 3 colors/slices
																							// with the image
													convolutionKernelStack, // stack with 3 colors/slices convolution
																			// kernels
													convolveFFTSize, // 128 - fft size, kernel size should be size/2
													threadsMax, updateStatus); // update status info
											// Combine Gaussian and Deconvolved
											noiseMask = EYESIS_CORRECTIONS.extractNoiseMask(
													imageNoiseGains[nChn][nSubChn], // contains 3-slice stack (r,b,g)
													nonlinParameters.noiseGainWeights[0], // coefficient for slice 0 (r)
													nonlinParameters.noiseGainWeights[1], // coefficient for slice 1 (b)
													nonlinParameters.noiseGainWeights[2], // coefficient for slice 2 (g)
													1, // decimate result (not yet supported)
													nonlinParameters.noiseGainPower);
// show noise mask here?
											nonlinParameters.showMask = processParameters.showDenoiseMask;
//				          if (DEBUG_LEVEL>1) System.out.println ( " noiseMask.length="+((noiseMask==null)?"null":(noiseMask.length+" noiseMask[0].length="+noiseMask[0].length)));

											stack = combineLoHiStacks(stack_d, // ImageStack with the image, convolved
																				// with the reversed PSF (sharp but with
																				// high noise)
													stack_g, // ImageStack with the image, convolved with the Gaussian
																// (just lateral compensated) (blurred, but low noise)
													nChn, nSubChn, nonlinParameters, // show mask generated and used
													noiseMask, // 2-d array of kernelsNoiseGain (divide mask by it)
													32, // linear pixels per noiseMask pixels (32)
													threadsMax, updateStatus); // update status info
											if (processParameters.saveDenoiseMask
													|| processParameters.showDenoiseMask) {
												ImagePlus denoiseMask = ShowDoubleFloatArrays.makeArrays(DENOISE_MASK,
														DENOISE_MASK_WIDTH, DENOISE_MASK.length / DENOISE_MASK_WIDTH,
														title + "-MASK");
												if (processParameters.jpeg) {
// crop Mask to original image size
													if (processParameters.crop) {
														denoiseMask = cropImage32(denoiseMask, splitParameters);
													}
// rotate the result
													if (processParameters.rotate) {
														denoiseMask = rotateImage32CW(denoiseMask);
													}
// scale the result
													if (processParameters.JPEG_scale != 1.0) {
														ImageProcessor ip = denoiseMask.getProcessor();
														ip.setInterpolationMethod(ImageProcessor.BICUBIC);
														ip = ip.resize(
																(int) (ip.getWidth() * processParameters.JPEG_scale),
																(int) (ip.getHeight() * processParameters.JPEG_scale));
														denoiseMask = new ImagePlus(denoiseMask.getTitle(), ip);
														denoiseMask.updateAndDraw();
													}
													if (processParameters.showDenoiseMask)
														denoiseMask.show();
// public ImagePlus Image32toGreyRGB24(ImagePlus imp);
													if (processParameters.saveDenoiseMask) {
														ImagePlus denoiseMaskRGB24 = Image32toGreyRGB24(denoiseMask);
														saveAndShow(denoiseMaskRGB24, filesParameters,
																processParameters.saveDenoiseMask, false, // processParameters.showDenoiseMask,
																processParameters.JPEG_quality);
														denoiseMaskRGB24 = null;
													}
												} else {
													saveAndShow(denoiseMask, filesParameters,
															processParameters.saveDenoiseMask,
															processParameters.showDenoiseMask);
												}
											}
											stack_g = null;
											stack_d = null;
											titleFull = title + "-COMBO";
										} else { // end of if (processParameters.combine)
											stack = stack_d;
										} // end of else if (processParameters.combine)
									} else if (processParameters.combine) { // just create convolution with Gaussians
										// Read Gaussian kernels
										kernelPath = filesParameters.kernelDirectory + Prefs.getFileSeparator()
												+ filesParameters.gaussianNames[nChn][nSubChn];
										file = new File(kernelPath);
										if (!file.exists()) {
											System.out.println("Gaussian stack file " + kernelPath + " does not exist");
											if (!saveResult) {
												result[nFile][nChn][nSubChn] = null; // erase results to save memory
												runtime.gc();
												if (DEBUG_LEVEL > 1)
													System.out.println("--- Free memory=" + runtime.freeMemory()
															+ " (of " + runtime.totalMemory() + ")");
											}
											continue;
										}
										imp_kernels = new ImagePlus(kernelPath);
										if (imp_kernels.getStackSize() < 3) {
											System.out.println("Need a 3-layer stack with gaussian kernels");
											if (!saveResult) {
												result[nFile][nChn][nSubChn] = null; // erase results to save memory
												runtime.gc();
												if (DEBUG_LEVEL > 1)
													System.out.println("--- Free memory=" + runtime.freeMemory()
															+ " (of " + runtime.totalMemory() + ")");
											}
											continue;
										}
										convolutionKernelStack = imp_kernels.getStack();
										if (DEBUG_LEVEL > 1)
											System.out.println(
													"(3)Using Gaussian stack " + kernelPath + " for convolution with "
															+ result[nFile][nChn][nSubChn].getTitle());
										stack_g = convolveStackWithKernelStack(stack, // stack with 3 colors/slices with
																						// the image
												convolutionKernelStack, // stack with 3 colors/slices convolution
																		// kernels
												convolveFFTSize, // 128 - fft size, kernel size should be size/2
												threadsMax, updateStatus); // update status info
										titleFull = title + "-LOWRES";
									} // end of if (processParameters.deconvolve)
										// stack now has the result, titleFull - correct title for the image
									if (!processParameters.colorProc) {
										result[nFile][nChn][nSubChn] = new ImagePlus(titleFull, stack);
										saveAndShow(result[nFile][nChn][nSubChn], processParameters, filesParameters);
										if (!saveResult) {
											result[nFile][nChn][nSubChn] = null; // erase results to save memory
											runtime.gc();
											if (DEBUG_LEVEL > 1)
												System.out.println("--- Free memory=" + runtime.freeMemory() + " (of "
														+ runtime.totalMemory() + ")");
										}
										continue;
									}
									// Processing colors - changing stack sequence to r-g-b (was r-b-g)
									if (!fixSliceSequence(stack)) {
										if (!saveResult) {
											result[nFile][nChn][nSubChn] = null; // erase results to save memory
											runtime.gc();
											if (DEBUG_LEVEL > 1)
												System.out.println("--- Free memory=" + runtime.freeMemory() + " (of "
														+ runtime.totalMemory() + ")");
										}
										continue;
									}
									processColorsWeights(stack,
											255.0 / PSF_SUBPIXEL_SHOULD_BE_4 / PSF_SUBPIXEL_SHOULD_BE_4,
											colorProcParameters, colorCalibParameters, nChn, nSubChn);
									if (DEBUG_LEVEL > 1)
										System.out.println(
												"Processed colors to YPbPr, total number of slices=" + stack.getSize());
// Show/save color denoise mask
									if ((processParameters.saveChromaDenoiseMask
											|| processParameters.showChromaDenoiseMask)
											&& (DENOISE_MASK_CHROMA != null)) {
										ImagePlus chromaDenoiseMask = ShowDoubleFloatArrays.makeArrays(DENOISE_MASK_CHROMA,
												DENOISE_MASK_CHROMA_WIDTH,
												DENOISE_MASK_CHROMA.length / DENOISE_MASK_CHROMA_WIDTH,
												title + "-MASK_CHROMA");
										if (processParameters.jpeg) {
//crop Mask to original image size
											if (processParameters.crop) {
												chromaDenoiseMask = cropImage32(chromaDenoiseMask, splitParameters);
											}
//rotate the result
											if (processParameters.rotate) {
												chromaDenoiseMask = rotateImage32CW(chromaDenoiseMask);
											}
//scale the result
											if (processParameters.JPEG_scale != 1.0) {
												ImageProcessor ip = chromaDenoiseMask.getProcessor();
												ip.setInterpolationMethod(ImageProcessor.BICUBIC);
												ip = ip.resize((int) (ip.getWidth() * processParameters.JPEG_scale),
														(int) (ip.getHeight() * processParameters.JPEG_scale));
												chromaDenoiseMask = new ImagePlus(chromaDenoiseMask.getTitle(), ip);
												chromaDenoiseMask.updateAndDraw();
											}
											if (processParameters.showChromaDenoiseMask)
												chromaDenoiseMask.show();
//public ImagePlus Image32toGreyRGB24(ImagePlus imp);
											if (processParameters.saveChromaDenoiseMask) {
												ImagePlus chromaDenoiseMaskRGB24 = Image32toGreyRGB24(
														chromaDenoiseMask);
												saveAndShow(chromaDenoiseMaskRGB24, filesParameters,
														processParameters.saveChromaDenoiseMask, false, // processParameters.showChromaDenoiseMask,
														processParameters.JPEG_quality);
												chromaDenoiseMaskRGB24 = null;
											}
										} else {
											saveAndShow(chromaDenoiseMask, filesParameters,
													processParameters.saveChromaDenoiseMask,
													processParameters.showChromaDenoiseMask);
										}
									}

									if (processParameters.toRGB) {
										YPrPbToRGB(stack, colorProcParameters.kr, // 0.299;
												colorProcParameters.kb, // 0.114;
												colorProcParameters.useFirstY ? 9 : 8, // int sliceY,
												6, // int slicePr,
												7// int slicePb
										);
										title = titleFull; // including "-DECONV" or "-COMBO"
										titleFull = title + "-RGB-float";
										// Trim stack to just first 3 slices
										while (stack.getSize() > 3)
											stack.deleteLastSlice();
										if (DEBUG_LEVEL > 1)
											System.out.println("Trimming color stack");
									} else {
										title = titleFull; // including "-DECONV" or "-COMBO"
										titleFull = title + "-YPrPb"; // including "-DECONV" or "-COMBO"
										if (DEBUG_LEVEL > 1)
											System.out.println("Using full stack, including YPbPr");
									}
									result[nFile][nChn][nSubChn] = new ImagePlus(titleFull, stack);
									// Crop image to match original one (scaled to oversampling)
									if (processParameters.crop) {
										stack = cropStack32(stack, splitParameters);
									}
									// rotate the result
									if (processParameters.rotate) {
										stack = rotateStack32CW(stack);
									}
									if (!processParameters.toRGB && !processParameters.jpeg) {
										saveAndShow(result[nFile][nChn][nSubChn], processParameters, filesParameters);
										if (!saveResult) {
											result[nFile][nChn][nSubChn] = null; // erase results to save memory
											runtime.gc();
											if (DEBUG_LEVEL > 1)
												System.out.println("--- Free memory=" + runtime.freeMemory() + " (of "
														+ runtime.totalMemory() + ")");
										}
										continue;
									} else { // that's not the end result, save if required
										saveAndShow(result[nFile][nChn][nSubChn], filesParameters,
												processParameters.save32, false, processParameters.JPEG_quality); // save,
																													// no
																													// show
									}
									// convert to RGB48 (16 bits per color component)
									stack = convertRGB32toRGB16Stack(stack, rgbParameters);
									titleFull = title + "-RGB48";
									result[nFile][nChn][nSubChn] = new ImagePlus(titleFull, stack);
									compositeImage = convertToComposite(result[nFile][nChn][nSubChn]);
									if (!processParameters.jpeg) { // RGB48 was the end result
										saveAndShow(compositeImage, processParameters, filesParameters);
										if (!saveResult) {
											result[nFile][nChn][nSubChn] = null; // erase results to save memory
											runtime.gc();
											if (DEBUG_LEVEL > 1)
												System.out.println("--- Free memory=" + runtime.freeMemory() + " (of "
														+ runtime.totalMemory() + ")");
										}
										continue;
									} else { // that's not the end result, save if required
										saveAndShow(compositeImage, filesParameters, processParameters.save16, false); // save,
																														// no
																														// show
									}
									ImagePlus imp_RGB24 = convertRGB48toRGB24(stack, title + "-RGB24", 0, 65536, // r
																													// range
																													// 0->0,
																													// 65536->256
											0, 65536, // g range
											0, 65536, // b range
											0, 65536);// alpha range
									if (processParameters.JPEG_scale != 1.0) {
										ImageProcessor ip = imp_RGB24.getProcessor();
										ip.setInterpolationMethod(ImageProcessor.BICUBIC);
										ip = ip.resize((int) (ip.getWidth() * processParameters.JPEG_scale),
												(int) (ip.getHeight() * processParameters.JPEG_scale));
										imp_RGB24 = new ImagePlus(imp_RGB24.getTitle(), ip);
										imp_RGB24.updateAndDraw();

									}
									saveAndShow(imp_RGB24, processParameters, filesParameters);
									if (!saveResult) {
										result[nFile][nChn][nSubChn] = null; // erase results to save memory
										runtime.gc();
										if (DEBUG_LEVEL > 1)
											System.out.println("---- Free memory=" + runtime.freeMemory() + " (of "
													+ runtime.totalMemory() + ")");
									}
								} // next nSubChn
						if (!saveResult) {
							result[nFile][nChn] = null;
							runtime.gc();
							if (DEBUG_LEVEL > 0)
								System.out.println("At " + IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3)
										+ " seconds  Free memory="
										+ IJ.d2s(runtime.freeMemory() / (1024.0 * 1024.0 * 1024.0), 3) + " GB (of "
										+ IJ.d2s(runtime.totalMemory() / (1024.0 * 1024.0 * 1024.0), 3) + " GB), used "
										+ IJ.d2s((runtime.totalMemory() - runtime.freeMemory())
												/ (1024.0 * 1024.0 * 1024.0), 3)
										+ " GB");
						}

					} // next nChn, next nFile
		System.out
				.println("Processing done in " + IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3) + " seconds");
		return result;
	}

	/* ======================================================================== */
	private void saveAndShow(ImagePlus imp, EyesisCorrectionParameters.ProcessParameters processParameters,
			EyesisCorrectionParameters.FilesParameters filesParameters) {
		saveAndShow(imp, processParameters, filesParameters, true, true);
	}

	private void saveAndShow(ImagePlus imp, EyesisCorrectionParameters.ProcessParameters processParameters,
			EyesisCorrectionParameters.FilesParameters filesParameters, boolean enableSave, boolean enableShow) {
		saveAndShow(imp, filesParameters, processParameters.save && enableSave, processParameters.show && enableShow,
				processParameters.JPEG_quality);
	}

	private void saveAndShow(ImagePlus imp, EyesisCorrectionParameters.FilesParameters filesParameters, boolean save,
			boolean show) {
		saveAndShow(imp, filesParameters, save, show, -1);
	}

	private void saveAndShow(ImagePlus imp, EyesisCorrectionParameters.FilesParameters filesParameters, boolean save,
			boolean show, int jpegQuality) {
		String path;
		if (save) {
			// Ask for the destination directory if it is undefined
			if ((filesParameters.resultsDirectory == null) || (filesParameters.resultsDirectory.length() == 0)) {
				path = selectResultsDirectory(filesParameters.resultsDirectory);
				if (path != null)
					filesParameters.resultsDirectory = path;
			}
			File destDir = new File(filesParameters.resultsDirectory);
			if (!destDir.exists()) {
				if (!destDir.mkdirs()) {
					IJ.showMessage("Error", "Failed to create results directory " + filesParameters.resultsDirectory);
					save = false;
				}
			}
		}
		if (save) {
			path = filesParameters.resultsDirectory + Prefs.getFileSeparator() + imp.getTitle();
			if (((imp.getStackSize() == 1)) && ((imp.getFileInfo().fileType == FileInfo.RGB) || (jpegQuality > 0))) {
				if (DEBUG_LEVEL > 0)
					System.out.println("Saving result to " + path + ".jpeg");
				FileSaver fs = new FileSaver(imp);
				if (jpegQuality > 0)
					FileSaver.setJpegQuality(jpegQuality);
				fs.saveAsJpeg(path + ".jpeg");
			} else {
				if (DEBUG_LEVEL > 0)
					System.out.println("Saving result to " + path + ".tiff");
				FileSaver fs = new FileSaver(imp);
				if (imp.getStackSize() > 1)
					fs.saveAsTiffStack(path + ".tiff");
				else
					fs.saveAsTiff(path + ".tiff");
			}
		}
		if (show) {
			imp.getProcessor().resetMinAndMax(); // probably not needed
			imp.show();
		}
	}

	private void saveAndShow(CompositeImage compositeImage,
			EyesisCorrectionParameters.ProcessParameters processParameters,
			EyesisCorrectionParameters.FilesParameters filesParameters) {
		saveAndShow(compositeImage, processParameters, filesParameters, true, true);
	}

	private void saveAndShow(CompositeImage compositeImage,
			EyesisCorrectionParameters.ProcessParameters processParameters,
			EyesisCorrectionParameters.FilesParameters filesParameters, boolean enableSave, boolean enableShow) {

		saveAndShow(compositeImage, filesParameters, processParameters.save && enableSave,
				processParameters.show && enableShow);
	}

	private void saveAndShow(CompositeImage compositeImage, EyesisCorrectionParameters.FilesParameters filesParameters,
			boolean save, boolean show) {
		String path;
		if (save) {
			// Ask for the destination directory if it is undefined
			if ((filesParameters.resultsDirectory == null) || (filesParameters.resultsDirectory.length() == 0)) {
				path = selectResultsDirectory(filesParameters.resultsDirectory);
				if (path != null)
					filesParameters.resultsDirectory = path;
			}
			File destDir = new File(filesParameters.resultsDirectory);
			if (!destDir.exists()) {
				if (!destDir.mkdirs()) {
					IJ.showMessage("Error", "Failed to create results directory " + filesParameters.resultsDirectory);
					save = false;
				}
			}
		}

		if (save) {
			path = filesParameters.resultsDirectory + Prefs.getFileSeparator() + compositeImage.getTitle();
			if (DEBUG_LEVEL > 0)
				System.out.println("Saving result to " + path + ".tiff");
			FileSaver fs = new FileSaver(compositeImage);
			if (compositeImage.getStackSize() > 1)
				fs.saveAsTiffStack(path + ".tiff");
			else
				fs.saveAsTiff(path + ".tiff");
//		  IJ.saveAs(compositeImage,"tif",path);
		}

		if (show) {
			compositeImage.show();
		}
	}

	/* ======================================================================== */
	/*
	 * Create a Thread[] array as large as the number of processors available. From
	 * Stephan Preibisch's Multithreading.java class. See:
	 * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.
	 * java;hb=HEAD
	 */
	private Thread[] newThreadArray(int maxCPUs) {
		int n_cpus = Runtime.getRuntime().availableProcessors();
		if (n_cpus > maxCPUs)
			n_cpus = maxCPUs;
		return new Thread[n_cpus];
	}

	/*
	 * Start all given threads and wait on each of them until all are done. From
	 * Stephan Preibisch's Multithreading.java class. See:
	 * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.
	 * java;hb=HEAD
	 */
	public static void startAndJoin(Thread[] threads) {
		for (int ithread = 0; ithread < threads.length; ++ithread) {
			threads[ithread].setPriority(Thread.NORM_PRIORITY);
			threads[ithread].start();
		}

		try {
			for (int ithread = 0; ithread < threads.length; ++ithread)
				threads[ithread].join();
		} catch (InterruptedException ie) {
			throw new RuntimeException(ie);
		}
	}

	/* ======================================================================== */
	/* ======================================================================== */
	/* ======================================================================== */
	/* ======================================================================== */
	/* ======================================================================== */
	/* ======================================================================== */
	/* ======================================================================== */
	/* ======================================================================== */
	/* ======================================================================== */
	public double[] initWindowFunction(int size) {
		double[] windowFunction = new double[size * size];
		double[] windowFunction_line = new double[size];
		double a, k;
		int i, j;
		if (GAUSS_WIDTH <= 0) {
			for (i = 0; i < size; i++)
				windowFunction_line[i] = (0.54 - 0.46 * Math.cos((i * 2.0 * Math.PI) / size));
		} else {
			k = 2.0 / (size * GAUSS_WIDTH);
			for (i = 0; i < size; i++) {
				a = (i - size / 2) * k;
				windowFunction_line[i] = Math.exp(-a * a);
			}
		}
		for (i = 0; i < size; i++)
			for (j = 0; j < size; j++) {
				windowFunction[size * i + j] = windowFunction_line[i] * windowFunction_line[j];
			}
		return windowFunction;
	}

	/* ======================================================================== */
	/*
	 * convolve image stack with the kernel stack using FHT. kernels should be
	 * (size/2)*(size/2) - currently 64x64, then image will be split into same
	 * (size/2)*(size/2) overlapping by step=size/4 segments. Both are zero-padded
	 * to size x size, so after convolution the result will not roll over, and
	 * processed 128x128 result arrays are accumulated in the output stack. The
	 * input image should be properly extended by size/4 in each direction (and so
	 * the kernel arrays should match it) - that would minimize border effects.
	 */

	/* ======================================================================== */
	public ImageStack convolveStackWithKernelStack(final ImageStack imageStack, // stack with 3 colors/slices with the
																				// image
			final ImageStack kernelStack, // stack with 3 colors/slices convolution kernels
			final int size, // 128 - fft size, kernel size should be size/2
			final int threadsMax, // maximal number of threads to launch
			final boolean updateStatus) // update status info
	{
		if ((imageStack == null) || (kernelStack == null))
			return null;
		final int imgWidth = imageStack.getWidth();
		final int imgHeight = imageStack.getHeight();
		final int length = imgWidth * imgHeight;
		final int step = size / 4;
		final int kernelSize = size / 2;
		final int tilesX = imgWidth / step - 1; // horizontal number of overlapping tiles in the source image (should be
												// expanded from the registerd one by "step" in each direction)
		final int tilesY = imgHeight / step - 1; // vertical number of overlapping tiles in the source image (should be
													// expanded from the registerd one by "step" in each direction)
		final int kernelWidth = kernelStack.getWidth();
		final int kernelNumHor = kernelWidth / (size / 2);
		final int nChn = imageStack.getSize();
		final float[][] outPixels = new float[nChn][length]; // GLOBAL same as input
		// float [][] outPixels=new float[nChn][length]; // same as input
		int i, j;
		for (i = 0; i < nChn; i++)
			for (j = 0; j < length; j++)
				outPixels[i][j] = 0.0f;
		final double[] slidingWindow = getSlidingMask(kernelSize); // 64x64
		final Thread[] threads = newThreadArray(threadsMax);
		final AtomicInteger ai = new AtomicInteger(0);
		final int numberOfKernels = tilesY * tilesX * nChn;
		final int numberOfKernelsInChn = tilesY * tilesX;
		if (MASTER_DEBUG_LEVEL > 1)
			System.out.println("Eyesis_Correction:convolveStackWithKernelStack :\n" + "MASTER_DEBUG_LEVEL="
					+ MASTER_DEBUG_LEVEL + "\n" + "imgWidth=" + imgWidth + "\n" + "imgHeight=" + imgHeight + "\n"
					+ "tilesX=" + tilesX + "\n" + "tilesY=" + tilesY + "\n" + "step=" + step + "\n" + "kernelSize="
					+ kernelSize + "\n" + "tilesX=" + tilesX + "\n" + "tilesY=" + tilesY + "\n" + "kernelWidth="
					+ kernelWidth + "\n" + "kernelNumHor=" + kernelNumHor + "\n" + "numberOfKernelsInChn="
					+ numberOfKernelsInChn + "\n");

		final long startTime = System.nanoTime();
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				@Override
				public void run() {
					float[] pixels = null; // will be initialized at first use
					float[] kernelPixels = null; // will be initialized at first use
					double[] kernel = new double[kernelSize * kernelSize];
					double[] inTile = new double[kernelSize * kernelSize];
					double[] outTile = new double[size * size];
					double[] doubleKernel = new double[size * size];
					int chn, tileY, tileX;
					int chn0 = -1;
//				  double debug_sum;
//				  int i;
					DoubleFHT fht_instance = new DoubleFHT(); // provide DoubleFHT instance to save on initializations
																// (or null)
					for (int nTile = ai.getAndIncrement(); nTile < numberOfKernels; nTile = ai.getAndIncrement()) {
						chn = nTile / numberOfKernelsInChn;
						tileY = (nTile % numberOfKernelsInChn) / tilesX;
						tileX = nTile % tilesX;
						if (tileX == 0) {
							if (updateStatus)
								IJ.showStatus("Convolving image with kernels, channel " + (chn + 1) + " of " + nChn
										+ ", row " + (tileY + 1) + " of " + tilesY);
							if (MASTER_DEBUG_LEVEL > 2)
								System.out.println("Processing kernels, channel " + (chn + 1) + " of " + nChn + ", row "
										+ (tileY + 1) + " of " + tilesY + " : "
										+ IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3));
						}

						if (chn != chn0) {
							pixels = (float[]) imageStack.getPixels(chn + 1);
							kernelPixels = (float[]) kernelStack.getPixels(chn + 1);
							chn0 = chn;
						}
						/* Read source image tile */
						extractSquareTile(pixels, // source pixel array,
								inTile, // will be filled, should have correct size before call
								slidingWindow, // window (same size as the kernel)
								imgWidth, // width of pixels array
								tileX * step, // left corner X
								tileY * step); // top corner Y
						/* zero pad twice the original size */
						outTile = extendFFTInputTo(inTile, size);
						/* FHT transform of the source image data */
						fht_instance.swapQuadrants(outTile);
						fht_instance.transform(outTile);
						/* read convolution kernel */
						EYESIS_CORRECTIONS.extractOneKernel(kernelPixels, // array of combined square kernels, each
								kernel, // will be filled, should have correct size before call
								kernelNumHor, // number of kernels in a row
								// tileX*kernelSize, // horizontal number of kernel to extract
								// tileY*kernelSize); // vertical number of kernel to extract
								tileX, // horizontal number of kernel to extract
								tileY); // vertical number of kernel to extract
						/* zero pad twice the original size */
						doubleKernel = extendFFTInputTo(kernel, size);
//					  debug_sum=0;
//					  for (i=0;i<doubleKernel.length;i++) debug_sum+=doubleKernel[i];
//					  if (MASTER_DEBUG_LEVEL>1) System.out.println("kernel sum="+debug_sum);

						// if ((tileY==tilesY/2) && (tileX==tilesX/2))
						// ShowDoubleFloatArrays.showArrays(doubleKernel,size,size, "doubleKernel-"+chn);
						/* FHT transform of the kernel */
						fht_instance.swapQuadrants(doubleKernel);
						fht_instance.transform(doubleKernel);
						/* multiply in frequency domain */
						outTile = fht_instance.multiply(outTile, doubleKernel, false);
						/* FHT inverse transform of the product - back to space domain */
						fht_instance.inverseTransform(outTile);
						fht_instance.swapQuadrants(outTile);
						/* accumulate result */
						// if ((tileY==tilesY/2) && (tileX==tilesX/2))
						// ShowDoubleFloatArrays.showArrays(outTile,size,size, "out-"+chn);
						/*
						 * This is synchronized method. It is possible to make threads to write to
						 * non-overlapping regions of the outPixels, but as the accumulation takes just
						 * small fraction of severtal FHTs, it should be OK - reasonable number of
						 * threads will spread and not "stay in line"
						 */
						accumulateSquareTile(outPixels[chn], // float pixels array to accumulate tile
								outTile, // data to accumulate to the pixels array
								imgWidth, // width of pixels array
								(tileX - 1) * step, // left corner X
								(tileY - 1) * step); // top corner Y
					}
				}
			};
		}
		startAndJoin(threads);
		if (DEBUG_LEVEL > 1)
			System.out.println("Threads done at " + IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3));

		/* prepare result stack to return */
		ImageStack outStack = new ImageStack(imgWidth, imgHeight);
		for (i = 0; i < nChn; i++) {
			outStack.addSlice(imageStack.getSliceLabel(i + 1), outPixels[i]);
		}
		return outStack;
	}
	/* ======================================================================== */

	/* ======================================================================== */
	// uses global OUT_PIXELS to accumulate results
	public ImageStack aliasScissorsStack(final ImageStack imageStack, // stack with 3 colors/slices with the image
			final EyesisCorrectionParameters.DebayerParameters debayerParameters, // 64 - fft size
			final boolean generateDebayerEnergy, final int threadsMax, // maximal step in pixels on the maxRadius for 1
																		// angular step (i.e. 0.5)
			final boolean updateStatus) // update status info

	{
		final int wasDebugLevel = DEBUG_LEVEL;
		// this debug of a specific tile will work in a single-threading only, because
		// it uses global DEBUG_LEVEL
		final int xTileDebug, yTileDebug;
		if (!debayerParameters.debug || (debayerParameters.xDebug < 0) || (debayerParameters.xDebug < 0)) {
			xTileDebug = -1;
			yTileDebug = -1;
		} else {
			xTileDebug = (debayerParameters.xDebug >= (debayerParameters.size / 4))
					? ((debayerParameters.xDebug - debayerParameters.size / 4) / (debayerParameters.size / 2))
					: 0;
			yTileDebug = (debayerParameters.yDebug >= (debayerParameters.size / 4))
					? ((debayerParameters.yDebug - debayerParameters.size / 4) / (debayerParameters.size / 2))
					: 0;
		}

		if (imageStack == null)
			return null;
		final int imgWidth = imageStack.getWidth();
		final int imgHeight = imageStack.getHeight();
		final int length = imgWidth * imgHeight;
		final int step = debayerParameters.size / 2;
		final int tilesX = imgWidth / step - 1; // horizontal number of overlapping tiles in the source image (should be
												// expanded from the registerd one by "step" in each direction)
		final int tilesY = imgHeight / step - 1; // vertical number of overlapping tiles in the source image (should be
													// expanded from the registerd one by "step" in each direction)
		final int nChn = imageStack.getSize();
		int i, chn; // tileX,tileY;
		/*
		 * find number of the green channel - should be called "green", if none - use
		 * last
		 */
		i = nChn - 1;
		for (chn = 0; chn < nChn; chn++)
			if (imageStack.getSliceLabel(chn + 1).equals("green")) {
				i = chn;
				break;
			}
		final int greenChn = i;
		final float[][] outPixles = new float[nChn][length]; // same as input
		DEBAYER_ENERGY = null;
		if (generateDebayerEnergy) {
			DEBAYER_ENERGY = new double[tilesY * tilesX];
		}

		for (chn = 0; chn < nChn; chn++)
			for (i = 0; i < length; i++)
				outPixles[chn][i] = 0.0f;
		final double[] slidingWindow = getSlidingMask(debayerParameters.size); // 64x64

//	  outPixles=new float[nChn][length]; // GLOBAL same as input
		final Thread[] threads = newThreadArray(threadsMax);
		final AtomicInteger ai = new AtomicInteger(0);
		final int numberOfKernels = tilesY * tilesX;
		final long startTime = System.nanoTime();
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				@Override
				public void run() {
					double[][] tile = new double[nChn][debayerParameters.size * debayerParameters.size];
					double[][] both_masks;
					float[][] pixels = new float[nChn][];
					int chn, tileY, tileX, i;
					for (chn = 0; chn < nChn; chn++)
						pixels[chn] = (float[]) imageStack.getPixels(chn + 1);
					DoubleFHT fht_instance = new DoubleFHT(); // provide DoubleFHT instance to save on initializations
																// (or null)

					DeBayerScissors debayer_instance = new DeBayerScissors(debayerParameters.size, // size of the square
																									// array, centar is
																									// at size/2,
																									// size/2, only top
																									// half+line will be
																									// used
							debayerParameters.polarStep, // maximal step in pixels on the maxRadius for 1 angular step
															// (i.e. 0.5)
							debayerParameters.debayerRelativeWidthGreen, // result green mask mpy by scaled default
																			// (diamond)
							debayerParameters.debayerRelativeWidthRedblue, // result red/blue mask mpy by scaled default
																			// (square)
							debayerParameters.debayerRelativeWidthRedblueMain, // green mask when applied to red/blue,
																				// main (center)
							debayerParameters.debayerRelativeWidthRedblueClones);// green mask when applied to red/blue,
																					// clones

					for (int nTile = ai.getAndIncrement(); nTile < numberOfKernels; nTile = ai.getAndIncrement()) {
						tileY = nTile / tilesX;
						tileX = nTile % tilesX;
						if (tileX == 0) {
							if (updateStatus)
								IJ.showStatus("(2)Reducing sampling aliases, row " + (tileY + 1) + " of " + tilesY);
							if (MASTER_DEBUG_LEVEL > 1)
								System.out.println("(2)Reducing sampling aliases, row " + (tileY + 1) + " of " + tilesY
										+ " : " + IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3));
						}

						if ((tileY == yTileDebug) && (tileX == xTileDebug))
							DEBUG_LEVEL = 4;
						else
							DEBUG_LEVEL = wasDebugLevel;
						for (chn = 0; chn < nChn; chn++) {
							extractSquareTile(pixels[chn], // source pixel array,
									tile[chn], // will be filled, should have correct size before call
									slidingWindow, // window (same size as the kernel)
									imgWidth, // width of pixels array
									tileX * step, // left corner X
									tileY * step); // top corner Y
						}

						/*
						 * Scale green channel x0.5 as there are twice more pixels there as in red or
						 * blue. Or move it somewhere else and multiply to original range ?
						 */
						for (i = 0; i < tile[greenChn].length; i++)
							tile[greenChn][i] *= 0.5;
						if ((tileY == yTileDebug) && (tileX == xTileDebug)) {
							ShowDoubleFloatArrays.showArrays(tile.clone(), debayerParameters.size, debayerParameters.size,
									"x" + (tileX * step) + "_y" + (tileY * step));
						}
						for (chn = 0; chn < nChn; chn++) {
							fht_instance.swapQuadrants(tile[chn]);
							fht_instance.transform(tile[chn]);
						}
						if ((tileY == yTileDebug) && (tileX == xTileDebug) )
							ShowDoubleFloatArrays.showArrays(tile.clone(), debayerParameters.size, debayerParameters.size,
									"tile-fht");
						both_masks = debayer_instance.aliasScissors(tile[greenChn], // fht array for green, will be
																					// masked in-place
								debayerParameters.debayerThreshold, // no high frequencies - use default uniform filter
								debayerParameters.debayerGamma, // power function applied to the amplitudes before
																// generating spectral masks
								debayerParameters.debayerBonus, // scale far pixels as (1.0+bonus*r/rmax)
								debayerParameters.mainToAlias, // relative main/alias amplitudes to enable lixels (i.e.
																// 0.5 means that if alias is >0.5*main, the pixel will
																// be masked out)
								debayerParameters.debayerMaskBlur, // for both masks sigma for gaussian blur of the
																	// binary masks (<0 -do not use "scissors")
								debayerParameters.debayerUseScissors, // use "scissors", if false - just apply "diamond"
																		// ands "square" with
																		// DEBAYER_PARAMETERS.debayerRelativeWidthGreen
																		// and
																		// DEBAYER_PARAMETERS.debayerRelativeWidthRedblue
								((tileY == yTileDebug) && (tileX == xTileDebug)) ? 4 : 1);
						// 1); // internal debug level ((DEBUG_LEVEL>2) && (yTile==yTile0) &&
						// (xTile==xTile0))?3:1;
						if ((tileY == yTileDebug) && (tileX == xTileDebug)) {
							ShowDoubleFloatArrays.showArrays(tile.clone(), debayerParameters.size, debayerParameters.size,
									"A00");
							ShowDoubleFloatArrays.showArrays(both_masks.clone(), debayerParameters.size, debayerParameters.size,
									"masks");
						}
						if (DEBAYER_ENERGY != null) {
							DEBAYER_ENERGY[tileY * tilesX + tileX] = debayer_instance.getMidEnergy();
						}
						for (chn = 0; chn < nChn; chn++) {
							tile[chn] = fht_instance.multiply(tile[chn], both_masks[(chn == greenChn) ? 0 : 1], false);
							fht_instance.inverseTransform(tile[chn]);
							fht_instance.swapQuadrants(tile[chn]);
							/* accumulate result */
							/*
							 * This is synchronized method. It is possible to make threads to write to
							 * non-overlapping regions of the outPixles, but as the accumulation takes just
							 * small fraction of severtal FHTs, it should be OK - reasonable number of
							 * threads will spread and not "stay in line"
							 */

							accumulateSquareTile(outPixles[chn], // float pixels array to accumulate tile
									tile[chn], // data to accumulate to the pixels array
									imgWidth, // width of pixels array
									tileX * step, // left corner X
									tileY * step); // top corner Y
						}
						if ((tileY == yTileDebug) && (tileX == xTileDebug))
							ShowDoubleFloatArrays.showArrays(tile.clone(), debayerParameters.size, debayerParameters.size,
									"B00");

					}
				}
			};
		}
		startAndJoin(threads);
		DEBUG_LEVEL = wasDebugLevel;
		/* prepare result stack to return */
		ImageStack outStack = new ImageStack(imgWidth, imgHeight);
		for (chn = 0; chn < nChn; chn++) {
			outStack.addSlice(imageStack.getSliceLabel(chn + 1), outPixles[chn]);
		}
		DEBAYER_ENERGY_WIDTH = (DEBAYER_ENERGY != null) ? tilesX : 0; // for the image to be displayed externally
		return outStack;
	}

	/* ======================================================================== */
	/*
	 * Filters mask that selects between hi-res/high-noise deconvolved image and
	 * lo-res/lo-noise image convolved with Gaussian by rejecting frequencies that
	 * correspond to multiples of JPEG blocks (here with the current settings it is
	 * 32 pixels - twice 16x16 macroblock
	 */
	// uses global MASK_LOHIRES to accumulate results
	public double[] createFilterForBlockArtifacts(final int size, // size of square FHT
			final int rejectPeriod, // period (pixels) of the block artifacts to reject (32)
			final double rejectSigma, // sigma of the rejection spots ( 0.0 - just zero a single point)
			final double lopassSigma) // sigma of the low pass filter (frequency units, 0.0 - do not filter)
	{
		double[] maskFHT = new double[size * size];
		int freqPeriod = size / rejectPeriod;
		int i, j;
		for (i = 0; i < size * size; i++)
			maskFHT[i] = 1.0; // Initialize mask
		double[] rejGauss;
		double k;
		int pointX, pointY, x, y;
		if (rejectSigma >= 0.0) {
			int rejSize = (int) (rejectSigma * 4) + 1;
			if (rejSize > (freqPeriod / 2))
				rejSize = (freqPeriod / 2);
			rejGauss = new double[rejSize];
			rejGauss[0] = 1.0;
			k = 1.0 / (2 * rejectSigma * rejectSigma);
			for (i = 1; i < rejSize; i++) {
				rejGauss[i] = Math.exp(-(k * i * i));
			}
			for (pointY = 0; pointY < size; pointY += freqPeriod)
				for (pointX = 0; pointX < size; pointX += freqPeriod)
					if ((pointY > 0) || (pointX > 0)) {
						for (y = -rejSize + 1; y < rejSize; y++)
							for (x = -rejSize + 1; x < rejSize; x++) {
								i = (pointY + y + size) % size;
								j = (pointX + x + size) % size;
								maskFHT[i * size + j] = 1.0 - rejGauss[Math.abs(y)] * rejGauss[Math.abs(x)];

							}
					}
		}

		if (lopassSigma > 0) {
			double[] maskTmp = maskFHT;
			maskFHT = new double[size * size];
			for (i = 0; i < size * size; i++)
				maskFHT[i] = 0.0; // Initialize mask

			int lopassSize = (int) (lopassSigma * 4) + 1;
			if (lopassSize > (size / 2))
				lopassSize = (size / 2);
			rejGauss = new double[lopassSize];
			rejGauss[0] = 1.0;
			k = 1.0 / (2 * lopassSigma * lopassSigma);
			for (i = 1; i < lopassSize; i++) {
				rejGauss[i] = Math.exp(-(k * i * i));
			}
			for (y = -lopassSize + 1; y < lopassSize; y++)
				for (x = -lopassSize + 1; x < lopassSize; x++) {
					i = (y + size) % size;
					j = (x + size) % size;
					maskFHT[i * size + j] = maskTmp[i * size + j] * rejGauss[Math.abs(y)] * rejGauss[Math.abs(x)];
				}
		}
		return maskFHT;
	}

	private int extendDimension(int dimension, int step) {
		return step * ((int) Math.ceil(dimension / step) + 2);
	}

	public double[] extendDoubleArrayForSlidingWindow(double[] ipixels, // input pixel array
			int imgWidth, // width of the image
			int step) { // size of sliding step (half of the sliding window size)

		int imgHeight = ipixels.length / imgWidth; // width of the image
		int width = extendDimension(imgWidth, step);
		int height = extendDimension(imgHeight, step);
		double[] pixels = new double[width * height];
		int i, j, k;
		int index = 0;
		for (i = 0; i < height; i++) {
			if ((i < step) || (i >= (imgHeight + step))) {
				k = (i + 1) * width;
				for (j = i * width; j < k; j++)
					pixels[j] = 0.0;
			} else {
				k = i * width + step;
				for (j = i * width; j < k; j++)
					pixels[j] = 0.0;
				k = (i + 1) * width;
				for (j = i * width + step + imgWidth; j < k; j++)
					pixels[j] = 0.0;
				k = i * width + step + imgWidth;
				for (j = i * width + step; j < k; j++)
					pixels[j] = ipixels[index++];

			}
		}
		return pixels;
	}

	public double[] reducedDoubleArrayAfterSlidingWindow(double[] ipixels, // input pixel array
			int imgWidth, // width of the image
			int imgHeight, int step) { // size of sliding step (half of the sliding window size)

		int width = extendDimension(imgWidth, step);
		double[] pixels = new double[imgWidth * imgHeight];
		int i, j, base;
		int index = 0;
		for (i = 0; i < imgHeight; i++) {
			base = width * (i + step) + step;
			for (j = 0; j < imgWidth; j++)
				pixels[index++] = ipixels[base++];
		}
		return pixels;
	}

	public double[] filterMaskFromBlockArtifacts(
//  public float [] filterMaskFromBlockArtifacts(
			final double[] pixels, // input pixel array
//		  final float []     pixels, // input pixel array
			final int imgWidth, // width of the image
			final int imgHeight, // width of the image
			final int size, // size of sliding FHT
			final double[] filter, // filter to multiply FHT (created once for the whole filter mask)
			final int threadsMax, // maximal step in pixels on the maxRadius for 1 angular step (i.e. 0.5)
			final boolean updateStatus) // update status info
	{

		if (MASTER_DEBUG_LEVEL > 1)
			System.out.println("filterMaskFromBlockArtifacts, imgWidth=" + imgWidth);
		if (MASTER_DEBUG_LEVEL > 1)
			System.out.println("filterMaskFromBlockArtifacts, imgHeight=" + imgHeight);

		if (pixels == null)
			return null;
		final int length = imgWidth * imgHeight;
		final int step = size / 2;
		final int tilesX = imgWidth / step - 1; // horizontal number of overlapping tiles in the source image (should be
												// expanded from the registered one by "step" in each direction)
		final int tilesY = imgHeight / step - 1; // vertical number of overlapping tiles in the source image (should be
													// expanded from the registered one by "step" in each direction)
		if (MASTER_DEBUG_LEVEL > 1)
			System.out.println("filterMaskFromBlockArtifacts, tilesX=" + tilesX);
		if (MASTER_DEBUG_LEVEL > 1)
			System.out.println("filterMaskFromBlockArtifacts, tilesY=" + tilesY);

		int i; // tileX,tileY;

//	  for (i=0;i<length;i++) MASK_LOHIRES[i]=0.0;
//	  MASK_LOHIRES=new float[length];
		MASK_LOHIRES = new double[length];
		for (i = 0; i < length; i++)
			MASK_LOHIRES[i] = 0.0;
		final double[] slidingWindow = getSlidingMask(size); // 256x256?
		final Thread[] threads = newThreadArray(threadsMax);
		final AtomicInteger ai = new AtomicInteger(0);
		final int numberOfKernels = tilesY * tilesX;
		final long startTime = System.nanoTime();
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				@Override
				public void run() {
					double[] tile = new double[size * size];
					int tileY, tileX;
					DoubleFHT fht_instance = new DoubleFHT(); // provide DoubleFHT instance to save on initializations
																// (or null)
					for (int nTile = ai.getAndIncrement(); nTile < numberOfKernels; nTile = ai.getAndIncrement()) {
						tileY = nTile / tilesX;
						tileX = nTile % tilesX;
						if (tileX == 0) {
							if (updateStatus)
								IJ.showStatus("Filtering noise rejection mask, row " + (tileY + 1) + " of " + tilesY);
							if (MASTER_DEBUG_LEVEL > 1)
								System.out.println("Filtering noise rejection mask, row " + (tileY + 1) + " of "
										+ tilesY + " : " + IJ.d2s(0.000000001 * (System.nanoTime() - startTime), 3));
						}
						extractSquareTile(pixels, // source pixel array,
								tile, // will be filled, should have correct size before call
								slidingWindow, // window (same size as the kernel)
								imgWidth, // width of pixels array
								tileX * step, // left corner X
								tileY * step); // top corner Y
						fht_instance.swapQuadrants(tile);
						fht_instance.transform(tile);
						tile = fht_instance.multiply(tile, filter, false);
						fht_instance.inverseTransform(tile);
						fht_instance.swapQuadrants(tile);
						/* accumulate result */
						/*
						 * This is synchronized method. It is possible to make threads to write to
						 * non-overlapping regions of the OUT_PIXELS, but as the accumulation takes just
						 * small fraction of several FHTs, it should be OK - reasonable number of
						 * threads will spread and not "stay in line"
						 */
						accumulateSquareTile(MASK_LOHIRES, // float pixels array to accumulate tile
								tile, // data to accumulate to the pixels array
								imgWidth, // width of pixels array
								tileX * step, // left corner X
								tileY * step); // top corner Y

					}
				}
			};
		}
		startAndJoin(threads);
		return MASK_LOHIRES;
	}

	/* ======================================================================== */
	public double[] ringFilter(double[] dmask, // mask to be filtered
			int width, // mask width
			double minMaxValue, // min value for the local maximum to be processed (absolute, not relative)
			double overRingThreshold, // Ratio of the local maximum to maximal value in a ring to trigger filter
			double overRingLimit, // limit for the pixels in the center ring relative to the maximum in a ring
			double ringIR, // ring inner radius
			double ringOR) { // ring outer radius
		if (dmask == null)
			return null;
		int margins = (int) Math.ceil(ringIR);
		int height = dmask.length / width;
		double[] result = dmask.clone();
		int i, j, nr, nc, y, x, n;
		double ringIR2 = ringIR * ringIR;
		double ringOR2 = ringOR * ringOR;
		double r2;
		nc = 0;
		nr = 0;
		for (i = -margins + 1; i < margins; i++)
			for (j = -margins + 1; j < margins; j++) {
				r2 = i * i + j * j;
				if (r2 < ringIR2)
					nc++;
				else if (r2 <= ringOR2)
					nr++;
			}
		if ((nc == 0) || (nr == 0))
			return result; // do not filter;
		int[] indxc = new int[nc];
		int[] indxr = new int[nr];
		nc = 0;
		nr = 0;
		for (i = -margins + 1; i < margins; i++)
			for (j = -margins + 1; j < margins; j++) {
				r2 = i * i + j * j;
				if (r2 < ringIR2) {
					indxc[nc++] = j + width * i;
				} else if (r2 <= ringOR2) {
					indxr[nr++] = j + width * i;
				}
			}
		int[] neighb = { -width, -width + 1, 1, width + 1, width, width - 1, -1, -width - 1 };
		int index;
		boolean isMax;
		double d, maxInRing;
		for (y = margins; y < height - margins; y++)
			for (x = margins; x < width - margins; x++) {
				index = y * width + x;
				d = dmask[index];
				if (d < minMaxValue)
					continue; // too small value - don't bother to filter
				isMax = true;
				for (n = 0; n < neighb.length; n++)
					if (dmask[index + neighb[n]] > d) {
						isMax = false;
						break;
					}
				if (!isMax)
					continue; // only process local maximums
				maxInRing = dmask[index + indxr[0]];
				for (n = 1; n < nr; n++)
					if (dmask[index + indxr[n]] > maxInRing)
						maxInRing = dmask[index + indxr[n]];
				if (d < (maxInRing * overRingThreshold))
					continue; // under threshold, nop
// limit values in the circle
				maxInRing *= overRingLimit;
				for (n = 0; n < nc; n++)
					if (dmask[index + indxc[n]] > maxInRing)
						result[index + indxc[n]] = maxInRing;
			}
		return result;
	}

	/* ======================================================================== */
	/*
	 * Combine two 3-slice image stacks generated from the same source image - one
	 * high-res/high noise, other low-res/low noise
	 * 
	 * @param nonlinParameters TODO
	 */
	public ImageStack combineLoHiStacks(ImageStack stack_convolved, // ImageStack with the image, convolved with the
																	// reversed PSF (sharp but with high noise)
			ImageStack stack_gaussian, // ImageStack with the image, convolved with the Gaussian (just lateral
										// compensated) (blurred, but low noise)
			int nChn, // number of channel to apply to the min/max. If <0 - do not apply
			int nSubChn, // number of sub channel to apply to the min/max. If <0 - do not apply
			EyesisCorrectionParameters.NonlinParameters nonlinParameters, // show mask generated and used
			final double[][] noiseMask, // 2-d array of kernelsNoiseGain (divide mask by it)
			final int noiseStep, // linear pixels per noiseMask pixels (32)
			final int threadsMax, // maximal step in pixels on the maxRadius for 1 angular step (i.e. 0.5)
			final boolean updateStatus) { // update status info

		int i, j, k;
		int imgWidth = stack_convolved.getWidth();
		int imgHeight = stack_convolved.getHeight();
		double[] diffGreens = new double[imgWidth * imgHeight];
		double[] diffGreens1;
		double filtMin = nonlinParameters.filtMin;
		double filtMax = nonlinParameters.filtMax;
		if ((nChn >= 0) && (nSubChn >= 0)) {
			filtMin *= nonlinParameters.thresholdCorrection[nChn][nSubChn];
			filtMax *= nonlinParameters.thresholdCorrection[nChn][nSubChn];
		}
		/*
		 * find number of the green channel - should be called "green", if none - use
		 * last
		 */
		int greenChn = 2;
		for (i = 0; i < 3; i++)
			if (stack_convolved.getSliceLabel(i + 1).equals("green")) {
				greenChn = i;
				break;
			}
		double d;
		double max = 0.0f;
//      double average=0.0f;
		DoubleGaussianBlur gb = new DoubleGaussianBlur();

		float[] hipassPixels = (float[]) stack_convolved.getPixels(greenChn + 1);
		float[] lopassPixels = (float[]) stack_gaussian.getPixels(greenChn + 1);
		/*
		 * for (i=0;i<lopassPixels.length;i++) {
		 * 
		 * d=hipassPixels[i]-lopassPixels[i]; diffGreens[i]=d*d; if
		 * (max<lopassPixels[i]) max=lopassPixels[i]; }
		 */
		for (i = 0; i < lopassPixels.length; i++) {

//          d=hipassPixels[i]-lopassPixels[i];
//          diffGreens[i]=d*d;
			diffGreens[i] = hipassPixels[i] - lopassPixels[i];
		}
		if ((DEBUG_LEVEL > 3) && nonlinParameters.showMask)
			ShowDoubleFloatArrays.showArrays(diffGreens.clone(), imgWidth, imgHeight, "diffGreens-nofilter");
		if (nonlinParameters.blurSigma > 0) {
			if (DEBUG_LEVEL > 1)
				System.out.println(
						"Applying gaussian blur to difference hi/lo pass, blurSigma=" + nonlinParameters.blurSigma);
			gb.blurDouble(diffGreens, imgWidth, imgHeight, nonlinParameters.blurSigma, nonlinParameters.blurSigma,
					0.01);
		}
		if ((DEBUG_LEVEL > 3) && nonlinParameters.showMask)
			ShowDoubleFloatArrays.showArrays(diffGreens.clone(), imgWidth, imgHeight, "diffGreens-blurred");
		for (i = 0; i < lopassPixels.length; i++) {
			diffGreens[i] = diffGreens[i] * diffGreens[i];
		}
		if ((DEBUG_LEVEL > 2) && nonlinParameters.showMask)
			ShowDoubleFloatArrays.showArrays(lopassPixels.clone(), imgWidth, imgHeight, "lopassPixels");

		for (i = 0; i < lopassPixels.length; i++) {
			if (max < lopassPixels[i])
				max = lopassPixels[i];
		}
		if (DEBUG_LEVEL > 1)
			System.out.println("max(lopassPixels)=" + max);
//      max*=((float) NONLIN_PARAMETERS.threshold);
// Make threshold absolute - when (blured) intensity is below thershold, the divisor is not decreasing
		max = ((float) NONLIN_PARAMETERS.threshold);
		if ((DEBUG_LEVEL > 3) && nonlinParameters.showMask)
			ShowDoubleFloatArrays.showArrays(diffGreens.clone(), imgWidth, imgHeight, "diffGreens-squared");
		for (i = 0; i < lopassPixels.length; i++) {
			diffGreens[i] /= (float) Math.max(max, lopassPixels[i]);
		}
		if ((DEBUG_LEVEL > 1) && nonlinParameters.showMask)
			ShowDoubleFloatArrays.showArrays(diffGreens.clone(), imgWidth, imgHeight, "diffG-norm-limited");
		if (nonlinParameters.useRejectBlocksFilter) { // use frequency domain filtering

			double lowpassSigmaFreq = 1.0 * nonlinParameters.maskFFTSize
					/ (2 * Math.PI * nonlinParameters.lowPassSigma); // low pass sigma in frequency domain
			double[] filterFHT = createFilterForBlockArtifacts(nonlinParameters.maskFFTSize, // size of square FHT
					nonlinParameters.blockPeriod, // period (pixels) of the block artifacts to reject (32)
					nonlinParameters.rejectFreqSigma, // sigma of the rejection spots ( 0.0 - just zero a single point)
					lowpassSigmaFreq); // sigma of the low pass filter (frequency units, 0.0 - do not filter)
			if ((DEBUG_LEVEL > 3) && nonlinParameters.showMask)
				ShowDoubleFloatArrays.showArrays(filterFHT, "filterFHT");
// Extend at least by half sliding window in each direction to reduce border effect
			diffGreens1 = extendDoubleArrayForSlidingWindow(diffGreens, // input pixel array
					imgWidth, // width of the image
					nonlinParameters.maskFFTSize / 2); // size of sliding step (half of the sliding window size)
			int extendedWidth = extendDimension(imgWidth, (nonlinParameters.maskFFTSize / 2));
			int extendedHeight = extendDimension(imgHeight, (nonlinParameters.maskFFTSize / 2));

			if ((DEBUG_LEVEL > 3) && nonlinParameters.showMask)
				ShowDoubleFloatArrays.showArrays(diffGreens1.clone(), extendedWidth, extendedHeight, "diffGreens-extended");

// run block rejection filter
			diffGreens1 = filterMaskFromBlockArtifacts(diffGreens1, // input pixel array
					extendedWidth, // width of the image
					extendedHeight, // width of the image
					nonlinParameters.maskFFTSize, // size of sliding FHT
					filterFHT, // filter to multiply FHT (created once for the whole filter mask)
					threadsMax, // maximal step in pixels on the maxRadius for 1 angular step (i.e. 0.5)
					updateStatus); // update status info
			if ((DEBUG_LEVEL > 3) && nonlinParameters.showMask)
				ShowDoubleFloatArrays.showArrays(diffGreens1.clone(), extendedWidth, extendedHeight,
						"diffGreens-filtered-extended");/**/
// cut extra margins, crop to original size
			diffGreens1 = reducedDoubleArrayAfterSlidingWindow(diffGreens1, // input pixel array
					imgWidth, // width of the image
					imgHeight, nonlinParameters.maskFFTSize / 2); // size of sliding step (half of the sliding window
																	// size)
			if ((DEBUG_LEVEL > 2) && nonlinParameters.showMask)
				ShowDoubleFloatArrays.showArrays(diffGreens1.clone(), imgWidth, imgHeight, "diffGreens-filtered");
			if (nonlinParameters.combineBothModes) {
//      		DoubleGaussianBlur gb=new DoubleGaussianBlur();
				gb.blurDouble(diffGreens, imgWidth, imgHeight, nonlinParameters.lowPassSigma,
						nonlinParameters.lowPassSigma, 0.01);
				for (i = 0; i < diffGreens.length; i++) {
					d = diffGreens[i] * diffGreens1[i];
					diffGreens[i] = (d > 0) ? Math.sqrt(diffGreens[i] * diffGreens1[i]) : 0.0;
				}
			} else {
				diffGreens = diffGreens1;
			}
		} else { // just apply low-pass filter to the mask
//    		DoubleGaussianBlur gb=new DoubleGaussianBlur();
			gb.blurDouble(diffGreens, imgWidth, imgHeight, nonlinParameters.lowPassSigma, nonlinParameters.lowPassSigma,
					0.01);
		}
		if ((DEBUG_LEVEL > 1) && nonlinParameters.showMask)
			ShowDoubleFloatArrays.showArrays(diffGreens.clone(), imgWidth, imgHeight, "diffGreens-filtered");

//      final double [][]       noiseMask, // 2-d array of kernelsNoiseGain (divide mask by it)
//      final int               noiseStep, // linear pixels per noiseMask pixels (32)
		/* divide mask by noiseMask, if defined */
		if (noiseMask != null) {
			if (DEBUG_LEVEL > 1)
				System.out.println("diffGreens.length=" + diffGreens.length + " imgWidth=" + imgWidth
						+ " noiseMask.length=" + noiseMask.length + " noiseMask[0].length=" + noiseMask[0].length);

			for (i = 0; i < diffGreens.length; i++) {
				j = (i / imgWidth) / noiseStep;
				k = (i % imgWidth) / noiseStep;
				if (j >= noiseMask.length)
					j = noiseMask.length - 1;
				if (k >= noiseMask[j].length)
					k = noiseMask[j].length - 1;
				diffGreens[i] /= noiseMask[j][k];
			}
		}
		if ((DEBUG_LEVEL > 1) && nonlinParameters.showMask)
			ShowDoubleFloatArrays.showArrays(diffGreens.clone(), imgWidth, imgHeight, "diffGreens-noise");
		if (nonlinParameters.useRingFilter) {
			diffGreens = ringFilter(diffGreens, // mask to be filtered
					imgWidth, // mask width
					nonlinParameters.minMaxValue * nonlinParameters.filtMax, // min value for the local maximum to be
																				// processed (absolute, not relative)
					nonlinParameters.overRingThreshold, // Ratio of the local maximum to maximal value in a ring to
														// trigger filter
					nonlinParameters.overRingLimit, // limit for the pixels in the center ring relative to the maximum
													// in a ring
					nonlinParameters.ringIR, // ring inner radius
					nonlinParameters.ringOR); // ring outer radius
			if ((DEBUG_LEVEL > 1) && nonlinParameters.showMask)
				ShowDoubleFloatArrays.showArrays(diffGreens.clone(), imgWidth, imgHeight, "diffGreens-ring");
		}
		if (DEBUG_LEVEL > 1)
			System.out.println("filtMax=" + filtMax + " filtMin=" + filtMin);
		d = (float) (1.0 / (filtMax - filtMin));
		if (filtMax > filtMin) {
			for (i = 0; i < diffGreens.length; i++) {
				if (diffGreens[i] < filtMin)
					diffGreens[i] = 0.0f;
				else if (diffGreens[i] > filtMax)
					diffGreens[i] = 1.0f;
				else
					diffGreens[i] = d * (diffGreens[i] - (float) filtMin);
			}
		}
//      if (nonlinParameters.showMask) {
//    	  ShowDoubleFloatArrays.showArrays(diffGreens, imgWidth, imgHeight,"mask");
//      }
		DENOISE_MASK = diffGreens;
		DENOISE_MASK_WIDTH = imgWidth;

		/* Combine 2 stacks and a mask */
		return combineStacksWithMask(stack_gaussian, stack_convolved, diffGreens);
	}

	/* ======================================================================== */

	/* Combine 2 stacks and a mask */
	public ImageStack combineStacksWithMask(ImageStack stack_bg, ImageStack stack_fg,
			// float [] mask ) {
			double[] mask) {

		ImageStack stack = new ImageStack(stack_bg.getWidth(), stack_bg.getHeight());
		int slice, i;
		float[] fpixels;
		float[] fpixels_bg;
		float[] fpixels_fg;
		for (slice = 1; slice <= stack_bg.getSize(); slice++) {
			fpixels_bg = (float[]) stack_bg.getPixels(slice);
			fpixels_fg = (float[]) stack_fg.getPixels(slice);
			fpixels = new float[fpixels_bg.length];
			for (i = 0; i < fpixels_bg.length; i++)
				fpixels[i] = (float) (mask[i] * fpixels_fg[i] + (1.0f - mask[i]) * fpixels_bg[i]);
			stack.addSlice(stack_fg.getSliceLabel(slice), fpixels);
		}
		return stack;
	}

	/* ======================================================================== */
	/*
	 * Convert source Bayer pattern (GR/BG) image to higher resolution, add margins
	 * by duplicating pattern around
	 */
	public ImageStack bayerToStack(ImagePlus imp, // source bayer image, linearized, 32-bit (float))
			EyesisCorrectionParameters.SplitParameters splitParameters) {

		if (imp == null)
			return null;
//    String [] chnNames={"red","blue","green"};
		String[] chnNames = { "Red", "Blue", "Green" }; // Different sequence than RGB!!
		int nChn = chnNames.length;
		ImageProcessor ip = imp.getProcessor();
		int inWidth = imp.getWidth();
		int inHeight = imp.getHeight();
		int outHeight = inHeight * splitParameters.oversample + splitParameters.addTop + splitParameters.addBottom;
		int outWidth = inWidth * splitParameters.oversample + splitParameters.addLeft + splitParameters.addRight;
		int outLength = outWidth * outHeight;

		float[][] outPixels = new float[nChn][outLength];
		float[] pixels = (float[]) ip.getPixels();
		int chn, y, x, i, index;
		int bayerPeriod = 2 * splitParameters.oversample;
		int ovrWidth = inWidth * splitParameters.oversample;
		int ovrHeight = inHeight * splitParameters.oversample;
		for (chn = 0; chn < nChn; chn++)
			for (i = 0; i < outPixels[chn].length; i++)
				outPixels[chn][i] = 0.0f;
		/* Can be optimized - now it calculate input address for all those 0-es */
		for (index = 0; index < outLength; index++) {
			y = (index / outWidth) - splitParameters.addTop;
			x = (index % outWidth) - splitParameters.addLeft;
			if (y < 0)
				y = (bayerPeriod - ((-y) % bayerPeriod)) % bayerPeriod;
			else if (y >= ovrHeight)
				y = ovrHeight - bayerPeriod + ((y - ovrHeight) % bayerPeriod);
			if (x < 0)
				x = (bayerPeriod - ((-x) % bayerPeriod)) % bayerPeriod;
			else if (x >= ovrWidth)
				x = ovrWidth - bayerPeriod + ((x - ovrWidth) % bayerPeriod);
			if (((y % splitParameters.oversample) == 0) && ((x % splitParameters.oversample) == 0)) {
				x /= splitParameters.oversample;
				y /= splitParameters.oversample;
				chn = ((x & 1) == (y & 1)) ? 2 : (((x & 1) != 0) ? 0 : 1);
				outPixels[chn][index] = pixels[y * inWidth + x];
			}
		}
		/* prepare result stack to return */
		ImageStack outStack = new ImageStack(outWidth, outHeight);
		for (chn = 0; chn < nChn; chn++) {
			outStack.addSlice(chnNames[chn], outPixels[chn]);
		}
		return outStack;
	}

	/* ======================================================================== */
	/** extract and multiply by window function (same size as kernel itself) */
	void extractSquareTile(float[] pixels, // source pixel array,
			double[] tile, // will be filled, should have correct size before call
			double[] window, // window (same size as the kernel)
			int width, // width of pixels array
			int x0, // left corner X
			int y0) { // top corner Y
		int length = tile.length;
		int size = (int) Math.sqrt(length);
		int i, j, x, y;
		int height = pixels.length / width;
		int index = 0;
		for (i = 0; i < size; i++) {
			y = y0 + i;
			if ((y >= 0) && (y < height)) {
				index = i * size;
				for (j = 0; j < size; j++) {
					x = x0 + j;
					if ((x >= 0) && (x < width))
						tile[index] = pixels[y * width + x] * window[index];
					index++;
				}
			}
		}
	}

	/* ======================================================================== */
	void extractSquareTile(double[] pixels, // source pixel array,
			double[] tile, // will be filled, should have correct size before call
			double[] window, // window (same size as the kernel)
			int width, // width of pixels array
			int x0, // left corner X
			int y0) { // top corner Y
		int length = tile.length;
		int size = (int) Math.sqrt(length);
		int i, j, x, y;
		int height = pixels.length / width;
		int index = 0;
		for (i = 0; i < size; i++) {
			y = y0 + i;
			if ((y >= 0) && (y < height)) {
				index = i * size;
				for (j = 0; j < size; j++) {
					x = x0 + j;
					if ((x >= 0) && (x < width))
						tile[index] = pixels[y * width + x] * window[index];
					index++;
				}
			}
		}
	}

	/* ======================================================================== */
	/*
	 * accumulate square tile to the pixel array (tile may extend beyond the array,
	 * will be cropped)
	 */
	synchronized void accumulateSquareTile(float[] pixels, // float pixels array to accumulate tile
			double[] tile, // data to accumulate to the pixels array
			int width, // width of pixels array
			int x0, // left corner X
			int y0) { // top corner Y
		int length = tile.length;
		int size = (int) Math.sqrt(length);
		int i, j, x, y;
		int height = pixels.length / width;
		int index = 0;
		for (i = 0; i < size; i++) {
			y = y0 + i;
			if ((y >= 0) && (y < height)) {
				index = i * size;
				for (j = 0; j < size; j++) {
					x = x0 + j;
					if ((x >= 0) && (x < width))
						pixels[y * width + x] += tile[index];
					index++;
				}
			}
		}
	}

	synchronized void accumulateSquareTile(double[] pixels, // float pixels array to accumulate tile
			double[] tile, // data to accumulate to the pixels array
			int width, // width of pixels array
			int x0, // left corner X
			int y0) { // top corner Y
		int length = tile.length;
		int size = (int) Math.sqrt(length);
		int i, j, x, y;
		int height = pixels.length / width;
		int index = 0;
		for (i = 0; i < size; i++) {
			y = y0 + i;
			if ((y >= 0) && (y < height)) {
				index = i * size;
				for (j = 0; j < size; j++) {
					x = x0 + j;
					if ((x >= 0) && (x < width))
						pixels[y * width + x] += tile[index];
					index++;
				}
			}
		}
	}

	/* ======================================================================== */
	double[] getSlidingMask(int size) {
		double[] mask = new double[size * size];
		double[] maskLine = new double[size];
		double k = 2 * Math.PI / size;
		int i, j, index;
		for (i = 0; i < size; i++)
			maskLine[i] = 0.5 * (1.0 - Math.cos(i * k));
		index = 0;
		for (i = 0; i < size; i++)
			for (j = 0; j < size; j++)
				mask[index++] = maskLine[i] * maskLine[j];
		return mask;
	}
	/* ======================================================================== */

	/* Adds zero pixels around the image, "extending canvas" */

	public double[][] extendFFTInputTo(double[][] input_pixels, int newSize) {
		double[][] pixels = new double[input_pixels.length][];
		int i;
		for (i = 0; i < pixels.length; i++)
			pixels[i] = extendFFTInputTo(input_pixels[i], newSize);
		return pixels;
	}

	public double[][] extendFFTInput(double[][] input_pixels, int subDivFreq) {
		double[][] pixels = new double[input_pixels.length][];
		int i;
		for (i = 0; i < pixels.length; i++)
			pixels[i] = extendFFTInput(input_pixels[i], subDivFreq);
		return pixels;
	}

	public double[] extendFFTInputTo(double[] input_pixels, int newSize) {
		int subDivFreq = newSize / ((int) Math.sqrt(input_pixels.length));
		return extendFFTInput(input_pixels, subDivFreq);

	}

	public double[] extendFFTInput(double[] input_pixels, int subDivFreq) {
		if (input_pixels == null)
			return null;
		int width = (int) Math.sqrt(input_pixels.length);
		return extendFFTInput(input_pixels, width, // width of the image
				subDivFreq);
	}

	public double[] extendFFTInput(double[] input_pixels, int width, // width of the image
			int subDivFreq) {
		if (input_pixels == null)
			return null;
		double[] pixels = new double[input_pixels.length * subDivFreq * subDivFreq];
		int j, base, x, y;
		int height = input_pixels.length / width;
		for (j = 0; j < pixels.length; j++)
			pixels[j] = 0.0;
		j = 0;
		for (y = 0; y < height; y++) {
			base = width * (subDivFreq - 1) * (width * subDivFreq + 1) / 2 + y * width * subDivFreq;
			for (x = 0; x < width; x++)
				pixels[base + x] = input_pixels[j++];
		}
		return pixels;
	}

	/* ======================================================================== */
	public double[][] normalizeAndWindow(double[][] pixels, double[] windowFunction) {
		return normalizeAndWindow(pixels, windowFunction, true);
	}

	public double[] normalizeAndWindow(double[] pixels, double[] windowFunction) {
		return normalizeAndWindow(pixels, windowFunction, true);
	}

	public double[][] normalizeAndWindow(double[][] pixels, double[] windowFunction, boolean removeDC) {
		int i;
		for (i = 0; i < pixels.length; i++)
			if (pixels[i] != null)
				pixels[i] = normalizeAndWindow(pixels[i], windowFunction, removeDC);
		return pixels;
	}

	public double[] normalizeAndWindow(double[] pixels, double[] windowFunction, boolean removeDC) {
		int j;
		double s = 0.0;
		if (pixels == null)
			return null;
		if (removeDC) {
			for (j = 0; j < pixels.length; j++)
				s += pixels[j];
			s /= pixels.length;
		}
		for (j = 0; j < pixels.length; j++)
			pixels[j] = (pixels[j] - s) * windowFunction[j];
		return pixels;
	}

	/* ======================================================================== */
	public void YPrPbToRGB(ImageStack stack, double Kr, // 0.299;
			double Kb, // 0.114;
			int sliceY, int slicePr, int slicePb) {
		float[] fpixels_r = (float[]) stack.getPixels(1);
		float[] fpixels_g = (float[]) stack.getPixels(2);
		float[] fpixels_b = (float[]) stack.getPixels(3);
		float[] fpixels_Y = (float[]) stack.getPixels(sliceY);
		float[] fpixels_Pr = (float[]) stack.getPixels(slicePr);
		float[] fpixels_Pb = (float[]) stack.getPixels(slicePb);
		int length = fpixels_r.length;
		double Kg = 1.0 - Kr - Kb;
		int i;
		/**
		 * R= Y+ Pr*2.0*(1-Kr) B= Y+ Pb*2.0*(1-Kb) G= Y +Pr*(- 2*Kr*(1-Kr))/Kg +
		 * Pb*(-2*Kb*(1-Kb))/Kg
		 * 
		 */
		double KPrR = 2.0 * (1 - Kr);
		double KPbB = 2.0 * (1 - Kb);
		double KPrG = -2.0 * Kr * (1 - Kr) / Kg;
		double KPbG = -2.0 * Kb * (1 - Kb) / Kg;
		double Y, Pr, Pb;
		for (i = 0; i < length; i++) {
			Pb = fpixels_Pb[i];
			Pr = fpixels_Pr[i];
			Y = fpixels_Y[i];
			fpixels_r[i] = (float) (Y + Pr * KPrR);
			fpixels_b[i] = (float) (Y + Pb * KPbB);
			fpixels_g[i] = (float) (Y + Pr * KPrG + Pb * KPbG);
		}
	}

	/* ======================================================================== */
	/* ======================================================================== */
	/*
	 * ============== Dialogs
	 * ==========================================================
	 */
	/*
	 * true, // crop - crop image to the sensor size true, // jpeg - convert to
	 * 8-bit RGB and save jpeg (if save is true) true, // save - save result true,
	 * // save16 - save 16-bit tiff also if the end result is 8 bit true, // save32
	 * - save 32-bit tiff also if the end result is 8 or 16 bit
	 * 
	 */

	public boolean showProcessDialog(EyesisCorrectionParameters.ProcessParameters processParameters) {
		GenericDialog gd = new GenericDialog("Process parameters");
		// gd.addCheckbox ("Use first approximation for Y",
		// colorProcParameters.useFirstY);
		int i, j;
		gd.addCheckbox("Eyesis camera mode (3 composite frames)", processParameters.eyesisMode);
		for (i = 0; i < processParameters.frames.length; i++)
			for (j = 0; j < processParameters.frames[i].length; j++)
				gd.addCheckbox("Enable processing channel " + (i + 1) + ", subframe " + (j + 1),
						processParameters.frames[i][j]);
		gd.addCheckbox("Open file selection dialog (false - use selected)", processParameters.selectFile);
		gd.addCheckbox("Process only selected file (false - all 3)", processParameters.thisFileOnly);
		gd.addNumericField("Channel to process (if only selected file, 1..3)", processParameters.subChannelToProcess,
				0);

		gd.addCheckbox("Splt into Bayer stack (if false will exit)", processParameters.split);
		gd.addCheckbox("De-mosaic (if false will exit)", processParameters.debayer);
		gd.addCheckbox("Show de-mosaic middle-frequency 'energy", processParameters.showDebayerEnergy);
		gd.addCheckbox("Save de-mosaic middle-frequency 'energy", processParameters.saveDebayerEnergy);
		gd.addCheckbox("Sharpen (convolve with calibration kernels)", processParameters.deconvolve);
		gd.addCheckbox("Denoise (convolve with Gaussian in smooth areas)", processParameters.combine);
		gd.addCheckbox("Show denoise mask (white - use hi-res, black - low-res)", processParameters.showDenoiseMask);
		gd.addCheckbox("Save denoise mask (white - use hi-res, black - low-res)", processParameters.saveDenoiseMask);
		gd.addCheckbox("Show kernel noise gains", processParameters.showNoiseGains);
		gd.addCheckbox("Save kernel noise gains", processParameters.saveNoiseGains);
		gd.addCheckbox("Convert colors", processParameters.colorProc);
		gd.addCheckbox("Show chroma denoise mask (white - use hi-res, black - low-res)",
				processParameters.showChromaDenoiseMask);
		gd.addCheckbox("Save chroma denoise mask (white - use hi-res, black - low-res)",
				processParameters.saveChromaDenoiseMask);
		gd.addCheckbox("Rotate result image", processParameters.rotate);
		gd.addCheckbox("Crop result image to the original size", processParameters.crop);
		gd.addCheckbox("Convert to RGB48", processParameters.toRGB);
		gd.addCheckbox("Convert to 8 bit RGB (and save JPEG if save is enabled)", processParameters.jpeg);
		gd.addCheckbox("Save the result to file system", processParameters.save);
		gd.addCheckbox("Save 16-bit tiff if the result is 8 bit", processParameters.save16);
		gd.addCheckbox("Save 32-bit tiff if the result is 8 or 16 bit", processParameters.save32);
		gd.addCheckbox("Show the result image", processParameters.show);
		gd.addNumericField("JPEG quality (%)", processParameters.JPEG_quality, 0);
		gd.addNumericField("JPEG scale   (%)", 100 * processParameters.JPEG_scale, 0);
		gd.addCheckbox("Save current settings with results", processParameters.saveSettings);
		gd.addCheckbox("Update ImageJ status", UPDATE_STATUS);
		gd.addNumericField("Debug Level:", MASTER_DEBUG_LEVEL, 0);
		WindowTools.addScrollBars(gd);
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		processParameters.eyesisMode = gd.getNextBoolean();
		for (i = 0; i < processParameters.frames.length; i++)
			for (j = 0; j < processParameters.frames[i].length; j++)
				processParameters.frames[i][j] = gd.getNextBoolean();
		processParameters.selectFile = gd.getNextBoolean();
		processParameters.thisFileOnly = gd.getNextBoolean();
		processParameters.subChannelToProcess = (int) gd.getNextNumber();
		processParameters.split = gd.getNextBoolean();
		processParameters.debayer = gd.getNextBoolean();
		processParameters.showDebayerEnergy = gd.getNextBoolean();
		processParameters.saveDebayerEnergy = gd.getNextBoolean();
		processParameters.deconvolve = gd.getNextBoolean();
		processParameters.combine = gd.getNextBoolean();
		processParameters.showDenoiseMask = gd.getNextBoolean();
		processParameters.saveDenoiseMask = gd.getNextBoolean();
		processParameters.showNoiseGains = gd.getNextBoolean();
		processParameters.saveNoiseGains = gd.getNextBoolean();
		processParameters.colorProc = gd.getNextBoolean();
		processParameters.showChromaDenoiseMask = gd.getNextBoolean();
		processParameters.saveChromaDenoiseMask = gd.getNextBoolean();
		processParameters.rotate = gd.getNextBoolean();
		processParameters.crop = gd.getNextBoolean();
		processParameters.toRGB = gd.getNextBoolean();
		processParameters.jpeg = gd.getNextBoolean();
		processParameters.save = gd.getNextBoolean();
		processParameters.save16 = gd.getNextBoolean();
		processParameters.save32 = gd.getNextBoolean();
		processParameters.show = gd.getNextBoolean();
		processParameters.JPEG_quality = (int) gd.getNextNumber();
		processParameters.JPEG_scale = 0.01 * gd.getNextNumber();
		processParameters.saveSettings = gd.getNextBoolean();
		UPDATE_STATUS = gd.getNextBoolean();
		MASTER_DEBUG_LEVEL = (int) gd.getNextNumber();
		return true;
	}
	/* ======================================================================== */

	public boolean showFilesDialog(EyesisCorrectionParameters.FilesParameters filesParameters,
			EyesisCorrectionParameters.ProcessParameters processParameters) {
		GenericDialog gd = new GenericDialog("Kernel paths");
		int i, j;
		gd.addCheckbox("Eyesis camera mode (3 composite frames)", processParameters.eyesisMode);
		if (processParameters.eyesisMode) {
			for (i = 0; i < filesParameters.rPSFNames.length; i++)
				for (j = 0; j < filesParameters.rPSFNames[i].length; j++)
					if (filesParameters.rPSFNames[i][j] != null) {
						gd.addStringField("Deconvolution kernel name, channel " + (i + 1) + ", subframe " + (j + 1),
								filesParameters.rPSFNames[i][j], 40);

					}
			for (i = 0; i < filesParameters.rPSFNames.length; i++)
				for (j = 0; j < filesParameters.gaussianNames[i].length; j++)
					if (filesParameters.gaussianNames[i][j] != null) {
						gd.addStringField("Gaussian kernel name, channel " + (i + 1) + ", subframe " + (j + 1),
								filesParameters.gaussianNames[i][j], 40);

					}
		} else {
			gd.addStringField("Deconvolution kernel name ", filesParameters.rPSFNames[0][0], 40);
			gd.addStringField("Gaussian kernel name", filesParameters.gaussianNames[0][0], 40);
		}
//sourceFile
		if (filesParameters.sourceFiles != null) {
			if (filesParameters.sourceFiles.length == 1) {
				gd.addStringField("Source file(s) path (select one - will include other channels):    ",
						filesParameters.sourceFiles[0], 80);
			} else if (filesParameters.sourceFiles.length > 1) {
				gd.addMessage("Multiple source files selected");
			}

		} else {
			gd.addMessage("Source file list is empty");
		}
		gd.addStringField("Kernel  directory path:    ", filesParameters.kernelDirectory, 80);
		gd.addStringField("Results directory path:    ", filesParameters.resultsDirectory, 80);
		gd.addCheckbox("Use XML format to save/restore settings", filesParameters.useXML);
		WindowTools.addScrollBars(gd);
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		boolean newEyesisMode = gd.getNextBoolean();

		if (processParameters.eyesisMode) {
			for (i = 0; i < filesParameters.rPSFNames.length; i++)
				for (j = 0; j < filesParameters.rPSFNames[i].length; j++)
					if (filesParameters.rPSFNames[i][j] != null) {
						filesParameters.rPSFNames[i][j] = gd.getNextString();

					}
			for (i = 0; i < filesParameters.rPSFNames.length; i++)
				for (j = 0; j < filesParameters.gaussianNames[i].length; j++)
					if (filesParameters.gaussianNames[i][j] != null) {
						filesParameters.gaussianNames[i][j] = gd.getNextString();

					}
		} else {
			filesParameters.rPSFNames[0][0] = gd.getNextString();
			filesParameters.gaussianNames[0][0] = gd.getNextString();
		}
		if ((filesParameters.sourceFiles != null) && (filesParameters.sourceFiles.length == 1)) {
			filesParameters.sourceFiles[0] = gd.getNextString();
		}
		filesParameters.kernelDirectory = gd.getNextString();
		filesParameters.resultsDirectory = gd.getNextString();
		filesParameters.useXML = gd.getNextBoolean();
		if (newEyesisMode != processParameters.eyesisMode) {
			processParameters.eyesisMode = newEyesisMode;
			return showFilesDialog(filesParameters, processParameters);
		}
		return true;
	}

	/* ======================================================================== */

	public boolean showRGBProcessDialog(EyesisCorrectionParameters.RGBParameters rgbParameters) {
		GenericDialog gd = new GenericDialog("RGB conversion parameters");

		gd.addNumericField("Red   color black level", rgbParameters.r_min, 3);
		gd.addNumericField("Green color black level", rgbParameters.g_min, 3);
		gd.addNumericField("Blue  color black level", rgbParameters.b_min, 3);
		gd.addNumericField("Red   color white level", rgbParameters.r_max, 3);
		gd.addNumericField("Green color white level", rgbParameters.g_max, 3);
		gd.addNumericField("Blue  color white level", rgbParameters.b_max, 3);
		gd.addNumericField("Alpha channel min", rgbParameters.alpha_min, 3);
		gd.addNumericField("Alpha channel max", rgbParameters.alpha_max, 3);
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		rgbParameters.r_min = gd.getNextNumber();
		rgbParameters.g_min = gd.getNextNumber();
		rgbParameters.b_min = gd.getNextNumber();
		rgbParameters.r_max = gd.getNextNumber();
		rgbParameters.g_max = gd.getNextNumber();
		rgbParameters.b_max = gd.getNextNumber();
		rgbParameters.alpha_min = gd.getNextNumber();
		rgbParameters.alpha_max = gd.getNextNumber();
		return true;
	}

	/* ======================================================================== */
	public boolean showColorCalibDialog(EyesisCorrectionParameters.ColorCalibParameters colorCalibParameters) {
		int i, j;
		GenericDialog gd = new GenericDialog("Individual channels colors/gains");
		for (i = 0; i < colorCalibParameters.gain.length; i++)
			for (j = 0; j < colorCalibParameters.gain[i].length; j++)
				gd.addNumericField("Gain (brightness) for channel " + (i + 1) + " subframe " + (j + 1),
						colorCalibParameters.gain[i][j], 3);
		for (i = 0; i < colorCalibParameters.balanceRed.length; i++)
			for (j = 0; j < colorCalibParameters.balanceRed[i].length; j++)
				gd.addNumericField("Balance Red/Green for channel " + (i + 1) + " subframe " + (j + 1),
						colorCalibParameters.balanceRed[i][j], 3);
		for (i = 0; i < colorCalibParameters.balanceBlue.length; i++)
			for (j = 0; j < colorCalibParameters.balanceBlue[i].length; j++)
				gd.addNumericField("Balance Blue/Green for channel " + (i + 1) + " subframe " + (j + 1),
						colorCalibParameters.balanceBlue[i][j], 3);
		WindowTools.addScrollBars(gd);
		gd.showDialog();
		if (gd.wasCanceled())
			return false;

		for (i = 0; i < colorCalibParameters.gain.length; i++)
			for (j = 0; j < colorCalibParameters.gain[i].length; j++)
				colorCalibParameters.gain[i][j] = gd.getNextNumber();
		for (i = 0; i < colorCalibParameters.balanceRed.length; i++)
			for (j = 0; j < colorCalibParameters.balanceRed[i].length; j++)
				colorCalibParameters.balanceRed[i][j] = gd.getNextNumber();
		for (i = 0; i < colorCalibParameters.balanceBlue.length; i++)
			for (j = 0; j < colorCalibParameters.balanceBlue[i].length; j++)
				colorCalibParameters.balanceBlue[i][j] = gd.getNextNumber();
		return true;
	}
///    	showColorCalibDialog(COLOR_CALIB_PARAMETERS);

	/* ======================================================================== */

	public boolean showCombinePairDialog(EyesisCorrectionParameters.NonlinParameters nonlinParameters,
			EyesisCorrectionParameters.ProcessParameters processParameters) {
		GenericDialog gd = new GenericDialog("Combining high-re and low-res images");
		gd.addNumericField("Mask low-pass filter mask sigma (spatial domain)", nonlinParameters.lowPassSigma, 3); // 5.0-
																													// sigma
																													// for
																													// the
																													// nonlinear
																													// filtering
		gd.addCheckbox("Apply block rejection filter to the mask", nonlinParameters.useRejectBlocksFilter);
		gd.addCheckbox("Combine masks withe blocks rejected and not", nonlinParameters.combineBothModes);
		gd.addNumericField("Sliding FFT size for block filtering", nonlinParameters.maskFFTSize, 0);
		gd.addNumericField("JPEG block period adjusted to oversampling (32)", nonlinParameters.blockPeriod, 0);
		gd.addNumericField("Mask block rejection filter sigma (freq. domain)", nonlinParameters.rejectFreqSigma, 3); // 1.0-
		gd.addNumericField("Nonlinear filter mask min. level", nonlinParameters.filtMin, 3); // 0.01 minimal low-pass
																								// filtered squared
																								// difference between
																								// the corrected and
																								// original pixels to
																								// trigger sharpness
																								// enhancement
		gd.addNumericField("Nonlinear filter mask max. level", nonlinParameters.filtMax, 3); // 0.15 squared low-pass
																								// filtered difference
																								// between the corrected
																								// and original pixels,
																								// so abopve that level
																								// 100% corrected image
																								// is used
		/*
		 * for (i=0; i<nonlinParameters.thresholdCorrection.length;i++) for
		 * (j=0;j<nonlinParameters.thresholdCorrection[i].length;j++)
		 * gd.addNumericField("Filter Min/Max scale for channel "+(i+1)+" subframe "+(j+
		 * 1), nonlinParameters.thresholdCorrection[i][j], 3);
		 */
		for (int i = 0; i < nonlinParameters.thresholdCorr.length; i++)
			gd.addNumericField("Filter Min/Max scale for channel " + i, nonlinParameters.thresholdCorr[i], 3);

		gd.addNumericField("Nonlinear filter threshold", nonlinParameters.threshold, 3); // 0.01 when blurred intensity
																							// is below this value, use
																							// it as a denominator
		gd.addCheckbox("Show generated/used mask", processParameters.showDenoiseMask);
		gd.addCheckbox("Save generated/used mask", processParameters.saveDenoiseMask);
		gd.addCheckbox("Use differntial noise gains", nonlinParameters.useDiffNoiseGains);
		gd.addNumericField("Noise gain weight 0 (red)", nonlinParameters.noiseGainWeights[0], 3); // Weight of red
																									// component noise
																									// gain (0.0)
		gd.addNumericField("Noise gain weight 1 (blue)", nonlinParameters.noiseGainWeights[1], 3); // Weight of blue
																									// component noise
																									// gain (0.0)
		gd.addNumericField("Noise gain weight 2 (green)", nonlinParameters.noiseGainWeights[2], 3); // Weight of green
																									// component noise
																									// gain (1.0)
		gd.addNumericField("Blur kernels for noise calculation", nonlinParameters.blurSigma, 3); // // blur sigma for
																									// mask calculation
																									// (blur convolution
																									// kernels for noise
																									// gain calculation
		gd.addNumericField("Noise Gain power", nonlinParameters.noiseGainPower, 3);

		gd.addCheckbox("Filter out spots on denoise mask", nonlinParameters.useRingFilter); // filter out spots on
																							// denoise mask
		gd.addNumericField("Minimal relative value of local max", nonlinParameters.minMaxValue, 3); // minimal value
																									// (relative to
																									// filtMax) of the
																									// local maximum to
																									// be processed
		gd.addNumericField("Relative max over ring threshold", nonlinParameters.overRingThreshold, 3); // ratio of local
																										// max. and
																										// maximal value
																										// in the
																										// surrounding
																										// ring to
																										// trigger
																										// filter
		gd.addNumericField("Limit center circle over ring max", nonlinParameters.overRingLimit, 3); // limit values in
																									// the center circle
																									// to scaled maximum
																									// in a ring
		gd.addNumericField("Ring inner radius", nonlinParameters.ringIR, 3); // ring inner radius (center circle radius)
		gd.addNumericField("Ring outer radius", nonlinParameters.ringOR, 3); // ring outer radius
		gd.addNumericField("Debug Level:", MASTER_DEBUG_LEVEL, 0);
		WindowTools.addScrollBars(gd);
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		nonlinParameters.lowPassSigma = gd.getNextNumber();
		nonlinParameters.useRejectBlocksFilter = gd.getNextBoolean();
		nonlinParameters.combineBothModes = gd.getNextBoolean();
		nonlinParameters.maskFFTSize = (int) gd.getNextNumber();
		nonlinParameters.blockPeriod = (int) gd.getNextNumber();
		nonlinParameters.rejectFreqSigma = gd.getNextNumber();
		nonlinParameters.filtMin = gd.getNextNumber();
		nonlinParameters.filtMax = gd.getNextNumber();
		/*
		 * for (i=0; i<nonlinParameters.thresholdCorrection.length;i++) for
		 * (j=0;j<nonlinParameters.thresholdCorrection[i].length;j++)
		 * nonlinParameters.thresholdCorrection[i][j]=gd.getNextNumber();
		 */
		for (int i = 0; i < nonlinParameters.thresholdCorr.length; i++)
			nonlinParameters.thresholdCorr[i] = gd.getNextNumber();

		nonlinParameters.threshold = gd.getNextNumber();
		processParameters.showDenoiseMask = gd.getNextBoolean();
		processParameters.saveDenoiseMask = gd.getNextBoolean();
		nonlinParameters.useDiffNoiseGains = gd.getNextBoolean();
		nonlinParameters.noiseGainWeights[0] = gd.getNextNumber();
		nonlinParameters.noiseGainWeights[1] = gd.getNextNumber();
		nonlinParameters.noiseGainWeights[2] = gd.getNextNumber();
		nonlinParameters.blurSigma = gd.getNextNumber();
		nonlinParameters.noiseGainPower = gd.getNextNumber();
		nonlinParameters.useRingFilter = gd.getNextBoolean();
		nonlinParameters.minMaxValue = gd.getNextNumber();
		nonlinParameters.overRingThreshold = gd.getNextNumber();
		nonlinParameters.overRingLimit = gd.getNextNumber();
		nonlinParameters.ringIR = gd.getNextNumber();
		nonlinParameters.ringOR = gd.getNextNumber();
		MASTER_DEBUG_LEVEL = (int) gd.getNextNumber();
		return true;
	}

	/* ======================================================================== */
	public boolean showStackConvolutionDialog() {
		int i;
		GenericDialog gd = new GenericDialog("Stack convolution parameters");
		gd.addNumericField("Convolution FFT size (twice the kernel size)", CONVOLVE_FFT_SIZE, 0); // 128
		gd.addNumericField("Maximal number of concurrent threads", THREADS_MAX, 0); // 100
		gd.addCheckbox("Update ImageJ status", UPDATE_STATUS);

		gd.addNumericField("Debug Level:", MASTER_DEBUG_LEVEL, 0);
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		CONVOLVE_FFT_SIZE = 1;
		for (i = (int) gd.getNextNumber(); i > 1; i >>= 1)
			CONVOLVE_FFT_SIZE <<= 1; /* make it to be power of 2 */
		UPDATE_STATUS = gd.getNextBoolean();
		THREADS_MAX = (int) gd.getNextNumber();
		MASTER_DEBUG_LEVEL = (int) gd.getNextNumber();
		return true;
	}

	/* ======================================================================== */
	public boolean showDeBayerDialog(EyesisCorrectionParameters.DebayerParameters debayerParameters,
			EyesisCorrectionParameters.ProcessParameters processParameters) {
		int i;
		GenericDialog gd = new GenericDialog("De-bayer parameters");
		gd.addNumericField("Debayer threshold (lower use default filtering)", debayerParameters.debayerThreshold, 3); // =0.2;
																														// Measuring
																														// maximal
																														// spectral
																														// component
																														// amplitude
																														// in
																														// mid-frequencies.
																														// If
																														// below
																														// this
																														// -
																														// use
																														// default
																														// de-bayer
																														// mask
		gd.addNumericField("Debayer lo-pass relative width for green", debayerParameters.debayerRelativeWidthGreen, 3); // 1.5
																														// result
																														// green
																														// mask
																														// mpy
																														// by
																														// scaled
																														// default
																														// (diamond)
		gd.addNumericField("Debayer lo-pass relative width for red/blue", debayerParameters.debayerRelativeWidthRedblue,
				3); // 1.5 result red/blue mask mpy by scaled default (square)
		gd.addNumericField("Debayer lo-pass relative width for red/blue (main)",
				debayerParameters.debayerRelativeWidthRedblueMain, 3); // 1.3 green mask when applied to red/blue, main
																		// (center)
		gd.addNumericField("Debayer lo-pass relative width for red/blue (clones)",
				debayerParameters.debayerRelativeWidthRedblueClones, 3); // 2.0 green mask when applied to red/blue,
																			// clones
		gd.addNumericField("Debayer mask - power for the ampliutude", debayerParameters.debayerGamma, 3); // 0.3
		gd.addNumericField("relative increase value with radius", debayerParameters.debayerBonus, 3); // 0.5 - scale far
																										// pixels as
																										// (1.0+bonus*r/rmax)
		gd.addNumericField("Relative alais strength to mask out point", debayerParameters.mainToAlias, 3); // 0.5; //
																											// relative
																											// main/alias
																											// amplitudes
																											// to enable
																											// lixels
																											// (i.e. 0.5
																											// means
																											// that if
																											// alias is
																											// >0.5*main,
																											// the pixel
																											// will be
																											// masked
																											// out)
		gd.addNumericField("Gaussian blur sigma for the alias-rejecting masks", debayerParameters.debayerMaskBlur, 3); // 2.0

		gd.addCheckbox("Use 'scissors' filter", debayerParameters.debayerUseScissors); // true; // use "scissors", if
																						// false - just apply "diamond"
																						// ands "square" with
																						// debayerParameters.debayerRelativeWidthGreen
																						// and
																						// debayerParameters.debayerRelativeWidthRedblue
		gd.addCheckbox("Show mid frequency components energy plots", processParameters.showDebayerEnergy); // false -
																											// plot
																											// debayer
																											// high
																											// frequency
																											// energy
																											// (use to
																											// select
																											// between
																											// "scissors"
																											// and
																											// default
																											// uniform
		gd.addCheckbox("Save mid frequency components energy plots", processParameters.saveDebayerEnergy); // false -
																											// plot
																											// debayer
																											// high
																											// frequency
																											// energy
																											// (use to
																											// select
																											// between
																											// "scissors"
																											// and
																											// default
																											// uniform

//

		gd.addNumericField("Polar grid largest cell size to cartesian one ratio", debayerParameters.polarStep, 3); // 0.5;//
																													// size
																													// of
																													// largest
																													// polar
																													// cell
																													// to
																													// cartesian
																													// one
		gd.addNumericField("Debayer FFT Size (64)", debayerParameters.size, 0); // 64

		gd.addCheckbox("Debug: show data for selected tile", debayerParameters.debug); // false
		gd.addNumericField("Debug: X-coordinate of the point of interest  (oversampled)", debayerParameters.xDebug, 0);
		gd.addNumericField("Debug: Y-coordinate of the point of interest  (oversampled)", debayerParameters.yDebug, 0);

		gd.addNumericField("Maximal number of concurrent threads", THREADS_MAX, 0); // 100
		gd.addCheckbox("Update ImageJ status", UPDATE_STATUS);
		gd.addCheckbox("Show debayer debug images as stacks (false - individual)", debayerParameters.debayerStacks); // true

		gd.addNumericField("Debug Level:", MASTER_DEBUG_LEVEL, 0);
		WindowTools.addScrollBars(gd);
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		debayerParameters.debayerThreshold = gd.getNextNumber();
		debayerParameters.debayerRelativeWidthGreen = gd.getNextNumber();
		debayerParameters.debayerRelativeWidthRedblue = gd.getNextNumber();
		debayerParameters.debayerRelativeWidthRedblueMain = gd.getNextNumber();
		debayerParameters.debayerRelativeWidthRedblueClones = gd.getNextNumber();

		debayerParameters.debayerGamma = gd.getNextNumber();
		debayerParameters.debayerBonus = gd.getNextNumber();
		debayerParameters.mainToAlias = gd.getNextNumber();
		debayerParameters.debayerMaskBlur = gd.getNextNumber();

		debayerParameters.debayerUseScissors = gd.getNextBoolean();
//    debayerParameters.showEnergy=                  gd.getNextBoolean();
		processParameters.showDebayerEnergy = gd.getNextBoolean();
		processParameters.saveDebayerEnergy = gd.getNextBoolean();
		debayerParameters.polarStep = gd.getNextNumber();

		debayerParameters.size = 1;
		for (i = (int) gd.getNextNumber(); i > 1; i >>= 1)
			debayerParameters.size <<= 1; /* make it to be power of 2 */

		debayerParameters.debug = gd.getNextBoolean();
		debayerParameters.xDebug = (int) gd.getNextNumber();
		debayerParameters.yDebug = (int) gd.getNextNumber();

		THREADS_MAX = (int) gd.getNextNumber();
		UPDATE_STATUS = gd.getNextBoolean();
		debayerParameters.debayerStacks = gd.getNextBoolean();
		MASTER_DEBUG_LEVEL = (int) gd.getNextNumber();
		return true;
	}

	/* ======================================================================== */
	public boolean showSplitBayerToStackDialog(EyesisCorrectionParameters.SplitParameters splitParameters) {
		GenericDialog gd = new GenericDialog("Interpolate kernels parameters");
		gd.addNumericField("Interpolation step between original kernels", splitParameters.oversample, 0); // 2
		gd.addNumericField("Interpolation add on the top (in output, subdivided steps)", splitParameters.addTop, 0); // 32
		gd.addNumericField("Interpolation add on the left (in output, subdivided steps)", splitParameters.addLeft, 0); // 32
		gd.addNumericField("Interpolation add on the right (in output, subdivided steps)", splitParameters.addRight, 0); // 32
		gd.addNumericField("Interpolation add on the bottom (in output, subdivided steps)", splitParameters.addBottom,
				0); // 32

		gd.addNumericField("Debug Level:", MASTER_DEBUG_LEVEL, 0);
		gd.showDialog();
		if (gd.wasCanceled())
			return false;
		splitParameters.oversample = (int) gd.getNextNumber();
		splitParameters.addTop = (int) gd.getNextNumber();
		splitParameters.addLeft = (int) gd.getNextNumber();
		splitParameters.addRight = (int) gd.getNextNumber();
		splitParameters.addBottom = (int) gd.getNextNumber();
		MASTER_DEBUG_LEVEL = (int) gd.getNextNumber();
		return true;
	}

	/* ======================================================================== */
	/* ======================================================================== */
	/* ======================================================================== */
	/* ======================================================================== */
	/* unfinished/not used */
	/*
	 * ========================================================================
	 * 
	 * @param colorProcParameters TODO
	 */

	public void processColorsWeights(ImageStack stack, double scale, // initila maximal pixel value (16))
			ColorProcParameters colorProcParameters,
			EyesisCorrectionParameters.ColorCalibParameters colorCalibParameters, // if null - assume all 1-s
			int nChn, int nSubChn) {
		double thisGain = colorProcParameters.gain;
		double thisBalanceRed = colorProcParameters.balanceRed;
		double thisBalanceBlue = colorProcParameters.balanceBlue;
		if (colorCalibParameters != null) {
			thisGain *= colorCalibParameters.gain[nChn][nSubChn];
			thisBalanceRed *= colorCalibParameters.balanceRed[nChn][nSubChn];
			thisBalanceBlue *= colorCalibParameters.balanceBlue[nChn][nSubChn];
		}
		float[] fpixels_r = (float[]) stack.getPixels(1);
		float[] fpixels_g = (float[]) stack.getPixels(2);
		float[] fpixels_b = (float[]) stack.getPixels(3);
		boolean useWeights = (stack.getSize() >= 5);
		if (!useWeights) {
			stack.addSlice("dummy1", fpixels_r);
			stack.addSlice("dummy2", fpixels_g);
		}
		float[] fpixels_wr = (float[]) stack.getPixels(4);
		float[] fpixels_wb = (float[]) stack.getPixels(5);
		int length = fpixels_r.length;
		int width = stack.getWidth();
		int height = stack.getHeight();
		/* Scale colors, gamma-convert */
		int i;
		double gain_red = thisBalanceRed * thisGain / scale;
		double gain_blue = thisBalanceBlue * thisGain / scale;
		double gain_green = thisGain / scale;
		double gamma_a = Math.pow(colorProcParameters.minLin, colorProcParameters.gamma)
				* (1.0 - colorProcParameters.gamma);
		gamma_a = gamma_a / (1.0 - gamma_a);
		double gamma_linK = (1.0 + gamma_a) * colorProcParameters.gamma
				* Math.pow(colorProcParameters.minLin, colorProcParameters.gamma) / colorProcParameters.minLin;
		if (DEBUG_LEVEL > 0) {
			System.out.println("gain_red=" + gain_red);
			System.out.println("gain_green=" + gain_green);
			System.out.println("gain_blue=" + gain_blue);
		}

		double[][] blueLeakRgb = new double[3][length];
		for (int px = 0; px < length; px++) {
			blueLeakRgb[0][px] = fpixels_r[px] * gain_red;
			blueLeakRgb[1][px] = fpixels_g[px] * gain_green;
			blueLeakRgb[2][px] = fpixels_b[px] * gain_blue;
		}
		BlueLeak blueLeak = new BlueLeak(colorProcParameters, blueLeakRgb, width, null, // "blue_corr",
				DEBUG_LEVEL + 1);
		double[][] blueRemovedRGB = blueLeak.process(); // will later return corrected RGB to use
		/*
		 * for (i=0;i<length;i++) { fpixels_r[i]=(float)
		 * linGamma(colorProcParameters.gamma, gamma_a, gamma_linK,
		 * colorProcParameters.minLin, fpixels_r[i]*gain_red); fpixels_g[i]=(float)
		 * linGamma(colorProcParameters.gamma, gamma_a, gamma_linK,
		 * colorProcParameters.minLin, fpixels_g[i]*gain_green); fpixels_b[i]=(float)
		 * linGamma(colorProcParameters.gamma, gamma_a, gamma_linK,
		 * colorProcParameters.minLin, fpixels_b[i]*gain_blue); }
		 */
		for (i = 0; i < length; i++) {
			fpixels_r[i] = (float) linGamma(colorProcParameters.gamma, gamma_a, gamma_linK, colorProcParameters.minLin,
					blueRemovedRGB[0][i]);
			fpixels_g[i] = (float) linGamma(colorProcParameters.gamma, gamma_a, gamma_linK, colorProcParameters.minLin,
					blueRemovedRGB[1][i]);
			fpixels_b[i] = (float) linGamma(colorProcParameters.gamma, gamma_a, gamma_linK, colorProcParameters.minLin,
					blueRemovedRGB[2][i]);
		}

		/* Convert to YPbPr */
		double Y, Pb, Pr;
		double Kg = 1.0 - colorProcParameters.kr - colorProcParameters.kb;
		double Sb = 0.5 / (1.0 - colorProcParameters.kb) * colorProcParameters.saturationBlue;
		double Sr = 0.5 / (1.0 - colorProcParameters.kr) * colorProcParameters.saturationRed;
		double Yr, Yg, Yb, Wr, Wg, Wb, S;
		/*
		 * coefficients to find Y from Pb, Pr and a color (R,G or B) Yr = R- Pr*KPrR Yb
		 * = B- Pb*KPbB Yg = G+ Pr*KPrG + Pb*KPbG
		 */
		double KPrR = -(2.0 * (1 - colorProcParameters.kr)) / colorProcParameters.saturationRed;
		double KPbB = -(2.0 * (1 - colorProcParameters.kb)) / colorProcParameters.saturationBlue;
		double KPrG = 2.0 * colorProcParameters.kr * (1 - colorProcParameters.kr) / Kg
				/ colorProcParameters.saturationRed;
		double KPbG = 2.0 * colorProcParameters.kb * (1 - colorProcParameters.kb) / Kg
				/ colorProcParameters.saturationBlue;
		if (DEBUG_LEVEL > 1) {
			System.out.println(" processColorsWeights() gain_red=" + gain_red + " gain_green=" + gain_green
					+ " gain_blue=" + gain_blue);
			System.out.println(" processColorsWeights() gamma=" + colorProcParameters.gamma + " minLin="
					+ colorProcParameters.minLin + " gamma_a=" + gamma_a + " gamma_linK=" + gamma_linK);
			System.out.println(" processColorsWeights() Kr=" + colorProcParameters.kr + " Kg=" + Kg + " Kb="
					+ colorProcParameters.kb + " Sr=" + Sr + " Sb=" + Sb);
			System.out.println(
					" processColorsWeights() KPrR=" + KPrR + " KPbB=" + KPbB + " KPrG=" + KPrG + " KPbG=" + KPbG);
		}

		float[] fpixels_pb = new float[length];
		float[] fpixels_pr = new float[length];
		float[] fpixels_y0 = new float[length];
		float[] fpixels_y = fpixels_y0;

		float[] fpixels_yR = null;
		float[] fpixels_yG = null;
		float[] fpixels_yB = null;

		if (DEBUG_LEVEL > 2) {
			fpixels_yR = new float[length];
			fpixels_yG = new float[length];
			fpixels_yB = new float[length];
		}
		for (i = 0; i < length; i++) {
			Y = colorProcParameters.kr * fpixels_r[i] + Kg * fpixels_g[i] + colorProcParameters.kb * fpixels_b[i];
			fpixels_pb[i] = (float) (Sb * (fpixels_b[i] - Y));
			fpixels_pr[i] = (float) (Sr * (fpixels_r[i] - Y));
			fpixels_y0[i] = (float) Y;
		}
		/*
		 * calculate Y from weighted colors, weights derived from how good each color
		 * component predicts signal in each subpixel of Bayer pattern
		 */
		if (useWeights) {
			fpixels_y = new float[length];
			for (i = 0; i < length; i++) {
				Pb = fpixels_pb[i];
				Pr = fpixels_pr[i];
				Yr = fpixels_r[i] - Pr * KPrR;
				Yb = fpixels_b[i] - Pb * KPbB;
				Yg = fpixels_g[i] + Pr * KPrG + Pb * KPbG;
				Wr = fpixels_wr[i];
				Wb = fpixels_wb[i];
				Wg = 1.0 - Wr - Wb;
				S = 1.0 / (Wr * (colorProcParameters.weightScaleR - 1.0) + Wb * (colorProcParameters.weightScaleB - 1.0)
						+ 1.0);
				Wr *= S * colorProcParameters.weightScaleR;
				Wb *= S * colorProcParameters.weightScaleB;
				Wg *= S;
				Y = Yr * Wr + Yg * Wg + Yb * Wb;
				fpixels_y[i] = (float) Y;
				if (DEBUG_LEVEL > 2) {
					fpixels_yR[i] = (float) Yr;
					fpixels_yG[i] = (float) Yg;
					fpixels_yB[i] = (float) Yb;
				}
			}
		}
		/* Low-pass filter Pb and Pr */
		DoubleGaussianBlur gb = new DoubleGaussianBlur();
		double[] dpixels_pr = new double[fpixels_pr.length];
		double[] dpixels_pb = new double[fpixels_pb.length];
		for (i = 0; i < dpixels_pr.length; i++) {
			dpixels_pr[i] = fpixels_pr[i];
			dpixels_pb[i] = fpixels_pb[i];
		}
		if (colorProcParameters.maskMax > 0.0) {
			double[] dmask = new double[fpixels_y0.length];
			for (i = 0; i < dpixels_pr.length; i++)
				dmask[i] = fpixels_y0[i];
			double[] dpixels_pr_dark = dpixels_pr.clone();
			double[] dpixels_pb_dark = dpixels_pb.clone();
			gb.blurDouble(dmask, width, height, colorProcParameters.maskSigma, colorProcParameters.maskSigma, 0.01);
			gb.blurDouble(dpixels_pr, width, height, colorProcParameters.chromaBrightSigma,
					colorProcParameters.chromaBrightSigma, 0.01);
			gb.blurDouble(dpixels_pb, width, height, colorProcParameters.chromaBrightSigma,
					colorProcParameters.chromaBrightSigma, 0.01);
			gb.blurDouble(dpixels_pr_dark, width, height, colorProcParameters.chromaDarkSigma,
					colorProcParameters.chromaDarkSigma, 0.01);
			gb.blurDouble(dpixels_pb_dark, width, height, colorProcParameters.chromaDarkSigma,
					colorProcParameters.chromaDarkSigma, 0.01);
			if (DEBUG_LEVEL > 3) {
				ShowDoubleFloatArrays.showArrays(dmask, width, height, "dmask");
				ShowDoubleFloatArrays.showArrays(dpixels_pr, width, height, "dpixels_pr");
				ShowDoubleFloatArrays.showArrays(dpixels_pb, width, height, "dpixels_pb");
				ShowDoubleFloatArrays.showArrays(dpixels_pr_dark, width, height, "dpixels_pr_dark");
				ShowDoubleFloatArrays.showArrays(dpixels_pb_dark, width, height, "dpixels_pb_dark");
			}
			double mp;
			double k = 1.0 / (colorProcParameters.maskMax - colorProcParameters.maskMin);
			for (i = 0; i < dmask.length; i++) {
				mp = dmask[i];
				if (mp < colorProcParameters.maskMin) {
					dmask[i] = 0.0;
				} else if (mp < colorProcParameters.maskMax) {
					dmask[i] = k * (mp - colorProcParameters.maskMin);
				} else
					dmask[i] = 1.0;
			}
//TODO: null DENOISE_MASK if it is not calculated
			if (colorProcParameters.combineWithSharpnessMask) {
				if (DENOISE_MASK == null) {
					System.out.println("Can not combine masks as DENOISE_MASK is null (i.e. no denoise was performed)");
				} else if (DENOISE_MASK.length != dmask.length) {
					System.out.println("Can not combine masks as DENOISE_MASK length is different from that of dmask");
				} else {
					for (i = 0; i < dmask.length; i++) {
						dmask[i] += DENOISE_MASK[i];
						if (dmask[i] > 1.0)
							dmask[i] = 1.0;
					}
				}

			}
			for (i = 0; i < dmask.length; i++) {
				mp = dmask[i];
				dpixels_pb[i] = (1.0 - mp) * dpixels_pb_dark[i] + mp * dpixels_pb[i];
				dpixels_pr[i] = (1.0 - mp) * dpixels_pr_dark[i] + mp * dpixels_pr[i];
			}
			DENOISE_MASK_CHROMA = dmask; // (global, used to return denoise mask to save/show
			DENOISE_MASK_CHROMA_WIDTH = width; // width of the DENOISE_MASK_CHROMA image
		} else {
			gb.blurDouble(dpixels_pr, width, height, colorProcParameters.chromaBrightSigma,
					colorProcParameters.chromaBrightSigma, 0.01);
			gb.blurDouble(dpixels_pb, width, height, colorProcParameters.chromaBrightSigma,
					colorProcParameters.chromaBrightSigma, 0.01);
			DENOISE_MASK_CHROMA = null; // (global, used to return denoise mask to save/show
		}
		for (i = 0; i < dpixels_pr.length; i++) {
			fpixels_pr[i] = (float) dpixels_pr[i];
			fpixels_pb[i] = (float) dpixels_pb[i];
		}
		stack.addSlice("Pr", fpixels_pr);
		stack.addSlice("Pb", fpixels_pb);
		stack.addSlice("Y", fpixels_y);
		stack.addSlice("Y0", fpixels_y0); // not filtered by low-pass, preliminary (for comaprison only)
		if (DEBUG_LEVEL > 2) {
			stack.addSlice("Yr", fpixels_yR);
			stack.addSlice("Yg", fpixels_yG);
			stack.addSlice("Yb", fpixels_yB);
		}

	}

	/* ======================================================================== */
	public double linGamma(double gamma, double a, double k, double x0, double x) {
		if (x < 0)
			return 0.0;

		if (x <= x0)
			return k * x;
		return (1.0 + a) * Math.pow(x, gamma) - a;
//  return x;
	}

	/**
	 * Main method for debugging.
	 *
	 * For debugging, it is convenient to have a method that starts ImageJ, loads an
	 * image and calls the plugin, e.g. after setting breakpoints. Grabbed from
	 * https://github.com/imagej/minimal-ij1-plugin
	 * 
	 * @param args unused
	 */
	public static void main(String[] args) {
	        // set the plugins.dir property to make the plugin appear in the Plugins menu
	        Class<?> clazz = Eyesis_Correction.class;
	        String url = clazz.getResource("/" + clazz.getName().replace('.', '/') + ".class").toString();
	        String pluginsDir = url.substring(5, url.length() - clazz.getName().length() - 6);
	        System.setProperty("plugins.dir", pluginsDir);
	        // start ImageJ
	        if (!GraphicsEnvironment.isHeadless()) {
	            new ImageJ();
	        }
	        // run the plugin
	        IJ.runPlugIn(clazz.getName(), "");
	}
}
