Search in sources :

Example 21 with PathObjectHierarchy

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;
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PathObject(qupath.lib.objects.PathObject) PolylineROI(qupath.lib.roi.PolylineROI) ROI(qupath.lib.roi.interfaces.ROI) PolygonROI(qupath.lib.roi.PolygonROI)

Example 22 with PathObjectHierarchy

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);
    }
}
Also used : Logger(org.slf4j.Logger) Point2D(java.awt.geom.Point2D) PathClassTools(qupath.lib.objects.classes.PathClassTools) MouseEvent(javafx.scene.input.MouseEvent) PathObjects(qupath.lib.objects.PathObjects) LoggerFactory(org.slf4j.LoggerFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PolylineROI(qupath.lib.roi.PolylineROI) Collectors(java.util.stream.Collectors) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathObject(qupath.lib.objects.PathObject) Cursor(javafx.scene.Cursor) ROI(qupath.lib.roi.interfaces.ROI) RoiEditor(qupath.lib.roi.RoiEditor) ImagePlane(qupath.lib.regions.ImagePlane) Reclassifier(qupath.lib.objects.classes.Reclassifier) PolygonROI(qupath.lib.roi.PolygonROI) Collections(java.util.Collections) PathPrefs(qupath.lib.gui.prefs.PathPrefs) QuPathGUI(qupath.lib.gui.QuPathGUI) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PolylineROI(qupath.lib.roi.PolylineROI) ROI(qupath.lib.roi.interfaces.ROI) PolygonROI(qupath.lib.roi.PolygonROI) Reclassifier(qupath.lib.objects.classes.Reclassifier)

Example 23 with PathObjectHierarchy

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;
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PathObject(qupath.lib.objects.PathObject) ArrayList(java.util.ArrayList)

Example 24 with PathObjectHierarchy

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);
    }
}
Also used : PathClass(qupath.lib.objects.classes.PathClass) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData) PathObject(qupath.lib.objects.PathObject) ImageData(qupath.lib.images.ImageData) ROI(qupath.lib.roi.interfaces.ROI) BufferedImage(java.awt.image.BufferedImage) Test(org.junit.jupiter.api.Test)

Example 25 with PathObjectHierarchy

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;
// }
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathObject(qupath.lib.objects.PathObject) RoiEditor(qupath.lib.roi.RoiEditor) TMACoreObject(qupath.lib.objects.TMACoreObject) PathROIObject(qupath.lib.objects.PathROIObject) ROI(qupath.lib.roi.interfaces.ROI)

Aggregations

PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)85 PathObject (qupath.lib.objects.PathObject)57 ArrayList (java.util.ArrayList)38 ROI (qupath.lib.roi.interfaces.ROI)31 List (java.util.List)24 Collectors (java.util.stream.Collectors)24 TMACoreObject (qupath.lib.objects.TMACoreObject)23 BufferedImage (java.awt.image.BufferedImage)22 IOException (java.io.IOException)22 Logger (org.slf4j.Logger)22 LoggerFactory (org.slf4j.LoggerFactory)22 ImageData (qupath.lib.images.ImageData)20 PathAnnotationObject (qupath.lib.objects.PathAnnotationObject)20 Collection (java.util.Collection)19 Collections (java.util.Collections)19 Map (java.util.Map)19 File (java.io.File)17 Arrays (java.util.Arrays)16 PathObjectTools (qupath.lib.objects.PathObjectTools)16 GeneralTools (qupath.lib.common.GeneralTools)15