use of qupath.opencv.ml.pixel.PixelClassifierTools.CreateObjectOptions in project qupath by qupath.
the class PixelClassifierUI method promptToCreateObjects.
/**
* Prompt the user to create objects directly from the pixels of an {@link ImageServer}.
* Often, the {@link ImageServer} has been created by applying a {@link PixelClassifier}.
*
* @param imageData the {@link ImageData} to which objects should be added
* @param classifier the {@link ImageServer} used to generate objects
* @param classifierName the name of the classifier; if not null and the command runs to completion, it will be logged in the history
* workflow of the {@link ImageData} for later scripting.
* @return true if changes were made, false otherwise
*/
public static boolean promptToCreateObjects(ImageData<BufferedImage> imageData, PixelClassifier classifier, String classifierName) {
Objects.requireNonNull(imageData);
Objects.requireNonNull(classifier);
// Check what is selected
List<SelectionChoice> choices = buildChoiceList(imageData.getHierarchy(), SelectionChoice.FULL_IMAGE, SelectionChoice.CURRENT_SELECTION, SelectionChoice.ANNOTATIONS, SelectionChoice.TMA);
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 parentChoice = Dialogs.showChoiceDialog("Pixel classifier", "Choose parent objects", choices, defaultChoice);
if (parentChoice == null)
return false;
var outputObjectTypes = Arrays.asList("Annotation", "Detection");
// To avoid confusing the user unnecessarily, if we *only* have ignored classes then set default for includeIgnored to true
var labels = classifier.getMetadata().getClassificationLabels();
boolean allIgnored = !labels.isEmpty() && labels.values().stream().allMatch(p -> p == null || PathClassTools.isIgnoredClass(p));
boolean includeIgnored = allIgnored;
var cal = imageData.getServer().getPixelCalibration();
var units = cal.unitsMatch2D() ? cal.getPixelWidthUnit() + "^2" : cal.getPixelWidthUnit() + "x" + cal.getPixelHeightUnit();
ParameterList params;
if (lastCreateObjectParams != null) {
params = lastCreateObjectParams.duplicate();
params.setHiddenParameters(false, params.getKeyValueParameters(true).keySet().toArray(String[]::new));
((BooleanParameter) params.getParameters().get("includeIgnored")).setValue(includeIgnored);
} else {
params = new ParameterList().addChoiceParameter("objectType", "New object type", "Annotation", outputObjectTypes, "Define the type of objects that will be created").addDoubleParameter("minSize", "Minimum object size", 0, units, "Minimum size of a region to keep (smaller regions will be dropped)").addDoubleParameter("minHoleSize", "Minimum hole size", 0, units, "Minimum size of a hole to keep (smaller holes will be filled)").addBooleanParameter("doSplit", "Split objects", false, "Split multi-part regions into separate objects").addBooleanParameter("clearExisting", "Delete existing objects", false, "Delete any existing objects within the selected object before adding new objects (or entire image if no object is selected)").addBooleanParameter("includeIgnored", "Create objects for ignored classes", includeIgnored, "Create objects for classifications that are usually ignored (e.g. \"Ignore*\", \"Region*\")").addBooleanParameter("selectNew", "Set new objects to selected", false, "Set the newly-created objects to be selected");
}
if (!Dialogs.showParameterDialog("Create objects", params))
return false;
boolean createDetections = params.getChoiceParameterValue("objectType").equals("Detection");
boolean doSplit = params.getBooleanParameterValue("doSplit");
includeIgnored = params.getBooleanParameterValue("includeIgnored");
double minSize = params.getDoubleParameterValue("minSize");
double minHoleSize = params.getDoubleParameterValue("minHoleSize");
boolean clearExisting = params.getBooleanParameterValue("clearExisting");
boolean selectNew = params.getBooleanParameterValue("selectNew");
lastCreateObjectParams = params;
parentChoice.handleSelection(imageData);
List<CreateObjectOptions> options = new ArrayList<>();
if (doSplit)
options.add(CreateObjectOptions.SPLIT);
if (clearExisting)
options.add(CreateObjectOptions.DELETE_EXISTING);
if (includeIgnored)
options.add(CreateObjectOptions.INCLUDE_IGNORED);
else if (allIgnored) {
Dialogs.showErrorMessage(title, "Cannot create objects - all class names have an asterisk to show they should be 'ignored'!");
return false;
}
if (selectNew)
options.add(CreateObjectOptions.SELECT_NEW);
var optionsArray = options.toArray(CreateObjectOptions[]::new);
String optionsString = "";
if (!options.isEmpty())
optionsString = ", " + options.stream().map(o -> "\"" + o.name() + "\"").collect(Collectors.joining(", "));
try {
if (createDetections) {
if (PixelClassifierTools.createDetectionsFromPixelClassifier(imageData, classifier, minSize, minHoleSize, optionsArray)) {
if (classifierName != null) {
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Pixel classifier create detections", String.format("createDetectionsFromPixelClassifier(\"%s\", %s, %s)", classifierName, minSize, minHoleSize + optionsString)));
}
return true;
}
} else {
if (PixelClassifierTools.createAnnotationsFromPixelClassifier(imageData, classifier, minSize, minHoleSize, optionsArray)) {
if (classifierName != null) {
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Pixel classifier create annotations", String.format("createAnnotationsFromPixelClassifier(\"%s\", %s, %s)", classifierName, minSize, minHoleSize + optionsString)));
}
return true;
}
}
} catch (IOException e) {
Dialogs.showErrorMessage(title, e);
}
return false;
}
use of qupath.opencv.ml.pixel.PixelClassifierTools.CreateObjectOptions in project qupath by qupath.
the class DensityMaps method threshold.
/**
* Threshold one or more channels of a density map to generate new annotations.
*
* @param hierarchy hierarchy to which objects should be added
* @param densityServer density map
* @param thresholds map between channel numbers and thresholds
* @param pathClassName name of the classification to apply to the generated annotations
* @param options additional options to customize how annotations are created
* @return true if changes were made, false otherwise
* @throws IOException
*/
public static boolean threshold(PathObjectHierarchy hierarchy, ImageServer<BufferedImage> densityServer, Map<Integer, ? extends Number> thresholds, String pathClassName, CreateObjectOptions... options) throws IOException {
logger.debug("Thresholding {} with thresholds {}, options", densityServer, thresholds, Arrays.asList(options));
// Apply threshold to densities
PathClass lessThan = PathClassFactory.getPathClass(StandardPathClasses.IGNORE);
PathClass greaterThan = PathClassFactory.getPathClass(pathClassName);
// If we request to delete existing objects, apply this only to annotations with the target class
var optionsList = Arrays.asList(options);
boolean changes = false;
if (optionsList.contains(CreateObjectOptions.DELETE_EXISTING)) {
Collection<PathObject> toRemove;
if (hierarchy.getSelectionModel().noSelection())
toRemove = hierarchy.getAnnotationObjects().stream().filter(p -> p.getPathClass() == greaterThan).collect(Collectors.toList());
else {
toRemove = new HashSet<>();
var selectedObjects = new LinkedHashSet<>(hierarchy.getSelectionModel().getSelectedObjects());
for (var selected : selectedObjects) {
PathObjectTools.getDescendantObjects(selected, toRemove, PathAnnotationObject.class);
}
// Don't remove selected objects
toRemove.removeAll(selectedObjects);
}
if (!toRemove.isEmpty()) {
hierarchy.removeObjects(toRemove, true);
changes = true;
}
// Remove option
options = optionsList.stream().filter(o -> o != CreateObjectOptions.DELETE_EXISTING).toArray(CreateObjectOptions[]::new);
}
var thresholdedServer = PixelClassifierTools.createThresholdServer(densityServer, thresholds, lessThan, greaterThan);
return PixelClassifierTools.createAnnotationsFromPixelClassifier(hierarchy, thresholdedServer, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, options) | changes;
}
Aggregations