use of qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep in project qupath by qupath.
the class Commands method promptToDeleteObjects.
/**
* Prompt to delete objects of a specified type, or all objects.
* @param imageData
* @param cls
*/
public static void promptToDeleteObjects(ImageData<?> imageData, Class<? extends PathObject> cls) {
if (imageData == null)
return;
PathObjectHierarchy hierarchy = imageData.getHierarchy();
// Handle no specified class - indicates all objects of all types should be cleared
if (cls == null) {
int n = hierarchy.nObjects();
if (n == 0)
return;
String message;
if (n == 1)
message = "Delete object?";
else
message = "Delete all " + n + " objects?";
if (Dialogs.showYesNoDialog("Delete objects", message)) {
hierarchy.clearAll();
hierarchy.getSelectionModel().setSelectedObject(null);
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Clear all objects", "clearAllObjects();"));
}
return;
}
// Handle clearing TMA grid
if (TMACoreObject.class.equals(cls)) {
if (hierarchy.getTMAGrid() != null) {
if (Dialogs.showYesNoDialog("Delete objects", "Clear TMA grid?")) {
hierarchy.setTMAGrid(null);
PathObject selected = hierarchy.getSelectionModel().getSelectedObject();
if (selected instanceof TMACoreObject)
hierarchy.getSelectionModel().setSelectedObject(null);
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Clear TMA Grid", "clearTMAGrid();"));
}
return;
}
}
// Handle clearing objects of another specified type
Collection<PathObject> pathObjects = hierarchy.getObjects(null, cls);
if (pathObjects.isEmpty())
return;
int n = pathObjects.size();
String message = n == 1 ? "Delete 1 object?" : "Delete " + n + " objects?";
if (Dialogs.showYesNoDialog("Delete objects", message)) {
hierarchy.removeObjects(pathObjects, true);
PathObject selected = hierarchy.getSelectionModel().getSelectedObject();
if (selected != null && selected.getClass().isAssignableFrom(cls))
hierarchy.getSelectionModel().setSelectedObject(null);
if (selected != null && selected.getClass().isAssignableFrom(cls))
hierarchy.getSelectionModel().setSelectedObject(null);
if (cls == PathDetectionObject.class)
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Clear detections", "clearDetections();"));
else if (cls == PathAnnotationObject.class)
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Clear annotations", "clearAnnotations();"));
else if (cls == TMACoreObject.class)
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Clear TMA grid", "clearTMAGrid();"));
else
logger.warn("Cannot clear all objects for class {}", cls);
}
}
use of qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep in project qupath by qupath.
the class Commands method requestShapeFeatures.
private static void requestShapeFeatures(ImageData<?> imageData, Collection<ShapeFeatures> features) {
if (imageData == null)
return;
var featureArray = features.toArray(ShapeFeatures[]::new);
if (featureArray.length == 0)
return;
Collection<PathObject> selected = imageData.getHierarchy().getSelectionModel().getSelectedObjects();
if (selected.isEmpty()) {
Dialogs.showWarningNotification("Shape features", "No objects selected!");
} else {
selected = new ArrayList<>(selected);
String featureString = Arrays.stream(featureArray).map(f -> "\"" + f.name() + "\"").collect(Collectors.joining(", "));
QP.addShapeMeasurements(imageData, selected, featureArray);
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Add shape measurements", String.format("addShapeMeasurements(%s)", featureString)));
if (selected.size() == 1)
Dialogs.showInfoNotification("Shape features", "Shape features calculated for one object");
else
Dialogs.showInfoNotification("Shape features", "Shape features calculated for " + selected.size() + " objects");
}
}
use of qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep in project qupath by qupath.
the class Commands method resetClassifications.
/**
* Select objects that are instances of a specified class, logging an appropriate method in the workflow.
*
* @param imageData
* @param cls
*/
public static void resetClassifications(final ImageData<?> imageData, final Class<? extends PathObject> cls) {
if (imageData == null) {
logger.warn("No classifications to reset!");
return;
}
// Do the reset
QP.resetClassifications(imageData.getHierarchy(), cls);
// Log the appropriate command
Map<String, String> params = Collections.singletonMap("Type", PathObjectTools.getSuitableName(cls, false));
String method;
if (cls == PathDetectionObject.class)
method = "resetDetectionClassifications();";
else
// TODO: Get a suitable name to disguise Java classes
method = "resetClassifications(" + cls.getName() + ");";
WorkflowStep newStep = new DefaultScriptableWorkflowStep("Reset classifications", params, method);
WorkflowStep lastStep = imageData.getHistoryWorkflow().getLastStep();
if (newStep.equals(lastStep))
imageData.getHistoryWorkflow().replaceLastStep(newStep);
else
imageData.getHistoryWorkflow().addStep(newStep);
}
use of qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep in project qupath by qupath.
the class Commands method mergeSelectedAnnotations.
// /**
// * Merge the points ROIs of different objects to create a single object containing all points with a specific {@link PathClass}.
// * @param imageData the image data containing points to merge
// * @param selectedOnly if true, use only classes found within the currently selected objects
// */
// public static void mergePointsForClasses(ImageData<?> imageData, boolean selectedOnly) {
// var hierarchy = imageData == null ? null : imageData.getHierarchy();
// if (hierarchy == null) {
// Dialogs.showNoImageError("Merge points");
// return;
// }
// if (selectedOnly) {
// PathObjectTools.mergePointsForSelectedObjectClasses(hierarchy);
// imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep(
// "Merge points for selected classifications",
// "mergePointsForSelectedObjectClasses();"
// ));
// } else {
// PathObjectTools.mergePointsForAllClasses(hierarchy);
// imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep(
// "Merge points for all classifications",
// "mergePointsForAllClasses();"
// ));
// }
// }
/**
* Merge the currently-selected annotations for an image, replacing them with a single new annotation.
* @param imageData
*/
public static void mergeSelectedAnnotations(ImageData<?> imageData) {
if (imageData == null)
return;
PathObjectHierarchy hierarchy = imageData.getHierarchy();
logger.debug("Merging selected annotations");
QP.mergeSelectedAnnotations(hierarchy);
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Merge selected annotations", "mergeSelectedAnnotations()"));
}
use of qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep in project qupath by qupath.
the class ExportObjectsCommand method runGeoJsonExport.
/**
* Run the path object GeoJSON export command.
* @param qupath
* @return success
* @throws IOException
*/
public static boolean runGeoJsonExport(QuPathGUI qupath) throws IOException {
// Get ImageData
var imageData = qupath.getImageData();
if (imageData == null)
return false;
// Get hierarchy
PathObjectHierarchy hierarchy = imageData.getHierarchy();
String allObjects = "All objects";
String selectedObjects = "Selected objects";
String defaultObjects = hierarchy.getSelectionModel().noSelection() ? allObjects : selectedObjects;
// Params
var parameterList = new ParameterList().addChoiceParameter("exportOptions", "Export ", defaultObjects, Arrays.asList(allObjects, selectedObjects), "Choose which objects to export - run a 'Select annotations/detections' command first if needed").addBooleanParameter("excludeMeasurements", "Exclude measurements", false, "Exclude object measurements during export - for large numbers of detections this can help reduce the file size").addBooleanParameter("doPretty", "Pretty JSON", false, "Pretty GeoJSON is more human-readable but results in larger file sizes").addBooleanParameter("doFeatureCollection", "Export as FeatureCollection", true, "Export as a 'FeatureCollection', which is a standard GeoJSON way to represent multiple objects; if not, a regular JSON object/array will be export").addBooleanParameter("doZip", "Compress data (zip)", false, "Compressed files take less memory");
if (!Dialogs.showParameterDialog("Export objects", parameterList))
return false;
Collection<PathObject> toProcess;
var comboChoice = parameterList.getChoiceParameterValue("exportOptions");
if (comboChoice.equals("Selected objects")) {
if (hierarchy.getSelectionModel().noSelection()) {
Dialogs.showErrorMessage("No selection", "No selection detected!");
return false;
}
toProcess = hierarchy.getSelectionModel().getSelectedObjects();
} else
toProcess = hierarchy.getObjects(null, null);
// Remove PathRootObject
toProcess = toProcess.stream().filter(e -> !e.isRootObject()).collect(Collectors.toList());
// Check if includes ellipse(s), as they will need to be polygonized
var nEllipses = toProcess.stream().filter(ann -> isEllipse(ann)).count();
if (nEllipses > 0) {
String message = nEllipses == 1 ? "1 ellipse will be polygonized, continue?" : String.format("%d ellipses will be polygonized, continue?", nEllipses);
var response = Dialogs.showYesNoDialog("Ellipse polygonization", message);
if (!response)
return false;
}
File outFile;
// Get default name & output directory
var project = qupath.getProject();
String defaultName = imageData.getServer().getMetadata().getName();
if (project != null) {
var entry = project.getEntry(imageData);
if (entry != null)
defaultName = entry.getImageName();
}
defaultName = GeneralTools.getNameWithoutExtension(defaultName);
File defaultDirectory = project == null || project.getPath() == null ? null : project.getPath().toFile();
while (defaultDirectory != null && !defaultDirectory.isDirectory()) defaultDirectory = defaultDirectory.getParentFile();
if (parameterList.getBooleanParameterValue("doZip"))
outFile = Dialogs.promptToSaveFile("Export to file", defaultDirectory, defaultName + ".zip", "ZIP archive", ".zip");
else
outFile = Dialogs.promptToSaveFile("Export to file", defaultDirectory, defaultName + ".geojson", "GeoJSON", ".geojson");
// If user cancels
if (outFile == null)
return false;
List<GeoJsonExportOptions> options = new ArrayList<>();
if (parameterList.getBooleanParameterValue("excludeMeasurements"))
options.add(GeoJsonExportOptions.EXCLUDE_MEASUREMENTS);
if (parameterList.getBooleanParameterValue("doPretty"))
options.add(GeoJsonExportOptions.PRETTY_JSON);
if (parameterList.getBooleanParameterValue("doFeatureCollection"))
options.add(GeoJsonExportOptions.FEATURE_COLLECTION);
// Export
QP.exportObjectsToGeoJson(toProcess, outFile.getAbsolutePath(), options.toArray(GeoJsonExportOptions[]::new));
// Notify user of success
int nObjects = toProcess.size();
String message = nObjects == 1 ? "1 object was exported to " + outFile.getAbsolutePath() : String.format("%d objects were exported to %s", nObjects, outFile.getAbsolutePath());
Dialogs.showInfoNotification("Succesful export", message);
// Get history workflow
var historyWorkflow = imageData.getHistoryWorkflow();
// args for workflow step
Map<String, String> map = new LinkedHashMap<>();
map.put("path", outFile.getPath());
String method = comboChoice.equals(allObjects) ? "exportAllObjectsToGeoJson" : "exportSelectedObjectsToGeoJson";
String methodTitle = comboChoice.equals(allObjects) ? "Export all objects" : "Export selected objects";
String optionsString = options.stream().map(o -> "\"" + o.name() + "\"").collect(Collectors.joining(", "));
map.put("options", optionsString);
if (!optionsString.isEmpty())
optionsString = ", " + optionsString;
String methodString = String.format("%s(%s%s)", method, "\"" + GeneralTools.escapeFilePath(outFile.getPath()) + "\"", optionsString);
historyWorkflow.addStep(new DefaultScriptableWorkflowStep(methodTitle, map, methodString));
return true;
}
Aggregations