Search in sources :

Example 26 with RegionRequest

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

the class AbstractImageRegionStore method getThumbnail.

/* (non-Javadoc)
	 * @see qupath.lib.images.stores.ImageRegionStore#getThumbnail(qupath.lib.images.servers.ImageServer, int, int, boolean)
	 */
@Override
@SuppressWarnings("unchecked")
public synchronized T getThumbnail(ImageServer<T> server, int zPosition, int tPosition, boolean addToCache) {
    RegionRequest request = getThumbnailRequest(server, zPosition, tPosition);
    Object result = requestImageTile(server, request, thumbnailCache, true);
    if (!(result instanceof TileWorker<?>))
        return (T) result;
    logger.debug("Thumbnail request for {}, ({}, {})", server, zPosition, tPosition);
    TileWorker<T> worker = (TileWorker<T>) result;
    try {
        return worker.get();
    } catch (InterruptedException e) {
        logger.error(e.getLocalizedMessage());
    } catch (ExecutionException e) {
        logger.error(e.getLocalizedMessage());
    }
    try {
        // Last resort... shouldn't happen
        logger.warn("Fallback to requesting thumbnail directly...");
        return server.readBufferedImage(request);
    } catch (IOException e) {
        logger.error("Unable to obtain thumbnail for " + request, e);
        return null;
    }
}
Also used : IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) RegionRequest(qupath.lib.regions.RegionRequest)

Example 27 with RegionRequest

use of qupath.lib.regions.RegionRequest 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 28 with RegionRequest

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

the class LocalBinaryPatternsPlugin method processObject.

static boolean processObject(final PathObject pathObject, final ParameterList params, final ImageServer<BufferedImage> server, final ColorDeconvolutionStains stains) throws InterruptedException, IOException {
    String stainsName = (String) params.getChoiceParameterValue("stainChoice");
    double mag = params.getDoubleParameterValue("magnification");
    // int d = params.getIntParameterValue("haralickDistance");
    boolean includeStats = params.getBooleanParameterValue("includeStats");
    boolean doCircular = params.getBooleanParameterValue("doCircular");
    double downsample = server.getMetadata().getMagnification() / mag;
    ROI pathROI = pathObject.getROI();
    if (pathROI == null)
        return false;
    // Get bounds
    ImmutableDimension size = getPreferredTileSizePixels(server, params);
    if (size.getWidth() / downsample < 1 || size.getHeight() / downsample < 1)
        return false;
    RegionRequest region = RegionRequest.createInstance(server.getPath(), downsample, (int) (pathROI.getCentroidX() + .5) - size.width / 2, (int) (pathROI.getCentroidY() + .5) - size.height / 2, size.width, size.height, pathROI.getT(), pathROI.getZ());
    // System.out.println(bounds);
    // System.out.println("Size: " + size);
    BufferedImage img = server.readBufferedImage(region);
    // System.out.println("Image size: " + img.getWidth() + " x " + img.getHeight() + " pixels");
    // Get a buffer containing the image pixels
    int w = img.getWidth();
    int h = img.getHeight();
    int[] buf = img.getRGB(0, 0, w, h, null, 0, w);
    // Create a color transformer to get the images we need
    float[] pixels = new float[buf.length];
    SimpleModifiableImage pxImg = SimpleImages.createFloatImage(pixels, w, h);
    MeasurementList measurementList = pathObject.getMeasurementList();
    String postfix = " (" + getDiameterString(server, params) + ")";
    if (stainsName.equals("H-DAB")) {
        processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_DAB, stains, includeStats, doCircular);
        processTransformedImage(pxImg, buf, pixels, measurementList, "DAB" + postfix, ColorTransformer.ColorTransformMethod.DAB_H_DAB, stains, includeStats, doCircular);
    } else if (stainsName.equals("H&E")) {
        processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_E, stains, includeStats, doCircular);
        processTransformedImage(pxImg, buf, pixels, measurementList, "Eosin" + postfix, ColorTransformer.ColorTransformMethod.Eosin_H_E, stains, includeStats, doCircular);
    } else if (stainsName.equals("H-DAB (8-bit)")) {
        processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin 8-bit" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_DAB_8_bit, stains, includeStats, doCircular);
        processTransformedImage(pxImg, buf, pixels, measurementList, "DAB 8-bit" + postfix, ColorTransformer.ColorTransformMethod.DAB_H_DAB_8_bit, stains, includeStats, doCircular);
    } else if (stainsName.equals("H&E (8-bit)")) {
        processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin 8-bit" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_E_8_bit, stains, includeStats, doCircular);
        processTransformedImage(pxImg, buf, pixels, measurementList, "Eosin 8-bit" + postfix, ColorTransformer.ColorTransformMethod.Eosin_H_E_8_bit, stains, includeStats, doCircular);
    } else if (stainsName.equals("Optical density")) {
        processTransformedImage(pxImg, buf, pixels, measurementList, "OD sum" + postfix, ColorTransformer.ColorTransformMethod.Optical_density_sum, stains, includeStats, doCircular);
    } else if (stainsName.equals("RGB")) {
        processTransformedImage(pxImg, buf, pixels, measurementList, "Red" + postfix, ColorTransformer.ColorTransformMethod.Red, stains, includeStats, doCircular);
        processTransformedImage(pxImg, buf, pixels, measurementList, "Green" + postfix, ColorTransformer.ColorTransformMethod.Green, stains, includeStats, doCircular);
        processTransformedImage(pxImg, buf, pixels, measurementList, "Blue" + postfix, ColorTransformer.ColorTransformMethod.Blue, stains, includeStats, doCircular);
    } else if (stainsName.equals("Grayscale")) {
        processTransformedImage(pxImg, buf, pixels, measurementList, "Grayscale" + postfix, ColorTransformer.ColorTransformMethod.RGB_mean, stains, includeStats, doCircular);
    }
    measurementList.close();
    return true;
}
Also used : SimpleModifiableImage(qupath.lib.analysis.images.SimpleModifiableImage) MeasurementList(qupath.lib.measurements.MeasurementList) ImmutableDimension(qupath.lib.geom.ImmutableDimension) ROI(qupath.lib.roi.interfaces.ROI) RegionRequest(qupath.lib.regions.RegionRequest) BufferedImage(java.awt.image.BufferedImage)

Example 29 with RegionRequest

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

the class TMAExplorer method createAndShowStage.

private void createAndShowStage() {
    Project<BufferedImage> project = qupath.getProject();
    entries.clear();
    if (project != null) {
        // Create an output directory for the images
        File dirBaseImageOutput = Projects.getBaseDirectory(project);
        dirBaseImageOutput = new File(dirBaseImageOutput, "TMA");
        dirBaseImageOutput = new File(dirBaseImageOutput, "images");
        if (!dirBaseImageOutput.exists())
            dirBaseImageOutput.mkdirs();
        Map<String, RunningStatistics> statsMap = new HashMap<>();
        for (ProjectImageEntry<BufferedImage> imageEntry : project.getImageList()) {
            // Look for data file
            if (!imageEntry.hasImageData())
                continue;
            File dirImageOutput = new File(dirBaseImageOutput, imageEntry.getImageName());
            if (!dirImageOutput.exists())
                dirImageOutput.mkdirs();
            // Read data
            ImageData<BufferedImage> imageData;
            try {
                imageData = imageEntry.readImageData();
            } catch (IOException e) {
                logger.error("Error reading ImageData for " + imageEntry.getImageName(), e);
                continue;
            }
            TMAGrid tmaGrid = imageData.getHierarchy().getTMAGrid();
            if (tmaGrid == null) {
                logger.warn("No TMA data for {}", imageEntry.getImageName());
                continue;
            }
            // Figure out downsample value
            ImageServer<BufferedImage> server = imageData.getServer();
            double downsample = Math.round(5 / server.getPixelCalibration().getAveragedPixelSizeMicrons());
            // Read the TMA entries
            int counter = 0;
            for (TMACoreObject core : tmaGrid.getTMACoreList()) {
                counter++;
                String name = core.getName();
                if (name == null)
                    name = Integer.toString(counter);
                File fileOutput = new File(dirImageOutput, name + ".jpg");
                if (!fileOutput.exists()) {
                    try {
                        RegionRequest request = RegionRequest.createInstance(server.getPath(), downsample, core.getROI());
                        BufferedImage img = server.readBufferedImage(request);
                        ImageIO.write(img, "jpg", fileOutput);
                    } catch (Exception e) {
                        logger.error("Unable to write {}", fileOutput.getAbsolutePath());
                    }
                }
                var entry = TMAEntries.createDefaultTMAEntry(imageEntry.getImageName(), fileOutput.getAbsolutePath(), null, core.getName(), core.isMissing());
                MeasurementList ml = core.getMeasurementList();
                for (int i = 0; i < ml.size(); i++) {
                    String measurement = ml.getMeasurementName(i);
                    double val = ml.getMeasurementValue(i);
                    entry.putMeasurement(measurement, val);
                    if (!Double.isNaN(val)) {
                        RunningStatistics stats = statsMap.get(measurement);
                        if (stats == null) {
                            stats = new RunningStatistics();
                            statsMap.put(measurement, stats);
                        }
                        stats.addValue(val);
                    }
                }
                entries.add(entry);
            }
            try {
                server.close();
            } catch (Exception e) {
                logger.warn("Problem closing server", e);
            }
        }
        // Loop through all entries and perform outlier count
        double k = 3;
        for (TMAEntry entry : entries) {
            int outlierCount = 0;
            for (Entry<String, RunningStatistics> statsEntry : statsMap.entrySet()) {
                RunningStatistics stats = statsEntry.getValue();
                double val = entry.getMeasurementAsDouble(statsEntry.getKey());
                if (!(val >= stats.getMean() - stats.getStdDev() * k && val <= stats.getMean() + stats.getStdDev() * k))
                    outlierCount++;
            }
            entry.putMeasurement("Outlier count", outlierCount);
        }
    }
    Stage stage = new Stage();
    stage.initOwner(qupath.getStage());
    TMASummaryViewer summaryViewer = new TMASummaryViewer(stage);
    summaryViewer.setTMAEntries(entries);
    summaryViewer.getStage().show();
}
Also used : HashMap(java.util.HashMap) TMACoreObject(qupath.lib.objects.TMACoreObject) MeasurementList(qupath.lib.measurements.MeasurementList) TMAEntry(qupath.lib.gui.tma.TMAEntries.TMAEntry) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) IOException(java.io.IOException) BufferedImage(java.awt.image.BufferedImage) IOException(java.io.IOException) RunningStatistics(qupath.lib.analysis.stats.RunningStatistics) Stage(javafx.stage.Stage) File(java.io.File) RegionRequest(qupath.lib.regions.RegionRequest)

Example 30 with RegionRequest

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

the class OpenCVTools method extractZStack.

/**
 * Extract a list of Mats, where each Mat corresponds to a z-slice.
 *
 * @param server
 * @param request
 * @param zMin first z slice, inclusive
 * @param zMax last z slice, exclusive
 * @return
 * @throws IOException
 */
public static List<Mat> extractZStack(ImageServer<BufferedImage> server, RegionRequest request, int zMin, int zMax) throws IOException {
    List<Mat> list = new ArrayList<>();
    for (int z = zMin; z < zMax; z++) {
        RegionRequest request2 = RegionRequest.createInstance(server.getPath(), request.getDownsample(), request.getX(), request.getY(), request.getWidth(), request.getHeight(), z, request.getT());
        BufferedImage img = server.readBufferedImage(request2);
        list.add(imageToMat(img));
    }
    return list;
}
Also used : Mat(org.bytedeco.opencv.opencv_core.Mat) ArrayList(java.util.ArrayList) RegionRequest(qupath.lib.regions.RegionRequest) Point(org.bytedeco.opencv.opencv_core.Point) BufferedImage(java.awt.image.BufferedImage)

Aggregations

RegionRequest (qupath.lib.regions.RegionRequest)37 BufferedImage (java.awt.image.BufferedImage)29 IOException (java.io.IOException)18 ROI (qupath.lib.roi.interfaces.ROI)15 ArrayList (java.util.ArrayList)13 PathObject (qupath.lib.objects.PathObject)10 List (java.util.List)9 Collection (java.util.Collection)8 Collectors (java.util.stream.Collectors)8 PixelCalibration (qupath.lib.images.servers.PixelCalibration)8 Collections (java.util.Collections)7 TMACoreObject (qupath.lib.objects.TMACoreObject)7 Graphics2D (java.awt.Graphics2D)6 Logger (org.slf4j.Logger)6 LoggerFactory (org.slf4j.LoggerFactory)6 ImageData (qupath.lib.images.ImageData)6 ImageServer (qupath.lib.images.servers.ImageServer)6 PathObjectTools (qupath.lib.objects.PathObjectTools)6 LinkedHashMap (java.util.LinkedHashMap)5 Map (java.util.Map)5