package gdsc.smlm.ij.plugins; /*----------------------------------------------------------------------------- * GDSC SMLM Software * * Copyright (C) 2017 Alex Herbert * Genome Damage and Stability Centre * University of Sussex, UK * * 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. *---------------------------------------------------------------------------*/ import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import gdsc.core.utils.TurboList; import gdsc.smlm.ij.plugins.ResultsManager.InputSource; import gdsc.smlm.results.MemoryPeakResults; import gdsc.smlm.results.PeakResult; import ij.IJ; import ij.ImagePlus; import ij.WindowManager; import ij.gui.GenericDialog; import ij.plugin.PlugIn; /** * Filters PeakFit results that are stored in memory using various fit criteria. */ public class CropResults implements PlugIn { private static final String TITLE = "Crop Results"; private static String inputOption = ""; private static double border = 0; private static double x = 0, y = 0, width = 0, height = 0; private static boolean selectRegion, overwrite, useRoi; private static String roiImage = ""; private boolean myUseRoi; private MemoryPeakResults results; /* * (non-Javadoc) * * @see ij.plugin.PlugIn#run(java.lang.String) */ public void run(String arg) { SMLMUsageTracker.recordPlugin(this.getClass(), arg); if (MemoryPeakResults.isMemoryEmpty()) { IJ.error(TITLE, "There are no fitting results in memory"); return; } // Show a dialog allowing the results set to be filtered GenericDialog gd = new GenericDialog(TITLE); gd.addMessage("Select a dataset to crop"); ResultsManager.addInput(gd, inputOption, InputSource.MEMORY); gd.showDialog(); if (gd.wasCanceled()) return; inputOption = ResultsManager.getInputSource(gd); results = ResultsManager.loadInputResults(inputOption, false); if (results == null || results.size() == 0) { IJ.error(TITLE, "No results could be loaded"); IJ.showStatus(""); return; } if (!showDialog()) return; cropResults(); } private boolean showDialog() { GenericDialog gd = new GenericDialog(TITLE); gd.addHelp(About.HELP_URL); // TODO - add option to crop using the bounding rectangle of an ROI on an open image. // See PC-PALM Molecules. // Build a list of all images with a region ROI TurboList<String> titles = new TurboList<String>(WindowManager.getWindowCount()); if (WindowManager.getWindowCount() > 0) { for (int imageID : WindowManager.getIDList()) { ImagePlus imp = WindowManager.getImage(imageID); if (imp != null && imp.getRoi() != null && imp.getRoi().isArea()) titles.add(imp.getTitle()); } } Rectangle bounds = results.getBounds(true); gd.addMessage(String.format("x=%d,y=%d,w=%d,h=%d", bounds.x, bounds.y, bounds.width, bounds.height)); gd.addNumericField("Border", border, 2); gd.addCheckbox("Select_region", selectRegion); gd.addNumericField("X", x, 2); gd.addNumericField("Y", y, 2); gd.addNumericField("Width", width, 2); gd.addNumericField("Height", height, 2); if (!titles.isEmpty()) { gd.addCheckbox("Use_ROI", useRoi); String[] items = titles.toArray(new String[titles.size()]); gd.addChoice("Image", items, roiImage); } gd.addCheckbox("Overwrite", overwrite); gd.showDialog(); if (gd.wasCanceled()) return false; border = Math.max(0, gd.getNextNumber()); selectRegion = gd.getNextBoolean(); x = gd.getNextNumber(); y = gd.getNextNumber(); width = Math.max(0, gd.getNextNumber()); height = Math.max(0, gd.getNextNumber()); if (!titles.isEmpty()) { myUseRoi = useRoi = gd.getNextBoolean(); roiImage = gd.getNextChoice(); } overwrite = gd.getNextBoolean(); return true; } /** * Apply the filters to the data */ private void cropResults() { MemoryPeakResults newResults = new MemoryPeakResults(); // These bounds are integer. But this is because the results are meant to come from an image. Rectangle bounds = results.getBounds(true); // The crop bounds can be floating point... // Border double xx = bounds.x + border; double yy = bounds.y + border; double w = Math.max(0, bounds.width - 2 * border); double h = Math.max(0, bounds.height - 2 * border); Rectangle2D borderBounds = new Rectangle2D.Double(xx, yy, w, h); // Bounding box if (selectRegion) { Rectangle2D boxBounds = new Rectangle2D.Double(x, y, width, height); borderBounds = borderBounds.createIntersection(boxBounds); } // If an ROI was chosen from an image, scale the roi to the bounds of this dataset // and create another intersection if (myUseRoi) { ImagePlus imp = WindowManager.getImage(roiImage); if (imp != null && imp.getRoi() != null) { Rectangle roi = imp.getRoi().getBounds(); int roiImageWidth = imp.getWidth(); int roiImageHeight = imp.getHeight(); double xscale = (double) roiImageWidth / bounds.width; double yscale = (double) roiImageHeight / bounds.height; Rectangle2D roiBounds = new Rectangle2D.Double(roi.x / xscale, roi.y / yscale, roi.width / xscale, roi.height / yscale); borderBounds = borderBounds.createIntersection(roiBounds); } } if (borderBounds.getWidth() > 0 && borderBounds.getHeight() > 0) { for (PeakResult result : results.getResults()) { if (borderBounds.contains(result.getXPosition(), result.getYPosition())) newResults.add(result); } } newResults.copySettings(results); newResults.setBounds(new Rectangle((int) Math.floor(borderBounds.getX()), (int) Math.floor(borderBounds.getY()), (int) Math.ceil(borderBounds.getWidth()), (int) Math.ceil(borderBounds.getHeight()))); if (!overwrite) { newResults.setName(results.getName() + " Cropped"); } MemoryPeakResults.addResults(newResults); IJ.showStatus(newResults.size() + " Cropped localisations"); } }