package gdsc.colocalisation.cda; /*----------------------------------------------------------------------------- * GDSC Plugins for ImageJ * * Copyright (C) 2011 Alex Herbert * Genome Damage and Stability Centre * University of Sussex, UK * * This class is based on the original CDA_Plugin developed by * Maria Osorio-Reich: * http://imagejdocu.tudor.lu/doku.php?id=plugin:analysis:confined_displacement_algorithm_determines_true_and_random_colocalization_:start *---------------------------------------------------------------------------*/ import java.awt.BorderLayout; import java.awt.Button; import java.awt.Checkbox; import java.awt.Choice; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.Point; import java.awt.Rectangle; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowEvent; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import javax.swing.JPanel; import gdsc.UsageTracker; import gdsc.colocalisation.cda.engine.CDAEngine; import gdsc.colocalisation.cda.engine.CalculationResult; import gdsc.core.ij.Utils; import gdsc.core.utils.Random; import gdsc.core.utils.StoredData; import ij.IJ; import ij.ImagePlus; import ij.ImageStack; 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.macro.MacroRunner; import ij.plugin.frame.PlugInFrame; import ij.process.ByteProcessor; import ij.process.ColorProcessor; import ij.process.FloatProcessor; import ij.process.FloatStatistics; import ij.process.ImageProcessor; import ij.process.LUT; import ij.process.ShortProcessor; import ij.text.TextWindow; import ij.util.Tools; /** * Test for significant colocalisation within images using the Confined Displacement Algorithm (CDA). * * Colocalisation of two channels is measured using Mander's coefficients (M1 & M2) and Pearson's correlation * coefficient (R). Significance is tested by shifting one image to all positions within a circular radius and computing * a probability distribution for M1, M2 and R. Significance is measured using a specified p-value. * * Supports stacks. Only a specific channel and time frame can be used and all input images must have the same number * of z-sections for the chosen channel/time frame. */ public class CDA_Plugin extends PlugInFrame implements ActionListener, ItemListener, KeyListener, Runnable { private static final long serialVersionUID = 1L; private static String TITLE = "CDA Plugin"; private static String OPTION_NONE = "(None)"; private static String OPTION_MASK = "Use as mask"; private static String OPTION_MIN_VALUE = "Min Display Value"; private static String OPTION_USE_ROI = "Use ROI"; private static String[] ROI_OPTIONS = { OPTION_NONE, OPTION_MIN_VALUE, OPTION_USE_ROI, OPTION_MASK }; private static String CHANNEL_IMAGE = "(Channel image)"; // Image titles private static String channel1RGBTitle = "CDA Channel 1"; private static String channel2RGBTitle = "CDA Channel 2"; private static String segmented1RGBTitle = "CDA ROI 1"; private static String segmented2RGBTitle = "CDA ROI 2"; private static String mergedChannelTitle = "CDA Merged channel"; private static String mergedSegmentedTitle = "CDA Merged ROI"; private static String lastShiftedChannelTitle = "CDA Merged channel maximum displacement"; private static String lastShiftedSegmentedTitle = "CDA Merged ROI maximum displacement"; private static String m1HistogramTitle = "CDA M1 PDF"; private static String m2HistogramTitle = "CDA M2 PDF"; private static String rHistogramTitle = "CDA R PDF"; private static String plotM1Title = "CDA M1 samples"; private static String plotM2Title = "CDA M2 samples"; private static String plotRTitle = "CDA R samples"; // Configuration options private static String choiceChannel1 = "Channel 1"; private static String choiceChannel2 = "Channel 2"; private static String choiceSegmentedChannel1 = "ROI for channel 1"; private static String choiceSegmentedChannel2 = "ROI for channel 2"; private static String choiceConfined = "Confined compartment"; private static String choiceExpandConfined = " Include ROIs"; private static String choiceMaximumRadius = "Maximum radial displacement"; private static String choiceRandomRadius = "Random radial displacement"; private static String choiceSubRandomSamples = "Compute sub-random samples"; private static String numberOfSamplesLabel = " Approx. number of samples: "; private static String choiceBinsNumber = "Bins for histogram"; private static String choiceCloseWindowsOnExit = "Close windows on exit"; private static String choiceSetOptions = "Set results options"; private static String okButtonLabel = "Apply"; private static String helpButtonLabel = "Help"; // PDFs private static String plotXLabel = "Radial displacement d"; private static String plotM1YLabel = "M1"; private static String plotM2YLabel = "M2"; private static String plotRYLabel = "R"; private static String plotPDFYLabel = "PDF"; private static String pixelsUnitString = "[pixels]"; // Status messages private static String mandersCalculationStatus = "Calculating manders coefficients..."; private static String gettingConfigStatus = "Processing parameters..."; private static String doneStatus = "Done."; private static String calculatingFirstMandersStatus = "Calculating M1 and M2"; private static String preparingPlotsStatus = "Creating plots and images..."; private static String processingMandersStatus = "Processing manders coefficients..."; private static Frame instance; private Label pixelsLabel; private Choice channel1List; private Choice channel2List; private Choice segmented1List; private Choice segmented2List; private Choice confinedList; private Choice segmented1Option; private Choice segmented2Option; private Choice confinedOption; private Checkbox expandConfinedCheckbox; private TextField maximumRadiusText; private TextField randomRadiusText; private Checkbox subRandomSamplesCheckbox; private Label numberOfSamplesField; private TextField binsText; private Checkbox closeWindowsOnExitCheckbox; private Checkbox setResultsOptionsCheckbox; private Button okButton; private Button helpButton; private static int fontWidth = 12; private static Font monoFont = new Font("Monospaced", 0, fontWidth); // Windows that are opened by the CDA Plug-in. // These should be closed on exit. private static TextWindow tw; private ImagePlus channel1RGB; private ImagePlus channel2RGB; private ImagePlus segmented1RGB; private ImagePlus segmented2RGB; private ImagePlus mergedChannelRGB; private ImagePlus mergedSegmentedRGB; private ImagePlus mergedChannelDisplacementRGB; private ImagePlus mergedSegmentedDisplacementRGB; private PlotWindow m1PlotWindow; private PlotWindow m2PlotWindow; private PlotWindow rPlotWindow; private DisplayStatistics m1Statistics; private DisplayStatistics m2Statistics; private DisplayStatistics rStatistics; // Options private final String OPT_LOCATION = "CDA.location"; private final String OPT_LOCATION_PLOT_M1 = "CDA.locationPlotM1"; private final String OPT_LOCATION_PLOT_M2 = "CDA.locationPlotM2"; private final String OPT_LOCATION_PLOT_R = "CDA.locationPlotR"; private final String OPT_LOCATION_STATS_M1 = "CDA.locationStatisticsM1"; private final String OPT_LOCATION_STATS_M2 = "CDA.locationStatisticsM2"; private final String OPT_LOCATION_STATS_R = "CDA.locationStatisticsR"; private final String OPT_CHANNEL1_INDEX = "CDA.channel1Index"; private final String OPT_CHANNEL2_INDEX = "CDA.channel2Index"; private final String OPT_SEGMENTED1_INDEX = "CDA.segmented1Index"; private final String OPT_SEGMENTED2_INDEX = "CDA.segmented2Index"; private final String OPT_CONFINED_INDEX = "CDA.confinedIndex"; private final String OPT_SEGMENTED1_OPTION_INDEX = "CDA.segmented1OptionIndex"; private final String OPT_SEGMENTED2_OPTION_INDEX = "CDA.segmented2OptionIndex"; private final String OPT_CONFINED_OPTION_INDEX = "CDA.confinedOptionIndex"; private final String OPT_EXPAND_CONFINED = "CDA.expandConfined"; private final String OPT_MAXIMUM_RADIUS = "CDA.maximumRadius"; private final String OPT_RANDOM_RADIUS = "CDA.randomRadius"; private final String OPT_SUB_RANDOM_SAMPLES = "CDA.subRandomSamples"; private final String OPT_HISTOGRAM_BINS = "CDA.histogramBins"; private final String OPT_CLOSE_WINDOWS_ON_EXIT = "CDA.closeWindowsOnExit"; private final String OPT_SET_OPTIONS = "CDA.setOptions"; private final String OPT_SHOW_CHANNEL1_RGB = "CDA.showChannel1"; private final String OPT_SHOW_CHANNEL2_RGB = "CDA.showChannel2"; private final String OPT_SHOW_SEGMENTED1_RGB = "CDA.showSegmented1"; private final String OPT_SHOW_SEGMENTED2_RGB = "CDA.showSegmented2"; private final String OPT_SHOW_MERGED_CHANNEL_RGB = "CDA.showMergedChannel"; private final String OPT_SHOW_MERGED_SEGMENTED_RGB = "CDA.showMergedSegmented"; private final String OPT_SHOW_MERGED_CHANNEL_DISPLACEMENT_RGB = "CDA.showMergedChannelDisplacement"; private final String OPT_SHOW_MERGED_SEGMENTED_DISPLACEMENT_RGB = "CDA.showMergedSegmentedDisplacement"; private final String OPT_SHOW_M1_PLOT_WINDOW = "CDA.showM1PlotWindow"; private final String OPT_SHOW_M2_PLOT_WINDOW = "CDA.showM2PlotWindow"; private final String OPT_SHOW_R_PLOT_WINDOW = "CDA.showRPlotWindow"; private final String OPT_SHOW_M1_STATISTICS = "CDA.showM1Statistics"; private final String OPT_SHOW_M2_STATISTICS = "CDA.showM2Statistics"; private final String OPT_SHOW_R_STATISTICS = "CDA.showRStatistics"; private final String OPT_SAVE_RESULTS = "CDA.saveResults"; private final String OPT_RESULTS_DIRECTORY = "CDA.resultsDirectory"; private final String OPT_P_VALUE = "CDA.pValue"; private final String OPT_PERMUTATIONS = "CDA.permutations"; private final int DEFAULT_MAXIMUM_RADIUS = 12; private final int DEFAULT_RANDOM_RADIUS = 7; private final int DEFAULT_HISTOGRAM_BINS = 16; private int channel1Index = (int) Prefs.get(OPT_CHANNEL1_INDEX, 0); private int channel2Index = (int) Prefs.get(OPT_CHANNEL2_INDEX, 0); private int segmented1Index = (int) Prefs.get(OPT_SEGMENTED1_INDEX, 0); private int segmented2Index = (int) Prefs.get(OPT_SEGMENTED2_INDEX, 0); private int confinedIndex = (int) Prefs.get(OPT_CONFINED_INDEX, 0); private int segmented1OptionIndex = (int) Prefs.get(OPT_SEGMENTED1_OPTION_INDEX, 0); private int segmented2OptionIndex = (int) Prefs.get(OPT_SEGMENTED2_OPTION_INDEX, 0); private int confinedOptionIndex = (int) Prefs.get(OPT_CONFINED_OPTION_INDEX, 0); private boolean expandConfinedCompartment = Prefs.get(OPT_EXPAND_CONFINED, false); private int maximumRadius = (int) Prefs.get(OPT_MAXIMUM_RADIUS, DEFAULT_MAXIMUM_RADIUS); private int randomRadius = (int) Prefs.get(OPT_RANDOM_RADIUS, DEFAULT_RANDOM_RADIUS); private boolean subRandomSamples = Prefs.get(OPT_SUB_RANDOM_SAMPLES, true); private int histogramBins = (int) Prefs.get(OPT_HISTOGRAM_BINS, DEFAULT_HISTOGRAM_BINS); private boolean closeWindowsOnExit = Prefs.get(OPT_CLOSE_WINDOWS_ON_EXIT, false); private boolean setOptions = Prefs.get(OPT_SET_OPTIONS, false); private boolean showChannel1RGB = Prefs.get(OPT_SHOW_CHANNEL1_RGB, false); private boolean showChannel2RGB = Prefs.get(OPT_SHOW_CHANNEL2_RGB, false); private boolean showSegmented1RGB = Prefs.get(OPT_SHOW_SEGMENTED1_RGB, false); private boolean showSegmented2RGB = Prefs.get(OPT_SHOW_SEGMENTED2_RGB, false); private boolean showMergedChannelRGB = Prefs.get(OPT_SHOW_MERGED_CHANNEL_RGB, true); private boolean showMergedSegmentedRGB = Prefs.get(OPT_SHOW_MERGED_SEGMENTED_RGB, true); private boolean showMergedChannelDisplacementRGB = Prefs.get(OPT_SHOW_MERGED_CHANNEL_DISPLACEMENT_RGB, false); private boolean showMergedSegmentedDisplacementRGB = Prefs.get(OPT_SHOW_MERGED_SEGMENTED_DISPLACEMENT_RGB, false); private boolean showM1PlotWindow = Prefs.get(OPT_SHOW_M1_PLOT_WINDOW, true); private boolean showM2PlotWindow = Prefs.get(OPT_SHOW_M2_PLOT_WINDOW, true); private boolean showRPlotWindow = Prefs.get(OPT_SHOW_R_PLOT_WINDOW, true); private boolean showM1Statistics = Prefs.get(OPT_SHOW_M1_STATISTICS, true); private boolean showM2Statistics = Prefs.get(OPT_SHOW_M2_STATISTICS, true); private boolean showRStatistics = Prefs.get(OPT_SHOW_R_STATISTICS, true); private boolean saveResults = Prefs.get(OPT_SAVE_RESULTS, false); private String resultsDirectory = Prefs.get(OPT_RESULTS_DIRECTORY, System.getProperty("java.io.tmpdir")); private double pValue = Prefs.get(OPT_P_VALUE, 0.05); private int permutations = (int) Prefs.get(OPT_PERMUTATIONS, 200); // Store the channels and frames to use from image stacks private static int[] sliceOptions = new int[10]; // Store the display range min for each ROI private int[] displayMin = new int[3]; // Stores the list of images last used in the selection options private ArrayList<String> imageList = new ArrayList<String>(); public CDA_Plugin() { super(TITLE); } /* * (non-Javadoc) * * @see ij.plugin.frame.PlugInFrame#run(java.lang.String) */ public void run(String arg) { UsageTracker.recordPlugin(this.getClass(), arg); if (WindowManager.getImageCount() == 0) { IJ.showMessage("No images opened."); return; } if ("macro".equals(arg)) { runAsPlugin(); return; } if (instance != null) { if (!(instance.getTitle().equals(getTitle()))) { CDA_Plugin cda = (CDA_Plugin) instance; Prefs.saveLocation(OPT_LOCATION, cda.getLocation()); cda.close(); } else { instance.toFront(); return; } } instance = this; IJ.register(CDA_Plugin.class); WindowManager.addWindow(this); createFrame(); setup(); addKeyListener(IJ.getInstance()); pack(); Point loc = Prefs.getLocation(OPT_LOCATION); if (loc != null) setLocation(loc); else { GUI.center(this); } if (IJ.isMacOSX()) setResizable(false); setVisible(true); } private void setup() { ImagePlus imp = WindowManager.getCurrentImage(); if (imp == null) return; fillImagesList(); } public synchronized void actionPerformed(ActionEvent e) { Object actioner = e.getSource(); if (actioner == null) return; if (((Button) actioner == okButton) && (parametersReady())) { Thread thread = new Thread(this, "CDA_Plugin"); thread.start(); } if ((Button) actioner == helpButton) { String macro = "run('URL...', 'url=" + gdsc.help.URL.COLOCALISATION + "');"; new MacroRunner(macro); } super.notify(); } public void itemStateChanged(ItemEvent e) { Object actioner = e.getSource(); if (actioner == null) return; if ((Checkbox) actioner == subRandomSamplesCheckbox) { updateNumberOfSamples(); return; } if (setResultsOptionsCheckbox.getState()) { setResultsOptionsCheckbox.setState(false); setResultsOptions(); updateNumberOfSamples(); } } public void setResultsOptions() { GenericDialog gd = new GenericDialog("Set Results Options"); gd.addCheckbox("Show_channel_1", showChannel1RGB); gd.addCheckbox("Show_channel_2", showChannel2RGB); gd.addCheckbox("Show_ROI_1", showSegmented1RGB); gd.addCheckbox("Show_ROI_2", showSegmented2RGB); gd.addCheckbox("Show_merged_channel", showMergedChannelRGB); gd.addCheckbox("Show_merged_ROI", showMergedSegmentedRGB); gd.addCheckbox("Show_merged_channel_max_displacement", showMergedChannelDisplacementRGB); gd.addCheckbox("Show_merged_ROI_max_displacement", showMergedSegmentedDisplacementRGB); gd.addCheckbox("Show_M1_plot", showM1PlotWindow); gd.addCheckbox("Show_M2_plot", showM2PlotWindow); gd.addCheckbox("Show_R_plot", showRPlotWindow); gd.addCheckbox("Show_M1_statistics", showM1Statistics); gd.addCheckbox("Show_M2_statistics", showM2Statistics); gd.addCheckbox("Show_R_statistics", showRStatistics); gd.addCheckbox("Save_results", saveResults); gd.addStringField("Results_directory", resultsDirectory); gd.addNumericField("p-Value", pValue, 2); gd.addNumericField("Permutations", permutations, 0); gd.showDialog(); if (gd.wasCanceled()) return; showChannel1RGB = gd.getNextBoolean(); showChannel2RGB = gd.getNextBoolean(); showSegmented1RGB = gd.getNextBoolean(); showSegmented2RGB = gd.getNextBoolean(); showMergedChannelRGB = gd.getNextBoolean(); showMergedSegmentedRGB = gd.getNextBoolean(); showMergedChannelDisplacementRGB = gd.getNextBoolean(); showMergedSegmentedDisplacementRGB = gd.getNextBoolean(); showM1PlotWindow = gd.getNextBoolean(); showM2PlotWindow = gd.getNextBoolean(); showRPlotWindow = gd.getNextBoolean(); showM1Statistics = gd.getNextBoolean(); showM2Statistics = gd.getNextBoolean(); showRStatistics = gd.getNextBoolean(); saveResults = gd.getNextBoolean(); resultsDirectory = gd.getNextString(); pValue = gd.getNextNumber(); permutations = (int) gd.getNextNumber(); if (permutations < 0) permutations = 0; checkResultsDirectory(); } private boolean checkResultsDirectory() { if (!new File(resultsDirectory).exists()) { IJ.error("The results directory does not exist. Results will not be saved."); return false; } return true; } public void keyTyped(KeyEvent e) { // Do nothing } public void keyPressed(KeyEvent e) { // Do nothing } public void keyReleased(KeyEvent e) { Object actioner = e.getSource(); if (actioner == null) return; // Update the number of combinations updateNumberOfSamples(); } public void windowClosing(WindowEvent e) { closeWindowsOnExit = closeWindowsOnExitCheckbox.getState(); Prefs.set(OPT_CLOSE_WINDOWS_ON_EXIT, closeWindowsOnExit); Prefs.saveLocation(OPT_LOCATION, getLocation()); close(); } public void close() { if (closeWindowsOnExit) { closeImagePlus(channel1RGB); closeImagePlus(channel2RGB); closeImagePlus(segmented1RGB); closeImagePlus(segmented2RGB); closeImagePlus(mergedChannelRGB); closeImagePlus(mergedSegmentedRGB); closeImagePlus(mergedSegmentedRGB); closeImagePlus(mergedSegmentedDisplacementRGB); closeImagePlus(mergedChannelDisplacementRGB); closePlotWindow(m1PlotWindow, OPT_LOCATION_PLOT_M1); closePlotWindow(m2PlotWindow, OPT_LOCATION_PLOT_M2); closePlotWindow(rPlotWindow, OPT_LOCATION_PLOT_R); closeDisplayStatistics(m1Statistics, OPT_LOCATION_STATS_M1); closeDisplayStatistics(m2Statistics, OPT_LOCATION_STATS_M2); closeDisplayStatistics(rStatistics, OPT_LOCATION_STATS_R); if (tw != null && tw.isShowing()) { tw.close(); } } instance = null; super.close(); } private void closeImagePlus(ImagePlus w) { if (w != null && w.isVisible()) { w.close(); } } private void closePlotWindow(PlotWindow w, String locationKey) { if (w != null && !w.isClosed()) { Prefs.saveLocation(locationKey, w.getLocation()); w.close(); } } private void closeDisplayStatistics(DisplayStatistics w, String locationKey) { if (w != null && w.getPlotWindow() != null) { if (w.getPlotWindow().isShowing()) { Prefs.saveLocation(locationKey, w.getPlotWindow().getLocation()); w.close(); } } } public void windowActivated(WindowEvent e) { super.windowActivated(e); fillImagesList(); WindowManager.setWindow(this); } public static void update() { if (instance != null) { CDA_Plugin cda = (CDA_Plugin) instance; cda.fillImagesList(); } } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ public void run() { doCDA(); synchronized (this) { super.notify(); } } private void doCDA() { if (!(parametersReady())) { return; } IJ.showStatus(gettingConfigStatus); // Stores these so that they can be reset later channel1Index = channel1List.getSelectedIndex(); channel2Index = channel2List.getSelectedIndex(); segmented1Index = segmented1List.getSelectedIndex(); segmented2Index = segmented2List.getSelectedIndex(); confinedIndex = confinedList.getSelectedIndex(); segmented1OptionIndex = segmented1Option.getSelectedIndex(); segmented2OptionIndex = segmented2Option.getSelectedIndex(); confinedOptionIndex = confinedOption.getSelectedIndex(); expandConfinedCompartment = expandConfinedCheckbox.getState(); subRandomSamples = subRandomSamplesCheckbox.getState(); maximumRadius = getIntValue(maximumRadiusText.getText(), DEFAULT_MAXIMUM_RADIUS); randomRadius = getIntValue(randomRadiusText.getText(), DEFAULT_RANDOM_RADIUS); histogramBins = getIntValue(binsText.getText(), DEFAULT_HISTOGRAM_BINS); closeWindowsOnExit = closeWindowsOnExitCheckbox.getState(); ImagePlus imp1 = WindowManager.getImage(channel1List.getSelectedItem()); ImagePlus imp2 = WindowManager.getImage(channel2List.getSelectedItem()); ImagePlus roi1 = getRoiSource(imp1, segmented1List, segmented1Option); ImagePlus roi2 = getRoiSource(imp2, segmented2List, segmented2Option); ImagePlus roi = getRoiSource(null, confinedList, confinedOption); runCDA(imp1, imp2, roi1, roi2, roi); } private void runCDA(ImagePlus imp1, ImagePlus imp2, ImagePlus roi1, ImagePlus roi2, ImagePlus roi) { // Check for image stacks and get the channel and frame (if applicable) if (!getStackOptions(imp1, imp2, roi1, roi2, roi)) return; // TODO - Should the select channel/frames be saved (i.e. the slice options array)? saveOptions(); long startTime = System.currentTimeMillis(); // Extract images ImageStack imageStack1 = getStack(imp1, 0); ImageStack imageStack2 = getStack(imp2, 2); // Get the ROIs ImageStack roiStack1 = createROI(imp1, roi1, 4, segmented1OptionIndex); ImageStack roiStack2 = createROI(imp2, roi2, 6, segmented2OptionIndex); ImageStack confinedStack = createROI(imp1, roi, 8, confinedOptionIndex); // The images and the masks should be the same size. if (!checkDimensions(imageStack1, imageStack2, roiStack1, roiStack2, confinedStack)) return; // Check inputs if (roiStack1 == null || roiStack2 == null || confinedStack == null) { IJ.showMessage(TITLE, "ROIs must be at the same width and height of the input images"); return; } long[] sum = new long[1]; if (expandConfinedCompartment) { // Expand the confined compartment to include all of the ROIs unionMask(confinedStack, roiStack1); unionMask(confinedStack, roiStack2); } else { // Restrict the ROIs to the confined compartment roiStack1 = intersectMask(roiStack1, confinedStack, sum, false); roiStack2 = intersectMask(roiStack2, confinedStack, sum, false); } imageStack1 = intersectMask(imageStack1, confinedStack, sum, false); imageStack2 = intersectMask(imageStack2, confinedStack, sum, false); // This could be changed to check for a certain pixel count (e.g. (overlap / 255) < [count]) if (sum[0] == 0) { IJ.error("The two ROIs do not overlap ... quitting"); return; } IJ.showStatus(calculatingFirstMandersStatus); // Pre-calculate constants intersectMask(imageStack1, roiStack1, sum, true); double denom1 = (double) sum[0]; intersectMask(imageStack2, roiStack2, sum, true); double denom2 = (double) sum[0]; IJ.showStatus(mandersCalculationStatus); // Build list of shifts above and below the random radius int[] shiftIndices = buildShiftIndices(subRandomSamples, randomRadius, maximumRadius); List<CalculationResult> results = calculateResults(imageStack1, roiStack1, confinedStack, imageStack2, roiStack2, denom1, denom2, shiftIndices); if (Utils.isInterrupted()) return; // Get unshifted result CalculationResult unshifted = null; for (CalculationResult r : results) { if (r.distance == 0) { unshifted = r; break; } } // Display an image of the largest shift ImageStack lastChannelShiftedRawStack = null; ImageStack lastSegmentedShiftedRawStack = null; if (showMergedChannelDisplacementRGB || showMergedSegmentedDisplacementRGB) { TwinStackShifter twinImageShifter = new TwinStackShifter(imageStack1, roiStack1, confinedStack); twinImageShifter.run(maximumRadius, 0); lastChannelShiftedRawStack = twinImageShifter.getResultStack(); lastSegmentedShiftedRawStack = twinImageShifter.getResultStack2(); } reportResults(imp1, imp2, imageStack1, imageStack2, roiStack1, roiStack2, confinedStack, lastChannelShiftedRawStack, lastSegmentedShiftedRawStack, unshifted.m1, unshifted.m2, unshifted.r, results); IJ.showStatus(doneStatus + " Time taken = " + (System.currentTimeMillis() - startTime) / 1000.0); } private int[] buildShiftIndices(boolean subRandomSamples, int randomRadius, int maximumRadius) { int[] shiftIndices1 = randomIndices(randomRadius, maximumRadius); int[] shiftIndices2 = randomIndices(0, (subRandomSamples) ? randomRadius : 0); int[] shiftIndices = merge(shiftIndices1, shiftIndices2); return shiftIndices; } private int[] randomIndices(int minimumRadius, int maximumRadius) { // Count the number of permutations double maximumRadius2 = maximumRadius * maximumRadius; double minimumRadius2 = minimumRadius * minimumRadius; int totalPermutations = 0; for (int i = -maximumRadius; i <= maximumRadius; ++i) { for (int j = -maximumRadius; j <= maximumRadius; ++j) { double distance2 = i * i + j * j; if (distance2 > maximumRadius2 || distance2 <= minimumRadius2) continue; totalPermutations++; } } if (totalPermutations == 0) return new int[0]; // Randomise the permutations int[] indices = new int[totalPermutations]; totalPermutations = 0; for (int i = -maximumRadius; i <= maximumRadius; ++i) { for (int j = -maximumRadius; j <= maximumRadius; ++j) { double distance2 = i * i + j * j; if (distance2 > maximumRadius2 || distance2 <= minimumRadius2) continue; // Pack the magnitude of the shift into the first 4 bytes and then pack the signs int index = (Math.abs(i) & 0xff) << 8 | Math.abs(j) & 0xff; if (i < 0) index |= 0x00010000; if (j < 0) index |= 0x00020000; indices[totalPermutations++] = index; } } if (permutations < totalPermutations && permutations > 0) { // Fisher-Yates shuffle to randomly select Random rand = new Random(); rand.shuffle(indices); return Arrays.copyOf(indices, permutations); } return indices; } private int[] merge(int[] shiftIndices1, int[] shiftIndices2) { // Include an extra entry for zero shift int[] indices = new int[shiftIndices1.length + shiftIndices2.length + 1]; int j = 0; for (int index : shiftIndices1) indices[j++] = index; for (int index : shiftIndices2) indices[j++] = index; return indices; } private List<CalculationResult> calculateResults(ImageStack imageStack1, ImageStack roiStack1, ImageStack confinedStack, ImageStack imageStack2, ImageStack roiStack2, double denom1, double denom2, int[] shiftIndices) { // Initialise the progress count int totalSteps = shiftIndices.length; IJ.showStatus("Creating CDA Engine ..."); List<CalculationResult> results = new ArrayList<CalculationResult>(totalSteps); int threads = Prefs.getThreads(); CDAEngine engine = new CDAEngine(imageStack1, roiStack1, confinedStack, imageStack2, roiStack2, denom1, denom2, results, totalSteps, threads); // Wait for initialisation engine.isInitialised(); IJ.showProgress(0); IJ.showStatus("Computing shifts ..."); for (int n = 0; n < shiftIndices.length; n++) { int index = shiftIndices[n]; int y = index & 0xff; int x = (index & 0xff00) >> 8; if ((index & 0x00010000) == 0x00010000) x = -x; if ((index & 0x00020000) == 0x00020000) y = -y; engine.run(n, x, y); if (Utils.isInterrupted()) { engine.end(true); break; } } engine.end(false); //IJ.log("# of results = " + results.size() + " / " + totalSteps); IJ.showProgress(1.0); IJ.showStatus("Computing shifts ..."); return results; } private boolean checkDimensions(ImageStack imageStack1, ImageStack imageStack2, ImageStack roiStack1, ImageStack roiStack2, ImageStack confinedStack) { int w = imageStack1.getWidth(); int h = imageStack1.getHeight(); int s = imageStack1.getSize(); if (!checkDimensions(w, h, s, imageStack2)) return false; if (!checkDimensions(w, h, s, roiStack1)) return false; if (!checkDimensions(w, h, s, roiStack1)) return false; if (!checkDimensions(w, h, s, confinedStack)) return false; return true; } private boolean checkDimensions(int w, int h, int s, ImageStack stack) { if (stack != null && (w != stack.getWidth() || h != stack.getHeight() || s != stack.getSize())) { IJ.showMessage(TITLE, "Images must be the same width and height and depth"); return false; } return true; } private ImagePlus getRoiSource(ImagePlus imp, Choice imageList, Choice optionList) { // Get the ROI option if (optionList.getSelectedItem().equals(OPTION_NONE)) { // No ROI image return null; } // Find the image in the image list if (imageList.getSelectedItem().equals(CHANNEL_IMAGE)) { // Channel image is the source for the ROI return imp; } return WindowManager.getImage(imageList.getSelectedItem()); } private boolean getStackOptions(ImagePlus imp1, ImagePlus imp2, ImagePlus roi1, ImagePlus roi2, ImagePlus roi) { if (isStack(imp1) || isStack(imp1) || isStack(roi1) || isStack(roi2) || isStack(roi)) { GenericDialog gd = new GenericDialog("Slice options"); gd.addMessage("Stacks detected. Please select the slices."); boolean added = false; added |= addOptions(gd, imp1, "Image_1", 0); added |= addOptions(gd, imp2, "Image_2", 2); added |= addOptions(gd, roi1, "ROI_1", 4); added |= addOptions(gd, roi2, "ROI_2", 6); added |= addOptions(gd, roi, "Confined", 8); if (added) { gd.showDialog(); if (gd.wasCanceled()) return false; // Populate the channels and frames into an options array getOptions(gd, imp1, 0); getOptions(gd, imp2, 2); getOptions(gd, roi1, 4); getOptions(gd, roi2, 6); getOptions(gd, roi, 8); } } return true; } private boolean isStack(ImagePlus imp) { return (imp != null && imp.getStackSize() > 1); } private boolean addOptions(GenericDialog gd, ImagePlus imp, String title, int offset) { boolean added = false; if (imp != null) { String[] channels = getChannels(imp); String[] frames = getFrames(imp); if (channels.length > 1 || frames.length > 1) { added = true; //gd.addMessage(title); } setOption(gd, channels, title + "_Channel", offset); setOption(gd, frames, title + "_Frame", offset + 1); } return added; } private boolean setOption(GenericDialog gd, String[] choices, String title, int offset) { if (choices.length > 1) { // Restore previous selection int c = (sliceOptions[offset] > 0 && sliceOptions[offset] <= choices.length) ? sliceOptions[offset] - 1 : 0; gd.addChoice(title, choices, choices[c]); return true; } else { // Set to default sliceOptions[offset] = 1; return false; } } private String[] getChannels(ImagePlus imp) { int c = imp.getNChannels(); String[] result = new String[c]; for (int i = 0; i < c; i++) result[i] = Integer.toString(i + 1); return result; } private String[] getFrames(ImagePlus imp) { int c = imp.getNFrames(); String[] result = new String[c]; for (int i = 0; i < c; i++) result[i] = Integer.toString(i + 1); return result; } private void getOptions(GenericDialog gd, ImagePlus imp, int offset) { if (imp != null) { String[] channels = getChannels(imp); String[] frames = getFrames(imp); if (channels.length > 1) sliceOptions[offset] = gd.getNextChoiceIndex() + 1; if (frames.length > 1) sliceOptions[offset + 1] = gd.getNextChoiceIndex() + 1; } } private int getIntValue(String text, int defaultValue) { try { return Integer.parseInt(text); } catch (NumberFormatException e) { return defaultValue; } } private ImageStack getStack(ImagePlus imp, int offset) { int channel = sliceOptions[offset]; int frame = sliceOptions[offset + 1]; int slices = imp.getNSlices(); ImageStack stack = new ImageStack(imp.getWidth(), imp.getHeight()); ImageStack inputStack = imp.getImageStack(); for (int slice = 1; slice <= slices; slice++) { // Convert to a short processor ImageProcessor ip = inputStack.getProcessor(imp.getStackIndex(channel, slice, frame)); stack.addSlice(null, convertToShortProcessor(imp, ip, channel)); } return stack; } private ShortProcessor convertToShortProcessor(ImagePlus imp, ImageProcessor ip, int channel) { ShortProcessor result; if (ip.getClass() == ShortProcessor.class) result = (ShortProcessor) ip.duplicate(); else result = (ShortProcessor) (ip.convertToShort(false)); result.setMinAndMax(getDisplayRangeMin(imp, channel), imp.getDisplayRangeMax()); result.setRoi(imp.getRoi()); return result; } private ImageStack createROI(ImagePlus channelImp, ImagePlus roiImp, int offset, int optionIndex) { ByteProcessor bp; // Get the ROI option String option = ROI_OPTIONS[optionIndex]; if (option.equals(OPTION_NONE) || roiImp == null || roiImp.getWidth() != channelImp.getWidth() || roiImp.getHeight() != channelImp.getHeight()) { // No ROI - create a mask that covers the entire image return defaultMask(channelImp); } ImageStack inputStack = roiImp.getImageStack(); int channel = sliceOptions[offset]; int frame = sliceOptions[offset + 1]; int slices = roiImp.getNSlices(); if (option.equals(OPTION_MIN_VALUE) || option.equals(OPTION_MASK)) { // Use the ROI image to create a mask either using: // - non-zero pixels (i.e. a mask) // - all pixels above the minimum display value ImageStack result = new ImageStack(roiImp.getWidth(), roiImp.getHeight()); final int min = (option.equals(OPTION_MIN_VALUE)) ? getDisplayRangeMin(roiImp, channel) : 1; int minIndex = (offset - 4) / 2; displayMin[minIndex] = min; for (int slice = 1; slice <= slices; slice++) { bp = new ByteProcessor(roiImp.getWidth(), roiImp.getHeight()); ImageProcessor roiIp = inputStack.getProcessor(roiImp.getStackIndex(channel, slice, frame)); for (int i = roiIp.getPixelCount(); i-- > 0;) { if (roiIp.get(i) >= min) { bp.set(i, 255); } } result.addSlice(null, bp); } return result; } if (option.equals(OPTION_USE_ROI)) { // Use the ROI from the ROI image Roi roi = roiImp.getRoi(); if (roi != null) { // Use a mask for an irregular ROI ImageProcessor ipMask = roiImp.getMask(); // Create a mask from the ROI rectangle Rectangle bounds = roi.getBounds(); int xOffset = bounds.x; int yOffset = bounds.y; int rwidth = bounds.width; int rheight = bounds.height; ImageStack result = new ImageStack(roiImp.getWidth(), roiImp.getHeight()); for (int slice = 1; slice <= slices; slice++) { bp = new ByteProcessor(roiImp.getWidth(), roiImp.getHeight()); for (int y = 0; y < rheight; y++) { for (int x = 0; x < rwidth; x++) { if (ipMask == null || ipMask.get(x, y) != 0) { bp.set(x + xOffset, y + yOffset, 255); } } } result.addSlice(null, bp); } return result; } } return defaultMask(channelImp); } private int getDisplayRangeMin(ImagePlus imp, int channel) { // Composite images can have a display range for each color channel LUT[] luts = imp.getLuts(); if (luts != null && channel <= luts.length && channel > 0) return (int) luts[channel - 1].min; return (int) imp.getDisplayRangeMin(); } private ImageProcessor getRoiIp(ImageProcessor channelIp, int index) { ImageProcessor roiIp = null; if (index < 0) { roiIp = channelIp; } else { ImagePlus roiImage = WindowManager.getImage(imageList.get(index)); if (roiImage != null) { roiIp = roiImage.getProcessor(); roiIp.setRoi(roiImage.getRoi()); } } return roiIp; } /** * Build a stack that covers all z-slices in the image */ private ImageStack defaultMask(ImagePlus imp) { ImageStack result = new ImageStack(imp.getWidth(), imp.getHeight()); for (int s = imp.getNSlices(); s-- > 0;) { ByteProcessor bp = new ByteProcessor(imp.getWidth(), imp.getHeight()); bp.add(255); result.addSlice(null, bp); } return result; } private void reportResults(ImagePlus imp1, ImagePlus imp2, ImageStack imageStack1, ImageStack imageStack2, ImageStack roiStack1, ImageStack roiStack2, ImageStack confinedStack, ImageStack lastChannelShiftedRawStack, ImageStack lastSegmentedShiftedRawStack, double M1, double M2, double R, List<CalculationResult> results) { IJ.showStatus(processingMandersStatus); // Extract the results into individual arrays double[] distances = new double[results.size()]; double[] m1Values = new double[results.size()]; double[] m2Values = new double[results.size()]; double[] rValues = new double[results.size()]; int[] indexDistance = new int[m1Values.length]; int size = 0; for (int i = 0; i < m1Values.length; ++i) { CalculationResult result = results.get(i); distances[i] = result.distance; m1Values[i] = result.m1; m2Values[i] = result.m2; rValues[i] = result.r; // All results over the random distance threshold will be used for the significance calculation if (distances[i] > randomRadius) { indexDistance[size++] = i; } } // Extract the results above the random threshold float[] m1ValuesForRandom = new float[size]; float[] m2ValuesForRandom = new float[size]; float[] rValuesForRandom = new float[size]; for (int i = 0; i < m1ValuesForRandom.length; ++i) { m1ValuesForRandom[i] = (float) m1Values[indexDistance[i]]; m2ValuesForRandom[i] = (float) m2Values[indexDistance[i]]; rValuesForRandom[i] = (float) rValues[indexDistance[i]]; } // Sort the 'random' result arrays Arrays.sort(m1ValuesForRandom); Arrays.sort(m2ValuesForRandom); Arrays.sort(rValuesForRandom); // Initialise the graph points int deltaY = 10; double[] spacedX = new double[maximumRadius]; double[] spacedY = new double[deltaY]; double[] ceroValuesX = new double[maximumRadius]; double[] ceroValuesY = new double[deltaY]; for (int i = 0; i < maximumRadius; ++i) { spacedX[i] = i; ceroValuesX[i] = 0.0D; } for (int i = 0; i < deltaY; ++i) { spacedY[i] = (1.0D / deltaY * i); ceroValuesY[i] = 0.0D; } // TODO - Only compute the images that will be displayed or saved as results boolean isSaveResults = (saveResults && checkResultsDirectory()); IJ.showStatus(preparingPlotsStatus); Plot plotM1 = null, plotM2 = null, plotR = null; if (showM1PlotWindow) plotM1 = createPlot(distances, m1Values, Color.red, Color.blue, plotM1Title, plotXLabel, plotM1YLabel, spacedX, ceroValuesX, ceroValuesY, spacedY); if (showM2PlotWindow) plotM2 = createPlot(distances, m2Values, Color.green, Color.blue, plotM2Title, plotXLabel, plotM2YLabel, spacedX, ceroValuesX, ceroValuesY, spacedY); if (showRPlotWindow) plotR = createPlot(distances, rValues, Color.blue, Color.green, plotRTitle, plotXLabel, plotRYLabel, spacedX, ceroValuesX, ceroValuesY, spacedY); // Prepare output images // Output the channel 1 as red // Output the channel 2 as green ImageStack channel1RGBIP = null, channel2RGBIP = null, segmented1RGBIP = null, segmented2RGBIP = null; ImageStack mergedChannelIP = null, mergedSegmentedRGBIP = null, mergedChannelDisplacementIP = null, mergedSegmentedDisplacementIP = null; if (showChannel1RGB || showMergedChannelRGB) channel1RGBIP = createColorOutput(imageStack1, confinedStack, 0); if (showChannel2RGB || showMergedChannelRGB) channel2RGBIP = createColorOutput(imageStack2, confinedStack, 1); if (showSegmented1RGB || showMergedSegmentedRGB) segmented1RGBIP = createColorOutput(roiStack1, confinedStack, 0); if (showSegmented2RGB || showMergedSegmentedRGB) segmented2RGBIP = createColorOutput(roiStack2, confinedStack, 1); int w = imageStack1.getWidth(); int h = imageStack1.getHeight(); int slices = imageStack1.getSize(); if (showMergedChannelRGB) mergedChannelIP = new ImageStack(w, h, slices); if (showMergedSegmentedRGB) mergedSegmentedRGBIP = new ImageStack(w, h, slices); if (showMergedChannelDisplacementRGB) mergedChannelDisplacementIP = createColorOutput(lastChannelShiftedRawStack, confinedStack, 0); if (showMergedSegmentedDisplacementRGB) mergedSegmentedDisplacementIP = createColorOutput(lastSegmentedShiftedRawStack, confinedStack, 0); for (int n = 1; n <= imageStack1.getSize(); n++) { // Mix the channels if (showMergedChannelRGB) { ColorProcessor cp = new ColorProcessor(w, h); cp.setPixels(0, channel1RGBIP.getProcessor(n).toFloat(0, null)); cp.setPixels(1, channel2RGBIP.getProcessor(n).toFloat(1, null)); mergedChannelIP.setPixels(cp.getPixels(), n); } // Mix the masks if (showMergedSegmentedRGB) { ColorProcessor cp = new ColorProcessor(w, h); cp.setPixels(0, segmented1RGBIP.getProcessor(n).toFloat(0, null)); cp.setPixels(1, segmented2RGBIP.getProcessor(n).toFloat(1, null)); mergedSegmentedRGBIP.setPixels(cp.getPixels(), n); } // For reference output the maximum shift of channel 1 AND the original channel 2 if (showMergedChannelDisplacementRGB) { ColorProcessor cp = new ColorProcessor(w, h); cp.setPixels(mergedChannelDisplacementIP.getPixels(n)); cp.setPixels(1, channel2RGBIP.getProcessor(n).toFloat(1, null)); } if (showMergedSegmentedDisplacementRGB) { ColorProcessor cp = new ColorProcessor(w, h); cp.setPixels(mergedSegmentedDisplacementIP.getPixels(n)); cp.setPixels(1, segmented2RGBIP.getProcessor(n).toFloat(1, null)); } } // Set the area outside the ROI to white. // invert(channel1RGBIP, confinedStack); // invert(channel2RGBIP, confinedStack); // invert(segmented1RGBIP, confinedStack); // invert(segmented2RGBIP, confinedStack); // invert(mergedChannelIP, confinedStack); // invert(mergedSegmentedRGBIP, confinedStack); // invert(mergedChannelDisplacementIP, confinedStack); // invert(mergedSegmentedDisplacementIP, confinedStack); createDisplayImages(channel1RGBIP, channel2RGBIP, segmented1RGBIP, segmented2RGBIP, mergedChannelIP, mergedSegmentedRGBIP, mergedChannelDisplacementIP, mergedSegmentedDisplacementIP); // Update the images updateImage(channel1RGB, channel1RGBTitle, channel1RGBIP, showChannel1RGB); updateImage(channel2RGB, channel2RGBTitle, channel2RGBIP, showChannel2RGB); updateImage(segmented1RGB, segmented1RGBTitle, segmented1RGBIP, showSegmented1RGB); updateImage(segmented2RGB, segmented2RGBTitle, segmented2RGBIP, showSegmented2RGB); updateImage(mergedChannelRGB, mergedChannelTitle, mergedChannelIP, showMergedChannelRGB); updateImage(mergedSegmentedRGB, mergedSegmentedTitle, mergedSegmentedRGBIP, showMergedSegmentedRGB); updateImage(mergedChannelDisplacementRGB, lastShiftedChannelTitle, mergedChannelDisplacementIP, showMergedChannelDisplacementRGB); updateImage(mergedSegmentedDisplacementRGB, lastShiftedSegmentedTitle, mergedSegmentedDisplacementIP, showMergedSegmentedDisplacementRGB); // Create plots of the results m1PlotWindow = refreshPlotWindow(m1PlotWindow, showM1PlotWindow, plotM1, OPT_LOCATION_PLOT_M1); m2PlotWindow = refreshPlotWindow(m2PlotWindow, showM2PlotWindow, plotM2, OPT_LOCATION_PLOT_M2); rPlotWindow = refreshPlotWindow(rPlotWindow, showRPlotWindow, plotR, OPT_LOCATION_PLOT_R); // Create display statistics for the Mander's and correlation values FloatProcessor m1ValuesFP = new FloatProcessor(m1ValuesForRandom.length, 1, m1ValuesForRandom, null); FloatProcessor m2ValuesFP = new FloatProcessor(m2ValuesForRandom.length, 1, m2ValuesForRandom, null); FloatProcessor rValuesFP = new FloatProcessor(rValuesForRandom.length, 1, rValuesForRandom, null); m1Statistics = refreshDisplayStatistics(m1Statistics, showM1Statistics, m1ValuesFP, m1ValuesForRandom, m1HistogramTitle, Color.red, "M1 ", M1, OPT_LOCATION_STATS_M1); m2Statistics = refreshDisplayStatistics(m2Statistics, showM2Statistics, m2ValuesFP, m2ValuesForRandom, m2HistogramTitle, Color.green, "M2 ", M2, OPT_LOCATION_STATS_M2); rStatistics = refreshDisplayStatistics(rStatistics, showRStatistics, rValuesFP, rValuesForRandom, rHistogramTitle, Color.blue, "R ", R, OPT_LOCATION_STATS_R); String id = generateId(); StringBuffer heading = null; // Output the results to a text window if (tw == null || !tw.isShowing()) { heading = createHeading(heading); tw = new TextWindow(TITLE + " Results", heading.toString(), "", 1000, 300); } StringBuffer resultsEntry = new StringBuffer(); addField(resultsEntry, id); addField(resultsEntry, getImageTitle(imp1, 0)); addField(resultsEntry, getImageTitle(imp2, 2)); // Note that the segmented indices must be offset by one to account for the extra option // in the list of segmented images addRoiField(resultsEntry, segmented1OptionIndex, segmented1Index - 1, getRoiIp(imageStack1.getProcessor(1), segmented1Index - 1), 4); addRoiField(resultsEntry, segmented2OptionIndex, segmented2Index - 1, getRoiIp(imageStack2.getProcessor(1), segmented2Index - 1), 6); addRoiField(resultsEntry, confinedOptionIndex, confinedIndex, getRoiIp(confinedStack.getProcessor(1), confinedIndex), 8); addField(resultsEntry, expandConfinedCompartment); addField(resultsEntry, maximumRadius); addField(resultsEntry, randomRadius); addField(resultsEntry, results.size()); addField(resultsEntry, histogramBins); addField(resultsEntry, pValue); addResults(resultsEntry, m1Statistics); addResults(resultsEntry, m2Statistics); addResults(resultsEntry, rStatistics); tw.append(resultsEntry.toString()); if (isSaveResults) { try { // Save results to file String directory = Utils.combinePath(resultsDirectory, id); if (!new File(directory).mkdirs()) return; IJ.save(mergedSegmentedRGB, directory + File.separatorChar + "MergedROI.tif"); IJ.save(mergedChannelRGB, directory + File.separatorChar + "MergedChannel.tif"); FileOutputStream fos = new FileOutputStream(directory + File.separatorChar + "results.txt"); OutputStreamWriter out = new OutputStreamWriter(fos, "UTF-8"); String newLine = System.getProperty("line.separator"); heading = createHeading(heading); out.write(heading.toString()); out.write(newLine); out.write(resultsEntry.toString()); out.write(newLine); out.close(); } catch (Exception e) { return; } } } private Object getImageTitle(ImagePlus imp, int offset) { if (imp != null) { StringBuilder sb = new StringBuilder(imp.getTitle()); if (imp.getStackSize() > 1 && (imp.getNChannels() > 1 || imp.getNFrames() > 1)) { sb.append(" (c").append(sliceOptions[offset]).append(",t").append(sliceOptions[offset + 1]).append(")"); } return sb.toString(); } else return "[Unknown]"; } private String generateId() { DateFormat df = new SimpleDateFormat("yyyyMMdd_HHmmss"); return "cda" + df.format(new Date()); } private void createDisplayImages(ImageStack channel1RGBIP, ImageStack channel2RGBIP, ImageStack segmented1RGBIP, ImageStack segmented2RGBIP, ImageStack mergedChannelIP, ImageStack mergedSegmentedRGBIP, ImageStack mergedChannelDisplacementIP, ImageStack mergedSegmentedDisplacementIP) { // Only create the images if they have not yet been made. if (channel1RGB == null) channel1RGB = newImagePlus(channel1RGBTitle, channel1RGBIP); if (channel2RGB == null) channel2RGB = newImagePlus(channel2RGBTitle, channel2RGBIP); if (segmented1RGB == null) segmented1RGB = newImagePlus(segmented1RGBTitle, segmented1RGBIP); if (segmented2RGB == null) segmented2RGB = newImagePlus(segmented2RGBTitle, segmented2RGBIP); if (mergedChannelRGB == null) mergedChannelRGB = newImagePlus(mergedChannelTitle, mergedChannelIP); if (mergedSegmentedRGB == null) mergedSegmentedRGB = newImagePlus(mergedSegmentedTitle, mergedSegmentedRGBIP); if (mergedChannelDisplacementRGB == null) mergedChannelDisplacementRGB = newImagePlus(lastShiftedChannelTitle, mergedChannelDisplacementIP); if (mergedSegmentedDisplacementRGB == null) mergedSegmentedDisplacementRGB = newImagePlus(lastShiftedSegmentedTitle, mergedSegmentedDisplacementIP); } private ImagePlus newImagePlus(String title, ImageStack stack) { if (stack != null) return new ImagePlus(title, stack); return null; } private void updateImage(ImagePlus imp, String title, ImageStack stack, boolean show) { if (stack != null) imp.setStack(title, stack); //imp.getMask(); // Re-initialise the image? if (imp != null) { if (show) imp.show(); else imp.hide(); } } private StringBuffer createHeading(StringBuffer heading) { if (heading == null) { heading = new StringBuffer(); addField(heading, "Exp. Id"); addField(heading, choiceChannel1); addField(heading, choiceChannel2); addField(heading, "ROI 1"); addField(heading, "ROI 2"); addField(heading, "Confined"); addField(heading, choiceExpandConfined.trim()); addField(heading, "D-max"); addField(heading, "D-random"); addField(heading, "Samples"); addField(heading, "Bins"); addField(heading, "p-Value"); addField(heading, "M1"); addField(heading, "M1 av"); addField(heading, "M1 sd"); addField(heading, "M1 limits"); addField(heading, "M1 result"); addField(heading, "M2"); addField(heading, "M2 av"); addField(heading, "M2 sd"); addField(heading, "M2 limits"); addField(heading, "M2 result"); addField(heading, "R"); addField(heading, "R av"); addField(heading, "R sd"); addField(heading, "R limits"); addField(heading, "R result"); } return heading; } private PlotWindow refreshPlotWindow(PlotWindow plotWindow, boolean showPlotWindow, Plot plot, String locationKey) { if (showPlotWindow && plot != null) { if (plotWindow != null && !plotWindow.isClosed()) { plotWindow.drawPlot(plot); } else { plotWindow = plot.show(); // Restore location from preferences restoreLocation(plotWindow, Prefs.getLocation(locationKey)); } } else { closePlotWindow(plotWindow, locationKey); plotWindow = null; } return plotWindow; } private DisplayStatistics refreshDisplayStatistics(DisplayStatistics displayStatistics, boolean showStatistics, FloatProcessor m1ValuesFP, float[] valuesForRandom, String histogramTitle, Color colour, String xTitle, double value, String locationKey) { // Draw a plot of the values. This will be used within the DisplayStatistics chart PlotResults statsPlot = new PlotResults(value, histogramBins, Tools.toDouble(valuesForRandom), colour); statsPlot.setXTitle(xTitle); statsPlot.setYTitle(plotPDFYLabel); statsPlot.setTitle("CDA " + xTitle + " PDF"); // This is needed to calculate the probability limits for the results table // even if the plot is not shown statsPlot.calculate(pValue); // Generate the statistics needed for the plot FloatStatistics floatStatistics = new FloatStatistics(m1ValuesFP); Point point = null; if (displayStatistics == null) { displayStatistics = new DisplayStatistics(histogramTitle, xTitle); point = Prefs.getLocation(locationKey); } displayStatistics.setData(statsPlot, floatStatistics, value); // Show the new plot if (showStatistics) { displayStatistics.draw(); // Restore the position if necessary restoreLocation(displayStatistics.getPlotWindow(), point); } else { closeDisplayStatistics(displayStatistics, locationKey); } return displayStatistics; } private void restoreLocation(Frame frame, Point point) { if (point != null) { frame.setLocation(point); } } @SuppressWarnings("unused") private void invert(ImageStack imageStack, ImageStack confinedStack) { for (int n = 1; n <= imageStack.getSize(); n++) { ImageProcessor ip = imageStack.getProcessor(n); ImageProcessor confinedIp = confinedStack.getProcessor(n); for (int i = confinedIp.getPixelCount(); i-- > 0;) { if (confinedIp.get(i) == 0) { ip.set(i, 0xFFFFFFFF); } } } } private ImageStack createColorOutput(ImageStack image, ImageStack mask, int channel) { ImageStack result = new ImageStack(image.getWidth(), image.getHeight()); for (int n = 1; n <= image.getSize(); n++) { ByteProcessor byteIP = (ByteProcessor) (image.getProcessor(n).duplicate().convertToByte(true)); intersectMask(byteIP, (ByteProcessor) mask.getProcessor(n), byteIP); ColorProcessor cp = new ColorProcessor(byteIP.getWidth(), byteIP.getHeight()); cp.setPixels(channel, byteIP.toFloat(0, null)); result.addSlice(null, cp); } return result; } private Plot createPlot(double[] distances, double[] values, Color color, Color avColor, String title, String xLabel, String yLabel, double[] spacedX, double[] ceroValuesX, double[] ceroValuesY, double[] spacedY) { float[] dummy = null; Plot plot = new Plot(title, xLabel.concat(pixelsUnitString), yLabel, dummy, dummy, Plot.X_NUMBERS + Plot.Y_NUMBERS + Plot.X_TICKS + Plot.Y_TICKS); double min = 0; for (double d : values) { if (min > d) min = d; } plot.setLimits(0.0D, maximumRadius, min, 1.0D); plot.setColor(color); plot.addPoints(distances, values, Plot.X); addAverage(plot, distances, values, avColor); plot.setColor(Color.black); plot.addPoints(spacedX, ceroValuesX, 5); plot.addPoints(ceroValuesY, spacedY, 5); return plot; } private void addAverage(Plot plot, double[] distances, double[] values, Color color) { double maxDistance = 0; for (double d : distances) { if (maxDistance < d) maxDistance = d; } int n = (int) (maxDistance + 0.5) + 1; double[] sum = new double[n]; int[] count = new int[n]; for (int i = 0; i < distances.length; i++) { // Round up distance to nearest int int d = (int) (distances[i] + 0.5); sum[d] += values[i]; count[d]++; } StoredData avDistances = new StoredData(n); StoredData avValues = new StoredData(n); for (int i = (subRandomSamples) ? 0 : 1; i < n; i++) { if (count[i] > 0) { avDistances.add((double) i); avValues.add(sum[i] / count[i]); } } plot.setColor(color); plot.addPoints(avDistances.getValues(), avValues.getValues(), Plot.LINE); } private StringBuffer addField(StringBuffer buffer, Object field) { if (buffer.length() > 0) { buffer.append("\t"); } buffer.append(field); return buffer; } private void addRoiField(StringBuffer buffer, int roiIndex, int imageIndex, ImageProcessor roiIp, int offset) { String roiOption = ROI_OPTIONS[roiIndex]; addField(buffer, roiOption); if (!roiOption.equals(OPTION_NONE)) { buffer.append(" : "); if (imageIndex < 0) buffer.append(CHANNEL_IMAGE); else buffer.append(imageList.get(imageIndex)).append(" (c").append(sliceOptions[offset]).append(",t") .append(sliceOptions[offset + 1]).append(")"); if (roiOption.equals(OPTION_MIN_VALUE)) { if (roiIp != null) { int minIndex = (offset - 4) / 2; buffer.append(" (>"); buffer.append(displayMin[minIndex]); buffer.append(")"); } } else if (roiOption.equals(OPTION_USE_ROI)) { if (roiIp != null) { Rectangle r = roiIp.getRoi(); if (r != null) { buffer.append(" ("); buffer.append(r.x).append(",").append(r.y).append(":").append(r.x + r.width).append(",") .append(r.y + r.height); buffer.append(")"); } } } } } private void addResults(StringBuffer sb, DisplayStatistics displayStatistics) { double value = displayStatistics.getValue(); double av = displayStatistics.getAverage(); double sd = displayStatistics.getStdDev(); double lowerLimit = displayStatistics.getLowerLimit(); double upperLimit = displayStatistics.getUpperLimit(); String result; if (value < lowerLimit) { result = "Significant (non-colocated)"; } else if (value > upperLimit) { result = "Significant (colocated)"; } else { result = "Not significant"; } addField(sb, IJ.d2s(value, 4)); addField(sb, IJ.d2s(av, 4)); addField(sb, IJ.d2s(sd, 4)); addField(sb, IJ.d2s(lowerLimit, 4)).append(" : ").append(IJ.d2s(upperLimit, 4)); addField(sb, result); } @SuppressWarnings("unused") private void showImage(String title, ImageProcessor ip) { ImagePlus img = new ImagePlus(title, ip); img.show(); IJ.showMessage(title); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } img.close(); } private boolean parametersReady() { if (WindowManager.getImageCount() == 0) { // IJ.showMessage(TITLE, "No images opened."); return false; } if (channel1List.getItemCount() < 2) { // Check for an image stack with more than one channel/frame ImagePlus imp = WindowManager.getImage(channel1List.getSelectedItem()); if (imp == null || (imp.getNChannels() + imp.getNFrames() < 3)) { IJ.showMessage(TITLE, "Requires 2 images or a multi-channel/frame stack. Images must be 8-bit or 16-bit grayscale."); return false; } } maximumRadius = getIntValue(maximumRadiusText.getText(), 0); randomRadius = getIntValue(randomRadiusText.getText(), 0); if (randomRadius >= maximumRadius) { IJ.showMessage(TITLE, "Require '" + choiceRandomRadius + "' < '" + choiceMaximumRadius + "'"); return false; } return ((channel1List.getSelectedIndex() != -1) && (channel2List.getSelectedIndex() != -1) && (segmented1List.getSelectedIndex() != -1) && (segmented2List.getSelectedIndex() != -1) && (confinedList.getSelectedIndex() != -1) && (channel1List.getItemCount() != 0) && (channel2List.getItemCount() != 0) && (segmented1List.getItemCount() != 0) && (segmented2List.getItemCount() != 0) && (confinedList.getItemCount() != 0)); } public void fillImagesList() { // Find the currently open images ArrayList<String> newImageList = createImageList(); // Check if the image list has changed if (imageList.equals(newImageList)) return; imageList = newImageList; // Repopulate the image lists channel1List.removeAll(); channel2List.removeAll(); segmented1List.removeAll(); segmented2List.removeAll(); confinedList.removeAll(); segmented1List.add(CHANNEL_IMAGE); segmented2List.add(CHANNEL_IMAGE); for (String imageTitle : newImageList) { segmented1List.add(imageTitle); segmented2List.add(imageTitle); confinedList.add(imageTitle); channel1List.add(imageTitle); channel2List.add(imageTitle); } // Ensure the drop-downs are resized pack(); // Restore previous selection if ((channel1Index < channel1List.getItemCount()) && (channel1Index >= 0)) channel1List.select(channel1Index); if ((channel2Index < channel2List.getItemCount()) && (channel2Index >= 0)) channel2List.select(channel2Index); if ((segmented1Index < segmented1List.getItemCount()) && (segmented1Index >= 0)) segmented1List.select(segmented1Index); if ((segmented2Index < segmented2List.getItemCount()) && (segmented2Index >= 0)) segmented2List.select(segmented2Index); if ((confinedIndex < confinedList.getItemCount()) && (confinedIndex >= 0)) confinedList.select(confinedIndex); } public ArrayList<String> createImageList() { ArrayList<String> newImageList = new ArrayList<String>(); for (int id : gdsc.core.ij.Utils.getIDList()) { ImagePlus imp = WindowManager.getImage(id); // Image must be 8-bit/16-bit if (imp != null && (imp.getType() == ImagePlus.GRAY8 || imp.getType() == ImagePlus.GRAY16)) { // Check it is not one the result images String imageTitle = imp.getTitle(); if ((imageTitle.equals(channel1RGBTitle)) || (imageTitle.equals(channel2RGBTitle)) || (imageTitle.equals(segmented1RGBTitle)) || (imageTitle.equals(segmented2RGBTitle)) || (imageTitle.equals(plotM1Title)) || (imageTitle.equals(plotM2Title)) || (imageTitle.equals(m1HistogramTitle)) || (imageTitle.equals(m2HistogramTitle)) || (imageTitle.equals(mergedChannelTitle)) || (imageTitle.equals(mergedSegmentedTitle)) || (imageTitle.equals(lastShiftedSegmentedTitle)) || (imageTitle.equals(lastShiftedChannelTitle))) continue; newImageList.add(imageTitle); } } return newImageList; } private void createFrame() { Panel mainPanel = new Panel(); GridLayout mainGrid = new GridLayout(0, 1); mainGrid.setHgap(10); mainGrid.setVgap(10); mainPanel.setLayout(mainGrid); add(mainPanel); pixelsLabel = new Label(pixelsUnitString, 0); pixelsLabel.setFont(monoFont); mainPanel.add(createLabelPanel(null, "Channel/frame options for stacks pop-up at run-time", null)); channel1List = new Choice(); mainPanel.add(createChoicePanel(channel1List, choiceChannel1)); channel2List = new Choice(); mainPanel.add(createChoicePanel(channel2List, choiceChannel2)); segmented1List = new Choice(); segmented1Option = new Choice(); mainPanel.add(createRoiChoicePanel(segmented1List, segmented1Option, choiceSegmentedChannel1, segmented1OptionIndex)); segmented2List = new Choice(); segmented2Option = new Choice(); mainPanel.add(createRoiChoicePanel(segmented2List, segmented2Option, choiceSegmentedChannel2, segmented2OptionIndex)); confinedList = new Choice(); confinedOption = new Choice(); mainPanel.add(createRoiChoicePanel(confinedList, confinedOption, choiceConfined, confinedOptionIndex)); expandConfinedCheckbox = new Checkbox(); mainPanel.add(createCheckboxPanel(expandConfinedCheckbox, choiceExpandConfined, expandConfinedCompartment)); maximumRadiusText = new TextField(); mainPanel.add(createTextPanel(maximumRadiusText, choiceMaximumRadius, "" + maximumRadius)); maximumRadiusText.addKeyListener(this); randomRadiusText = new TextField(); mainPanel.add(createTextPanel(randomRadiusText, choiceRandomRadius, "" + randomRadius)); randomRadiusText.addKeyListener(this); subRandomSamplesCheckbox = new Checkbox(); mainPanel.add(createCheckboxPanel(subRandomSamplesCheckbox, choiceSubRandomSamples, subRandomSamples)); subRandomSamplesCheckbox.addItemListener(this); numberOfSamplesField = new Label(); mainPanel.add(createLabelPanel(numberOfSamplesField, numberOfSamplesLabel, "")); updateNumberOfSamples(); binsText = new TextField(); mainPanel.add(createTextPanel(binsText, choiceBinsNumber, "" + histogramBins)); closeWindowsOnExitCheckbox = new Checkbox(); mainPanel.add(createCheckboxPanel(closeWindowsOnExitCheckbox, choiceCloseWindowsOnExit, closeWindowsOnExit)); setResultsOptionsCheckbox = new Checkbox(); mainPanel.add(createCheckboxPanel(setResultsOptionsCheckbox, choiceSetOptions, false)); setResultsOptionsCheckbox.addItemListener(this); okButton = new Button(okButtonLabel); okButton.addActionListener(this); helpButton = new Button(helpButtonLabel); helpButton.addActionListener(this); JPanel buttonPanel = new JPanel(); FlowLayout l = new FlowLayout(); l.setVgap(0); buttonPanel.setLayout(l); buttonPanel.add(okButton, BorderLayout.CENTER); buttonPanel.add(helpButton, BorderLayout.CENTER); mainPanel.add(buttonPanel); } private void updateNumberOfSamples() { maximumRadius = getIntValue(maximumRadiusText.getText(), 0); randomRadius = getIntValue(randomRadiusText.getText(), 0); subRandomSamples = subRandomSamplesCheckbox.getState(); if (randomRadius >= maximumRadius) { randomRadiusText.setBackground(Color.ORANGE); } else { randomRadiusText.setBackground(Color.WHITE); } double subNumber = approximateSamples(randomRadius); double number = approximateSamples(maximumRadius) - subNumber; if (permutations > 0) { number = Math.min(permutations, number); subNumber = Math.min(permutations, subNumber); } double total = ((subRandomSamples) ? subNumber : 0) + number; if (total < 0) total = 0; numberOfSamplesField.setText("" + (long) total); } private double approximateSamples(int radius) { return Math.PI * radius * radius; } private Panel createChoicePanel(Choice list, String label) { Panel panel = new Panel(); panel.setLayout(new BorderLayout()); Label listLabel = new Label(label, 0); listLabel.setFont(monoFont); list.setSize(fontWidth * 3, fontWidth); panel.add(listLabel, BorderLayout.WEST); panel.add(list, BorderLayout.CENTER); return panel; } private Panel createRoiChoicePanel(Choice imageList, Choice optionList, String label, int selectedOptionIndex) { Panel panel = new Panel(); panel.setLayout(new BorderLayout()); Label listLabel = new Label(label, 0); listLabel.setFont(monoFont); // imageList.setSize(fontWidth * 3, fontWidth); panel.add(listLabel, BorderLayout.WEST); panel.add(optionList, BorderLayout.CENTER); panel.add(imageList, BorderLayout.EAST); optionList.add(OPTION_NONE); optionList.add(OPTION_MIN_VALUE); optionList.add(OPTION_USE_ROI); optionList.add(OPTION_MASK); imageList.add(CHANNEL_IMAGE); if (selectedOptionIndex < 4 && selectedOptionIndex >= 0) { optionList.select(selectedOptionIndex); } return panel; } private Panel createTextPanel(TextField textField, String label, String value) { Panel panel = new Panel(); panel.setLayout(new BorderLayout()); Label listLabel = new Label(label, 0); listLabel.setFont(monoFont); textField.setSize(fontWidth * 3, fontWidth); textField.setText(value); panel.add(listLabel, BorderLayout.WEST); panel.add(textField, BorderLayout.CENTER); return panel; } private Panel createCheckboxPanel(Checkbox checkbox, String label, boolean state) { Panel panel = new Panel(); panel.setLayout(new BorderLayout()); Label listLabel = new Label(label, 0); listLabel.setFont(monoFont); checkbox.setState(state); panel.add(listLabel, BorderLayout.WEST); panel.add(checkbox, BorderLayout.EAST); return panel; } private Panel createLabelPanel(Label labelField, String label, String value) { Panel panel = new Panel(); panel.setLayout(new BorderLayout()); Label listLabel = new Label(label, 0); listLabel.setFont(monoFont); panel.add(listLabel, BorderLayout.WEST); if (labelField != null) { labelField.setSize(fontWidth * 3, fontWidth); labelField.setText(value); panel.add(labelField, BorderLayout.CENTER); } return panel; } /** * Create the intersect of the two masks. Results are written back to the image processor in the target stack unless * the duplicate option is true. The sum of the source image intensity within the intersect is accumulated. */ private ImageStack intersectMask(ImageStack targetStack, ImageStack sourceStack, long[] sum, boolean duplicate) { ImageStack newStack = new ImageStack(targetStack.getWidth(), targetStack.getHeight()); sum[0] = 0; for (int s = 1; s <= targetStack.getSize(); s++) { ImageProcessor ip = targetStack.getProcessor(s); if (duplicate) ip = ip.duplicate(); sum[0] += intersectMask(ip, (ByteProcessor) sourceStack.getProcessor(s), ip); newStack.addSlice(null, ip); } return newStack; } /** * Create the intersect of the two masks. Results are written back to the output image processor if specified. * * @return The sum of the source image intensity within the intersect */ private long intersectMask(ImageProcessor sourceImage, ByteProcessor maskImage, ImageProcessor outputImage) { long sum = 0; // We need to do extra work if there is an output image so put this in a different loop if (outputImage != null) { int sourceIntensity; for (int i = maskImage.getPixelCount(); i-- > 0;) { if (maskImage.get(i) != 0) { sourceIntensity = sourceImage.get(i); outputImage.set(i, sourceIntensity); sum += sourceIntensity; } else { outputImage.set(i, 0); } } } else { for (int i = maskImage.getPixelCount(); i-- > 0;) { if (maskImage.get(i) != 0) { sum += sourceImage.get(i); } } } return sum; } /** * Modify mask1 to include all non-zero pixels from mask 2 * * @param mask1 * @param mask2 */ private void unionMask(ImageStack mask1, ImageStack mask2) { for (int s = 1; s <= mask1.getSize(); s++) { unionMask((ByteProcessor) mask1.getProcessor(s), (ByteProcessor) mask2.getProcessor(s)); } } /** * Modify mask1 to include all non-zero pixels from mask 2 * * @param mask1 * @param mask2 */ private void unionMask(ByteProcessor mask1, ByteProcessor mask2) { for (int i = mask1.getPixelCount(); i-- > 0;) { if (mask2.get(i) != 0) { mask1.set(i, 255); } } } private void saveOptions() { Prefs.set(OPT_CHANNEL1_INDEX, channel1Index); Prefs.set(OPT_CHANNEL2_INDEX, channel2Index); Prefs.set(OPT_SEGMENTED1_INDEX, segmented1Index); Prefs.set(OPT_SEGMENTED2_INDEX, segmented2Index); Prefs.set(OPT_CONFINED_INDEX, confinedIndex); Prefs.set(OPT_SEGMENTED1_OPTION_INDEX, segmented1OptionIndex); Prefs.set(OPT_SEGMENTED2_OPTION_INDEX, segmented2OptionIndex); Prefs.set(OPT_CONFINED_OPTION_INDEX, confinedOptionIndex); Prefs.set(OPT_EXPAND_CONFINED, expandConfinedCompartment); Prefs.set(OPT_MAXIMUM_RADIUS, maximumRadius); Prefs.set(OPT_RANDOM_RADIUS, randomRadius); Prefs.set(OPT_SUB_RANDOM_SAMPLES, subRandomSamples); Prefs.set(OPT_HISTOGRAM_BINS, histogramBins); Prefs.set(OPT_CLOSE_WINDOWS_ON_EXIT, closeWindowsOnExit); Prefs.set(OPT_SET_OPTIONS, setOptions); Prefs.set(OPT_SHOW_CHANNEL1_RGB, showChannel1RGB); Prefs.set(OPT_SHOW_CHANNEL2_RGB, showChannel2RGB); Prefs.set(OPT_SHOW_SEGMENTED1_RGB, showSegmented1RGB); Prefs.set(OPT_SHOW_SEGMENTED2_RGB, showSegmented2RGB); Prefs.set(OPT_SHOW_MERGED_CHANNEL_RGB, showMergedChannelRGB); Prefs.set(OPT_SHOW_MERGED_SEGMENTED_RGB, showMergedSegmentedRGB); Prefs.set(OPT_SHOW_MERGED_CHANNEL_DISPLACEMENT_RGB, showMergedChannelDisplacementRGB); Prefs.set(OPT_SHOW_MERGED_SEGMENTED_DISPLACEMENT_RGB, showMergedSegmentedDisplacementRGB); Prefs.set(OPT_SHOW_M1_PLOT_WINDOW, showM1PlotWindow); Prefs.set(OPT_SHOW_M2_PLOT_WINDOW, showM2PlotWindow); Prefs.set(OPT_SHOW_M1_STATISTICS, showM1Statistics); Prefs.set(OPT_SHOW_M2_STATISTICS, showM2Statistics); Prefs.set(OPT_SHOW_R_STATISTICS, showRStatistics); Prefs.set(OPT_SAVE_RESULTS, saveResults); Prefs.set(OPT_RESULTS_DIRECTORY, resultsDirectory); Prefs.set(OPT_P_VALUE, pValue); Prefs.set(OPT_PERMUTATIONS, permutations); } /** * Run using an ImageJ generic dialog to allow recording in macros */ private void runAsPlugin() { imageList = createImageList(); if (imageList.size() < 2) { // Check for an image stack with more than one channel/frame ImagePlus imp = (imageList.isEmpty()) ? null : WindowManager.getImage(imageList.get(0)); if (imp == null || (imp.getNChannels() + imp.getNFrames() < 3)) { IJ.showMessage(TITLE, "Requires 2 images or a multi-channel/frame stack. Images must be 8-bit or 16-bit grayscale."); } } String[] images = new String[imageList.size()]; String[] images2 = new String[imageList.size() + 1]; images2[0] = CHANNEL_IMAGE; for (int i = 0; i < imageList.size(); i++) { images[i] = images2[i + 1] = imageList.get(i); } GenericDialog gd = new GenericDialog(TITLE); gd.addChoice(choiceChannel1.replace(" ", "_"), images, getTitle(images, channel1Index)); gd.addChoice(choiceChannel2.replace(" ", "_"), images, getTitle(images, channel2Index)); // ROIs require two choice boxes addRoiChoice(gd, choiceSegmentedChannel1, ROI_OPTIONS, images2, segmented1OptionIndex, segmented1Index); addRoiChoice(gd, choiceSegmentedChannel2, ROI_OPTIONS, images2, segmented2OptionIndex, segmented2Index); addRoiChoice(gd, choiceConfined, ROI_OPTIONS, images, confinedOptionIndex, confinedIndex); gd.addCheckbox(choiceExpandConfined.trim().replace(" ", "_"), expandConfinedCompartment); gd.addNumericField(choiceMaximumRadius, maximumRadius, 0); gd.addNumericField(choiceRandomRadius, randomRadius, 0); gd.addCheckbox(choiceSubRandomSamples.trim().replace(" ", "_"), subRandomSamples); gd.addNumericField(choiceBinsNumber, histogramBins, 0); gd.addCheckbox(choiceSetOptions.replace(" ", "_"), setOptions); gd.showDialog(); if (gd.wasCanceled()) return; channel1Index = gd.getNextChoiceIndex(); channel2Index = gd.getNextChoiceIndex(); segmented1OptionIndex = gd.getNextChoiceIndex(); segmented1Index = gd.getNextChoiceIndex(); segmented2OptionIndex = gd.getNextChoiceIndex(); segmented2Index = gd.getNextChoiceIndex(); confinedOptionIndex = gd.getNextChoiceIndex(); confinedIndex = gd.getNextChoiceIndex(); expandConfinedCompartment = gd.getNextBoolean(); maximumRadius = (int) gd.getNextNumber(); randomRadius = (int) gd.getNextNumber(); subRandomSamples = gd.getNextBoolean(); histogramBins = (int) gd.getNextNumber(); if ((setOptions = gd.getNextBoolean())) setResultsOptions(); // Get the images ImagePlus imp1 = WindowManager.getImage(images[channel1Index]); ImagePlus imp2 = WindowManager.getImage(images[channel2Index]); ImagePlus roi1 = WindowManager.getImage(images2[segmented1Index]); ImagePlus roi2 = WindowManager.getImage(images2[segmented2Index]); ImagePlus roi = WindowManager.getImage(images[confinedIndex]); runCDA(imp1, imp2, roi1, roi2, roi); } private void addRoiChoice(GenericDialog gd, String title, String[] roiOptions, String[] images, int optionIndex, int index) { String name = title.replace(" ", "_"); gd.addChoice(name, roiOptions, getTitle(roiOptions, optionIndex)); gd.addChoice(name + "_image", images, getTitle(images, index)); } private String getTitle(String[] titles, int index) { if (index < titles.length) return titles[index]; return ""; } }