Search in sources :

Example 1 with SimpleImage

use of qupath.lib.analysis.images.SimpleImage in project qupath by qupath.

the class SubcellularDetection method processObject.

/**
 * Initial version of subcellular detection processing.
 *
 * @param pathObject
 * @param params
 * @param imageWrapper
 * @return
 * @throws InterruptedException
 * @throws IOException
 */
static boolean processObject(final PathObject pathObject, final ParameterList params, final ImageWrapper imageWrapper) throws InterruptedException, IOException {
    // Get the base classification for the object as it currently stands
    PathClass baseClass = PathClassTools.getNonIntensityAncestorClass(pathObject.getPathClass());
    // Variable to hold estimated spot count
    double estimatedSpots;
    // We assume that after this processing, any previous sub-cellular objects should be removed
    pathObject.clearPathObjects();
    // Ensure we have no existing subcellular detection measurements - if we do, remove them
    String[] existingMeasurements = pathObject.getMeasurementList().getMeasurementNames().stream().filter(n -> n.startsWith("Subcellular:")).toArray(n -> new String[n]);
    if (existingMeasurements.length > 0) {
        pathObject.getMeasurementList().removeMeasurements(existingMeasurements);
        pathObject.getMeasurementList().close();
    }
    // // If we're part of a TMA core, request the whole core...
    // if (pathObject.getParent() instanceof TMACoreObject && pathObject.getParent().hasROI()) {
    // regionStore.getImage(server, RegionRequest.createInstance(server.getPath(), 1, pathObject.getParent().getROI()), 25, true);
    // }
    ROI pathROI = pathObject.getROI();
    if (pathROI == null || pathROI.isEmpty())
        return false;
    // double downsample = 0.5;
    double downsample = 1;
    // Determine spot size
    ImageServer<BufferedImage> server = imageWrapper.getServer();
    PixelCalibration cal = server.getPixelCalibration();
    double minSpotArea, maxSpotArea, singleSpotArea;
    double pixelWidth, pixelHeight;
    if (cal.hasPixelSizeMicrons()) {
        double spotSizeMicrons = params.getDoubleParameterValue("spotSizeMicrons");
        double minSpotSizeMicrons = params.getDoubleParameterValue("minSpotSizeMicrons");
        double maxSpotSizeMicrons = params.getDoubleParameterValue("maxSpotSizeMicrons");
        pixelWidth = cal.getPixelWidthMicrons() * downsample;
        pixelHeight = cal.getPixelHeightMicrons() * downsample;
        singleSpotArea = spotSizeMicrons / (pixelWidth * pixelHeight);
        minSpotArea = minSpotSizeMicrons / (pixelWidth * pixelHeight);
        maxSpotArea = maxSpotSizeMicrons / (pixelWidth * pixelHeight);
    } else {
        singleSpotArea = params.getDoubleParameterValue("spotSizePixels");
        minSpotArea = params.getDoubleParameterValue("minSpotSizePixels");
        maxSpotArea = params.getDoubleParameterValue("maxSpotSizePixels");
        pixelWidth = downsample;
        pixelHeight = downsample;
    }
    boolean includeClusters = Boolean.TRUE.equals(params.getBooleanParameterValue("includeClusters"));
    boolean doSmoothing = Boolean.TRUE.equals(params.getBooleanParameterValue("doSmoothing"));
    boolean splitByIntensity = Boolean.TRUE.equals(params.getBooleanParameterValue("splitByIntensity"));
    boolean splitByShape = Boolean.TRUE.equals(params.getBooleanParameterValue("splitByShape"));
    // Get region to request - give a pixel as border
    int xStart = (int) Math.max(0, pathROI.getBoundsX() - 1);
    int yStart = (int) Math.max(0, pathROI.getBoundsY() - 1);
    int width = (int) Math.min(server.getWidth() - 1, pathROI.getBoundsX() + pathROI.getBoundsWidth() + 1.5) - xStart;
    int height = (int) Math.min(server.getHeight() - 1, pathROI.getBoundsY() + pathROI.getBoundsHeight() + 1.5) - yStart;
    if (width <= 0 || height <= 0) {
        logger.error("Negative ROI size for {}", pathROI);
        pathObject.setPathClass(baseClass);
        return false;
    }
    int z = pathROI.getZ();
    int t = pathROI.getT();
    // Don't associate with channel
    int c = -1;
    RegionRequest region = RegionRequest.createInstance(server.getPath(), 1.0, xStart, yStart, width, height, z, t);
    // Mask to indicate pixels within the cell
    byte[] cellMask = null;
    for (String channelName : imageWrapper.getChannelNames(true, true)) {
        double detectionThreshold = params.getDoubleParameterValue("detection[" + channelName + "]");
        if (Double.isNaN(detectionThreshold) || detectionThreshold < 0)
            continue;
        // // TODO: Consider whether to use channel numbers for non-brightfield images
        // if (!imageWrapper.imageData.isBrightfield())
        // c++;
        SimpleImage img = imageWrapper.getRegion(region, channelName);
        // Get an ImageJ-friendly calibration for ROI conversion
        Calibration calIJ = new Calibration();
        calIJ.xOrigin = -xStart / downsample;
        calIJ.yOrigin = -yStart / downsample;
        // Create a cell mask
        if (cellMask == null) {
            BufferedImage imgMask = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
            Graphics2D g2d = imgMask.createGraphics();
            if (downsample != 1)
                g2d.scale(1.0 / downsample, 1.0 / downsample);
            g2d.translate(-xStart, -yStart);
            Shape shape = RoiTools.getShape(pathROI);
            g2d.setColor(Color.WHITE);
            g2d.fill(shape);
            g2d.dispose();
            cellMask = (byte[]) ((DataBufferByte) imgMask.getRaster().getDataBuffer()).getData(0);
        }
        // Get a buffer containing the image pixels
        int w = img.getWidth();
        int h = img.getHeight();
        // Identify (& try to separate) spots
        // Mask out non-cell areas as we go
        FloatProcessor fpDetection = new FloatProcessor(w, h);
        if (doSmoothing) {
            for (int i = 0; i < w * h; i++) fpDetection.setf(i, img.getValue(i % w, i / w));
            fpDetection.smooth();
            for (int i = 0; i < w * h; i++) {
                if (cellMask[i] == (byte) 0)
                    fpDetection.setf(i, 0f);
            }
        } else {
            for (int i = 0; i < w * h; i++) {
                if (cellMask[i] == (byte) 0)
                    fpDetection.setf(i, 0f);
                else
                    fpDetection.setf(i, img.getValue(i % w, i / w));
            }
        }
        ByteProcessor bpSpots;
        if (splitByIntensity)
            bpSpots = new MaximumFinder().findMaxima(fpDetection, detectionThreshold / 10.0, detectionThreshold, MaximumFinder.SEGMENTED, false, false);
        else
            bpSpots = SimpleThresholding.thresholdAboveEquals(fpDetection, (float) detectionThreshold);
        if (splitByShape) {
            new EDM().toWatershed(bpSpots);
        }
        // Loop through spot ROIs & make a decision
        bpSpots.setThreshold(1, ImageProcessor.NO_THRESHOLD, ImageProcessor.NO_LUT_UPDATE);
        List<PolygonRoi> possibleSpotRois = RoiLabeling.getFilledPolygonROIs(bpSpots, Wand.FOUR_CONNECTED);
        List<PathObject> spotObjects = new ArrayList<>();
        List<PathObject> clusterObjects = new ArrayList<>();
        estimatedSpots = 0;
        for (PolygonRoi spotRoi : possibleSpotRois) {
            fpDetection.setRoi(spotRoi);
            ImageStatistics stats = fpDetection.getStatistics();
            // In v0.2
            // ImagePlane plane = ImagePlane.getPlaneWithChannel(spotRoi.getCPosition(), spotRoi.getZPosition(), spotRoi.getTPosition());
            // In v0.3
            ImagePlane plane = ImagePlane.getPlaneWithChannel(c, z, t);
            PathObject spotOrCluster = null;
            if (stats.pixelCount >= minSpotArea && stats.pixelCount <= maxSpotArea) {
                ROI roi = IJTools.convertToROI(spotRoi, calIJ, downsample, plane);
                // cluster = new SubcellularObject(roi, 1);
                spotOrCluster = createSubcellularObject(roi, 1);
                estimatedSpots += 1;
            } else if (includeClusters && stats.pixelCount >= minSpotArea) {
                // Add a cluster
                ROI roi = IJTools.convertToROI(spotRoi, calIJ, downsample, plane);
                double nSpots = stats.pixelCount / singleSpotArea;
                estimatedSpots += nSpots;
                // cluster = new SubcellularObject(roi, nSpots);
                spotOrCluster = createSubcellularObject(roi, nSpots);
            }
            if (spotOrCluster != null) {
                boolean isCluster = spotOrCluster.getMeasurementList().getMeasurementValue("Num spots") > 1;
                int rgb = imageWrapper.getChannelColor(channelName);
                rgb = isCluster ? ColorTools.makeScaledRGB(rgb, 0.5) : ColorTools.makeScaledRGB(rgb, 1.5);
                PathClass pathClass = PathClassFactory.getDerivedPathClass(spotOrCluster.getPathClass(), channelName + " object", rgb);
                spotOrCluster.setPathClass(pathClass);
                spotOrCluster.getMeasurementList().putMeasurement("Subcellular cluster: " + channelName + ": Area", stats.pixelCount * pixelWidth * pixelHeight);
                spotOrCluster.getMeasurementList().putMeasurement("Subcellular cluster: " + channelName + ": Mean channel intensity", stats.mean);
                // cluster.getMeasurementList().putMeasurement("Subcellular cluster: " + channelName +  ": Max channel intensity", stats.max);
                spotOrCluster.getMeasurementList().close();
                if (isCluster)
                    clusterObjects.add(spotOrCluster);
                else
                    spotObjects.add(spotOrCluster);
            }
        }
        // Add measurements
        MeasurementList measurementList = pathObject.getMeasurementList();
        measurementList.putMeasurement("Subcellular: " + channelName + ": Num spots estimated", estimatedSpots);
        measurementList.putMeasurement("Subcellular: " + channelName + ": Num single spots", spotObjects.size());
        measurementList.putMeasurement("Subcellular: " + channelName + ": Num clusters", clusterObjects.size());
        // Add spots
        pathObject.addPathObjects(spotObjects);
        pathObject.addPathObjects(clusterObjects);
    }
    return true;
}
Also used : Color(java.awt.Color) RoiLabeling(qupath.imagej.processing.RoiLabeling) ImageServer(qupath.lib.images.servers.ImageServer) ByteProcessor(ij.process.ByteProcessor) ImageProcessor(ij.process.ImageProcessor) IJTools(qupath.imagej.tools.IJTools) LoggerFactory(org.slf4j.LoggerFactory) DataBufferByte(java.awt.image.DataBufferByte) Wand(ij.gui.Wand) ParameterList(qupath.lib.plugins.parameters.ParameterList) ImageStatistics(ij.process.ImageStatistics) Map(java.util.Map) PluginRunner(qupath.lib.plugins.PluginRunner) Shape(java.awt.Shape) MeasurementListType(qupath.lib.measurements.MeasurementList.MeasurementListType) ColorTools(qupath.lib.common.ColorTools) BufferedImage(java.awt.image.BufferedImage) Collection(java.util.Collection) PathObjects(qupath.lib.objects.PathObjects) Collectors(java.util.stream.Collectors) EDM(ij.plugin.filter.EDM) StainVector(qupath.lib.color.StainVector) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathDetectionObject(qupath.lib.objects.PathDetectionObject) PathObject(qupath.lib.objects.PathObject) List(java.util.List) SimpleThresholding(qupath.imagej.processing.SimpleThresholding) ImagePlane(qupath.lib.regions.ImagePlane) AbstractInteractivePlugin(qupath.lib.plugins.AbstractInteractivePlugin) PathCellObject(qupath.lib.objects.PathCellObject) ColorTransformer(qupath.lib.color.ColorTransformer) PathClassTools(qupath.lib.objects.classes.PathClassTools) PolygonRoi(ij.gui.PolygonRoi) HashMap(java.util.HashMap) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) ArrayList(java.util.ArrayList) MeasurementList(qupath.lib.measurements.MeasurementList) ColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains) Graphics2D(java.awt.Graphics2D) MeasurementListFactory(qupath.lib.measurements.MeasurementListFactory) ImageData(qupath.lib.images.ImageData) RoiTools(qupath.lib.roi.RoiTools) Logger(org.slf4j.Logger) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) Calibration(ij.measure.Calibration) SimpleImage(qupath.lib.analysis.images.SimpleImage) ColorTransformMethod(qupath.lib.color.ColorTransformer.ColorTransformMethod) PathClass(qupath.lib.objects.classes.PathClass) IOException(java.io.IOException) TMACoreObject(qupath.lib.objects.TMACoreObject) PathObjectTools(qupath.lib.objects.PathObjectTools) ROI(qupath.lib.roi.interfaces.ROI) FloatProcessor(ij.process.FloatProcessor) PixelCalibration(qupath.lib.images.servers.PixelCalibration) MaximumFinder(ij.plugin.filter.MaximumFinder) SimpleImages(qupath.lib.analysis.images.SimpleImages) ByteProcessor(ij.process.ByteProcessor) Shape(java.awt.Shape) MeasurementList(qupath.lib.measurements.MeasurementList) MaximumFinder(ij.plugin.filter.MaximumFinder) ArrayList(java.util.ArrayList) DataBufferByte(java.awt.image.DataBufferByte) BufferedImage(java.awt.image.BufferedImage) EDM(ij.plugin.filter.EDM) PathClass(qupath.lib.objects.classes.PathClass) PolygonRoi(ij.gui.PolygonRoi) ImagePlane(qupath.lib.regions.ImagePlane) FloatProcessor(ij.process.FloatProcessor) PixelCalibration(qupath.lib.images.servers.PixelCalibration) Calibration(ij.measure.Calibration) PixelCalibration(qupath.lib.images.servers.PixelCalibration) ROI(qupath.lib.roi.interfaces.ROI) Graphics2D(java.awt.Graphics2D) PathObject(qupath.lib.objects.PathObject) ImageStatistics(ij.process.ImageStatistics) SimpleImage(qupath.lib.analysis.images.SimpleImage) RegionRequest(qupath.lib.regions.RegionRequest)

Aggregations

PolygonRoi (ij.gui.PolygonRoi)1 Wand (ij.gui.Wand)1 Calibration (ij.measure.Calibration)1 EDM (ij.plugin.filter.EDM)1 MaximumFinder (ij.plugin.filter.MaximumFinder)1 ByteProcessor (ij.process.ByteProcessor)1 FloatProcessor (ij.process.FloatProcessor)1 ImageProcessor (ij.process.ImageProcessor)1 ImageStatistics (ij.process.ImageStatistics)1 Color (java.awt.Color)1 Graphics2D (java.awt.Graphics2D)1 Shape (java.awt.Shape)1 BufferedImage (java.awt.image.BufferedImage)1 DataBufferByte (java.awt.image.DataBufferByte)1 IOException (java.io.IOException)1 ArrayList (java.util.ArrayList)1 Collection (java.util.Collection)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Map (java.util.Map)1