use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.
the class AbstractPathROITool method createNewAnnotation.
/**
* Create a new annotation & set it in the current viewer.
* @param e
* @param x
* @param y
* @return
*/
PathObject createNewAnnotation(MouseEvent e, double x, double y) {
var viewer = getViewer();
var currentObject = viewer.getSelectedObject();
var editor = viewer.getROIEditor();
if (currentObject != null && currentObject.getParent() == null && currentObject.getROI() == editor.getROI() && (editor.isTranslating() || editor.hasActiveHandle())) {
logger.warn("Creating a new annotation before a previous one was complete - {} will be discarded!", currentObject);
}
logger.trace("Creating new annotation at ({}, {}", x, y);
PathObjectHierarchy hierarchy = viewer.getHierarchy();
if (hierarchy == null) {
logger.warn("Cannot create new annotation - no hierarchy available!");
return null;
}
ROI roi = createNewROI(e, x, y, viewer.getImagePlane());
if (roi == null)
return null;
PathObject pathObject = PathObjects.createAnnotationObject(roi, PathPrefs.autoSetAnnotationClassProperty().get());
var selectionModel = hierarchy.getSelectionModel();
if (PathPrefs.selectionModeProperty().get() && !selectionModel.noSelection())
viewer.setSelectedObject(pathObject, true);
else
viewer.setSelectedObject(pathObject);
return pathObject;
}
use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.
the class AbstractPathROITool method commitObjectToHierarchy.
/**
* When drawing an object is complete, add it to the hierarchy - or whatever else is required.
*
* @param e
* @param pathObject
*/
void commitObjectToHierarchy(MouseEvent e, PathObject pathObject) {
if (pathObject == null)
return;
var viewer = getViewer();
PathObjectHierarchy hierarchy = viewer.getHierarchy();
var currentROI = pathObject.getROI();
// If we are in selection mode, try to get objects to select
if (PathPrefs.selectionModeProperty().get()) {
var pathClass = PathPrefs.autoSetAnnotationClassProperty().get();
var toSelect = hierarchy.getObjectsForROI(null, currentROI);
if (!toSelect.isEmpty() && pathClass != null) {
boolean retainIntensityClass = !(PathClassTools.isPositiveOrGradedIntensityClass(pathClass) || PathClassTools.isNegativeClass(pathClass));
var reclassified = toSelect.stream().filter(p -> p.getPathClass() != pathClass).map(p -> new Reclassifier(p, pathClass, retainIntensityClass)).filter(r -> r.apply()).map(r -> r.getPathObject()).collect(Collectors.toList());
if (!reclassified.isEmpty()) {
hierarchy.fireObjectClassificationsChangedEvent(this, reclassified);
}
}
if (pathObject.getParent() != null)
hierarchy.removeObject(pathObject, true);
// viewer.getHierarchy().fireHierarchyChangedEvent(this);
if (toSelect.isEmpty())
viewer.setSelectedObject(null);
else if (e.isShiftDown()) {
hierarchy.getSelectionModel().deselectObject(pathObject);
hierarchy.getSelectionModel().selectObjects(toSelect);
} else
hierarchy.getSelectionModel().setSelectedObjects(toSelect, null);
} else {
if (!requestParentClipping(e)) {
if (currentROI.isEmpty()) {
pathObject = null;
} else
// Ensure object is within the hierarchy
hierarchy.addPathObject(pathObject);
} else {
ROI roiNew = refineROIByParent(pathObject.getROI());
if (roiNew.isEmpty()) {
hierarchy.removeObject(pathObject, true);
pathObject = null;
} else {
((PathAnnotationObject) pathObject).setROI(roiNew);
hierarchy.addPathObjectBelowParent(getCurrentParent(), pathObject, true);
}
}
if (pathObject != null)
viewer.setSelectedObject(pathObject);
else
viewer.getHierarchy().getSelectionModel().clearSelection();
}
var editor = viewer.getROIEditor();
editor.ensureHandlesUpdated();
editor.resetActiveHandle();
if (preferReturnToMove()) {
var qupath = QuPathGUI.getInstance();
if (qupath != null)
qupath.setSelectedTool(PathTools.MOVE);
}
}
use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.
the class AbstractPathTool method getSelectableObjectList.
/**
* Get a list of all selectable objects overlapping the specified x, y coordinates, ordered by depth in the hierarchy
* @param x
* @param y
* @return
*/
List<PathObject> getSelectableObjectList(double x, double y) {
var viewer = getViewer();
PathObjectHierarchy hierarchy = viewer.getHierarchy();
if (hierarchy == null)
return Collections.emptyList();
Collection<PathObject> pathObjects = PathObjectTools.getObjectsForLocation(hierarchy, x, y, viewer.getZPosition(), viewer.getTPosition(), viewer.getMaxROIHandleSize());
if (pathObjects.isEmpty())
return Collections.emptyList();
List<PathObject> pathObjectList = new ArrayList<>(pathObjects);
if (pathObjectList.size() == 1)
return pathObjectList;
Collections.sort(pathObjectList, PathObjectHierarchy.HIERARCHY_COMPARATOR);
return pathObjectList;
}
use of qupath.lib.objects.hierarchy.PathObjectHierarchy 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.hierarchy.PathObjectHierarchy 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;
// }
}
Aggregations