Search in sources :

Example 36 with PathClass

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

the class DensityMapDialog method buildAllObjectsPane.

private Pane buildAllObjectsPane(ObservableDensityMapBuilder params) {
    ComboBox<DensityMapObjects> comboObjectType = new ComboBox<>();
    comboObjectType.getItems().setAll(DensityMapObjects.values());
    comboObjectType.getSelectionModel().select(DensityMapObjects.DETECTIONS);
    params.allObjectTypes.bind(comboObjectType.getSelectionModel().selectedItemProperty());
    ComboBox<PathClass> comboAllObjects = new ComboBox<>(createObservablePathClassList(DensityMapUI.ANY_CLASS));
    comboAllObjects.setButtonCell(GuiTools.createCustomListCell(p -> classificationText(p)));
    comboAllObjects.setCellFactory(c -> GuiTools.createCustomListCell(p -> classificationText(p)));
    params.allObjectClass.bind(comboAllObjects.getSelectionModel().selectedItemProperty());
    comboAllObjects.getSelectionModel().selectFirst();
    ComboBox<PathClass> comboPrimary = new ComboBox<>(createObservablePathClassList(DensityMapUI.ANY_CLASS, DensityMapUI.ANY_POSITIVE_CLASS));
    comboPrimary.setButtonCell(GuiTools.createCustomListCell(p -> classificationText(p)));
    comboPrimary.setCellFactory(c -> GuiTools.createCustomListCell(p -> classificationText(p)));
    params.densityObjectClass.bind(comboPrimary.getSelectionModel().selectedItemProperty());
    comboPrimary.getSelectionModel().selectFirst();
    ComboBox<DensityMapType> comboDensityType = new ComboBox<>();
    comboDensityType.getItems().setAll(DensityMapType.values());
    comboDensityType.getSelectionModel().select(DensityMapType.SUM);
    params.densityType.bind(comboDensityType.getSelectionModel().selectedItemProperty());
    var pane = createGridPane();
    int row = 0;
    var labelObjects = createTitleLabel("Choose all objects to include");
    PaneTools.addGridRow(pane, row++, 0, null, labelObjects, labelObjects, labelObjects);
    PaneTools.addGridRow(pane, row++, 0, "Select objects used to generate the density map.\n" + "Use 'All detections' to include all detection objects (including cells and tiles).\n" + "Use 'All cells' to include cell objects only.\n" + "Use 'Point annotations' to use annotated points rather than detections.", new Label("Object type"), comboObjectType, comboObjectType);
    PaneTools.addGridRow(pane, row++, 0, "Select object classifications to include.\n" + "Use this to filter out detections that should not contribute to the density map at all.\n" + "For example, this can be used to selectively consider tumor cells and ignore everything else.\n" + "If used in combination with 'Secondary class' and 'Density type: Objects %', the 'Secondary class' defines the numerator and the 'Main class' defines the denominator.", new Label("Main class"), comboAllObjects, comboAllObjects);
    var labelDensities = createTitleLabel("Define density map");
    PaneTools.addGridRow(pane, row++, 0, null, labelDensities);
    PaneTools.addGridRow(pane, row++, 0, "Calculate the density of objects containing a specified classification.\n" + "If used in combination with 'Main class' and 'Density type: Objects %', the 'Secondary class' defines the numerator and the 'Main class' defines the denominator.\n" + "For example, choose 'Main class: Tumor', 'Secondary class: Positive' and 'Density type: Objects %' to define density as the proportion of tumor cells that are positive.", new Label("Secondary class"), comboPrimary, comboPrimary);
    PaneTools.addGridRow(pane, row++, 0, "Select method of normalizing densities.\n" + "Choose whether to show raw counts, or normalize densities by area or the number of objects locally.\n" + "This can be used to distinguish between the total number of objects in an area with a given classification, " + "and the proportion of objects within the area with that classification.\n" + "Gaussian weighting gives a smoother result, but it can be harder to interpret.", new Label("Density type"), comboDensityType, comboDensityType);
    var sliderRadius = new Slider(0, 1000, params.radius.get());
    sliderRadius.valueProperty().bindBidirectional(params.radius);
    initializeSliderSnapping(sliderRadius, 50, 1, 0.1);
    var tfRadius = createTextField();
    boolean expandSliderLimits = true;
    GuiTools.bindSliderAndTextField(sliderRadius, tfRadius, expandSliderLimits, 2);
    GuiTools.installRangePrompt(sliderRadius);
    PaneTools.addGridRow(pane, row++, 0, "Select smoothing radius used to calculate densities.\n" + "This is defined in calibrated pixel units (e.g. " + GeneralTools.micrometerSymbol() + " if available).", new Label("Density radius"), sliderRadius, tfRadius);
    PaneTools.setToExpandGridPaneWidth(comboObjectType, comboPrimary, comboAllObjects, comboDensityType, sliderRadius);
    return pane;
}
Also used : Change(javafx.collections.ListChangeListener.Change) ImageServer(qupath.lib.images.servers.ImageServer) LoggerFactory(org.slf4j.LoggerFactory) PixelClassificationOverlay(qupath.lib.gui.viewer.overlays.PixelClassificationOverlay) MinMax(qupath.process.gui.commands.density.DensityMapUI.MinMax) Future(java.util.concurrent.Future) ComboBox(javafx.scene.control.ComboBox) ColorModelRenderer(qupath.lib.gui.images.stores.ColorModelRenderer) PathObjectHierarchyEvent(qupath.lib.objects.hierarchy.events.PathObjectHierarchyEvent) Gson(com.google.gson.Gson) Map(java.util.Map) QuPathGUI(qupath.lib.gui.QuPathGUI) Pane(javafx.scene.layout.Pane) Shape(java.awt.Shape) TextField(javafx.scene.control.TextField) BufferedImage(java.awt.image.BufferedImage) ColorMap(qupath.lib.color.ColorMaps.ColorMap) DensityMapBuilder(qupath.lib.analysis.heatmaps.DensityMaps.DensityMapBuilder) Set(java.util.Set) Spinner(javafx.scene.control.Spinner) QuPathViewerListener(qupath.lib.gui.viewer.QuPathViewerListener) Executors(java.util.concurrent.Executors) PathObject(qupath.lib.objects.PathObject) Objects(java.util.Objects) Platform(javafx.application.Platform) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) ColorModels(qupath.lib.analysis.heatmaps.ColorModels) ToggleButton(javafx.scene.control.ToggleButton) GuiTools(qupath.lib.gui.tools.GuiTools) DensityMaps(qupath.lib.analysis.heatmaps.DensityMaps) PathObjectPredicates(qupath.lib.objects.PathObjectPredicates) SimpleDoubleProperty(javafx.beans.property.SimpleDoubleProperty) ColorModelBuilder(qupath.lib.analysis.heatmaps.ColorModels.ColorModelBuilder) ThreadTools(qupath.lib.common.ThreadTools) ObservableList(javafx.collections.ObservableList) ColorMaps(qupath.lib.color.ColorMaps) Scene(javafx.scene.Scene) ObjectExpression(javafx.beans.binding.ObjectExpression) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) GsonTools(qupath.lib.io.GsonTools) FXCollections(javafx.collections.FXCollections) DensityMapObjects(qupath.process.gui.commands.density.DensityMapUI.DensityMapObjects) HashMap(java.util.HashMap) DoubleProperty(javafx.beans.property.DoubleProperty) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) ImageRenderer(qupath.lib.gui.images.stores.ImageRenderer) Bindings(javafx.beans.binding.Bindings) HashSet(java.util.HashSet) Insets(javafx.geometry.Insets) Slider(javafx.scene.control.Slider) DensityMapType(qupath.lib.analysis.heatmaps.DensityMaps.DensityMapType) ImageInterpolation(qupath.lib.gui.viewer.ImageInterpolation) LinkedHashSet(java.util.LinkedHashSet) ExecutorService(java.util.concurrent.ExecutorService) GridPane(javafx.scene.layout.GridPane) ImageData(qupath.lib.images.ImageData) ObjectProperty(javafx.beans.property.ObjectProperty) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) TitledPane(javafx.scene.control.TitledPane) GeneralTools(qupath.lib.common.GeneralTools) PathClass(qupath.lib.objects.classes.PathClass) IOException(java.io.IOException) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) ColorModel(java.awt.image.ColorModel) Stage(javafx.stage.Stage) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) ObservableValue(javafx.beans.value.ObservableValue) PathObjectPredicate(qupath.lib.objects.PathObjectPredicates.PathObjectPredicate) PathObjectHierarchyListener(qupath.lib.objects.hierarchy.events.PathObjectHierarchyListener) Collections(java.util.Collections) PaneTools(qupath.lib.gui.tools.PaneTools) Slider(javafx.scene.control.Slider) ComboBox(javafx.scene.control.ComboBox) Label(javafx.scene.control.Label) PathClass(qupath.lib.objects.classes.PathClass) DensityMapType(qupath.lib.analysis.heatmaps.DensityMaps.DensityMapType) DensityMapObjects(qupath.process.gui.commands.density.DensityMapUI.DensityMapObjects)

Example 37 with PathClass

use of qupath.lib.objects.classes.PathClass 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)

Example 38 with PathClass

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

the class TileExporter method writeTiles.

/**
 * Export the image tiles to the specified directory.
 * @param dirOutput full path to the export directory
 * @throws IOException if an error occurs during export
 */
public void writeTiles(String dirOutput) throws IOException {
    if (!new File(dirOutput).isDirectory())
        throw new IOException("Output directory " + dirOutput + " does not exist!");
    // Make sure we have any required subdirectories
    if (imageSubDir != null)
        new File(dirOutput, imageSubDir).mkdirs();
    if (labelSubDir != null)
        new File(dirOutput, labelSubDir).mkdirs();
    if (serverLabeled != null) {
        if (extLabeled == null)
            extLabeled = serverLabeled.getMetadata().getChannelType() == ChannelType.CLASSIFICATION ? ".png" : ".tif";
    }
    // Work out which RegionRequests to use
    Collection<RegionRequestWrapper> requests = createRequests();
    if (requests.isEmpty()) {
        logger.warn("No regions to export!");
        return;
    }
    if (requests.size() > 1)
        logger.info("Exporting {} tiles", requests.size());
    var pool = Executors.newFixedThreadPool(ThreadTools.getParallelism(), ThreadTools.createThreadFactory("tile-exporter", true));
    String imageName = GeneralTools.stripInvalidFilenameChars(GeneralTools.getNameWithoutExtension(server.getMetadata().getName()));
    // Create something we can input as the image path for export
    String imagePathName = null;
    var uris = server.getURIs();
    if (uris.isEmpty())
        imagePathName = imageName;
    else if (uris.size() == 1)
        imagePathName = uris.iterator().next().toString();
    else
        imagePathName = "[" + uris.stream().map(u -> u.toString()).collect(Collectors.joining("|")) + "]";
    // // If we have pixel calibration information, use it in the export
    // PixelCalibration pixelSize = server.getPixelCalibration();
    // if (pixelSize.equals(PixelCalibration.getDefaultInstance()))
    // pixelSize = null;
    // else
    // pixelSize = pixelSize.createScaledInstance(downsample, downsample);
    int tileWidth = this.tileWidth;
    int tileHeight = this.tileHeight;
    // int tileWidth = includePartialTiles || (parentObjects != null && useParentRoiBounds) ? -1 : this.tileWidth;
    // int tileHeight = includePartialTiles || (parentObjects != null && useParentRoiBounds) ? -1 : this.tileHeight;
    // Maintain a record of what we exported
    List<TileExportEntry> exportImages = new ArrayList<>();
    for (var r : requests) {
        boolean ensureSize = !r.partialTile;
        String baseName = String.format("%s [%s]", imageName, getRegionString(r.request));
        String exportImageName = baseName + ext;
        if (imageSubDir != null)
            exportImageName = Paths.get(imageSubDir, exportImageName).toString();
        String pathImageOutput = Paths.get(dirOutput, exportImageName).toAbsolutePath().toString();
        ExportTask taskImage = new ExportTask(server, r.request, pathImageOutput, tileWidth, tileHeight, ensureSize);
        String exportLabelName = null;
        ExportTask taskLabels = null;
        if (serverLabeled != null) {
            String labelName = baseName;
            if ((labelSubDir == null || labelSubDir.equals(imageSubDir)) && labelId == null && ext.equals(extLabeled)) {
                labelName = baseName + "-labelled";
            } else if (labelId != null)
                labelName = baseName + labelId;
            exportLabelName = labelName + extLabeled;
            if (labelSubDir != null)
                exportLabelName = Paths.get(labelSubDir, exportLabelName).toString();
            String pathLabelsOutput = Paths.get(dirOutput, exportLabelName).toAbsolutePath().toString();
            taskLabels = new ExportTask(serverLabeled, r.request.updatePath(serverLabeled.getPath()), pathLabelsOutput, tileWidth, tileHeight, ensureSize);
        }
        exportImages.add(new TileExportEntry(r.request.updatePath(imagePathName), // pixelSize,
        exportImageName, exportLabelName));
        if (taskImage != null)
            pool.submit(taskImage);
        if (taskLabels != null) {
            pool.submit(taskLabels);
        }
    }
    // Write JSON, if we need to
    if (exportJson) {
        var gson = GsonTools.getInstance(true).newBuilder().disableHtmlEscaping().create();
        var data = new TileExportData(dirOutput, exportImages);
        if (serverLabeled instanceof LabeledImageServer) {
            var labels = ((LabeledImageServer) serverLabeled).getLabels();
            var boundaryLabels = ((LabeledImageServer) serverLabeled).getBoundaryLabels();
            List<TileExportLabel> labelList = new ArrayList<>();
            Set<PathClass> existingLabels = new HashSet<>();
            for (var entry : labels.entrySet()) {
                var pathClass = entry.getKey();
                var label = new TileExportLabel(pathClass.toString(), entry.getValue(), boundaryLabels.getOrDefault(pathClass, null));
                labelList.add(label);
            }
            for (var entry : boundaryLabels.entrySet()) {
                var pathClass = entry.getKey();
                if (!existingLabels.contains(pathClass)) {
                    var label = new TileExportLabel(pathClass.toString(), null, boundaryLabels.getOrDefault(pathClass, null));
                    labelList.add(label);
                }
            }
            data.labels = labelList;
        }
        var pathJson = Paths.get(dirOutput, imageName + "-tiles.json");
        if (Files.exists(pathJson)) {
            logger.warn("Overwriting existing JSON file {}", pathJson);
        }
        try (var writer = Files.newBufferedWriter(pathJson, StandardCharsets.UTF_8)) {
            gson.toJson(data, writer);
        }
    }
    pool.shutdown();
    try {
        pool.awaitTermination(24, TimeUnit.HOURS);
    } catch (InterruptedException e) {
        pool.shutdownNow();
        logger.error("Tile export interrupted: {}", e);
        logger.error("", e);
    }
}
Also used : ArrayList(java.util.ArrayList) IOException(java.io.IOException) LabeledImageServer(qupath.lib.images.servers.LabeledImageServer) PathClass(qupath.lib.objects.classes.PathClass) File(java.io.File) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Example 39 with PathClass

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

the class LabeledImageServer method createIndexedColorTile.

private BufferedImage createIndexedColorTile(TileRequest tileRequest, Collection<PathObject> pathObjects) {
    RegionRequest request = tileRequest.getRegionRequest();
    double downsampleFactor = request.getDownsample();
    // Fill in the background color
    int width = tileRequest.getTileWidth();
    int height = tileRequest.getTileHeight();
    boolean doRGB = maxLabel > 255;
    // If we have > 255 labels, we can only use Graphics2D if we pretend to have an RGB image
    BufferedImage img = doRGB ? new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) : new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
    WritableRaster raster = img.getRaster();
    Graphics2D g2d = img.createGraphics();
    int bgLabel = params.labels.get(params.unannotatedClass);
    Color color = getColorForLabel(bgLabel, doRGB);
    g2d.setColor(color);
    g2d.fillRect(0, 0, width, height);
    if (!pathObjects.isEmpty()) {
        g2d.setClip(0, 0, width, height);
        double scale = 1.0 / downsampleFactor;
        g2d.scale(scale, scale);
        g2d.translate(-request.getX(), -request.getY());
        BasicStroke stroke = new BasicStroke((float) (params.lineThickness * tileRequest.getDownsample()));
        g2d.setStroke(stroke);
        // We want to order consistently to avoid confusing overlaps
        for (var entry : params.labels.entrySet()) {
            var pathClass = getPathClass(entry.getKey());
            int c = entry.getValue();
            color = getColorForLabel(c, doRGB);
            List<PathObject> toDraw;
            if (instanceClassMapInverse != null) {
                var temp = instanceClassMapInverse.get(c);
                if (temp == null)
                    continue;
                toDraw = Collections.singletonList(temp);
            } else
                toDraw = pathObjects.stream().filter(p -> getPathClass(p) == pathClass).collect(Collectors.toList());
            for (var pathObject : toDraw) {
                var roi = params.roiFunction.apply(pathObject);
                g2d.setColor(color);
                if (roi.isArea())
                    g2d.fill(roi.getShape());
                else if (roi.isLine())
                    g2d.draw(roi.getShape());
                else if (roi.isPoint()) {
                    for (var p : roi.getAllPoints()) {
                        int x = (int) ((p.getX() - request.getX()) / downsampleFactor);
                        int y = (int) ((p.getY() - request.getY()) / downsampleFactor);
                        if (x >= 0 && x < width && y >= 0 && y < height) {
                            if (doRGB)
                                img.setRGB(x, y, color.getRGB());
                            else
                                raster.setSample(x, y, 0, c);
                        }
                    }
                }
            }
        }
        for (var entry : params.boundaryLabels.entrySet()) {
            int c = entry.getValue();
            color = getColorForLabel(c, doRGB);
            for (var pathObject : pathObjects) {
                // if (pathObject.getPathClass() == pathClass) {
                var pathClass = getPathClass(pathObject);
                if (params.labels.containsKey(pathClass)) {
                    // && !PathClassTools.isIgnoredClass(pathObject.getPathClass())) {
                    var roi = params.roiFunction.apply(pathObject);
                    if (roi.isArea()) {
                        g2d.setColor(color);
                        g2d.draw(roi.getShape());
                    }
                }
            }
        }
    }
    g2d.dispose();
    if (doRGB) {
        // Resort to RGB if we have to
        if (maxLabel >= 65536)
            return img;
        // Convert to unsigned short if we can
        var shortRaster = WritableRaster.createBandedRaster(DataBuffer.TYPE_USHORT, width, height, 1, null);
        int[] samples = img.getRGB(0, 0, width, height, null, 0, width);
        shortRaster.setSamples(0, 0, width, height, 0, samples);
        // System.err.println("Before: " + Arrays.stream(samples).summaryStatistics());
        raster = shortRaster;
    // samples = raster.getSamples(0, 0, width, height, 0, (int[])null);
    // System.err.println("After: " + Arrays.stream(samples).summaryStatistics());
    }
    return new BufferedImage((IndexColorModel) colorModel, raster, false, null);
}
Also used : BasicStroke(java.awt.BasicStroke) Color(java.awt.Color) LoggerFactory(org.slf4j.LoggerFactory) HashMap(java.util.HashMap) Random(java.util.Random) PathObjectFilter(qupath.lib.objects.PathObjectFilter) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) DataBufferByte(java.awt.image.DataBufferByte) Function(java.util.function.Function) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) ChannelType(qupath.lib.images.servers.ImageServerMetadata.ChannelType) BandedSampleModel(java.awt.image.BandedSampleModel) ImageRegion(qupath.lib.regions.ImageRegion) Graphics2D(java.awt.Graphics2D) Map(java.util.Map) IndexColorModel(java.awt.image.IndexColorModel) ColorModelFactory(qupath.lib.color.ColorModelFactory) URI(java.net.URI) ColorToolsAwt(qupath.lib.color.ColorToolsAwt) ImageData(qupath.lib.images.ImageData) PathClassifierTools(qupath.lib.classifiers.PathClassifierTools) RoiTools(qupath.lib.roi.RoiTools) Logger(org.slf4j.Logger) ColorTools(qupath.lib.common.ColorTools) BufferedImage(java.awt.image.BufferedImage) RegionRequest(qupath.lib.regions.RegionRequest) Predicate(java.util.function.Predicate) Collection(java.util.Collection) PathClass(qupath.lib.objects.classes.PathClass) IOException(java.io.IOException) UUID(java.util.UUID) ServerBuilder(qupath.lib.images.servers.ImageServerBuilder.ServerBuilder) Collectors(java.util.stream.Collectors) PathObjectTools(qupath.lib.objects.PathObjectTools) PathObject(qupath.lib.objects.PathObject) ROI(qupath.lib.roi.interfaces.ROI) List(java.util.List) ColorModel(java.awt.image.ColorModel) TreeMap(java.util.TreeMap) BasicStroke(java.awt.BasicStroke) WritableRaster(java.awt.image.WritableRaster) Collections(java.util.Collections) DataBuffer(java.awt.image.DataBuffer) Color(java.awt.Color) BufferedImage(java.awt.image.BufferedImage) Graphics2D(java.awt.Graphics2D) PathObject(qupath.lib.objects.PathObject) WritableRaster(java.awt.image.WritableRaster) RegionRequest(qupath.lib.regions.RegionRequest)

Example 40 with PathClass

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

the class DefaultPathObjectComparator method compare.

@Override
public int compare(PathObject o1, PathObject o2) {
    Objects.nonNull(o1);
    Objects.nonNull(o2);
    // Quick check...
    if (o1 == o2)
        return 0;
    // // Handle nulls
    // if (o1 == null) {
    // if (o2 == null)
    // return 0;
    // return 1;
    // } else if (o2 == null)
    // return -1;
    // Handle class order
    int temp = -Boolean.compare(o1.isRootObject(), o2.isRootObject());
    if (temp != 0)
        return temp;
    temp = -Boolean.compare(o1.isTMACore(), o2.isTMACore());
    if (temp != 0)
        return temp;
    temp = -Boolean.compare(o1.isAnnotation(), o2.isAnnotation());
    if (temp != 0)
        return temp;
    temp = -Boolean.compare(o1.isDetection(), o2.isDetection());
    if (temp != 0)
        return temp;
    // Try object class again
    temp = o1.getClass().getName().compareTo(o2.getClass().getName());
    if (temp != 0)
        return temp;
    // Handle ROI location
    temp = DefaultROIComparator.getInstance().compare(o1.getROI(), o2.getROI());
    if (temp != 0)
        return temp;
    // Try classifications
    PathClass pc1 = o1.getPathClass();
    PathClass pc2 = o2.getPathClass();
    if (pc1 != null && pc2 != null)
        return pc1.compareTo(pc2);
    if (pc1 == null)
        return -1;
    if (pc2 != null)
        return 1;
    // Shouldn't end up here much...
    return 0;
}
Also used : PathClass(qupath.lib.objects.classes.PathClass)

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