gt:     subtractThreshold = gd.getNextBoolean();
context: /*-
   * #%L
   * Genome Damage and Stability Centre ImageJ Plugins
   *
   * Software for microscopy image analysis
   * %%
   * Copyright (C) 2011 - 2019 Alex Herbert
   * %%
   * This program 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/gpl-3.0.html>.
   * #L%
   */
  
  package uk.ac.sussex.gdsc.colocalisation;
  
  import uk.ac.sussex.gdsc.UsageTracker;
  import uk.ac.sussex.gdsc.colocalisation.cda.Cda_PlugIn;
  import uk.ac.sussex.gdsc.colocalisation.cda.TwinStackShifter;
  import uk.ac.sussex.gdsc.core.ij.ThresholdUtils;
  import uk.ac.sussex.gdsc.core.ij.gui.ExtendedGenericDialog;
  import uk.ac.sussex.gdsc.core.threshold.AutoThreshold;
  import uk.ac.sussex.gdsc.core.utils.Correlator;
  import uk.ac.sussex.gdsc.utils.SliceCollection;
  
  import ij.IJ;
  import ij.ImagePlus;
  import ij.ImageStack;
  import ij.plugin.filter.PlugInFilter;
  import ij.process.Blitter;
  import ij.process.ByteProcessor;
  import ij.process.ImageProcessor;
  import ij.text.TextWindow;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  
  /**
   * Processes a stack image with multiple channels. Requires three channels. Each frame is processed
   * separately. Extracts all the channels (collating z-stacks) and performs: (1). Thresholding to
   * create a mask for each channel (2). CDA analysis of channel 1 vs channel 2 within the region
   * defined by channel 3.
   */
  public class StackColocalisationAnalyser_PlugIn implements PlugInFilter {
    private static final String TITLE = "Stack Colocalisation Analyser";
    private static final String COMBINE = "Combine 1+2";
    private static final String NONE = "None";
  
    // ImageJ indexes for the dimensions array
    private static final int C = 2;
    private static final int Z = 3;
    private static final int T = 4;
  
    private static TextWindow tw;
  
    private static String methodOption = AutoThreshold.Method.OTSU.toString();
    private static int channel1 = 1;
    private static int channel2 = 2;
    private static int channel3;
  
    // Options flags
    private static boolean logThresholds;
    private static boolean logResults;
    private static boolean showMask;
    private static boolean subtractThreshold;
  
    private static int permutations = 100;
    private static int minimumRadius = 9;
    private static int maximumRadius = 16;
    private static double pCut = 0.05;
  
    // Store a reference to the current working image
    private ImagePlus imp;
  
    private boolean firstResult;
    private Correlator correlator;
    private int[] ii1;
    private int[] ii2;
  
    /** {@inheritDoc} */
    @Override
    public int setup(String arg, ImagePlus imp) {
      UsageTracker.recordPlugin(this.getClass(), arg);
  
      if (imp == null) {
        return DONE;
      }
  
      final int[] dimensions = imp.getDimensions();
  
      if (dimensions[2] < 2 || imp.getStackSize() < 2
          || (imp.getBitDepth() != 8 && imp.getBitDepth() != 16)) {
        if (IJ.isMacro()) {
          IJ.log("Multi-channel stack required (8-bit or 16-bit): " + imp.getTitle());
        } else {
          IJ.showMessage("Multi-channel stack required (8-bit or 16-bit)");
        }
        return DONE;
      }
  
      this.imp = imp;
  
      return DOES_16 + DOES_8G + NO_CHANGES;
    }
  
    /** {@inheritDoc} */
    @Override
    public void run(ImageProcessor inputProcessor) {
      final int[] dimensions = imp.getDimensions();
      final int currentSlice = imp.getCurrentSlice();
  
      final String[] methods = getMethods();
      // channel3 is set within getMethods()
      final int nChannels = (channel3 != 1) ? 3 : 2;
  
      final int size = dimensions[0] * dimensions[1];
      correlator = new Correlator(size);
      ii1 = new int[size];
      ii2 = new int[size];
  
      for (final String method : methods) {
        if (logThresholds || logResults) {
          IJ.log("Stack colocalisation (" + method + ") : " + imp.getTitle());
        }
  
        ImageStack maskStack = null;
        ImagePlus maskImage = null;
        if (showMask) {
          // The stack will only have 3 channels
          maskStack = new ImageStack(imp.getWidth(), imp.getHeight(),
              nChannels * dimensions[Z] * dimensions[T]);
  
          // Ensure empty layers are filled to avoid ImageJ error creating ImagePlus
          final byte[] empty = new byte[maskStack.getWidth() * maskStack.getHeight()];
          Arrays.fill(empty, (byte) 255);
          maskStack.setPixels(empty, 1);
  
          maskImage = new ImagePlus(imp.getTitle() + ":" + method, maskStack);
          maskImage.setDimensions(nChannels, dimensions[Z], dimensions[T]);
        }
  
        for (int t = 1; t <= dimensions[T]; t++) {
          final ArrayList<AnalysisSliceCollection> sliceCollections = new ArrayList<>();
  
          // Extract the channels
          for (int c = 1; c <= dimensions[C]; c++) {
            // Process all slices together
            final AnalysisSliceCollection sliceCollection = new AnalysisSliceCollection(c);
            for (int z = 1; z <= dimensions[Z]; z++) {
              sliceCollection.add(imp.getStackIndex(c, z, t));
            }
            sliceCollections.add(sliceCollection);
          }
  
          // Get the channels:
          final AnalysisSliceCollection s1 = sliceCollections.get(channel1 - 1);
          final AnalysisSliceCollection s2 = sliceCollections.get(channel2 - 1);
  
          // Create masks
          extractImageAndCreateOutputMask(method, maskImage, 1, t, s1);
          extractImageAndCreateOutputMask(method, maskImage, 2, t, s2);
  
          // Note that channel 3 is offset by 1 because it contains the [none] option
          AnalysisSliceCollection s3;
          if (channel3 > 1) {
            s3 = sliceCollections.get(channel3 - 2);
            extractImageAndCreateOutputMask(method, maskImage, 3, t, s3);
          } else {
            s3 = new AnalysisSliceCollection(0);
            if (channel3 == 0) {
              // Combine the two masks
              combineMasksAndCreateOutputMask(method, maskImage, 3, t, s1, s2, s3);
            }
          }
  
          final double[] results = correlate(s1, s2, s3);
  
          reportResult(method, t, s1.getSliceName(), s2.getSliceName(), s3.getSliceName(), results);
        }
  
        if (showMask) {
          maskImage.show();
          IJ.run("Stack to Hyperstack...", "order=xyczt(default) channels=" + nChannels + " slices="
              + dimensions[Z] + " frames=" + dimensions[T] + " display=Color");
        }
      }
      imp.setSlice(currentSlice);
    }
  
    private void extractImageAndCreateOutputMask(String method, ImagePlus maskImage, int channel,
        int frame, AnalysisSliceCollection sliceCollection) {
      sliceCollection.createStack(imp);
      sliceCollection.createMask(method);
      if (logThresholds) {
        IJ.log("t" + frame + sliceCollection.getSliceName() + " threshold = "
            + sliceCollection.threshold);
      }
  
      if (showMask) {
        final ImageStack maskStack = maskImage.getImageStack();
        for (int s = 1; s <= sliceCollection.maskStack.getSize(); s++) {
          final int originalSliceNumber = sliceCollection.get(s - 1);
          final int newSliceNumber = maskImage.getStackIndex(channel, s, frame);
          maskStack.setSliceLabel(method + ":" + imp.getStack().getSliceLabel(originalSliceNumber),
              newSliceNumber);
          maskStack.setPixels(sliceCollection.maskStack.getPixels(s), newSliceNumber);
        }
      }
    }
  
    private static void combineMasksAndCreateOutputMask(String method, ImagePlus maskImage,
        int channel, int frame, AnalysisSliceCollection s1, AnalysisSliceCollection s2,
        AnalysisSliceCollection sliceCollection) {
      sliceCollection.createMask(s1.maskStack, s2.maskStack);
      if (showMask) {
        final ImageStack maskStack = maskImage.getImageStack();
        for (int s = 1; s <= sliceCollection.maskStack.getSize(); s++) {
          final int newSliceNumber = maskImage.getStackIndex(channel, s, frame);
          maskStack.setSliceLabel(method + ":" + COMBINE, newSliceNumber);
          maskStack.setPixels(sliceCollection.maskStack.getPixels(s), newSliceNumber);
        }
      }
    }
  
    private String[] getMethods() {
      final ExtendedGenericDialog gd = new ExtendedGenericDialog(TITLE);
      gd.addMessage(TITLE);
      firstResult = true;
  
      String[] indices = new String[imp.getNChannels()];
      for (int i = 1; i <= indices.length; i++) {
        indices[i - 1] = "" + i;
      }
  
      gd.addChoice("Channel_1", indices, channel1 - 1);
      gd.addChoice("Channel_2", indices, channel2 - 1);
      indices = addNoneOption(indices);
      gd.addChoice("Channel_3", indices, channel3);
  
      // Commented out the methods that take a long time on 16-bit images.
      String[] methods = {"Try all", AutoThreshold.Method.DEFAULT.toString(),
          // "Huang",
          // "Intermodes",
          // "IsoData",
          AutoThreshold.Method.LI.toString(), AutoThreshold.Method.MAX_ENTROPY.toString(),
          AutoThreshold.Method.MEAN.toString(), AutoThreshold.Method.MIN_ERROR_I.toString(),
          // "Minimum",
          AutoThreshold.Method.MOMENTS.toString(), AutoThreshold.Method.OTSU.toString(),
          AutoThreshold.Method.PERCENTILE.toString(), AutoThreshold.Method.RENYI_ENTROPY.toString(),
          // "Shanbhag",
          AutoThreshold.Method.TRIANGLE.toString(), AutoThreshold.Method.YEN.toString(),
          AutoThreshold.Method.NONE.toString()};
  
      gd.addChoice("Method", methods, methodOption);
      gd.addCheckbox("Log_thresholds", logThresholds);
      gd.addCheckbox("Log_results", logResults);
      gd.addCheckbox("Show_mask", showMask);
      gd.addCheckbox("Subtract threshold", subtractThreshold);
      gd.addNumericField("Permutations", permutations, 0);
      gd.addNumericField("Minimum_shift", minimumRadius, 0);
      gd.addNumericField("Maximum_shift", maximumRadius, 0);
      gd.addNumericField("Significance", pCut, 3);
      gd.addHelp(uk.ac.sussex.gdsc.help.UrlUtils.COLOCALISATION);
  
      gd.showDialog();
      if (gd.wasCanceled()) {
        return new String[0];
      }
  
      channel1 = gd.getNextChoiceIndex() + 1;
      channel2 = gd.getNextChoiceIndex() + 1;
      channel3 = gd.getNextChoiceIndex();
      methodOption = gd.getNextChoice();
      logThresholds = gd.getNextBoolean();
      logResults = gd.getNextBoolean();
      showMask = gd.getNextBoolean();
  