Search in sources :

Example 16 with ImageServer

use of qupath.lib.images.servers.ImageServer 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 17 with ImageServer

use of qupath.lib.images.servers.ImageServer 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)

Aggregations

BufferedImage (java.awt.image.BufferedImage)17 ImageServer (qupath.lib.images.servers.ImageServer)17 IOException (java.io.IOException)15 Logger (org.slf4j.Logger)14 LoggerFactory (org.slf4j.LoggerFactory)14 ArrayList (java.util.ArrayList)13 PathObject (qupath.lib.objects.PathObject)13 Collectors (java.util.stream.Collectors)12 Collections (java.util.Collections)11 ROI (qupath.lib.roi.interfaces.ROI)11 Collection (java.util.Collection)10 List (java.util.List)10 Map (java.util.Map)10 ImageData (qupath.lib.images.ImageData)10 PathObjectTools (qupath.lib.objects.PathObjectTools)10 RegionRequest (qupath.lib.regions.RegionRequest)10 Arrays (java.util.Arrays)9 LinkedHashMap (java.util.LinkedHashMap)9 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)9 HashSet (java.util.HashSet)8