Search in sources :

Example 1 with ImagePlane

use of qupath.lib.regions.ImagePlane in project qupath by qupath.

the class DelaunayTools method createFromCentroids.

/**
 * Create a {@link Subdivision} using the centroid coordinates of ROIs.
 * <p>
 * Note: centroids must be distinct. If multiple objects have identical centroids, one or more objects may be lost
 * from the resulting {@link Subdivision}.
 *
 * @param pathObjects collection of objects from which to construct the {@link Subdivision}
 * @param preferNucleusROI if true, prefer the nucleus ROI when extracting the centroid from a cell
 * @return a new {@link Subdivision} computed from the centroids of the provided objects
 *
 * @see #createFromGeometryCoordinates(Collection, boolean, double)
 */
public static Subdivision createFromCentroids(Collection<PathObject> pathObjects, boolean preferNucleusROI) {
    logger.debug("Creating subdivision from ROI centroids for {} objects", pathObjects.size());
    var coords = new HashMap<Coordinate, PathObject>();
    ImagePlane plane = null;
    var precisionModel = GeometryTools.getDefaultFactory().getPrecisionModel();
    for (var pathObject : pathObjects) {
        var roi = PathObjectTools.getROI(pathObject, preferNucleusROI);
        if (plane == null)
            plane = roi.getImagePlane();
        else if (!plane.equals(roi.getImagePlane())) {
            logger.warn("Non-matching image planes: {} and {}! Object will be skipped...", plane, roi.getImagePlane());
            continue;
        }
        double x = precisionModel.makePrecise(roi.getCentroidX());
        double y = precisionModel.makePrecise(roi.getCentroidY());
        var coord = new Coordinate(x, y);
        coords.put(coord, pathObject);
    }
    return new Subdivision(createSubdivision(coords.keySet(), 0.01), pathObjects, coords, plane);
}
Also used : HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Coordinate(org.locationtech.jts.geom.Coordinate) ImagePlane(qupath.lib.regions.ImagePlane) QuadEdgeSubdivision(org.locationtech.jts.triangulate.quadedge.QuadEdgeSubdivision)

Example 2 with ImagePlane

use of qupath.lib.regions.ImagePlane in project qupath by qupath.

the class PathObjectTools method mergePointsForAllClasses.

/**
 * Merge point annotations sharing the same {@link PathClass} and {@link ImagePlane},
 * creating multi-point annotations for all matching points and removing the (previously-separated) annotations.
 *
 * @param hierarchy object hierarchy to modify
 * @return true if changes are made to the hierarchy, false otherwise
 */
public static boolean mergePointsForAllClasses(PathObjectHierarchy hierarchy) {
    if (hierarchy == null)
        return false;
    var pathClasses = hierarchy.getAnnotationObjects().stream().filter(p -> p.getROI().isPoint()).map(p -> p.getPathClass()).collect(Collectors.toSet());
    boolean changes = false;
    for (PathClass pathClass : pathClasses) changes = changes || mergePointsForClass(hierarchy, pathClass);
    return changes;
}
Also used : PathClassTools(qupath.lib.objects.classes.PathClassTools) LoggerFactory(org.slf4j.LoggerFactory) HashMap(java.util.HashMap) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Function(java.util.function.Function) ArrayList(java.util.ArrayList) MeasurementList(qupath.lib.measurements.MeasurementList) EllipseROI(qupath.lib.roi.EllipseROI) HashSet(java.util.HashSet) ROIs(qupath.lib.roi.ROIs) PointsROI(qupath.lib.roi.PointsROI) Point2(qupath.lib.geom.Point2) ImageRegion(qupath.lib.regions.ImageRegion) Map(java.util.Map) PreparedGeometry(org.locationtech.jts.geom.prep.PreparedGeometry) LinkedHashSet(java.util.LinkedHashSet) LineROI(qupath.lib.roi.LineROI) Line2D(java.awt.geom.Line2D) RoiTools(qupath.lib.roi.RoiTools) Logger(org.slf4j.Logger) Iterator(java.util.Iterator) Predicate(java.util.function.Predicate) Collection(java.util.Collection) PathClass(qupath.lib.objects.classes.PathClass) Set(java.util.Set) AffineTransform(java.awt.geom.AffineTransform) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) ROI(qupath.lib.roi.interfaces.ROI) List(java.util.List) Entry(java.util.Map.Entry) ImagePlane(qupath.lib.regions.ImagePlane) Geometry(org.locationtech.jts.geom.Geometry) Comparator(java.util.Comparator) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) Collections(java.util.Collections) PreparedGeometryFactory(org.locationtech.jts.geom.prep.PreparedGeometryFactory) STRtree(org.locationtech.jts.index.strtree.STRtree) PathClass(qupath.lib.objects.classes.PathClass)

Example 3 with ImagePlane

use of qupath.lib.regions.ImagePlane in project qupath by qupath.

the class PointIO method writePoints.

/**
 * Write a list of point annotations to a stream.
 * @param stream
 * @param pathObjects
 * @throws IOException
 */
public static void writePoints(OutputStream stream, Collection<? extends PathObject> pathObjects) throws IOException {
    // Check that all PathObjects contain only point annotations
    int unfilteredSize = pathObjects.size();
    pathObjects = pathObjects.stream().filter(p -> p.getROI() instanceof PointsROI).collect(Collectors.toList());
    int filteredSize = pathObjects.size();
    if (unfilteredSize != filteredSize)
        logger.warn(unfilteredSize - filteredSize + " of the " + filteredSize + " elements in list is/are not point annotations. These will be skipped.");
    try (Writer writer = new BufferedWriter(new OutputStreamWriter(stream, StandardCharsets.UTF_8))) {
        List<String> cols = new ArrayList<>();
        cols.addAll(Arrays.asList("x", "y"));
        String sep = "\t";
        ImagePlane defaultPlane = ImagePlane.getDefaultPlane();
        boolean hasClass = pathObjects.stream().anyMatch(p -> p.getPathClass() != null);
        boolean hasName = pathObjects.stream().anyMatch(p -> p.getName() != null);
        boolean hasColor = pathObjects.stream().anyMatch(p -> p.getColorRGB() != null);
        boolean hasC = pathObjects.stream().anyMatch(p -> p.getROI().getC() > defaultPlane.getC());
        boolean hasZ = pathObjects.stream().anyMatch(p -> p.getROI().getZ() > defaultPlane.getZ());
        boolean hasT = pathObjects.stream().anyMatch(p -> p.getROI().getT() > defaultPlane.getT());
        if (hasC)
            cols.add("c");
        if (hasZ)
            cols.add("z");
        if (hasT)
            cols.add("t");
        if (hasClass)
            cols.add("class");
        if (hasName)
            cols.add("name");
        if (hasColor)
            cols.add("color");
        for (String col : cols) writer.write(col + sep);
        writer.write(System.lineSeparator());
        for (PathObject pathObject : pathObjects) {
            if (!PathObjectTools.hasPointROI(pathObject))
                continue;
            PointsROI points = (PointsROI) pathObject.getROI();
            for (Point2 point : points.getAllPoints()) {
                String[] row = new String[cols.size()];
                row[cols.indexOf("x")] = point.getX() + "";
                row[cols.indexOf("y")] = sep + point.getY();
                if (hasC)
                    row[cols.indexOf("c")] = sep + points.getC();
                if (hasZ)
                    row[cols.indexOf("z")] = sep + points.getZ();
                if (hasT)
                    row[cols.indexOf("t")] = sep + points.getT();
                if (hasClass)
                    row[cols.indexOf("class")] = pathObject.getPathClass() != null ? sep + pathObject.getPathClass() : sep;
                if (hasName)
                    row[cols.indexOf("name")] = pathObject.getName() != null ? sep + pathObject.getName() : sep;
                if (hasColor)
                    row[cols.indexOf("color")] = pathObject.getColorRGB() != null ? sep + pathObject.getColorRGB() : sep;
                for (String val : row) writer.write(val);
                writer.write(System.lineSeparator());
            }
        }
    }
}
Also used : ArrayList(java.util.ArrayList) PointsROI(qupath.lib.roi.PointsROI) BufferedWriter(java.io.BufferedWriter) PathObject(qupath.lib.objects.PathObject) Point2(qupath.lib.geom.Point2) OutputStreamWriter(java.io.OutputStreamWriter) ImagePlane(qupath.lib.regions.ImagePlane) OutputStreamWriter(java.io.OutputStreamWriter) BufferedWriter(java.io.BufferedWriter) Writer(java.io.Writer)

Example 4 with ImagePlane

use of qupath.lib.regions.ImagePlane in project qupath by qupath.

the class BrushTool method getUpdatedObject.

private PathObject getUpdatedObject(MouseEvent e, ROI shapeROI, PathObject currentObject, double flatness) {
    Point2D p = mouseLocationToImage(e, true, requestPixelSnapping());
    var viewer = getViewer();
    ImagePlane plane = shapeROI == null ? ImagePlane.getPlane(viewer.getZPosition(), viewer.getTPosition()) : shapeROI.getImagePlane();
    Geometry shapeNew;
    boolean subtractMode = isSubtractMode(e);
    Geometry shapeCurrent = shapeROI == null ? null : shapeROI.getGeometry();
    Geometry shapeDrawn = createShape(e, p.getX(), p.getY(), PathPrefs.useTileBrushProperty().get() && !e.isShiftDown(), subtractMode ? null : shapeCurrent);
    if (shapeDrawn == null)
        return currentObject;
    // Do our pixel snapping now, with the simpler geometry (rather than latter when things are already complex)
    if (requestPixelSnapping())
        shapeDrawn = GeometryTools.roundCoordinates(shapeDrawn);
    lastPoint = p;
    try {
        if (shapeROI != null) {
            // Check to see if any changes are required at all
            if (shapeDrawn == null || (subtractMode && !shapeCurrent.intersects(shapeDrawn)) || (!subtractMode && shapeCurrent.covers(shapeDrawn)))
                return currentObject;
            // TODO: Consider whether a preference should be used rather than the shift key?
            // Anyhow, this will switch to 'dodge' mode, and avoid overlapping existing annotations
            boolean avoidOtherAnnotations = requestParentClipping(e);
            if (subtractMode) {
                // If subtracting... then just subtract
                shapeNew = shapeROI.getGeometry().difference(shapeDrawn);
            } else if (avoidOtherAnnotations) {
                shapeNew = shapeCurrent.union(shapeDrawn);
                shapeNew = refineGeometryByParent(shapeNew);
            } else {
                // Just add, regardless of whether there are other annotations below or not
                var temp = shapeROI.getGeometry();
                try {
                    shapeNew = temp.union(shapeDrawn);
                } catch (Exception e2) {
                    shapeNew = shapeROI.getGeometry();
                }
            }
        } else {
            shapeNew = shapeDrawn;
        }
        // If we aren't snapping, at least remove some vertices
        if (!requestPixelSnapping()) {
            try {
                shapeNew = VWSimplifier.simplify(shapeNew, 0.1);
            } catch (Exception e2) {
                logger.error("Error simplifying ROI: " + e2.getLocalizedMessage(), e2);
            }
        }
        // Make sure we fit inside the image
        shapeNew = GeometryTools.constrainToBounds(shapeNew, 0, 0, viewer.getServerWidth(), viewer.getServerHeight());
        // Sometimes we can end up with a GeometryCollection containing lines/non-areas... if so, remove these
        if (shapeNew instanceof GeometryCollection) {
            shapeNew = GeometryTools.ensurePolygonal(shapeNew);
        }
        ROI roiNew = GeometryTools.geometryToROI(shapeNew, plane);
        if (currentObject instanceof PathAnnotationObject) {
            ((PathAnnotationObject) currentObject).setROI(roiNew);
            return currentObject;
        }
        // shapeNew = new PathAreaROI(new Area(shapeNew.getShape()));
        PathObject pathObjectNew = PathObjects.createAnnotationObject(roiNew, PathPrefs.autoSetAnnotationClassProperty().get());
        if (currentObject != null) {
            pathObjectNew.setName(currentObject.getName());
            pathObjectNew.setColorRGB(currentObject.getColorRGB());
            pathObjectNew.setPathClass(currentObject.getPathClass());
        }
        return pathObjectNew;
    } catch (Exception ex) {
        logger.error("Error updating ROI", ex);
        return currentObject;
    }
}
Also used : Geometry(org.locationtech.jts.geom.Geometry) GeometryCollection(org.locationtech.jts.geom.GeometryCollection) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathObject(qupath.lib.objects.PathObject) Point2D(java.awt.geom.Point2D) ImagePlane(qupath.lib.regions.ImagePlane) RectangleROI(qupath.lib.roi.RectangleROI) ROI(qupath.lib.roi.interfaces.ROI)

Example 5 with ImagePlane

use of qupath.lib.regions.ImagePlane in project qupath by qupath.

the class QP method makeInverseAnnotation.

/**
 * Make an annotation, for which the ROI is obtained by subtracting the ROIs of the specified objects from the closest
 * common ancestor ROI (or entire image if the closest ancestor is the root).
 * <p>
 * In an inverted annotation can be created, it is added to the hierarchy and set as selected.
 *
 * @param imageData the image containing the annotation
 * @param pathObjects the annotation to invert
 * @return true if an inverted annotation is added to the hierarchy, false otherwise.
 */
public static boolean makeInverseAnnotation(final ImageData<?> imageData, Collection<PathObject> pathObjects) {
    if (imageData == null)
        return false;
    var map = pathObjects.stream().filter(p -> p.hasROI() && p.getROI().isArea()).collect(Collectors.groupingBy(p -> p.getROI().getImagePlane()));
    if (map.isEmpty()) {
        logger.warn("No area annotations available - cannot created inverse ROI!");
        return false;
    }
    if (map.size() > 1) {
        logger.error("Cannot merge annotations from different image planes!");
        return false;
    }
    ImagePlane plane = map.keySet().iterator().next();
    List<PathObject> pathObjectList = map.get(plane);
    PathObjectHierarchy hierarchy = imageData.getHierarchy();
    // Try to get the best candidate parent
    Collection<PathObject> parentSet = pathObjectList.stream().map(p -> p.getParent()).collect(Collectors.toCollection(HashSet::new));
    PathObject parent;
    if (parentSet.size() > 1) {
        parentSet.clear();
        boolean firstTime = true;
        for (PathObject temp : pathObjectList) {
            if (firstTime)
                parentSet.addAll(PathObjectTools.getAncestorList(temp));
            else
                parentSet.retainAll(PathObjectTools.getAncestorList(temp));
            firstTime = false;
        }
        List<PathObject> parents = new ArrayList<>(parentSet);
        Collections.sort(parents, Comparator.comparingInt(PathObject::getLevel).reversed().thenComparingDouble(p -> p.hasROI() ? p.getROI().getArea() : Double.MAX_VALUE));
        parent = parents.get(0);
    } else
        parent = parentSet.iterator().next();
    // Get the parent area
    Geometry geometryParent;
    if (parent == null || parent.isRootObject() || !parent.hasROI())
        geometryParent = GeometryTools.createRectangle(0, 0, imageData.getServer().getWidth(), imageData.getServer().getHeight());
    else
        geometryParent = parent.getROI().getGeometry();
    // Get the parent area to use
    var union = GeometryTools.union(pathObjectList.stream().map(p -> p.getROI().getGeometry()).collect(Collectors.toList()));
    var geometry = geometryParent.difference(union);
    // Create the new ROI
    ROI shapeNew = GeometryTools.geometryToROI(geometry, plane);
    PathObject pathObjectNew = PathObjects.createAnnotationObject(shapeNew);
    parent.addPathObject(pathObjectNew);
    hierarchy.fireHierarchyChangedEvent(parent);
    hierarchy.getSelectionModel().setSelectedObject(pathObjectNew);
    return true;
}
Also used : FeatureExtractors(qupath.opencv.ml.objects.features.FeatureExtractors) Arrays(java.util.Arrays) ServerTools(qupath.lib.images.servers.ServerTools) PathTileObject(qupath.lib.objects.PathTileObject) IJTools(qupath.imagej.tools.IJTools) GroovyCV(qupath.opencv.tools.GroovyCV) CommandLinePluginRunner(qupath.lib.plugins.CommandLinePluginRunner) ImageRegion(qupath.lib.regions.ImageRegion) Map(java.util.Map) PixelClassifierTools(qupath.opencv.ml.pixel.PixelClassifierTools) Path(java.nio.file.Path) ColorTools(qupath.lib.common.ColorTools) Member(java.lang.reflect.Member) PathObjects(qupath.lib.objects.PathObjects) Set(java.util.Set) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) Project(qupath.lib.projects.Project) Stream(java.util.stream.Stream) ColorModels(qupath.lib.analysis.heatmaps.ColorModels) ShapeFeatures(qupath.lib.analysis.features.ObjectMeasurements.ShapeFeatures) DensityMaps(qupath.lib.analysis.heatmaps.DensityMaps) PathObjectPredicates(qupath.lib.objects.PathObjectPredicates) GeoJsonExportOptions(qupath.lib.io.PathIO.GeoJsonExportOptions) GsonTools(qupath.lib.io.GsonTools) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Constructor(java.lang.reflect.Constructor) PathPlugin(qupath.lib.plugins.PathPlugin) Projects(qupath.lib.projects.Projects) ArrayList(java.util.ArrayList) ROIs(qupath.lib.roi.ROIs) ImageOps(qupath.opencv.ops.ImageOps) LinkedHashSet(java.util.LinkedHashSet) PathClassifierTools(qupath.lib.classifiers.PathClassifierTools) ObjectArrays(com.google.common.collect.ObjectArrays) Files(java.nio.file.Files) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) TileExporter(qupath.lib.images.writers.TileExporter) DistanceTools(qupath.lib.analysis.DistanceTools) IOException(java.io.IOException) Padding(qupath.lib.regions.Padding) Field(java.lang.reflect.Field) DelaunayTools(qupath.lib.analysis.DelaunayTools) File(java.io.File) PathObjectTools(qupath.lib.objects.PathObjectTools) ROI(qupath.lib.roi.interfaces.ROI) PixelClassifier(qupath.lib.classifiers.pixel.PixelClassifier) Paths(java.nio.file.Paths) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) UriResource(qupath.lib.io.UriResource) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) PathIO(qupath.lib.io.PathIO) RunSavedClassifierWorkflowStep(qupath.lib.plugins.workflow.RunSavedClassifierWorkflowStep) CreateObjectOptions(qupath.opencv.ml.pixel.PixelClassifierTools.CreateObjectOptions) ImageServer(qupath.lib.images.servers.ImageServer) LoggerFactory(org.slf4j.LoggerFactory) Scanner(java.util.Scanner) PathObjectFilter(qupath.lib.objects.PathObjectFilter) OpenCVMLClassifier(qupath.opencv.ml.objects.OpenCVMLClassifier) BufferedImageTools(qupath.lib.awt.common.BufferedImageTools) URI(java.net.URI) ImageServers(qupath.lib.images.servers.ImageServers) Method(java.lang.reflect.Method) ImageType(qupath.lib.images.ImageData.ImageType) ObjectMeasurements(qupath.lib.analysis.features.ObjectMeasurements) BufferedImage(java.awt.image.BufferedImage) PixelClassifiers(qupath.opencv.ml.pixel.PixelClassifiers) Predicate(java.util.function.Predicate) ImageServerProvider(qupath.lib.images.servers.ImageServerProvider) Collection(java.util.Collection) DensityMapBuilder(qupath.lib.analysis.heatmaps.DensityMaps.DensityMapBuilder) UriUpdater(qupath.lib.io.UriUpdater) Collectors(java.util.stream.Collectors) FileNotFoundException(java.io.FileNotFoundException) PathObject(qupath.lib.objects.PathObject) PathDetectionObject(qupath.lib.objects.PathDetectionObject) List(java.util.List) ProjectIO(qupath.lib.projects.ProjectIO) ContourTracing(qupath.lib.analysis.images.ContourTracing) PathObjectClassifier(qupath.lib.classifiers.PathObjectClassifier) Modifier(java.lang.reflect.Modifier) DnnTools(qupath.opencv.dnn.DnnTools) ImagePlane(qupath.lib.regions.ImagePlane) Geometry(org.locationtech.jts.geom.Geometry) Pattern(java.util.regex.Pattern) PathCellObject(qupath.lib.objects.PathCellObject) PathClassTools(qupath.lib.objects.classes.PathClassTools) ImageChannel(qupath.lib.images.servers.ImageChannel) OpenCVTools(qupath.opencv.tools.OpenCVTools) HashMap(java.util.HashMap) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) ColorTransforms(qupath.lib.images.servers.ColorTransforms) CellTools(qupath.lib.objects.CellTools) HashSet(java.util.HashSet) ColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains) ObjectClassifiers(qupath.lib.classifiers.object.ObjectClassifiers) GeometryTools(qupath.lib.roi.GeometryTools) NoSuchElementException(java.util.NoSuchElementException) WeakHashMap(java.util.WeakHashMap) ImageData(qupath.lib.images.ImageData) RoiTools(qupath.lib.roi.RoiTools) Logger(org.slf4j.Logger) ProjectImageEntry(qupath.lib.projects.ProjectImageEntry) ImageWriterTools(qupath.lib.images.writers.ImageWriterTools) PixelType(qupath.lib.images.servers.PixelType) PathClass(qupath.lib.objects.classes.PathClass) PointIO(qupath.lib.io.PointIO) TMACoreObject(qupath.lib.objects.TMACoreObject) ObjectClassifier(qupath.lib.classifiers.object.ObjectClassifier) Comparator(java.util.Comparator) Collections(java.util.Collections) Geometry(org.locationtech.jts.geom.Geometry) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PathObject(qupath.lib.objects.PathObject) ArrayList(java.util.ArrayList) ImagePlane(qupath.lib.regions.ImagePlane) ROI(qupath.lib.roi.interfaces.ROI)

Aggregations

ImagePlane (qupath.lib.regions.ImagePlane)14 ArrayList (java.util.ArrayList)9 PathObject (qupath.lib.objects.PathObject)8 HashMap (java.util.HashMap)7 ROI (qupath.lib.roi.interfaces.ROI)7 Geometry (org.locationtech.jts.geom.Geometry)6 List (java.util.List)5 PointsROI (qupath.lib.roi.PointsROI)5 Collection (java.util.Collection)4 Map (java.util.Map)4 Collectors (java.util.stream.Collectors)4 Logger (org.slf4j.Logger)4 LoggerFactory (org.slf4j.LoggerFactory)4 PathClass (qupath.lib.objects.classes.PathClass)4 PathClassFactory (qupath.lib.objects.classes.PathClassFactory)4 PathClassTools (qupath.lib.objects.classes.PathClassTools)4 RoiTools (qupath.lib.roi.RoiTools)4 Collections (java.util.Collections)3 Comparator (java.util.Comparator)3 PreparedGeometry (org.locationtech.jts.geom.prep.PreparedGeometry)3