use of qupath.lib.objects.PathObject in project qupath by qupath.
the class ObservableMeasurementTableDataTest method test.
@SuppressWarnings("javadoc")
@Test
public void test() {
// See https://github.com/locationtech/jts/issues/571
for (int counter = 0; counter < 50; counter++) {
ImageData<BufferedImage> imageData = new ImageData<>(null);
PathClass tumorClass = PathClassFactory.getPathClass(StandardPathClasses.TUMOR);
PathClass stromaClass = PathClassFactory.getPathClass(StandardPathClasses.STROMA);
// PathClass otherClass = PathClassFactory.getDefaultPathClass(PathClasses.OTHER);
PathClass artefactClass = PathClassFactory.getPathClass("Artefact");
PathObjectHierarchy hierarchy = imageData.getHierarchy();
// Add a parent annotation
PathObject parent = PathObjects.createAnnotationObject(ROIs.createRectangleROI(500, 500, 1000, 1000, ImagePlane.getDefaultPlane()));
// Create 100 tumor detections
// ROI emptyROI = ROIs.createEmptyROI();
ROI smallROI = ROIs.createRectangleROI(500, 500, 1, 1, ImagePlane.getDefaultPlane());
for (int i = 0; i < 100; i++) {
if (i < 25)
parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getNegative(tumorClass)));
else if (i < 50)
parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getOnePlus(tumorClass)));
else if (i < 75)
parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getTwoPlus(tumorClass)));
else if (i < 100)
parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getThreePlus(tumorClass)));
}
// Create 100 stroma detections
for (int i = 0; i < 100; i++) {
if (i < 50)
parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getNegative(stromaClass)));
else if (i < 60)
parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getOnePlus(stromaClass)));
else if (i < 70)
parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getTwoPlus(stromaClass)));
else if (i < 100)
parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getThreePlus(stromaClass)));
}
// Create 50 artefact detections
for (int i = 0; i < 50; i++) {
parent.addPathObject(PathObjects.createDetectionObject(smallROI, artefactClass));
}
hierarchy.addPathObject(parent);
ObservableMeasurementTableData model = new ObservableMeasurementTableData();
model.setImageData(imageData, Collections.singletonList(parent));
// Check tumor counts
assertEquals(100, model.getNumericValue(parent, "Num Tumor (base)"), EPSILON);
assertEquals(25, model.getNumericValue(parent, "Num Tumor: Negative"), EPSILON);
assertEquals(25, model.getNumericValue(parent, "Num Tumor: 1+"), EPSILON);
assertEquals(25, model.getNumericValue(parent, "Num Tumor: 2+"), EPSILON);
assertEquals(25, model.getNumericValue(parent, "Num Tumor: 3+"), EPSILON);
assertTrue(Double.isNaN(model.getNumericValue(parent, "Num Tumor: 4+")));
// Check tumor H-score, Allred score & positive %
assertEquals(150, model.getNumericValue(parent, "Tumor: H-score"), EPSILON);
assertEquals(75, model.getNumericValue(parent, "Tumor: Positive %"), EPSILON);
assertEquals(2, model.getNumericValue(parent, "Tumor: Allred intensity"), EPSILON);
assertEquals(5, model.getNumericValue(parent, "Tumor: Allred proportion"), EPSILON);
assertEquals(7, model.getNumericValue(parent, "Tumor: Allred score"), EPSILON);
// Check tumor H-score unaffected when tumor detections added without intensity classification
for (int i = 0; i < 10; i++) parent.addPathObject(PathObjects.createDetectionObject(smallROI, tumorClass));
hierarchy.fireHierarchyChangedEvent(this);
model.refreshEntries();
// model.setImageData(imageData, Collections.singletonList(parent));
assertEquals(100, model.getNumericValue(parent, "Num Stroma (base)"), EPSILON);
assertEquals(50, model.getNumericValue(parent, "Num Stroma: Negative"), EPSILON);
assertEquals(150, model.getNumericValue(parent, "Tumor: H-score"), EPSILON);
assertEquals(75, model.getNumericValue(parent, "Tumor: Positive %"), EPSILON);
// Check stroma scores
assertEquals(100, model.getNumericValue(parent, "Num Stroma (base)"), EPSILON);
assertEquals(120, model.getNumericValue(parent, "Stroma: H-score"), EPSILON);
// Check complete scores
assertEquals(135, model.getNumericValue(parent, "Stroma + Tumor: H-score"), EPSILON);
// Add a new parent that completely contains the current object, and confirm complete scores agree
PathObject parentNew = PathObjects.createAnnotationObject(ROIs.createRectangleROI(0, 0, 2000, 2000, ImagePlane.getDefaultPlane()));
hierarchy.addPathObject(parentNew);
model.refreshEntries();
assertEquals(135, model.getNumericValue(parent, "Stroma + Tumor: H-score"), EPSILON);
assertEquals(135, model.getNumericValue(parentNew, "Stroma + Tumor: H-score"), EPSILON);
// Create a new object and demonstrate Allred dependence on a single cell
PathObject parentAllred = PathObjects.createAnnotationObject(ROIs.createRectangleROI(4000, 4000, 1000, 1000, ImagePlane.getDefaultPlane()));
ROI newROI = ROIs.createEllipseROI(4500, 4500, 10, 10, ImagePlane.getDefaultPlane());
for (int i = 0; i < 100; i++) parentAllred.addPathObject(PathObjects.createDetectionObject(newROI, PathClassFactory.getNegative(tumorClass)));
hierarchy.addPathObject(parentAllred);
model.refreshEntries();
assertEquals(0, model.getNumericValue(parentAllred, "Tumor: Allred score"), EPSILON);
parentAllred.addPathObject(PathObjects.createDetectionObject(newROI, PathClassFactory.getThreePlus(tumorClass)));
hierarchy.fireHierarchyChangedEvent(parentAllred);
model.refreshEntries();
assertEquals(4, model.getNumericValue(parentAllred, "Tumor: Allred score"), EPSILON);
}
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class MoveTool method mouseReleased.
@Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
if (e.isConsumed())
return;
var viewer = getViewer();
RoiEditor editor = viewer.getROIEditor();
if (editor != null && (editor.hasActiveHandle() || editor.isTranslating())) {
boolean roiChanged = (editor.isTranslating() && editor.finishTranslation()) || editor.hasActiveHandle();
editor.resetActiveHandle();
// if (editor.isTranslating())
// editor.finishTranslation();
e.consume();
PathObject pathObject = viewer.getSelectedObject();
if (requestParentClipping(e) && pathObject instanceof PathAnnotationObject) {
ROI roiNew = refineROIByParent(pathObject.getROI());
((PathAnnotationObject) pathObject).setROI(roiNew);
}
if (pathObject != null && pathObject.hasROI() && pathObject.getROI().isEmpty()) {
if (pathObject.getParent() != null)
viewer.getHierarchy().removeObject(pathObject, true);
viewer.setSelectedObject(null);
} else {
PathObjectHierarchy hierarchy = viewer.getHierarchy();
if (pathObject instanceof TMACoreObject) {
hierarchy.fireHierarchyChangedEvent(pathObject);
} else if (pathObject != null) {
// Handle ROI changes only if required
if (roiChanged) {
var updatedROI = editor.getROI();
if (pathObject.getROI() != updatedROI && pathObject instanceof PathROIObject)
((PathROIObject) pathObject).setROI(updatedROI);
// PathObject parentPrevious = pathObject.getParent();
hierarchy.removeObjectWithoutUpdate(pathObject, true);
if (getCurrentParent() == null || !PathPrefs.clipROIsForHierarchyProperty().get() || e.isShiftDown())
hierarchy.addPathObject(pathObject);
else
hierarchy.addPathObjectBelowParent(getCurrentParent(), pathObject, true);
// PathObject parentNew = pathObject.getParent();
// if (parentPrevious == parentNew)
// hierarchy.fireHierarchyChangedEvent(this, parentPrevious);
// else
// hierarchy.fireHierarchyChangedEvent(this);
}
}
viewer.setSelectedObject(pathObject);
}
}
// Optionally continue a dragging movement until the canvas comes to a standstill
if (pDragging != null && requestDynamicDragging && System.currentTimeMillis() - lastDragTimestamp < 100 && (dx * dx + dy * dy > viewer.getDownsampleFactor())) {
mover = new ViewerMover(viewer);
mover.startMoving(dx, dy, false);
} else
viewer.setDoFasterRepaint(false);
// Make sure we don't have a previous point (to prevent weird dragging artefacts)
pDragging = null;
// // If we were translating, stop
// if (editor.isTranslating()) {
// editor.finishTranslation();
// // TODO: Make this more efficient!
// viewer.getPathObjectHierarchy().fireHierarchyChangedEvent();
// return;
// }
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class PathClassifierTools method getAvailableFeatures.
/**
* Get a set containing the names of all measurements found in the measurement lists of a specified object collection.
*
* @param pathObjects
* @return
*/
public static Set<String> getAvailableFeatures(final Collection<? extends PathObject> pathObjects) {
Set<String> featureSet = new LinkedHashSet<>();
// This has a small optimization that takes into consideration the fact that many objects share references to exactly the same MeasurementLists -
// so by checking the last list that was added, there is no need to bother the set to add the same thing again.
List<String> lastNames = null;
for (PathObject pathObject : pathObjects) {
if (!pathObject.hasMeasurements())
continue;
List<String> list = pathObject.getMeasurementList().getMeasurementNames();
if (lastNames != list)
featureSet.addAll(list);
lastNames = list;
}
return featureSet;
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class PathClassifierTools method runClassifier.
/**
* Apply a classifier to the detection objects in a hierarchy.
* @param hierarchy
* @param classifier
*/
public static void runClassifier(final PathObjectHierarchy hierarchy, final PathObjectClassifier classifier) {
// Apply classifier to everything
// If we have a TMA grid, do one core at a time
long startTime = System.currentTimeMillis();
TMAGrid tmaGrid = hierarchy.getTMAGrid();
Collection<PathObject> pathObjects = new ArrayList<>();
int nClassified = 0;
// tmaGrid = null;
if (tmaGrid != null) {
for (TMACoreObject core : tmaGrid.getTMACoreList()) {
pathObjects = PathObjectTools.getDescendantObjects(core, pathObjects, PathDetectionObject.class);
nClassified += classifier.classifyPathObjects(pathObjects);
pathObjects.clear();
}
} else {
hierarchy.getObjects(pathObjects, PathDetectionObject.class);
nClassified = classifier.classifyPathObjects(pathObjects);
}
long endTime = System.currentTimeMillis();
logger.info(String.format("Classification time: %.2f seconds", (endTime - startTime) / 1000.));
// Fire a change event for all detection objects
if (nClassified > 0)
hierarchy.fireObjectClassificationsChangedEvent(classifier, hierarchy.getObjects(null, PathDetectionObject.class));
else
logger.warn("No objects classified!");
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class ImageJMacroRunner method runPlugin.
@Override
public boolean runPlugin(final PluginRunner<BufferedImage> runner, final String arg) {
if (!parseArgument(runner.getImageData(), arg))
return false;
if (dialog == null) {
dialog = new Stage();
dialog.initOwner(qupath.getStage());
dialog.setTitle("ImageJ macro runner");
BorderPane pane = new BorderPane();
if (arg != null)
macroText = arg;
// Create text area
final TextArea textArea = new TextArea();
textArea.setPrefRowCount(12);
textArea.setPrefSize(400, 400);
textArea.setWrapText(true);
textArea.setFont(Font.font("Courier"));
if (macroText != null)
textArea.setText(macroText);
BorderPane panelMacro = new BorderPane();
// panelMacro.setBorder(BorderFactory.createTitledBorder("Macro"));
panelMacro.setCenter(textArea);
ParameterPanelFX parameterPanel = new ParameterPanelFX(getParameterList(runner.getImageData()));
panelMacro.setBottom(parameterPanel.getPane());
// Create button panel
Button btnRun = new Button("Run");
btnRun.setOnAction(e -> {
macroText = textArea.getText().trim();
if (macroText.length() == 0)
return;
PathObjectHierarchy hierarchy = getHierarchy(runner);
PathObject pathObject = hierarchy.getSelectionModel().singleSelection() ? hierarchy.getSelectionModel().getSelectedObject() : null;
if (pathObject instanceof PathAnnotationObject || pathObject instanceof TMACoreObject) {
SwingUtilities.invokeLater(() -> {
runMacro(params, qupath.getViewer().getImageData(), qupath.getViewer().getImageDisplay(), pathObject, macroText);
});
} else {
// DisplayHelpers.showErrorMessage(getClass().getSimpleName(), "Sorry, ImageJ macros can only be run for single selected images");
// logger.warn("ImageJ macro being run in current thread");
// runPlugin(runner, arg); // TODO: Consider running in a background thread?
// Run in a background thread
Collection<? extends PathObject> parents = getParentObjects(runner);
if (parents.isEmpty()) {
Dialogs.showErrorMessage("ImageJ macro runner", "No annotation or TMA core objects selected!");
return;
}
List<Runnable> tasks = new ArrayList<>();
for (PathObject parent : parents) addRunnableTasks(qupath.getViewer().getImageData(), parent, tasks);
qupath.submitShortTask(() -> runner.runTasks(tasks, true));
// runner.runTasks(tasks);
// Runnable r = new Runnable() {
// public void run() {
// runPlugin(runner, arg);
// }
// };
// new Thread(r).start();
}
});
Button btnClose = new Button("Close");
btnClose.setOnAction(e -> dialog.hide());
GridPane panelButtons = PaneTools.createRowGridControls(btnRun, btnClose);
pane.setCenter(panelMacro);
pane.setBottom(panelButtons);
panelButtons.setPadding(new Insets(5, 0, 0, 0));
pane.setPadding(new Insets(10, 10, 10, 10));
dialog.setScene(new Scene(pane));
}
dialog.show();
return true;
}
Aggregations