Search in sources :

Example 6 with PixelCalibration

use of qupath.lib.images.servers.PixelCalibration in project qupath by qupath.

the class PathImagePlus method getPixelCalibration.

@Override
public PixelCalibration getPixelCalibration() {
    if (calibration == null) {
        var builder = new PixelCalibration.Builder();
        Calibration cal = getImage().getCalibration();
        if (isMicrons(cal.getXUnit()) && isMicrons(cal.getYUnit())) {
            builder.pixelSizeMicrons(cal.pixelWidth, cal.pixelHeight);
        }
        if (isMicrons(cal.getZUnit())) {
            builder.zSpacingMicrons(cal.pixelDepth);
        }
        calibration = builder.build();
    }
    return calibration;
}
Also used : Calibration(ij.measure.Calibration) PixelCalibration(qupath.lib.images.servers.PixelCalibration)

Example 7 with PixelCalibration

use of qupath.lib.images.servers.PixelCalibration in project qupath by qupath.

the class HaralickFeaturesPlugin method getPreferredTileSizePixels.

static ImmutableDimension getPreferredTileSizePixels(final ImageServer<BufferedImage> server, final ParameterList params) {
    // Determine tile size
    int tileWidth, tileHeight;
    PixelCalibration cal = server.getPixelCalibration();
    if (cal.hasPixelSizeMicrons()) {
        double tileSize = params.getDoubleParameterValue("tileSizeMicrons");
        tileWidth = (int) (tileSize / cal.getPixelWidthMicrons() + .5);
        tileHeight = (int) (tileSize / cal.getPixelHeightMicrons() + .5);
    } else {
        tileWidth = (int) (params.getDoubleParameterValue("tileSizePx") + .5);
        tileHeight = tileWidth;
    }
    return ImmutableDimension.getInstance(tileWidth, tileHeight);
}
Also used : PixelCalibration(qupath.lib.images.servers.PixelCalibration)

Example 8 with PixelCalibration

use of qupath.lib.images.servers.PixelCalibration in project qupath by qupath.

the class ObjectMeasurements method addShapeMeasurements.

/**
 * Add shape measurements for multiple objects. If any of these objects is a cell, measurements will be made for both the
 * nucleus and cell boundary where possible.
 *
 * @param pathObjects the objects for which measurements should be added
 * @param cal pixel calibration, used to determine units and scaling
 * @param features specific features to add; if empty, all available shape features will be added
 */
public static void addShapeMeasurements(Collection<? extends PathObject> pathObjects, PixelCalibration cal, ShapeFeatures... features) {
    PixelCalibration calibration = cal == null || !cal.unitsMatch2D() ? PixelCalibration.getDefaultInstance() : cal;
    Collection<ShapeFeatures> featureCollection = features.length == 0 ? ALL_SHAPE_FEATURES : Arrays.asList(features);
    pathObjects.parallelStream().filter(p -> p.hasROI()).forEach(pathObject -> {
        if (pathObject instanceof PathCellObject) {
            addCellShapeMeasurements((PathCellObject) pathObject, calibration, featureCollection);
        } else {
            try (var ml = pathObject.getMeasurementList()) {
                addShapeMeasurements(ml, pathObject.getROI(), calibration, "", featureCollection);
            }
        }
    });
}
Also used : Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) ByteProcessor(ij.process.ByteProcessor) Length(org.locationtech.jts.algorithm.Length) MinimumBoundingCircle(org.locationtech.jts.algorithm.MinimumBoundingCircle) ImageProcessor(ij.process.ImageProcessor) IJTools(qupath.imagej.tools.IJTools) LoggerFactory(org.slf4j.LoggerFactory) StatisticalSummary(org.apache.commons.math3.stat.descriptive.StatisticalSummary) PixelImageIJ(qupath.imagej.tools.PixelImageIJ) MeasurementList(qupath.lib.measurements.MeasurementList) EllipseROI(qupath.lib.roi.EllipseROI) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) AffineTransformation(org.locationtech.jts.geom.util.AffineTransformation) LinkedHashSet(java.util.LinkedHashSet) ColorProcessor(ij.process.ColorProcessor) Logger(org.slf4j.Logger) BufferedImage(java.awt.image.BufferedImage) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) SimpleImage(qupath.lib.analysis.images.SimpleImage) Collection(java.util.Collection) Set(java.util.Set) IOException(java.io.IOException) Polygonal(org.locationtech.jts.geom.Polygonal) Area(org.locationtech.jts.algorithm.Area) Lineal(org.locationtech.jts.geom.Lineal) PathObject(qupath.lib.objects.PathObject) ROI(qupath.lib.roi.interfaces.ROI) FloatProcessor(ij.process.FloatProcessor) DescriptiveStatistics(org.apache.commons.math3.stat.descriptive.DescriptiveStatistics) PixelCalibration(qupath.lib.images.servers.PixelCalibration) MinimumDiameter(org.locationtech.jts.algorithm.MinimumDiameter) Polygon(org.locationtech.jts.geom.Polygon) Geometry(org.locationtech.jts.geom.Geometry) PathCellObject(qupath.lib.objects.PathCellObject) Collections(java.util.Collections) PixelCalibration(qupath.lib.images.servers.PixelCalibration) PathCellObject(qupath.lib.objects.PathCellObject)

Example 9 with PixelCalibration

use of qupath.lib.images.servers.PixelCalibration in project qupath by qupath.

the class IntensityFeaturesPlugin method processObject.

static boolean processObject(final PathObject pathObject, final ParameterList params, final ImageData<BufferedImage> imageData) throws IOException {
    // Determine amount to downsample
    var server = imageData.getServer();
    var stains = imageData.getColorDeconvolutionStains();
    PixelCalibration cal = server.getPixelCalibration();
    double downsample = calculateDownsample(cal, params);
    if (downsample <= 0) {
        logger.warn("Effective downsample must be > 0 (requested value {})", downsample);
    }
    // Determine region shape
    RegionType regionType = (RegionType) params.getChoiceParameterValue("region");
    // Try to get ROI
    boolean useROI = regionType == RegionType.ROI || regionType == RegionType.NUCLEUS;
    ROI roi = null;
    if (regionType == RegionType.NUCLEUS) {
        if (pathObject instanceof PathCellObject)
            roi = ((PathCellObject) pathObject).getNucleusROI();
    } else
        roi = pathObject.getROI();
    // pathROI = ((PathCellObject)pathObject).getNucleusROI();
    if (roi == null)
        return false;
    // Create a map - this is useful for occasions when tiling is needed
    Map<FeatureColorTransform, List<FeatureComputer>> map = new LinkedHashMap<>();
    if (server.isRGB()) {
        for (FeatureColorTransform transform : FeatureColorTransformEnum.values()) {
            List<FeatureComputer> list = new ArrayList<>();
            map.put(transform, list);
            for (FeatureComputerBuilder builder : builders) {
                list.add(builder.build());
            }
        }
    } else {
        for (FeatureColorTransform transform : getBasicChannelTransforms(server.nChannels())) {
            List<FeatureComputer> list = new ArrayList<>();
            map.put(transform, list);
            for (FeatureComputerBuilder builder : builders) {
                list.add(builder.build());
            }
        }
    }
    String prefix = getDiameterString(server, params);
    // Create tiled ROIs, if required
    ImmutableDimension sizePreferred = ImmutableDimension.getInstance((int) (2000 * downsample), (int) (2000 * downsample));
    // ImmutableDimension sizePreferred = new ImmutableDimension((int)(200*downsample), (int)(200*downsample));
    Collection<? extends ROI> rois = RoiTools.computeTiledROIs(roi, sizePreferred, sizePreferred, false, 0);
    if (rois.size() > 1)
        logger.info("Splitting {} into {} tiles for intensity measurements", roi, rois.size());
    for (ROI pathROI : rois) {
        if (Thread.currentThread().isInterrupted()) {
            logger.warn("Measurement skipped - thread interrupted!");
            return false;
        }
        // Get bounds
        RegionRequest region;
        if (useROI) {
            region = RegionRequest.createInstance(server.getPath(), downsample, pathROI);
        } else {
            ImmutableDimension size = getPreferredTileSizePixels(server, params);
            // 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());
            // Try to align with pixel boundaries according to the downsample being used - otherwise, interpolation can cause some strange, pattern artefacts
            int xStart = (int) (Math.round(pathROI.getCentroidX() / downsample) * downsample) - size.width / 2;
            int yStart = (int) (Math.round(pathROI.getCentroidY() / downsample) * downsample) - size.height / 2;
            int width = Math.min(server.getWidth(), xStart + size.width) - xStart;
            int height = Math.min(server.getHeight(), yStart + size.height) - yStart;
            region = RegionRequest.createInstance(server.getPath(), downsample, xStart, yStart, width, height, pathROI.getT(), pathROI.getZ());
        }
        // // Check image large enough to do *anything* of value
        // if (region.getWidth() / downsample < 1 || region.getHeight() / downsample < 1) {
        // logger.trace("Requested region is too small! {}", region);
        // return false;
        // }
        // System.out.println(bounds);
        // System.out.println("Size: " + size);
        BufferedImage img = server.readBufferedImage(region);
        if (img == null) {
            logger.error("Could not read image - unable to compute intensity features for {}", pathObject);
            return false;
        }
        // Create mask ROI if necessary
        // If we just have 1 pixel, we want to use it so that the mean/min/max measurements are valid (even if nothing else is)
        byte[] maskBytes = null;
        if (useROI && img.getWidth() * img.getHeight() > 1) {
            BufferedImage imgMask = BufferedImageTools.createROIMask(img.getWidth(), img.getHeight(), pathROI, region);
            maskBytes = ((DataBufferByte) imgMask.getRaster().getDataBuffer()).getData();
        }
        boolean isRGB = server.isRGB();
        List<FeatureColorTransform> transforms;
        if (isRGB)
            transforms = Arrays.asList(FeatureColorTransformEnum.values());
        else
            transforms = getBasicChannelTransforms(server.nChannels());
        int w = img.getWidth();
        int h = img.getHeight();
        int[] rgbBuffer = isRGB ? img.getRGB(0, 0, w, h, null, 0, w) : null;
        float[] pixels = null;
        for (FeatureColorTransform transform : transforms) {
            // Check if the color transform is requested
            if (params.containsKey(transform.getKey()) && Boolean.TRUE.equals(params.getBooleanParameterValue(transform.getKey()))) {
                // Transform the pixels
                pixels = transform.getTransformedPixels(img, rgbBuffer, stains, pixels);
                // Create the simple image
                SimpleModifiableImage pixelImage = SimpleImages.createFloatImage(pixels, w, h);
                // Apply any arbitrary mask
                if (maskBytes != null) {
                    for (int i = 0; i < pixels.length; i++) {
                        if (maskBytes[i] == (byte) 0)
                            pixelImage.setValue(i % w, i / w, Float.NaN);
                    }
                } else if (regionType == RegionType.CIRCLE) {
                    // Apply circular tile mask
                    double cx = (w - 1) / 2;
                    double cy = (h - 1) / 2;
                    double radius = Math.max(w, h) * .5;
                    double distThreshold = radius * radius;
                    for (int y = 0; y < h; y++) {
                        for (int x = 0; x < w; x++) {
                            if ((cx - x) * (cx - x) + (cy - y) * (cy - y) > distThreshold)
                                pixelImage.setValue(x, y, Float.NaN);
                        }
                    }
                }
                // Do the computations
                for (FeatureComputer computer : map.get(transform)) {
                    computer.updateFeatures(pixelImage, transform, params);
                }
            }
        }
    }
    // Add measurements to the parent object
    for (Entry<FeatureColorTransform, List<FeatureComputer>> entry : map.entrySet()) {
        String name = prefix + ": " + entry.getKey().getName(imageData, false) + ":";
        for (FeatureComputer computer : entry.getValue()) computer.addMeasurements(pathObject, name, params);
    }
    pathObject.getMeasurementList().close();
    // Lock any measurements that require it
    if (pathObject instanceof PathAnnotationObject)
        ((PathAnnotationObject) pathObject).setLocked(true);
    else if (pathObject instanceof TMACoreObject)
        ((TMACoreObject) pathObject).setLocked(true);
    return true;
}
Also used : ArrayList(java.util.ArrayList) BufferedImage(java.awt.image.BufferedImage) LinkedHashMap(java.util.LinkedHashMap) HaralickFeatureComputer(qupath.lib.analysis.features.HaralickFeatureComputer) SimpleModifiableImage(qupath.lib.analysis.images.SimpleModifiableImage) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) ArrayList(java.util.ArrayList) MeasurementList(qupath.lib.measurements.MeasurementList) ParameterList(qupath.lib.plugins.parameters.ParameterList) List(java.util.List) TMACoreObject(qupath.lib.objects.TMACoreObject) PixelCalibration(qupath.lib.images.servers.PixelCalibration) ROI(qupath.lib.roi.interfaces.ROI) ImmutableDimension(qupath.lib.geom.ImmutableDimension) RegionRequest(qupath.lib.regions.RegionRequest) PathCellObject(qupath.lib.objects.PathCellObject)

Example 10 with PixelCalibration

use of qupath.lib.images.servers.PixelCalibration in project qupath by qupath.

the class IntensityFeaturesPlugin method addRunnableTasks.

@Override
protected void addRunnableTasks(final ImageData<BufferedImage> imageData, final PathObject parentObject, List<Runnable> tasks) {
    final ParameterList params = getParameterList(imageData);
    final ImageServer<BufferedImage> server = imageData.getServer();
    // Don't add any tasks if the downsample isn't value
    PixelCalibration cal = server.getPixelCalibration();
    double downsample = calculateDownsample(cal, params);
    if (downsample <= 0) {
        throw new IllegalArgumentException("Effective downsample must be > 0 (requested value " + GeneralTools.formatNumber(downsample, 1) + ")");
    }
    tasks.add(new IntensityFeatureRunnable(imageData, parentObject, params));
}
Also used : ParameterList(qupath.lib.plugins.parameters.ParameterList) PixelCalibration(qupath.lib.images.servers.PixelCalibration) BufferedImage(java.awt.image.BufferedImage)

Aggregations

PixelCalibration (qupath.lib.images.servers.PixelCalibration)31 BufferedImage (java.awt.image.BufferedImage)9 ArrayList (java.util.ArrayList)8 ROI (qupath.lib.roi.interfaces.ROI)8 RegionRequest (qupath.lib.regions.RegionRequest)7 MeasurementList (qupath.lib.measurements.MeasurementList)6 PathObject (qupath.lib.objects.PathObject)6 Calibration (ij.measure.Calibration)5 PathCellObject (qupath.lib.objects.PathCellObject)5 ParameterList (qupath.lib.plugins.parameters.ParameterList)5 List (java.util.List)4 Collection (java.util.Collection)3 HashMap (java.util.HashMap)3 Map (java.util.Map)3 ImagePlus (ij.ImagePlus)2 ByteProcessor (ij.process.ByteProcessor)2 FloatProcessor (ij.process.FloatProcessor)2 ImageProcessor (ij.process.ImageProcessor)2 Graphics2D (java.awt.Graphics2D)2 LinkedHashMap (java.util.LinkedHashMap)2