Search in sources :

Example 61 with PathClass

use of qupath.lib.objects.classes.PathClass in project qupath by qupath.

the class DensityMaps method threshold.

/**
 * Threshold one or more channels of a density map to generate new annotations.
 *
 * @param hierarchy hierarchy to which objects should be added
 * @param densityServer density map
 * @param thresholds map between channel numbers and thresholds
 * @param pathClassName name of the classification to apply to the generated annotations
 * @param options additional options to customize how annotations are created
 * @return true if changes were made, false otherwise
 * @throws IOException
 */
public static boolean threshold(PathObjectHierarchy hierarchy, ImageServer<BufferedImage> densityServer, Map<Integer, ? extends Number> thresholds, String pathClassName, CreateObjectOptions... options) throws IOException {
    logger.debug("Thresholding {} with thresholds {}, options", densityServer, thresholds, Arrays.asList(options));
    // Apply threshold to densities
    PathClass lessThan = PathClassFactory.getPathClass(StandardPathClasses.IGNORE);
    PathClass greaterThan = PathClassFactory.getPathClass(pathClassName);
    // If we request to delete existing objects, apply this only to annotations with the target class
    var optionsList = Arrays.asList(options);
    boolean changes = false;
    if (optionsList.contains(CreateObjectOptions.DELETE_EXISTING)) {
        Collection<PathObject> toRemove;
        if (hierarchy.getSelectionModel().noSelection())
            toRemove = hierarchy.getAnnotationObjects().stream().filter(p -> p.getPathClass() == greaterThan).collect(Collectors.toList());
        else {
            toRemove = new HashSet<>();
            var selectedObjects = new LinkedHashSet<>(hierarchy.getSelectionModel().getSelectedObjects());
            for (var selected : selectedObjects) {
                PathObjectTools.getDescendantObjects(selected, toRemove, PathAnnotationObject.class);
            }
            // Don't remove selected objects
            toRemove.removeAll(selectedObjects);
        }
        if (!toRemove.isEmpty()) {
            hierarchy.removeObjects(toRemove, true);
            changes = true;
        }
        // Remove option
        options = optionsList.stream().filter(o -> o != CreateObjectOptions.DELETE_EXISTING).toArray(CreateObjectOptions[]::new);
    }
    var thresholdedServer = PixelClassifierTools.createThresholdServer(densityServer, thresholds, lessThan, greaterThan);
    return PixelClassifierTools.createAnnotationsFromPixelClassifier(hierarchy, thresholdedServer, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, options) | changes;
}
Also used : CreateObjectOptions(qupath.opencv.ml.pixel.PixelClassifierTools.CreateObjectOptions) Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) LoggerFactory(org.slf4j.LoggerFactory) ChannelType(qupath.lib.images.servers.ImageServerMetadata.ChannelType) Map(java.util.Map) PixelClassifierTools(qupath.opencv.ml.pixel.PixelClassifierTools) StandardPathClasses(qupath.lib.objects.classes.PathClassFactory.StandardPathClasses) BufferedImageTools(qupath.lib.awt.common.BufferedImageTools) Path(java.nio.file.Path) BufferedImage(java.awt.image.BufferedImage) PixelClassifiers(qupath.opencv.ml.pixel.PixelClassifiers) Collection(java.util.Collection) PathObjects(qupath.lib.objects.PathObjects) UUID(java.util.UUID) Collectors(java.util.stream.Collectors) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathObject(qupath.lib.objects.PathObject) Objects(java.util.Objects) Project(qupath.lib.projects.Project) ImagePlane(qupath.lib.regions.ImagePlane) ColorModelBuilder(qupath.lib.analysis.heatmaps.ColorModels.ColorModelBuilder) org.bytedeco.opencv.global.opencv_core(org.bytedeco.opencv.global.opencv_core) GsonTools(qupath.lib.io.GsonTools) OpenCVTools(qupath.opencv.tools.OpenCVTools) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) ROIs(qupath.lib.roi.ROIs) Point2(qupath.lib.geom.Point2) LinkedHashSet(java.util.LinkedHashSet) ImageData(qupath.lib.images.ImageData) IndexedPixel(qupath.opencv.tools.OpenCVTools.IndexedPixel) RoiTools(qupath.lib.roi.RoiTools) Logger(org.slf4j.Logger) Files(java.nio.file.Files) PointerScope(org.bytedeco.javacpp.PointerScope) RegionRequest(qupath.lib.regions.RegionRequest) PathClass(qupath.lib.objects.classes.PathClass) PixelClassifierMetadata(qupath.lib.classifiers.pixel.PixelClassifierMetadata) IOException(java.io.IOException) PathObjectTools(qupath.lib.objects.PathObjectTools) ROI(qupath.lib.roi.interfaces.ROI) PixelClassifier(qupath.lib.classifiers.pixel.PixelClassifier) ColorModel(java.awt.image.ColorModel) PixelCalibration(qupath.lib.images.servers.PixelCalibration) PathObjectPredicate(qupath.lib.objects.PathObjectPredicates.PathObjectPredicate) Comparator(java.util.Comparator) Collections(java.util.Collections) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) LinkedHashSet(java.util.LinkedHashSet) PathClass(qupath.lib.objects.classes.PathClass) PathObject(qupath.lib.objects.PathObject) CreateObjectOptions(qupath.opencv.ml.pixel.PixelClassifierTools.CreateObjectOptions)

Example 62 with PathClass

use of qupath.lib.objects.classes.PathClass in project qupath by qupath.

the class DensityMaps method findHotspots.

/**
 * Find hotspots in a density map.
 *
 * @param hierarchy hierarchy used to obtain selected objects and add hotspots
 * @param densityServer the density map to query
 * @param channel channel in which to find hotspots (usually 0)
 * @param nHotspots maximum number of hotspots to find per selected annotation
 * @param radius hotspot radius, in calibrated units
 * @param minCount minimum value required in the 'count' channel (the last channel)
 * @param hotspotClass the classification to apply to hotspots
 * @param deleteExisting optionally delete existing annotations identified as hotspots
 * @param peaksOnly optionally restrict hotspots to only include intensity peaks
 * @throws IOException
 */
public static void findHotspots(PathObjectHierarchy hierarchy, ImageServer<BufferedImage> densityServer, int channel, int nHotspots, double radius, double minCount, PathClass hotspotClass, boolean deleteExisting, boolean peaksOnly) throws IOException {
    if (nHotspots <= 0) {
        logger.warn("Number of hotspots requested is {}!", nHotspots);
        return;
    }
    logger.debug("Finding {} hotspots in {} for channel {}, radius {}", nHotspots, densityServer, channel, radius);
    Collection<PathObject> parents = new ArrayList<>(hierarchy.getSelectionModel().getSelectedObjects());
    if (parents.isEmpty())
        parents = Collections.singleton(hierarchy.getRootObject());
    double downsample = densityServer.getDownsampleForResolution(0);
    var toDelete = new HashSet<PathObject>();
    // Handle deleting existing hotspots
    if (deleteExisting) {
        toDelete.addAll(hierarchy.getAnnotationObjects().stream().filter(p -> p.getPathClass() == hotspotClass && p.isAnnotation() && p.getName() != null && p.getName().startsWith("Hotspot")).collect(Collectors.toList()));
    }
    // Convert radius to pixels
    double radiusPixels = radius / densityServer.getPixelCalibration().getAveragedPixelSize().doubleValue();
    try (@SuppressWarnings("unchecked") var scope = new PointerScope()) {
        for (var parent : parents) {
            ROI roi = parent.getROI();
            // We need a ROI to define the area of interest
            if (roi == null) {
                if (densityServer.nTimepoints() > 1 || densityServer.nZSlices() > 1) {
                    logger.warn("Hotspot detection without a parent object not supported for images with multiple z-slices/timepoints.");
                    logger.warn("I will apply detection to the first plane only. If you need hotspots elsewhere, create an annotation first and use it to define the ROI.");
                }
                roi = ROIs.createRectangleROI(0, 0, densityServer.getWidth(), densityServer.getHeight(), ImagePlane.getDefaultPlane());
            }
            // Erode the ROI & see if any hotspot could fit
            var roiEroded = RoiTools.buffer(roi, -radiusPixels);
            if (roiEroded.isEmpty() || roiEroded.getArea() == 0) {
                logger.warn("ROI is too small! Cannot detected hotspots with radius {} in {}", radius, parent);
                continue;
            }
            // Read the image
            var plane = roi.getImagePlane();
            RegionRequest request = RegionRequest.createInstance(densityServer.getPath(), downsample, 0, 0, densityServer.getWidth(), densityServer.getHeight(), plane.getZ(), plane.getT());
            var img = densityServer.readBufferedImage(request);
            // Create a mask
            var imgMask = BufferedImageTools.createROIMask(img.getWidth(), img.getHeight(), roiEroded, request);
            // Switch to OpenCV
            var mat = OpenCVTools.imageToMat(img);
            var matMask = OpenCVTools.imageToMat(imgMask);
            // Find hotspots
            var channels = OpenCVTools.splitChannels(mat);
            var density = channels.get(channel);
            if (minCount > 0) {
                var thresholdMask = opencv_core.greaterThan(channels.get(channels.size() - 1), minCount).asMat();
                opencv_core.bitwise_and(matMask, thresholdMask, matMask);
                thresholdMask.close();
            }
            // TODO: Limit to peaks
            if (peaksOnly) {
                var matMaxima = OpenCVTools.findRegionalMaxima(density);
                var matPeaks = OpenCVTools.shrinkLabels(matMaxima);
                matPeaks.put(opencv_core.greaterThan(matPeaks, 0));
                opencv_core.bitwise_and(matMask, matPeaks, matMask);
                matPeaks.close();
                matMaxima.close();
            }
            // Sort in descending order
            var maxima = new ArrayList<>(OpenCVTools.getMaskedPixels(density, matMask));
            Collections.sort(maxima, Comparator.comparingDouble((IndexedPixel p) -> p.getValue()).reversed());
            // Try to get as many maxima as we need
            // Impose minimum separation
            var points = maxima.stream().map(p -> new Point2(p.getX() * downsample, p.getY() * downsample)).collect(Collectors.toList());
            var hotspotCentroids = new ArrayList<Point2>();
            double distSqThreshold = radiusPixels * radiusPixels * 4;
            for (var p : points) {
                // Check not too close to an existing hotspot
                boolean skip = false;
                for (var p2 : hotspotCentroids) {
                    if (p.distanceSq(p2) < distSqThreshold) {
                        skip = true;
                        break;
                    }
                }
                if (!skip) {
                    hotspotCentroids.add(p);
                    if (hotspotCentroids.size() == nHotspots)
                        break;
                }
            }
            var hotspots = new ArrayList<PathObject>();
            int i = 0;
            for (var p : hotspotCentroids) {
                i++;
                var ellipse = ROIs.createEllipseROI(p.getX() - radiusPixels, p.getY() - radiusPixels, radiusPixels * 2, radiusPixels * 2, roi.getImagePlane());
                var hotspot = PathObjects.createAnnotationObject(ellipse, hotspotClass);
                hotspot.setName("Hotspot " + i);
                hotspots.add(hotspot);
            }
            if (hotspots.isEmpty())
                logger.warn("No hotspots found in {}", parent);
            else if (hotspots.size() < nHotspots) {
                logger.warn("Only {}/{} hotspots could be found in {}", hotspots.size(), nHotspots, parent);
            }
            parent.addPathObjects(hotspots);
        }
        hierarchy.fireHierarchyChangedEvent(DensityMaps.class);
        if (!toDelete.isEmpty())
            hierarchy.removeObjects(toDelete, true);
    }
}
Also used : CreateObjectOptions(qupath.opencv.ml.pixel.PixelClassifierTools.CreateObjectOptions) Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) LoggerFactory(org.slf4j.LoggerFactory) ChannelType(qupath.lib.images.servers.ImageServerMetadata.ChannelType) Map(java.util.Map) PixelClassifierTools(qupath.opencv.ml.pixel.PixelClassifierTools) StandardPathClasses(qupath.lib.objects.classes.PathClassFactory.StandardPathClasses) BufferedImageTools(qupath.lib.awt.common.BufferedImageTools) Path(java.nio.file.Path) BufferedImage(java.awt.image.BufferedImage) PixelClassifiers(qupath.opencv.ml.pixel.PixelClassifiers) Collection(java.util.Collection) PathObjects(qupath.lib.objects.PathObjects) UUID(java.util.UUID) Collectors(java.util.stream.Collectors) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathObject(qupath.lib.objects.PathObject) Objects(java.util.Objects) Project(qupath.lib.projects.Project) ImagePlane(qupath.lib.regions.ImagePlane) ColorModelBuilder(qupath.lib.analysis.heatmaps.ColorModels.ColorModelBuilder) org.bytedeco.opencv.global.opencv_core(org.bytedeco.opencv.global.opencv_core) GsonTools(qupath.lib.io.GsonTools) OpenCVTools(qupath.opencv.tools.OpenCVTools) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) ROIs(qupath.lib.roi.ROIs) Point2(qupath.lib.geom.Point2) LinkedHashSet(java.util.LinkedHashSet) ImageData(qupath.lib.images.ImageData) IndexedPixel(qupath.opencv.tools.OpenCVTools.IndexedPixel) RoiTools(qupath.lib.roi.RoiTools) Logger(org.slf4j.Logger) Files(java.nio.file.Files) PointerScope(org.bytedeco.javacpp.PointerScope) RegionRequest(qupath.lib.regions.RegionRequest) PathClass(qupath.lib.objects.classes.PathClass) PixelClassifierMetadata(qupath.lib.classifiers.pixel.PixelClassifierMetadata) IOException(java.io.IOException) PathObjectTools(qupath.lib.objects.PathObjectTools) ROI(qupath.lib.roi.interfaces.ROI) PixelClassifier(qupath.lib.classifiers.pixel.PixelClassifier) ColorModel(java.awt.image.ColorModel) PixelCalibration(qupath.lib.images.servers.PixelCalibration) PathObjectPredicate(qupath.lib.objects.PathObjectPredicates.PathObjectPredicate) Comparator(java.util.Comparator) Collections(java.util.Collections) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) ArrayList(java.util.ArrayList) IndexedPixel(qupath.opencv.tools.OpenCVTools.IndexedPixel) PointerScope(org.bytedeco.javacpp.PointerScope) ROI(qupath.lib.roi.interfaces.ROI) PathObject(qupath.lib.objects.PathObject) Point2(qupath.lib.geom.Point2) RegionRequest(qupath.lib.regions.RegionRequest) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Example 63 with PathClass

use of qupath.lib.objects.classes.PathClass in project qupath by qupath.

the class QP method classifySelected.

/**
 * Set the classification of the selected objects.
 *
 * @param hierarchy
 * @param pathClassName
 */
public static void classifySelected(final PathObjectHierarchy hierarchy, final String pathClassName) {
    PathClass pathClass = PathClassFactory.getPathClass(pathClassName);
    Collection<PathObject> selected = hierarchy.getSelectionModel().getSelectedObjects();
    if (selected.isEmpty()) {
        logger.info("No objects selected");
        return;
    }
    for (PathObject pathObject : selected) {
        pathObject.setPathClass(pathClass);
    }
    if (selected.size() == 1)
        logger.info("{} object classified as {}", selected.size(), pathClassName);
    else
        logger.info("{} objects classified as {}", selected.size(), pathClassName);
    hierarchy.fireObjectClassificationsChangedEvent(null, selected);
}
Also used : PathClass(qupath.lib.objects.classes.PathClass) PathObject(qupath.lib.objects.PathObject)

Example 64 with PathClass

use of qupath.lib.objects.classes.PathClass in project qupath by qupath.

the class QP method mergeAnnotations.

/**
 * Merge the specified annotations to create a new annotation containing the union of their ROIs.
 * <p>
 * Note:
 * <ul>
 * <li>The existing annotations will be removed from the hierarchy if possible, therefore should be duplicated first
 * if this is not desired.</li>
 * <li>The new object will be set to be the selected object in the hierarchy (which can be used to retrieve it if needed).</li>
 * </ul>
 *
 * @param hierarchy
 * @param annotations
 * @return true if changes are made to the hierarchy, false otherwise
 */
public static boolean mergeAnnotations(final PathObjectHierarchy hierarchy, final Collection<PathObject> annotations) {
    if (hierarchy == null)
        return false;
    // Get all the selected annotations with area
    ROI shapeNew = null;
    List<PathObject> merged = new ArrayList<>();
    Set<PathClass> pathClasses = new HashSet<>();
    for (PathObject annotation : annotations) {
        if (annotation.isAnnotation() && annotation.hasROI() && (annotation.getROI().isArea() || annotation.getROI().isPoint())) {
            if (shapeNew == null)
                // .duplicate();
                shapeNew = annotation.getROI();
            else if (shapeNew.getImagePlane().equals(annotation.getROI().getImagePlane()))
                shapeNew = RoiTools.combineROIs(shapeNew, annotation.getROI(), RoiTools.CombineOp.ADD);
            else {
                logger.warn("Cannot merge ROIs across different image planes!");
                return false;
            }
            if (annotation.getPathClass() != null)
                pathClasses.add(annotation.getPathClass());
            merged.add(annotation);
        }
    }
    // Check if we actually merged anything
    if (merged.isEmpty() || merged.size() == 1)
        return false;
    // Create and add the new object, removing the old ones
    PathObject pathObjectNew = PathObjects.createAnnotationObject(shapeNew);
    if (pathClasses.size() == 1)
        pathObjectNew.setPathClass(pathClasses.iterator().next());
    else
        logger.warn("Cannot assign class unambiguously - " + pathClasses.size() + " classes represented in selection");
    hierarchy.removeObjects(merged, true);
    hierarchy.addPathObject(pathObjectNew);
    hierarchy.getSelectionModel().setSelectedObject(pathObjectNew);
    // hierarchy.fireHierarchyChangedEvent(pathObject);
    return true;
}
Also used : PathClass(qupath.lib.objects.classes.PathClass) PathObject(qupath.lib.objects.PathObject) ArrayList(java.util.ArrayList) ROI(qupath.lib.roi.interfaces.ROI) LinkedHashSet(java.util.LinkedHashSet) HashSet(java.util.HashSet)

Example 65 with PathClass

use of qupath.lib.objects.classes.PathClass in project qupath by qupath.

the class TestCompositeClassifier method test_getPathClass.

@Test
public void test_getPathClass() {
    List<PathClass> outputPathClasses = new ArrayList<>();
    for (PathClass pathClass : new PathClass[] { pathClass1, pathClass2, pathClass3 }) {
        outputPathClasses.add(PathClassFactory.getPositive(pathClass));
        outputPathClasses.add(PathClassFactory.getNegative(pathClass));
    }
    var posNegClasses = Arrays.asList(PathClassFactory.getPositive(null), PathClassFactory.getNegative(null));
    assertTrue(outputPathClasses.size() == cp1.getPathClasses().size() && outputPathClasses.containsAll(cp1.getPathClasses()));
    assertTrue(cp2.getPathClasses().size() == posNegClasses.size() && cp2.getPathClasses().containsAll(posNegClasses));
    assertTrue(cp2.getPathClasses().size() == posNegClasses.size() && cp2.getPathClasses().containsAll(posNegClasses));
    assertTrue(cpInvalid.getPathClasses().size() == 0);
}
Also used : PathClass(qupath.lib.objects.classes.PathClass) ArrayList(java.util.ArrayList) Test(org.junit.jupiter.api.Test)

Aggregations

PathClass (qupath.lib.objects.classes.PathClass)66 ArrayList (java.util.ArrayList)42 PathObject (qupath.lib.objects.PathObject)34 List (java.util.List)29 Map (java.util.Map)25 IOException (java.io.IOException)21 Logger (org.slf4j.Logger)20 LoggerFactory (org.slf4j.LoggerFactory)20 Collections (java.util.Collections)17 Collectors (java.util.stream.Collectors)17 BufferedImage (java.awt.image.BufferedImage)16 LinkedHashMap (java.util.LinkedHashMap)16 ROI (qupath.lib.roi.interfaces.ROI)16 HashMap (java.util.HashMap)15 ImageData (qupath.lib.images.ImageData)15 PathClassFactory (qupath.lib.objects.classes.PathClassFactory)15 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)15 ParameterList (qupath.lib.plugins.parameters.ParameterList)15 Collection (java.util.Collection)14 TreeMap (java.util.TreeMap)11