use of qupath.lib.objects.PathObject in project qupath by qupath.
the class RetainedTrainingObjects method put.
Map<PathClass, List<PathObject>> put(final String key, final Map<PathClass, List<PathObject>> value) {
if (key == null)
throw new IllegalArgumentException("Cannot retain objects without a key! Do you have a project open?");
// Also, we create a new map to ensure it's sorted
Map<PathClass, List<PathObject>> newMap = new TreeMap<>();
for (PathClass pathClass : value.keySet()) {
List<PathObject> originalList = value.get(pathClass);
List<PathObject> newList = originalList.stream().map(p -> {
if (p.isDetection())
return PathObjects.createDetectionObject(p.getROI(), p.getPathClass(), p.getMeasurementList());
else {
logger.debug("Adding non-detection object to retained map: {}", p);
return p;
}
}).collect(Collectors.toList());
newMap.put(pathClass, newList);
value.put(pathClass, newList);
}
return retainedObjectsMap.put(key, value);
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class ClassifierBuilderPane method hierarchyChanged.
@Override
public void hierarchyChanged(PathObjectHierarchyEvent event) {
if (event.getSource() == this)
return;
if (!Platform.isFxApplicationThread()) {
Platform.runLater(() -> hierarchyChanged(event));
return;
}
// Flag that the hierarchy has changed if this is any kind of event other than an object classification event
hierarchyChanged = hierarchyChanged || !event.isObjectClassificationEvent();
// TODO: Avoid reliance on this, and instead check the training objects used
if (event.isStructureChangeEvent()) {
// Don't respond to adding/removing annotation objects without classes set
if (event.isAddedOrRemovedEvent()) {
PathObject pathObjectChanged = event.getChangedObjects().get(0);
if (!(pathObjectChanged instanceof PathAnnotationObject) || pathObjectChanged.getPathClass() == null)
return;
}
} else if (event.isObjectClassificationEvent()) {
// If classifications have changed, we only care if these contain annotations
boolean containsAnnotations = containsObjectsOfClass(event.getChangedObjects(), PathAnnotationObject.class);
if (!containsAnnotations)
return;
} else if (event.getEventType() == HierarchyEventType.CHANGE_OTHER) {
return;
}
// TODO: See if calls can be reduced
maybeUpdate();
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class FeatureSelectionPane method makeFeatureSelectionPanel.
BorderPane makeFeatureSelectionPanel(final QuPathGUI qupath) {
tableFeatures.setTooltip(new Tooltip("Select object features to be used by the classifier"));
TableColumn<SelectableFeature, String> columnName = new TableColumn<>("Feature name");
columnName.setCellValueFactory(new PropertyValueFactory<>("featureName"));
columnName.setEditable(false);
TableColumn<SelectableFeature, Boolean> columnSelected = new TableColumn<>("Selected");
columnSelected.setCellValueFactory(new PropertyValueFactory<>("selected"));
columnSelected.setCellFactory(column -> new CheckBoxTableCell<>());
columnSelected.setEditable(true);
columnSelected.setResizable(false);
columnName.prefWidthProperty().bind(tableFeatures.widthProperty().subtract(columnSelected.widthProperty()));
tableFeatures.getColumns().add(columnName);
tableFeatures.getColumns().add(columnSelected);
tableFeatures.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
tableFeatures.setEditable(true);
ContextMenu menu = new ContextMenu();
MenuItem itemSelect = new MenuItem("Select");
itemSelect.setOnAction(e -> {
for (SelectableFeature feature : tableFeatures.getSelectionModel().getSelectedItems()) feature.setSelected(true);
});
menu.getItems().add(itemSelect);
MenuItem itemDeselect = new MenuItem("Deselect");
itemDeselect.setOnAction(e -> {
for (SelectableFeature feature : tableFeatures.getSelectionModel().getSelectedItems()) feature.setSelected(false);
});
menu.getItems().add(itemDeselect);
menu.getItems().add(new SeparatorMenuItem());
MenuItem itemDelete = new MenuItem("Delete highlighted features");
itemDelete.setOnAction(e -> {
List<String> highlightedFeatures = new ArrayList<>();
for (SelectableFeature feature : tableFeatures.getSelectionModel().getSelectedItems()) highlightedFeatures.add(feature.getFeatureName());
int nFeatures = highlightedFeatures.size();
ImageData<?> imageData = qupath.getImageData();
if (nFeatures == 0 || imageData == null || imageData.getHierarchy().isEmpty())
return;
String f = nFeatures == 1 ? "1 feature" : nFeatures + " features";
if (!Dialogs.showYesNoDialog("Delete feature measurements", "Are you sure you want to permanently delete " + f + " from all objects?"))
return;
// Determine the features to remove
// Loop through objects and delete features
List<PathObject> changedObjects = new ArrayList<>();
for (PathObject pathObject : imageData.getHierarchy().getFlattenedObjectList(null)) {
// TODO: Consider if non-detection objects should be supported
if (!pathObject.isDetection())
continue;
// Remove measurements & log as changed, if necessary
MeasurementList ml = pathObject.getMeasurementList();
int sizeBefore = ml.size();
ml.removeMeasurements(highlightedFeatures.toArray(new String[0]));
ml.close();
int sizeAfter = ml.size();
if (sizeAfter != sizeBefore)
changedObjects.add(pathObject);
}
imageData.getHierarchy().fireObjectMeasurementsChangedEvent(this, changedObjects);
tableFeatures.getSelectionModel().clearSelection();
if (!hasFeatures())
ensureMeasurementsUpdated();
// classifierData.setFeaturesSelected(features, false);
// tableFeatures.repaint();
});
menu.getItems().add(itemDelete);
tableFeatures.setContextMenu(menu);
// Button to update the features
BorderPane panelButtons = new BorderPane();
// Button btnUpdateFeatures = new Button("Update feature table");
// btnUpdateFeatures.setTooltip(new Tooltip("Check all objects & available features"));
// btnUpdateFeatures.setOnAction(e -> {
// ensureMeasurementsUpdated();
// if (panelIntensities != null)
// panelIntensities.setAvailableMeasurements(getAvailableFeatures(), "mean", "dab");
// });
Button btnSelectAll = new Button("Select all");
btnSelectAll.setOnAction(e -> {
if (!hasFeatures())
ensureMeasurementsUpdated();
for (SelectableFeature feature : tableFeatures.getItems()) feature.setSelected(true);
});
Button btnSelectNone = new Button("Select none");
btnSelectNone.setOnAction(e -> {
if (!hasFeatures())
ensureMeasurementsUpdated();
for (SelectableFeature feature : tableFeatures.getItems()) feature.setSelected(false);
});
GridPane panelSelectButtons = PaneTools.createColumnGridControls(btnSelectAll, btnSelectNone);
panelButtons.setTop(panelSelectButtons);
// panelButtons.setBottom(btnUpdateFeatures);
// btnUpdateFeatures.prefWidthProperty().bind(panelButtons.widthProperty());
BorderPane panelFeatures = new BorderPane();
panelFeatures.setCenter(tableFeatures);
panelFeatures.setBottom(panelButtons);
ensureMeasurementsUpdated();
return panelFeatures;
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class ParameterDialogWrapper method createDialog.
private Stage createDialog(final PathInteractivePlugin<T> plugin, final ParameterList params, final PluginRunner<T> pluginRunner) {
panel = new ParameterPanelFX(params);
panel.getPane().setPadding(new Insets(5, 5, 5, 5));
// panel.addParameterChangeListener(new ParameterChangeListener() {
//
// @Override
// public void parameterChanged(ParameterList parameterList, String key, boolean isAdjusting) {
//
// if (!plugin.requestLiveUpdate())
// return;
//
// PathObjectHierarchy hierarchy = pluginRunner.getHierarchy();
// if (hierarchy == null)
// return;
//
// Collection<Class<? extends PathObject>> supportedParents = plugin.getSupportedParentObjectClasses();
//
// PathObject selectedObject = pluginRunner.getSelectedObject();
// if (selectedObject == null) {
// if (supportedParents.contains(PathRootObject.class))
// Collections.singleton(hierarchy.getRootObject());
// } else if (supportedParents.contains(selectedObject.getClass()))
// Collections.singleton(selectedObject);
// }
//
// });
// final Button btnRun = new Button("Run " + plugin.getName());
final Button btnRun = new Button("Run");
btnRun.textProperty().bind(Bindings.createStringBinding(() -> {
if (btnRun.isDisabled())
return "Please wait...";
else
return "Run";
}, btnRun.disabledProperty()));
final Stage dialog = new Stage();
QuPathGUI qupath = QuPathGUI.getInstance();
if (qupath != null)
dialog.initOwner(qupath.getStage());
dialog.setTitle(plugin.getName());
final String emptyLabel = " \n";
final Label label = new Label(emptyLabel);
label.setStyle("-fx-font-weight: bold;");
label.setPadding(new Insets(5, 5, 5, 5));
label.setAlignment(Pos.CENTER);
label.setTextAlignment(TextAlignment.CENTER);
btnRun.setOnAction(e -> {
// Check if we have the parent objects available to make this worthwhile
if (plugin instanceof PathInteractivePlugin) {
// // Strip off any of our extra parameters
// params.removeParameter(KEY_REGIONS);
boolean alwaysPrompt = plugin.alwaysPromptForObjects();
ImageData<?> imageData = pluginRunner.getImageData();
Collection<PathObject> selected = imageData == null ? Collections.emptyList() : imageData.getHierarchy().getSelectionModel().getSelectedObjects();
Collection<? extends PathObject> parents = PathObjectTools.getSupportedObjects(selected, plugin.getSupportedParentObjectClasses());
if (alwaysPrompt || parents == null || parents.isEmpty()) {
if (!ParameterDialogWrapper.promptForParentObjects(pluginRunner, plugin, alwaysPrompt && !parents.isEmpty()))
return;
}
// promptForParentObjects
}
dialog.getScene().setCursor(Cursor.WAIT);
btnRun.setDisable(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
WorkflowStep lastStep = pluginRunner.getImageData().getHistoryWorkflow().getLastStep();
boolean success = plugin.runPlugin(pluginRunner, ParameterList.getParameterListJSON(params, " "));
WorkflowStep lastStepNew = pluginRunner.getImageData().getHistoryWorkflow().getLastStep();
if (success && lastStep != lastStepNew)
lastWorkflowStep = lastStepNew;
else
lastWorkflowStep = null;
} catch (Exception e) {
Dialogs.showErrorMessage("Plugin error", e);
} catch (OutOfMemoryError e) {
// This doesn't actually work...
Dialogs.showErrorMessage("Out of memory error", "Out of memory - try to close other applications, or decrease the number of parallel processors in the QuPath preferences");
} finally {
Platform.runLater(() -> {
QuPathGUI.getInstance().pluginRunningProperty().set(false);
dialog.getScene().setCursor(Cursor.DEFAULT);
label.setText(plugin.getLastResultsDescription());
btnRun.setDisable(false);
});
}
}
};
Thread t = new Thread(runnable, "Plugin thread");
QuPathGUI.getInstance().pluginRunningProperty().set(true);
t.start();
});
BorderPane pane = new BorderPane();
ScrollPane scrollPane = new ScrollPane();
label.setMaxWidth(Double.MAX_VALUE);
scrollPane.setContent(panel.getPane());
scrollPane.setFitToWidth(true);
pane.setCenter(scrollPane);
btnRun.setMaxWidth(Double.MAX_VALUE);
btnRun.setPadding(new Insets(5, 5, 5, 5));
pane.setBottom(btnRun);
Scene scene = new Scene(pane);
dialog.setScene(scene);
// Request focus, to make it easier to run from the keyboard
btnRun.requestFocus();
dialog.sizeToScene();
return dialog;
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class PathIntensityClassifierPane method updateIntensityHistogram.
private void updateIntensityHistogram() {
String selected = comboIntensities.getSelectionModel().getSelectedItem();
PathObjectHierarchy hierarchy = getHierarchy();
// if (!"None".equals(selected) || hierarchy == null)
if ("None".equals(selected) || hierarchy == null) {
if (panelHistogram != null)
panelHistogram.getHistogramData().clear();
return;
}
// Try to make a histogram & set it in the panel
// PathObject pathObjectSelected = hierarchy.getSelectionModel().getSelectedPathObject();
// For now, always use all objects (not direct descendants only)
Collection<PathObject> pathObjects = null;
// if (pathObjectSelected == null || !pathObjectSelected.hasChildren())
pathObjects = hierarchy.getDetectionObjects();
// else
// pathObjects = hierarchy.getDescendantObjects(pathObjectSelected, pathObjects, PathDetectionObject.class);
// Histogram histogram = Histogram.makeMeasurementHistogram(pathObjects, (String)selected, 256);
double[] values = Histogram.getMeasurementValues(pathObjects, (String) selected);
Histogram histogram = new Histogram(values, 128);
// Compute quartile values
Arrays.sort(values);
int nNaNs = 0;
// NaNs should be at the end of the list
for (int i = values.length - 1; i >= 0; i--) {
if (Double.isNaN(values[i]))
nNaNs++;
else
break;
}
// Should be same as histogram.getCountSum() ?
int nValues = values.length - nNaNs;
assert nValues == histogram.getCountSum();
if (nValues > 0) {
double median = values[nValues / 2];
double quartile1 = values[(int) (nValues / 4 + .5)];
double quartile3 = values[(int) (nValues * 3 / 4 + .5)];
logger.info(String.format("%s Quartile 1: %.4f", selected, quartile1));
logger.info(String.format("%s Median: %.4f", selected, median));
logger.info(String.format("%s Quartile 3: %.4f", selected, quartile3));
RunningStatistics stats = StatisticsHelper.computeRunningStatistics(values);
logger.info(String.format("%s Mean: %.4f", selected, stats.getMean()));
logger.info(String.format("%s Std.Dev.: %.4f", selected, stats.getStdDev()));
panelHistogram.getHistogramData().setAll(HistogramPanelFX.createHistogramData(histogram, true, (Integer) null));
} else
panelHistogram.getHistogramData().clear();
updateHistogramThresholdLines();
}
Aggregations