use of qupath.lib.plugins.parameters.ParameterList in project qupath by qupath.
the class PixelClassifierPane method showAdvancedOptions.
private boolean showAdvancedOptions() {
var existingStrategy = helper.getBoundaryStrategy();
List<BoundaryStrategy> boundaryStrategies = new ArrayList<>();
boundaryStrategies.add(BoundaryStrategy.getSkipBoundaryStrategy());
boundaryStrategies.add(BoundaryStrategy.getDerivedBoundaryStrategy(1));
for (var pathClass : QuPathGUI.getInstance().getAvailablePathClasses()) boundaryStrategies.add(BoundaryStrategy.getClassifyBoundaryStrategy(pathClass, 1));
String PCA_NONE = "No feature reduction";
String PCA_BASIC = "Do PCA projection";
String PCA_NORM = "Do PCA projection + normalize output";
String pcaChoice = PCA_NONE;
if (normalization.getPCARetainedVariance() > 0) {
if (normalization.doPCANormalize())
pcaChoice = PCA_NORM;
else
pcaChoice = PCA_BASIC;
}
var params = new ParameterList().addTitleParameter("Live prediction").addIntParameter("numThreads", "Number of threads", nThreads.get(), null, "Maximum number of threads to use for live prediction, or -1 to use default threads").addTitleParameter("Training data").addIntParameter("maxSamples", "Maximum samples", maxSamples, null, "Maximum number of training samples - only needed if you have a lot of annotations, slowing down training").addIntParameter("rngSeed", "RNG seed", rngSeed, null, "Seed for the random number generator used when selecting training samples").addBooleanParameter("reweightSamples", "Reweight samples", reweightSamples, "Weight training samples according to frequency").addTitleParameter("Preprocessing").addChoiceParameter("normalization", "Feature normalization", normalization.getNormalization(), Arrays.asList(Normalization.values()), "Method to normalize features - use only if needed, may make no difference with some common classifiers").addChoiceParameter("featureReduction", "Feature reduction", pcaChoice, List.of(PCA_NONE, PCA_BASIC, PCA_NORM), "Use Principal Component Analysis for feature reduction (must also specify retained variance)").addDoubleParameter("pcaRetainedVariance", "PCA retained variance", normalization.getPCARetainedVariance(), "", "Retained variance if applying Principal Component Analysis for dimensionality reduction. Should be between 0 and 1; if <= 0 PCA will not be applied.").addTitleParameter("Annotation boundaries").addChoiceParameter("boundaryStrategy", "Boundary strategy", helper.getBoundaryStrategy(), boundaryStrategies, "Choose how annotation boundaries should influence classifier training").addDoubleParameter("boundaryThickness", "Boundary thickness", existingStrategy.getBoundaryThickness(), "pixels", "Set the boundary thickness whenever annotation boundaries are trained separately");
if (!Dialogs.showParameterDialog("Advanced options", params))
return false;
reweightSamples = params.getBooleanParameterValue("reweightSamples");
maxSamples = params.getIntParameterValue("maxSamples");
rngSeed = params.getIntParameterValue("rngSeed");
pcaChoice = (String) params.getChoiceParameterValue("featureReduction");
boolean pcaNormalize = PCA_NORM.equals(pcaChoice);
double pcaRetainedVariance = PCA_NONE.equals(pcaChoice) ? 0 : params.getDoubleParameterValue("pcaRetainedVariance");
normalization.setNormalization((Normalization) params.getChoiceParameterValue("normalization"));
normalization.setPCARetainedVariance(pcaRetainedVariance);
normalization.setPCANormalize(pcaNormalize);
nThreads.set(params.getIntParameterValue("numThreads"));
var strategy = (BoundaryStrategy) params.getChoiceParameterValue("boundaryStrategy");
strategy = BoundaryStrategy.setThickness(strategy, params.getDoubleParameterValue("boundaryThickness"));
helper.setBoundaryStrategy(strategy);
return true;
}
use of qupath.lib.plugins.parameters.ParameterList in project qupath by qupath.
the class PixelClassifierUI method promptToAddMeasurements.
private static boolean promptToAddMeasurements(ImageData<BufferedImage> imageData, PixelClassifier classifier, String classifierName) {
if (imageData == null) {
Dialogs.showNoImageError("Pixel classifier");
return false;
}
var hierarchy = imageData.getHierarchy();
List<SelectionChoice> choices = buildChoiceList(imageData.getHierarchy(), SelectionChoice.values());
SelectionChoice defaultChoice;
if (choices.contains(SelectionChoice.CURRENT_SELECTION))
defaultChoice = SelectionChoice.CURRENT_SELECTION;
else if (choices.contains(SelectionChoice.ANNOTATIONS))
defaultChoice = SelectionChoice.ANNOTATIONS;
else
defaultChoice = choices.get(0);
var params = new ParameterList().addStringParameter("id", "Measurement name", classifierName == null ? "Classifier" : classifierName, "Choose a base name for measurements - this helps distinguish between measurements from different classifiers").addChoiceParameter("choice", "Select objects", defaultChoice, choices, "Select the objects");
if (!Dialogs.showParameterDialog("Pixel classifier", params))
return false;
var measurementID = params.getStringParameterValue("id");
var selectionChoice = (SelectionChoice) params.getChoiceParameterValue("choice");
selectionChoice.handleSelection(imageData);
var objectsToMeasure = hierarchy.getSelectionModel().getSelectedObjects();
int n = objectsToMeasure.size();
if (objectsToMeasure.isEmpty()) {
objectsToMeasure = Collections.singleton(hierarchy.getRootObject());
logger.info("Requesting measurements for image");
} else if (n == 1)
logger.info("Requesting measurements for one object");
else
logger.info("Requesting measurements for {} objects", n);
if (PixelClassifierTools.addMeasurementsToSelectedObjects(imageData, classifier, measurementID)) {
if (classifierName != null) {
String idString = measurementID == null ? "null" : "\"" + measurementID + "\"";
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Pixel classifier measurements", String.format("addPixelClassifierMeasurements(\"%s\", %s)", classifierName, idString)));
}
return true;
}
return false;
}
use of qupath.lib.plugins.parameters.ParameterList in project qupath by qupath.
the class ImageJMacroRunner method addRunnableTasks.
@Override
protected void addRunnableTasks(final ImageData<BufferedImage> imageData, final PathObject parentObject, final List<Runnable> tasks) {
final ParameterList params = getParameterList(imageData);
boolean doParallel = Boolean.TRUE.equals(params.getBooleanParameterValue("doParallel"));
tasks.add(new Runnable() {
@Override
public void run() {
if (Thread.currentThread().isInterrupted()) {
logger.warn("Execution interrupted - skipping {}", parentObject);
return;
}
if (SwingUtilities.isEventDispatchThread() || doParallel)
// TODO: Deal with logging macro text properly
runMacro(params, imageData, null, parentObject, macroText);
else {
try {
SwingUtilities.invokeAndWait(() -> runMacro(params, imageData, null, parentObject, macroText));
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// TODO: Deal with logging macro text properly
}
}
});
}
use of qupath.lib.plugins.parameters.ParameterList in project qupath by qupath.
the class SubcellularDetection method processObject.
/**
* Initial version of subcellular detection processing.
*
* @param pathObject
* @param params
* @param imageWrapper
* @return
* @throws InterruptedException
* @throws IOException
*/
static boolean processObject(final PathObject pathObject, final ParameterList params, final ImageWrapper imageWrapper) throws InterruptedException, IOException {
// Get the base classification for the object as it currently stands
PathClass baseClass = PathClassTools.getNonIntensityAncestorClass(pathObject.getPathClass());
// Variable to hold estimated spot count
double estimatedSpots;
// We assume that after this processing, any previous sub-cellular objects should be removed
pathObject.clearPathObjects();
// Ensure we have no existing subcellular detection measurements - if we do, remove them
String[] existingMeasurements = pathObject.getMeasurementList().getMeasurementNames().stream().filter(n -> n.startsWith("Subcellular:")).toArray(n -> new String[n]);
if (existingMeasurements.length > 0) {
pathObject.getMeasurementList().removeMeasurements(existingMeasurements);
pathObject.getMeasurementList().close();
}
// // If we're part of a TMA core, request the whole core...
// if (pathObject.getParent() instanceof TMACoreObject && pathObject.getParent().hasROI()) {
// regionStore.getImage(server, RegionRequest.createInstance(server.getPath(), 1, pathObject.getParent().getROI()), 25, true);
// }
ROI pathROI = pathObject.getROI();
if (pathROI == null || pathROI.isEmpty())
return false;
// double downsample = 0.5;
double downsample = 1;
// Determine spot size
ImageServer<BufferedImage> server = imageWrapper.getServer();
PixelCalibration cal = server.getPixelCalibration();
double minSpotArea, maxSpotArea, singleSpotArea;
double pixelWidth, pixelHeight;
if (cal.hasPixelSizeMicrons()) {
double spotSizeMicrons = params.getDoubleParameterValue("spotSizeMicrons");
double minSpotSizeMicrons = params.getDoubleParameterValue("minSpotSizeMicrons");
double maxSpotSizeMicrons = params.getDoubleParameterValue("maxSpotSizeMicrons");
pixelWidth = cal.getPixelWidthMicrons() * downsample;
pixelHeight = cal.getPixelHeightMicrons() * downsample;
singleSpotArea = spotSizeMicrons / (pixelWidth * pixelHeight);
minSpotArea = minSpotSizeMicrons / (pixelWidth * pixelHeight);
maxSpotArea = maxSpotSizeMicrons / (pixelWidth * pixelHeight);
} else {
singleSpotArea = params.getDoubleParameterValue("spotSizePixels");
minSpotArea = params.getDoubleParameterValue("minSpotSizePixels");
maxSpotArea = params.getDoubleParameterValue("maxSpotSizePixels");
pixelWidth = downsample;
pixelHeight = downsample;
}
boolean includeClusters = Boolean.TRUE.equals(params.getBooleanParameterValue("includeClusters"));
boolean doSmoothing = Boolean.TRUE.equals(params.getBooleanParameterValue("doSmoothing"));
boolean splitByIntensity = Boolean.TRUE.equals(params.getBooleanParameterValue("splitByIntensity"));
boolean splitByShape = Boolean.TRUE.equals(params.getBooleanParameterValue("splitByShape"));
// Get region to request - give a pixel as border
int xStart = (int) Math.max(0, pathROI.getBoundsX() - 1);
int yStart = (int) Math.max(0, pathROI.getBoundsY() - 1);
int width = (int) Math.min(server.getWidth() - 1, pathROI.getBoundsX() + pathROI.getBoundsWidth() + 1.5) - xStart;
int height = (int) Math.min(server.getHeight() - 1, pathROI.getBoundsY() + pathROI.getBoundsHeight() + 1.5) - yStart;
if (width <= 0 || height <= 0) {
logger.error("Negative ROI size for {}", pathROI);
pathObject.setPathClass(baseClass);
return false;
}
int z = pathROI.getZ();
int t = pathROI.getT();
// Don't associate with channel
int c = -1;
RegionRequest region = RegionRequest.createInstance(server.getPath(), 1.0, xStart, yStart, width, height, z, t);
// Mask to indicate pixels within the cell
byte[] cellMask = null;
for (String channelName : imageWrapper.getChannelNames(true, true)) {
double detectionThreshold = params.getDoubleParameterValue("detection[" + channelName + "]");
if (Double.isNaN(detectionThreshold) || detectionThreshold < 0)
continue;
// // TODO: Consider whether to use channel numbers for non-brightfield images
// if (!imageWrapper.imageData.isBrightfield())
// c++;
SimpleImage img = imageWrapper.getRegion(region, channelName);
// Get an ImageJ-friendly calibration for ROI conversion
Calibration calIJ = new Calibration();
calIJ.xOrigin = -xStart / downsample;
calIJ.yOrigin = -yStart / downsample;
// Create a cell mask
if (cellMask == null) {
BufferedImage imgMask = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g2d = imgMask.createGraphics();
if (downsample != 1)
g2d.scale(1.0 / downsample, 1.0 / downsample);
g2d.translate(-xStart, -yStart);
Shape shape = RoiTools.getShape(pathROI);
g2d.setColor(Color.WHITE);
g2d.fill(shape);
g2d.dispose();
cellMask = (byte[]) ((DataBufferByte) imgMask.getRaster().getDataBuffer()).getData(0);
}
// Get a buffer containing the image pixels
int w = img.getWidth();
int h = img.getHeight();
// Identify (& try to separate) spots
// Mask out non-cell areas as we go
FloatProcessor fpDetection = new FloatProcessor(w, h);
if (doSmoothing) {
for (int i = 0; i < w * h; i++) fpDetection.setf(i, img.getValue(i % w, i / w));
fpDetection.smooth();
for (int i = 0; i < w * h; i++) {
if (cellMask[i] == (byte) 0)
fpDetection.setf(i, 0f);
}
} else {
for (int i = 0; i < w * h; i++) {
if (cellMask[i] == (byte) 0)
fpDetection.setf(i, 0f);
else
fpDetection.setf(i, img.getValue(i % w, i / w));
}
}
ByteProcessor bpSpots;
if (splitByIntensity)
bpSpots = new MaximumFinder().findMaxima(fpDetection, detectionThreshold / 10.0, detectionThreshold, MaximumFinder.SEGMENTED, false, false);
else
bpSpots = SimpleThresholding.thresholdAboveEquals(fpDetection, (float) detectionThreshold);
if (splitByShape) {
new EDM().toWatershed(bpSpots);
}
// Loop through spot ROIs & make a decision
bpSpots.setThreshold(1, ImageProcessor.NO_THRESHOLD, ImageProcessor.NO_LUT_UPDATE);
List<PolygonRoi> possibleSpotRois = RoiLabeling.getFilledPolygonROIs(bpSpots, Wand.FOUR_CONNECTED);
List<PathObject> spotObjects = new ArrayList<>();
List<PathObject> clusterObjects = new ArrayList<>();
estimatedSpots = 0;
for (PolygonRoi spotRoi : possibleSpotRois) {
fpDetection.setRoi(spotRoi);
ImageStatistics stats = fpDetection.getStatistics();
// In v0.2
// ImagePlane plane = ImagePlane.getPlaneWithChannel(spotRoi.getCPosition(), spotRoi.getZPosition(), spotRoi.getTPosition());
// In v0.3
ImagePlane plane = ImagePlane.getPlaneWithChannel(c, z, t);
PathObject spotOrCluster = null;
if (stats.pixelCount >= minSpotArea && stats.pixelCount <= maxSpotArea) {
ROI roi = IJTools.convertToROI(spotRoi, calIJ, downsample, plane);
// cluster = new SubcellularObject(roi, 1);
spotOrCluster = createSubcellularObject(roi, 1);
estimatedSpots += 1;
} else if (includeClusters && stats.pixelCount >= minSpotArea) {
// Add a cluster
ROI roi = IJTools.convertToROI(spotRoi, calIJ, downsample, plane);
double nSpots = stats.pixelCount / singleSpotArea;
estimatedSpots += nSpots;
// cluster = new SubcellularObject(roi, nSpots);
spotOrCluster = createSubcellularObject(roi, nSpots);
}
if (spotOrCluster != null) {
boolean isCluster = spotOrCluster.getMeasurementList().getMeasurementValue("Num spots") > 1;
int rgb = imageWrapper.getChannelColor(channelName);
rgb = isCluster ? ColorTools.makeScaledRGB(rgb, 0.5) : ColorTools.makeScaledRGB(rgb, 1.5);
PathClass pathClass = PathClassFactory.getDerivedPathClass(spotOrCluster.getPathClass(), channelName + " object", rgb);
spotOrCluster.setPathClass(pathClass);
spotOrCluster.getMeasurementList().putMeasurement("Subcellular cluster: " + channelName + ": Area", stats.pixelCount * pixelWidth * pixelHeight);
spotOrCluster.getMeasurementList().putMeasurement("Subcellular cluster: " + channelName + ": Mean channel intensity", stats.mean);
// cluster.getMeasurementList().putMeasurement("Subcellular cluster: " + channelName + ": Max channel intensity", stats.max);
spotOrCluster.getMeasurementList().close();
if (isCluster)
clusterObjects.add(spotOrCluster);
else
spotObjects.add(spotOrCluster);
}
}
// Add measurements
MeasurementList measurementList = pathObject.getMeasurementList();
measurementList.putMeasurement("Subcellular: " + channelName + ": Num spots estimated", estimatedSpots);
measurementList.putMeasurement("Subcellular: " + channelName + ": Num single spots", spotObjects.size());
measurementList.putMeasurement("Subcellular: " + channelName + ": Num clusters", clusterObjects.size());
// Add spots
pathObject.addPathObjects(spotObjects);
pathObject.addPathObjects(clusterObjects);
}
return true;
}
use of qupath.lib.plugins.parameters.ParameterList in project qupath by qupath.
the class PathIntensityClassifierPane method initialize.
private void initialize() {
panelHistogram = new HistogramPanelFX();
panelHistogram.setShowTickLabels(false);
panelHistogram.getChart().getXAxis().setVisible(false);
panelHistogram.getChart().getXAxis().setTickMarkVisible(false);
panelHistogram.getChart().getYAxis().setVisible(false);
panelHistogram.getChart().getYAxis().setTickMarkVisible(false);
panelHistogram.getChart().setMinHeight(10);
panelHistogram.getChart().setMinWidth(10);
panelHistogram.getChart().setVisible(false);
panelHistogram.getChart().setAnimated(false);
thresholdWrapper = new ThresholdedChartWrapper(panelHistogram.getChart());
comboIntensities = new ComboBox<>();
comboIntensities.setOnAction(e -> {
String selected = comboIntensities.getSelectionModel().getSelectedItem();
logger.trace("Intensities selected: {}", selected);
updateIntensityHistogram();
});
comboIntensities.setTooltip(new Tooltip("Select an intensity feature for thresholding, e.g. to sub-classify objects according to levels of positive staining"));
double threshold_1 = 0.2;
double threshold_2 = 0.4;
double threshold_3 = 0.6;
paramsIntensity = new ParameterList().addDoubleParameter("threshold_1", "Threshold 1+", threshold_1, null, 0, 1.5, "Set first intensity threshold, if required (lowest)").addDoubleParameter("threshold_2", "Threshold 2+", threshold_2, null, 0, 1.5, "Set second intensity threshold, if required (intermediate)").addDoubleParameter("threshold_3", "Threshold 3+", threshold_3, null, 0, 1.5, "Set third intensity threshold, if required (highest)").addBooleanParameter("single_threshold", "Use single threshold", false, "Use only the first intensity threshold to separate positive & negative objects");
pane.add(new Label("Intensity feature: "), 0, 0);
pane.add(comboIntensities, 1, 0);
comboIntensities.setMaxWidth(Double.MAX_VALUE);
GridPane.setHgrow(comboIntensities, Priority.ALWAYS);
this.panelParameters = new ParameterPanelFX(paramsIntensity);
this.panelParameters.addParameterChangeListener(new ParameterChangeListener() {
@Override
public void parameterChanged(ParameterList parameterList, String key, boolean isAdjusting) {
if ("single_threshold".equals(key)) {
boolean single = paramsIntensity.getBooleanParameterValue("single_threshold");
panelParameters.setParameterEnabled("threshold_2", !single);
panelParameters.setParameterEnabled("threshold_3", !single);
}
updateHistogramThresholdLines();
}
});
// pane.add(panelParameters.getPane(), 0, 1, 2, 1);
// GridPane.setFillWidth(panelParameters.getPane(), Boolean.FALSE);
//
// pane.add(thresholdWrapper.getPane(), 2, 1, 1, 1);
// // thresholdWrapper.getPane().setMinSize(10, 10);
// // GridPane.setHgrow(thresholdWrapper.getPane(), Priority.ALWAYS);
// GridPane.setFillHeight(thresholdWrapper.getPane(), Boolean.TRUE);
BorderPane paneBorder = new BorderPane();
paneBorder.setLeft(panelParameters.getPane());
paneBorder.setCenter(thresholdWrapper.getPane());
pane.add(paneBorder, 0, 1, 2, 1);
pane.setVgap(5);
pane.setHgap(5);
// if (addTitle)
// setBorder(BorderFactory.createTitledBorder("Intensity feature"));
}
Aggregations