use of qupath.lib.objects.PathObject in project qupath by qupath.
the class ObservableMeasurementTableData method updateMeasurementList.
/**
* Update the entire measurement list for the current objects.
* @see #setImageData(ImageData, Collection)
*/
public synchronized void updateMeasurementList() {
// PathPrefs.setAllredMinPercentagePositive(0);
builderMap.clear();
// Add the image name
if (!PathPrefs.maskImageNamesProperty().get())
builderMap.put("Image", new ImageNameMeasurementBuilder(imageData));
// Check if we have any annotations / TMA cores
boolean containsDetections = false;
boolean containsAnnotations = false;
// boolean containsParentAnnotations = false;
boolean containsTMACores = false;
boolean containsRoot = false;
List<PathObject> pathObjectListCopy = new ArrayList<>(list);
for (PathObject temp : pathObjectListCopy) {
if (temp instanceof PathAnnotationObject) {
// if (temp.hasChildren())
// containsParentAnnotations = true;
containsAnnotations = true;
} else if (temp instanceof TMACoreObject) {
containsTMACores = true;
} else if (temp instanceof PathDetectionObject) {
containsDetections = true;
} else if (temp.isRootObject())
containsRoot = true;
}
boolean detectionsAnywhere = imageData == null ? containsDetections : !imageData.getHierarchy().getDetectionObjects().isEmpty();
// Include the object displayed name
// if (containsDetections || containsAnnotations || containsTMACores)
builderMap.put("Name", new ObjectNameMeasurementBuilder());
// Include the class
if (containsAnnotations || containsDetections) {
builderMap.put("Class", new PathClassMeasurementBuilder());
// Get the name of the containing TMA core if we have anything other than cores
if (imageData != null && imageData.getHierarchy().getTMAGrid() != null) {
builderMap.put("TMA core", new TMACoreNameMeasurementBuilder());
}
// Get the name of the first parent object
builderMap.put("Parent", new ParentNameMeasurementBuilder());
}
// Include the TMA missing status, if appropriate
if (containsTMACores) {
builderMap.put("Missing", new MissingTMACoreMeasurementBuilder());
}
if (containsAnnotations || containsDetections) {
builderMap.put("ROI", new ROINameMeasurementBuilder());
}
// Add centroids
if (containsAnnotations || containsDetections || containsTMACores) {
// ROICentroidMeasurementBuilder builder = new ROICentroidMeasurementBuilder(imageData, CentroidType.X);
// builderMap.put("Centroid X", builder);
// builder = new ROICentroidMeasurementBuilder(imageData, CentroidType.Y);
// builderMap.put("Centroid Y", builder);
ROICentroidMeasurementBuilder builder = new ROICentroidMeasurementBuilder(imageData, CentroidType.X);
builderMap.put(builder.getName(), builder);
builder = new ROICentroidMeasurementBuilder(imageData, CentroidType.Y);
builderMap.put(builder.getName(), builder);
}
// If we have metadata, store it
Set<String> metadataNames = new LinkedHashSet<>();
metadataNames.addAll(builderMap.keySet());
for (PathObject pathObject : pathObjectListCopy) {
if (pathObject instanceof MetadataStore) {
metadataNames.addAll(((MetadataStore) pathObject).getMetadataKeys());
}
}
// Ensure we have suitable builders
for (String name : metadataNames) {
if (!builderMap.containsKey(name))
builderMap.put(name, new StringMetadataMeasurementBuilder(name));
}
// Get all the 'built-in' feature measurements, stored in the measurement list
Collection<String> features = PathClassifierTools.getAvailableFeatures(pathObjectListCopy);
// Add derived measurements if we don't have only detections
if (containsAnnotations || containsTMACores || containsRoot) {
if (detectionsAnywhere) {
var builder = new ObjectTypeCountMeasurementBuilder(PathDetectionObject.class);
builderMap.put(builder.getName(), builder);
features.add(builder.getName());
}
// Here, we allow TMA cores to act like annotations
manager = new DerivedMeasurementManager(getImageData(), containsAnnotations || containsTMACores);
for (MeasurementBuilder<?> builder2 : manager.getMeasurementBuilders()) {
builderMap.put(builder2.getName(), builder2);
features.add(builder2.getName());
}
}
// If we have an annotation, add shape features
if (containsAnnotations) {
boolean anyPoints = false;
boolean anyAreas = false;
boolean anyLines = false;
@SuppressWarnings("unused") boolean anyPolygons = false;
for (PathObject pathObject : pathObjectListCopy) {
if (!pathObject.isAnnotation())
continue;
ROI roi = pathObject.getROI();
if (roi == null)
continue;
if (roi.isPoint())
anyPoints = true;
if (roi.isArea())
anyAreas = true;
if (roi.isLine())
anyLines = true;
if (pathObject.getROI() instanceof PolygonROI)
anyPolygons = true;
}
// Add point count, if needed
if (anyPoints) {
MeasurementBuilder<?> builder = new NumPointsMeasurementBuilder();
builderMap.put(builder.getName(), builder);
features.add(builder.getName());
}
// Add spatial measurements, if needed
if (anyAreas) {
MeasurementBuilder<?> builder = new AreaMeasurementBuilder(imageData);
builderMap.put(builder.getName(), builder);
features.add(builder.getName());
builder = new PerimeterMeasurementBuilder(imageData);
builderMap.put(builder.getName(), builder);
features.add(builder.getName());
}
if (anyLines) {
MeasurementBuilder<?> builder = new LineLengthMeasurementBuilder(imageData);
builderMap.put(builder.getName(), builder);
features.add(builder.getName());
}
// if (anyPolygons) {
// MeasurementBuilder<?> builder = new MaxDiameterMeasurementBuilder(imageData);
// builderMap.put(builder.getName(), builder);
// features.add(builder.getName());
//
// builder = new MinDiameterMeasurementBuilder(imageData);
// builderMap.put(builder.getName(), builder);
// features.add(builder.getName());
// }
}
if (containsAnnotations || containsTMACores || containsRoot) {
var pixelClassifier = getPixelLayer(imageData);
if (pixelClassifier instanceof ImageServer<?>) {
ImageServer<BufferedImage> server = (ImageServer<BufferedImage>) pixelClassifier;
if (server.getMetadata().getChannelType() == ImageServerMetadata.ChannelType.CLASSIFICATION || server.getMetadata().getChannelType() == ImageServerMetadata.ChannelType.PROBABILITY) {
var pixelManager = new PixelClassificationMeasurementManager(server);
for (String name : pixelManager.getMeasurementNames()) {
// String nameLive = name + " (live)";
String nameLive = "(Live) " + name;
builderMap.put(nameLive, new PixelClassifierMeasurementBuilder(pixelManager, name));
features.add(nameLive);
}
}
}
}
// Update all the lists, if necessary
boolean changes = false;
if (metadataNames.size() != metadataList.size() || !metadataNames.containsAll(metadataList)) {
changes = metadataList.setAll(metadataNames);
}
if (features.size() != measurementList.size() || !features.containsAll(measurementList))
changes = measurementList.setAll(features);
if (changes) {
if (metadataList.isEmpty())
fullList.setAll(measurementList);
else {
fullList.setAll(metadataList);
fullList.addAll(measurementList);
}
}
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class AnnotationPane method synchronizeHierarchySelectionToListSelection.
/**
* Update the selected objects in the hierarchy to match those in the list,
* unless selection changes should be suppressed.
*/
void synchronizeHierarchySelectionToListSelection() {
if (hierarchy == null || suppressSelectionChanges)
return;
suppressSelectionChanges = true;
Set<PathObject> selectedSet = new HashSet<>(listAnnotations.getSelectionModel().getSelectedItems());
PathObject selectedObject = listAnnotations.getSelectionModel().getSelectedItem();
if (!selectedSet.contains(selectedObject))
selectedObject = null;
hierarchy.getSelectionModel().setSelectedObjects(selectedSet, selectedObject);
suppressSelectionChanges = false;
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class AnnotationPane method setImageData.
void setImageData(ImageData<BufferedImage> imageData) {
if (this.imageData == imageData)
return;
// Deal with listeners for the current ImageData
if (this.hierarchy != null) {
hierarchy.removePathObjectListener(this);
hierarchy.getSelectionModel().removePathObjectSelectionListener(this);
}
this.imageData = imageData;
if (this.imageData != null) {
hierarchy = imageData.getHierarchy();
hierarchy.getSelectionModel().addPathObjectSelectionListener(this);
hierarchy.addPathObjectListener(this);
PathObject selected = hierarchy.getSelectionModel().getSelectedObject();
listAnnotations.getItems().setAll(hierarchy.getAnnotationObjects());
hierarchy.getSelectionModel().setSelectedObject(selected);
} else {
listAnnotations.getItems().clear();
}
hasImageData.set(this.imageData != null);
pathClassPane.refresh();
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class ClassifierBuilderPane method completeClassification.
private void completeClassification(final PathObjectHierarchy hierarchy, final Collection<PathObject> classifiedObjects, final Collection<PathObject> originalObjects, final Map<PathClass, List<PathObject>> mapTest, final boolean testOnTrainingData) {
if (!Platform.isFxApplicationThread())
Platform.runLater(() -> completeClassification(hierarchy, classifiedObjects, originalObjects, mapTest, testOnTrainingData));
else {
// Test the classifications of the test set... which may or may not be the same as the training set
if (mapTest != null) {
int nCorrect = 0;
List<PathClass> pathClasses = new ArrayList<>(mapTest.keySet());
Collections.sort(pathClasses);
ConfusionMatrix<PathClass> confusion = new ConfusionMatrix<>(pathClasses);
int nCorrectTumor = 0;
// int nWrong = 0;
int nUnclassified = 0;
int n = 0;
PathClass tumorClass = PathClassFactory.getPathClass(StandardPathClasses.TUMOR);
// If we have multiple classes, it can be beneficial to see how tumor vs. everything else performs
// Create a tumor vs. everything else classifier
boolean multiclassContainsTumor = mapTest.containsKey(tumorClass) && mapTest.size() > 2;
for (Entry<PathClass, List<PathObject>> entry : mapTest.entrySet()) {
PathClass pathClass = entry.getKey();
boolean isTumor = pathClass.equals(tumorClass);
for (PathObject testObject : entry.getValue()) {
PathClass tempClass = testObject.getPathClass();
if (tempClass == null) {
nUnclassified++;
n++;
continue;
}
// We've probably applied an intensity classifier by now
PathClass resultClass = tempClass.getBaseClass();
confusion.registerClassification(pathClass, resultClass);
if (resultClass.equals(pathClass))
nCorrect++;
if (multiclassContainsTumor && (isTumor && pathClass.equals(resultClass) || (!isTumor && !resultClass.equals(tumorClass))))
nCorrectTumor++;
n++;
}
}
// Log the results
if (testOnTrainingData) {
logger.info(String.format("Percentage of correctly classified objects in TRAINING set: %.2f%% (n=%d)", nCorrect * 100. / n, n));
logger.warn("It is *strongly* advised not to report accuracies based on testing using the training set!");
} else {
logger.info(String.format("Percentage of correctly classified objects in test set: %.2f%% (n=%d)", nCorrect * 100. / n, n));
if (multiclassContainsTumor)
logger.info(String.format("Percentage of correctly classified objects in test set (Tumor vs. everything else): %.2f%% (n=%d)", nCorrectTumor * 100. / n, n));
}
if (nUnclassified > 0)
logger.info(String.format("Number of unclassified objects in the test set: %d (%.2f%%)", nUnclassified, nUnclassified * 100. / n));
logger.info("Confusion matrix\n" + confusion.toString());
// Only interested in changes from now
hierarchyChanged = false;
}
progressIndicator.setVisible(false);
// Update the classification of any proxy objects
int nChanged = classifiedObjects.parallelStream().mapToInt(p -> {
if (p instanceof PathObjectClassificationProxy && ((PathObjectClassificationProxy) p).updateObject())
return 1;
else
return 0;
}).sum();
if (classifiedObjects != originalObjects)
logger.info("Number of reclassified objects: {} of {}", nChanged, classifiedObjects.size());
// Update displayed list - names may have changed - and classifier summary
updateClassifierSummary(null);
btnSaveClassifier.setDisable(!classifier.isValid());
hierarchy.fireObjectClassificationsChangedEvent(this, originalObjects);
lastClassifierCompleted = classifier;
updatingClassification = false;
}
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class ClassifierBuilderPane method showRetainedTrainingMap.
void showRetainedTrainingMap(final RetainedTrainingObjects retainedObjects) {
Map<String, Map<PathClass, List<PathObject>>> map = retainedObjects.getMap();
// Determine columns
Set<PathClass> pathClasses = new TreeSet<>();
for (Map<PathClass, List<PathObject>> temp : map.values()) {
pathClasses.addAll(temp.keySet());
}
// Don't show a badly-formed table with nothing in it...
if (pathClasses.isEmpty()) {
Dialogs.showMessageDialog("Training objects", "No training objects selected!");
return;
}
// Set up table
TableView<String> table = new TableView<>();
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
table.getItems().setAll(map.keySet());
Collections.sort(table.getItems());
// Create columns
TableColumn<String, String> colName = new TableColumn<>("Image");
colName.setCellValueFactory(column -> new ReadOnlyObjectWrapper<>(column.getValue()));
// colName.setCellValueFactory(column -> new ReadOnlyObjectWrapper<>(ServerTools.getDefaultShortServerName(column.getValue())));
colName.setPrefWidth(240);
table.getColumns().add(colName);
int nColWidth = 80;
for (PathClass pathClass : pathClasses) {
TableColumn<String, Integer> col = new TableColumn<>(pathClass.getName());
col.setCellValueFactory(column -> {
if (map.get(column.getValue()).get(pathClass) == null)
return new ReadOnlyObjectWrapper<Integer>(0);
else
return new ReadOnlyObjectWrapper<Integer>(map.get(column.getValue()).get(pathClass).size());
});
col.setPrefWidth(nColWidth);
table.getColumns().add(col);
}
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
// Show
Stage dialog = new Stage();
dialog.initOwner(qupath.getStage());
dialog.setTitle("Training objects");
dialog.setScene(new Scene(table));
dialog.showAndWait();
}
Aggregations