Search in sources :

Example 11 with ImageServer

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

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

the class PixelClassifierTools method createObjectsFromPixelClassifier.

/**
 * Create objects based upon an {@link ImageServer} that provides classification or probability output.
 *
 * @param server image server providing pixels from which objects should be created
 * @param labels classification labels; if null, these will be taken from ImageServer#getMetadata() and all non-ignored classifications will be used.
 * 		   Providing a map makes it possible to explicitly exclude some classifications.
 * @param roi region of interest in which objects should be created (optional; if null, the entire image is used)
 * @param creator function to create an object from a ROI (e.g. annotation or detection)
 * @param minArea minimum area for an object fragment to retain, in calibrated units based on the pixel calibration
 * @param minHoleArea minimum area for a hole to fill, in calibrated units based on the pixel calibration
 * @param doSplit if true, split connected regions into separate objects
 * @return the objects created within the ROI
 * @throws IOException
 */
public static Collection<PathObject> createObjectsFromPixelClassifier(ImageServer<BufferedImage> server, Map<Integer, PathClass> labels, ROI roi, Function<ROI, ? extends PathObject> creator, double minArea, double minHoleArea, boolean doSplit) throws IOException {
    // We need classification labels to do anything
    if (labels == null)
        labels = parseClassificationLabels(server.getMetadata().getClassificationLabels(), false);
    if (labels == null || labels.isEmpty())
        throw new IllegalArgumentException("Cannot create objects for server - no classification labels are available!");
    ChannelThreshold[] thresholds = labels.entrySet().stream().map(e -> ChannelThreshold.create(e.getKey())).toArray(ChannelThreshold[]::new);
    if (roi != null && !roi.isArea()) {
        logger.warn("Cannot create objects for non-area ROIs");
        return Collections.emptyList();
    }
    Geometry clipArea = roi == null ? null : roi.getGeometry();
    // Identify regions for selected ROI or entire image
    // This is a list because it might need to handle multiple z-slices or timepoints
    List<RegionRequest> regionRequests;
    if (roi != null) {
        var request = RegionRequest.createInstance(server.getPath(), server.getDownsampleForResolution(0), roi);
        regionRequests = Collections.singletonList(request);
    } else {
        regionRequests = RegionRequest.createAllRequests(server, server.getDownsampleForResolution(0));
    }
    double pixelArea = server.getPixelCalibration().getPixelWidth().doubleValue() * server.getPixelCalibration().getPixelHeight().doubleValue();
    double minAreaPixels = minArea / pixelArea;
    double minHoleAreaPixels = minHoleArea / pixelArea;
    // Create output array
    var pathObjects = new ArrayList<PathObject>();
    // Loop through region requests (usually 1, unless we have a z-stack or time series)
    for (RegionRequest regionRequest : regionRequests) {
        Map<Integer, Geometry> geometryMap = ContourTracing.traceGeometries(server, regionRequest, clipArea, thresholds);
        var labelMap = labels;
        pathObjects.addAll(geometryMap.entrySet().parallelStream().flatMap(e -> geometryToObjects(e.getValue(), creator, labelMap.get(e.getKey()), minAreaPixels, minHoleAreaPixels, doSplit, regionRequest.getPlane()).stream()).collect(Collectors.toList()));
    }
    pathObjects.sort(DefaultPathObjectComparator.getInstance());
    return pathObjects;
}
Also used : ImageServer(qupath.lib.images.servers.ImageServer) Arrays(java.util.Arrays) PathClassTools(qupath.lib.objects.classes.PathClassTools) LoggerFactory(org.slf4j.LoggerFactory) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ChannelThreshold(qupath.lib.analysis.images.ContourTracing.ChannelThreshold) Function(java.util.function.Function) ArrayList(java.util.ArrayList) ClassifierFunction(qupath.opencv.ml.pixel.PixelClassifiers.ClassifierFunction) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) ChannelType(qupath.lib.images.servers.ImageServerMetadata.ChannelType) Map(java.util.Map) Reclassifier(qupath.lib.objects.classes.Reclassifier) GeometryTools(qupath.lib.roi.GeometryTools) ImageData(qupath.lib.images.ImageData) Logger(org.slf4j.Logger) RegionRequest(qupath.lib.regions.RegionRequest) BufferedImage(java.awt.image.BufferedImage) PathObjects(qupath.lib.objects.PathObjects) Collection(java.util.Collection) PathClass(qupath.lib.objects.classes.PathClass) Set(java.util.Set) DefaultPathObjectComparator(qupath.lib.objects.DefaultPathObjectComparator) IOException(java.io.IOException) 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) PixelClassifier(qupath.lib.classifiers.pixel.PixelClassifier) PixelClassificationImageServer(qupath.lib.classifiers.pixel.PixelClassificationImageServer) ColorModel(java.awt.image.ColorModel) ContourTracing(qupath.lib.analysis.images.ContourTracing) ImagePlane(qupath.lib.regions.ImagePlane) Geometry(org.locationtech.jts.geom.Geometry) Comparator(java.util.Comparator) Collections(java.util.Collections) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) DataBuffer(java.awt.image.DataBuffer) Geometry(org.locationtech.jts.geom.Geometry) ArrayList(java.util.ArrayList) ChannelThreshold(qupath.lib.analysis.images.ContourTracing.ChannelThreshold) RegionRequest(qupath.lib.regions.RegionRequest)

Example 13 with ImageServer

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

the class PixelClassifierTools method classifyObjectsByCentroid.

/**
 * Apply classification from a server to a collection of objects.
 *
 * @param classifierServer an {@link ImageServer} with output type
 * @param pathObjects
 * @param preferNucleusROI
 */
public static void classifyObjectsByCentroid(ImageServer<BufferedImage> classifierServer, Collection<PathObject> pathObjects, boolean preferNucleusROI) {
    var labels = classifierServer.getMetadata().getClassificationLabels();
    var reclassifiers = pathObjects.parallelStream().map(p -> {
        try {
            var roi = PathObjectTools.getROI(p, preferNucleusROI);
            int x = (int) roi.getCentroidX();
            int y = (int) roi.getCentroidY();
            int ind = getClassification(classifierServer, x, y, roi.getZ(), roi.getT());
            return new Reclassifier(p, labels.getOrDefault(ind, null), false);
        } catch (Exception e) {
            return new Reclassifier(p, null, false);
        }
    }).collect(Collectors.toList());
    reclassifiers.parallelStream().forEach(r -> r.apply());
}
Also used : ImageServer(qupath.lib.images.servers.ImageServer) Arrays(java.util.Arrays) PathClassTools(qupath.lib.objects.classes.PathClassTools) LoggerFactory(org.slf4j.LoggerFactory) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ChannelThreshold(qupath.lib.analysis.images.ContourTracing.ChannelThreshold) Function(java.util.function.Function) ArrayList(java.util.ArrayList) ClassifierFunction(qupath.opencv.ml.pixel.PixelClassifiers.ClassifierFunction) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) ChannelType(qupath.lib.images.servers.ImageServerMetadata.ChannelType) Map(java.util.Map) Reclassifier(qupath.lib.objects.classes.Reclassifier) GeometryTools(qupath.lib.roi.GeometryTools) ImageData(qupath.lib.images.ImageData) Logger(org.slf4j.Logger) RegionRequest(qupath.lib.regions.RegionRequest) BufferedImage(java.awt.image.BufferedImage) PathObjects(qupath.lib.objects.PathObjects) Collection(java.util.Collection) PathClass(qupath.lib.objects.classes.PathClass) Set(java.util.Set) DefaultPathObjectComparator(qupath.lib.objects.DefaultPathObjectComparator) IOException(java.io.IOException) 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) PixelClassifier(qupath.lib.classifiers.pixel.PixelClassifier) PixelClassificationImageServer(qupath.lib.classifiers.pixel.PixelClassificationImageServer) ColorModel(java.awt.image.ColorModel) ContourTracing(qupath.lib.analysis.images.ContourTracing) ImagePlane(qupath.lib.regions.ImagePlane) Geometry(org.locationtech.jts.geom.Geometry) Comparator(java.util.Comparator) Collections(java.util.Collections) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) DataBuffer(java.awt.image.DataBuffer) Reclassifier(qupath.lib.objects.classes.Reclassifier) IOException(java.io.IOException)

Example 14 with ImageServer

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

the class ServerSelector method promptToSelectServer.

@SuppressWarnings("unchecked")
public ImageServer<BufferedImage> promptToSelectServer() {
    if (serverList.isEmpty()) {
        logger.warn("No series available!");
        return null;
    } else if (serverList.size() == 1) {
        logger.warn("Only one server available!");
        return serverList.get(0);
    }
    // Get thumbnails in separate thread
    ExecutorService executor = Executors.newSingleThreadExecutor(ThreadTools.createThreadFactory("thumbnail-loader", true));
    ListView<ImageServer<BufferedImage>> listSeries = new ListView<>();
    listSeries.setPrefWidth(480);
    listSeries.setMinHeight(100);
    // thumbnailBank is the map for storing thumbnails
    Map<String, BufferedImage> thumbnailBank = new HashMap<String, BufferedImage>();
    for (ImageServer<BufferedImage> server : serverList) {
        executor.submit(() -> {
            try {
                thumbnailBank.put(server.getMetadata().getName(), ProjectCommands.getThumbnailRGB(server));
                Platform.runLater(() -> listSeries.refresh());
            } catch (IOException e) {
                logger.warn("Error loading thumbnail: " + e.getLocalizedMessage(), e);
            }
        });
    }
    ;
    double thumbnailSize = 80;
    listSeries.setCellFactory(v -> new ImageAndNameListCell(thumbnailBank, thumbnailSize, thumbnailSize));
    listSeries.getItems().setAll(serverList);
    // Info table - Changes according to selected series
    String[] attributes = new String[] { "Full Path", "Server Type", "Width", "Height", "Pixel Width", "Pixel Height", "Pixel Type", "Number of Channels", "Number of Resolutions" };
    Integer[] indices = new Integer[9];
    for (int index = 0; index < 9; index++) indices[index] = index;
    ObservableList<Integer> indexList = FXCollections.observableArrayList(indices);
    TableView<Integer> tableInfo = new TableView<>();
    tableInfo.setMinHeight(200);
    tableInfo.setMinWidth(500);
    // First column (attribute names)
    TableColumn<Integer, String> attributeCol = new TableColumn<Integer, String>("Attribute");
    attributeCol.setMinWidth(242);
    attributeCol.setResizable(false);
    attributeCol.setCellValueFactory(cellData -> {
        return new ReadOnlyObjectWrapper<String>(attributes[cellData.getValue()]);
    });
    // Second column (attribute values)
    TableColumn<Integer, String> valueCol = new TableColumn<Integer, String>("Value");
    valueCol.setMinWidth(242);
    valueCol.setResizable(false);
    valueCol.setCellValueFactory(cellData -> {
        if (selectedSeries != null)
            return getSeriesQuickInfo(selectedSeries, cellData.getValue());
        else
            return null;
    });
    // Adding the values on hover over the info table
    tableInfo.setRowFactory(tableView -> {
        final TableRow<Integer> row = new TableRow<>();
        row.hoverProperty().addListener((observable) -> {
            final var element = row.getItem();
            if (row.isHover() && selectedSeries != null) {
                ObservableValue<String> value = getSeriesQuickInfo(selectedSeries, element);
                Tooltip tooltip = new Tooltip(value.getValue());
                Tooltip.install(row, tooltip);
            }
        });
        return row;
    });
    // Set items to info table
    tableInfo.setItems(indexList);
    tableInfo.getColumns().addAll(attributeCol, valueCol);
    // Pane structure
    BorderPane paneSelector = new BorderPane();
    BorderPane paneSeries = new BorderPane(listSeries);
    BorderPane paneInfo = new BorderPane(tableInfo);
    paneInfo.setMaxHeight(100);
    paneSelector.setCenter(paneSeries);
    paneSelector.setBottom(paneInfo);
    BorderPane pane = new BorderPane();
    pane.setCenter(paneSelector);
    Dialog<ButtonType> dialog = new Dialog<>();
    var qupath = QuPathGUI.getInstance();
    if (qupath != null)
        dialog.initOwner(qupath.getStage());
    dialog.setTitle("Open image");
    ButtonType typeImport = new ButtonType("Open", ButtonData.OK_DONE);
    dialog.getDialogPane().getButtonTypes().addAll(typeImport, ButtonType.CANCEL);
    dialog.getDialogPane().setContent(pane);
    listSeries.getSelectionModel().selectedItemProperty().addListener((obs, previousSelectedRow, selectedRow) -> {
        if (selectedRow != null) {
            selectedSeries = selectedRow;
            indexList.removeAll(indexList);
            indexList.addAll(indices);
        }
    });
    listSeries.setOnMouseClicked(new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent click) {
            ImageServer<BufferedImage> selectedItem = listSeries.getSelectionModel().getSelectedItem();
            if (click.getClickCount() == 2 && selectedItem != null) {
                Button okButton = (Button) dialog.getDialogPane().lookupButton(typeImport);
                okButton.fire();
            }
        }
    });
    Optional<ButtonType> result = dialog.showAndWait();
    var selectedToReturn = listSeries.getSelectionModel().getSelectedItem();
    try {
        executor.shutdownNow();
    } catch (Exception e) {
        logger.warn(e.getLocalizedMessage(), e);
    } finally {
        try {
            for (ImageServer<BufferedImage> server : serverList) {
                if (server != selectedToReturn)
                    server.close();
            }
        } catch (Exception e) {
            logger.debug(e.getLocalizedMessage(), e);
        }
        selectedSeries = null;
    }
    if (!result.isPresent() || result.get() != typeImport || result.get() == ButtonType.CANCEL)
        return null;
    return selectedToReturn;
}
Also used : BorderPane(javafx.scene.layout.BorderPane) HashMap(java.util.HashMap) BufferedImage(java.awt.image.BufferedImage) ListView(javafx.scene.control.ListView) Button(javafx.scene.control.Button) Dialog(javafx.scene.control.Dialog) ButtonType(javafx.scene.control.ButtonType) TableView(javafx.scene.control.TableView) ImageServer(qupath.lib.images.servers.ImageServer) MouseEvent(javafx.scene.input.MouseEvent) Tooltip(javafx.scene.control.Tooltip) IOException(java.io.IOException) TableColumn(javafx.scene.control.TableColumn) IOException(java.io.IOException) TableRow(javafx.scene.control.TableRow) ExecutorService(java.util.concurrent.ExecutorService) ReadOnlyObjectWrapper(javafx.beans.property.ReadOnlyObjectWrapper)

Example 15 with ImageServer

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

the class OMEPyramidWriterCommand method run.

@Override
public void run() {
    if (currentTask != null && !currentTask.isDone()) {
        if (!Dialogs.showConfirmDialog("OME Pyramid writer", "Do you want to stop the current export?"))
            // TODO: Delete exporting file?
            return;
        else {
            currentTask.cancel(true);
        }
    }
    QuPathViewer viewer = qupath.getViewer();
    int zPos = viewer.getZPosition();
    int tPos = viewer.getTPosition();
    ImageData<BufferedImage> imageData = viewer.getImageData();
    if (imageData == null) {
        Dialogs.showNoImageError("OME Pyramid writer");
        return;
    }
    ImageServer<BufferedImage> server = imageData.getServer();
    // Region
    PathObject selected = imageData.getHierarchy().getSelectionModel().getSelectedObject();
    ImageRegion region = null;
    int width, height;
    if (selected == null || !selected.hasROI() || !selected.getROI().isArea()) {
        width = server.getWidth();
        height = server.getHeight();
    } else {
        region = ImageRegion.createInstance(selected.getROI());
        width = region.getWidth();
        height = region.getHeight();
    }
    // Set compression - with a sanity check for validity, defaulting to another comparable method if necessary
    CompressionType compression = getDefaultPyramidCompression();
    List<String> compatibleCompression = Arrays.stream(CompressionType.values()).filter(c -> c.supportsImage(server)).map(c -> c.toFriendlyString()).collect(Collectors.toList());
    if (!compatibleCompression.contains(compression.toFriendlyString()))
        compression = CompressionType.DEFAULT;
    var params = new ParameterList().addChoiceParameter("compression", "Compression type", compression.toFriendlyString(), compatibleCompression).addIntParameter("scaledDownsample", "Pyramidal downsample", scaledDownsample.get(), "", 1, 8, "Amount to downsample each consecutive pyramidal level; use 1 to indicate the image should not be pyramidal").addIntParameter("tileSize", "Tile size", getDefaultTileSize(), "px", "Tile size for export (should be between 128 and 8192)").addBooleanParameter("parallelize", "Parallelize export", parallelizeTiling.get(), "Export image tiles in parallel - " + "this should be faster, best keep it on unless you encounter export problems").addBooleanParameter("allZ", "All z-slices", allZ.get(), "Include all z-slices in the stack").addBooleanParameter("allT", "All timepoints", allT.get(), "Include all timepoints in the time-series");
    boolean singleTile = server.getTileRequestManager().getTileRequests(RegionRequest.createInstance(server)).size() == 1;
    params.setHiddenParameters(server.nZSlices() == 1, "allZ");
    params.setHiddenParameters(server.nTimepoints() == 1, "allT");
    params.setHiddenParameters(singleTile, "tileSize", "parallelize");
    if (!Dialogs.showParameterDialog("Export OME-TIFF", params))
        return;
    compression = CompressionType.fromFriendlyString((String) params.getChoiceParameterValue("compression"));
    defaultPyramidCompression.set(compression);
    int downsampleScale = params.getIntParameterValue("scaledDownsample");
    scaledDownsample.set(downsampleScale);
    int tileSize = params.getIntParameterValue("tileSize");
    boolean parallelize = params.getBooleanParameterValue("parallelize");
    if (!singleTile) {
        tileSize = GeneralTools.clipValue(tileSize, 128, 8192);
        defaultTileSize.set(tileSize);
        parallelizeTiling.set(parallelize);
    }
    boolean doAllZ = false;
    boolean doAllT = false;
    if (server.nZSlices() > 1) {
        doAllZ = params.getBooleanParameterValue("allZ");
        allZ.set(doAllZ);
    }
    if (server.nTimepoints() > 1) {
        doAllT = params.getBooleanParameterValue("allT");
        allT.set(doAllT);
    }
    OMEPyramidWriter.Builder builder = new OMEPyramidWriter.Builder(server);
    if (region != null) {
        builder = builder.region(region);
    } else {
        if (server.nZSlices() > 1 && !doAllZ)
            builder.zSlice(zPos);
        if (server.nTimepoints() > 1 && !doAllT)
            builder.timePoint(tPos);
    }
    builder.compression(compression);
    if (downsampleScale <= 1 || Math.max(width, height) / server.getDownsampleForResolution(0) < minSizeForTiling.get())
        builder.downsamples(server.getDownsampleForResolution(0));
    else
        builder.scaledDownsampling(server.getDownsampleForResolution(0), downsampleScale);
    // Set tile size; if we just have one tile, use the image width & height
    if (singleTile)
        builder = builder.tileSize(width, height);
    else
        builder = builder.tileSize(tileSize).parallelize(parallelize);
    if (server.nZSlices() > 1 && doAllZ)
        builder.allZSlices();
    if (server.nTimepoints() > 1 && doAllT)
        builder.allTimePoints();
    // Prompt for file
    File fileOutput = Dialogs.promptToSaveFile("Write pyramid", null, null, "OME TIFF pyramid", ".ome.tif");
    if (fileOutput == null)
        return;
    String name = fileOutput.getName();
    // We can have trouble with only the '.tif' part of the name being included
    if (name.endsWith(".tif") && !name.endsWith(".ome.tif"))
        fileOutput = new File(fileOutput.getParentFile(), name.substring(0, name.length() - 4) + ".ome.tif");
    OMEPyramidSeries writer = builder.build();
    if (pool == null) {
        pool = Executors.newSingleThreadExecutor(ThreadTools.createThreadFactory("ome-pyramid-export", false));
    }
    currentTask = pool.submit(new WriterTask(OMEPyramidWriter.createWriter(writer), fileOutput.getAbsolutePath()));
}
Also used : Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) LoggerFactory(org.slf4j.LoggerFactory) IntegerProperty(javafx.beans.property.IntegerProperty) Dialogs(qupath.lib.gui.dialogs.Dialogs) Future(java.util.concurrent.Future) ParameterList(qupath.lib.plugins.parameters.ParameterList) OMEPyramidSeries(qupath.lib.images.writers.ome.OMEPyramidWriter.OMEPyramidSeries) ImageRegion(qupath.lib.regions.ImageRegion) CompressionType(qupath.lib.images.writers.ome.OMEPyramidWriter.CompressionType) ExecutorService(java.util.concurrent.ExecutorService) QuPathGUI(qupath.lib.gui.QuPathGUI) ImageData(qupath.lib.images.ImageData) ObjectProperty(javafx.beans.property.ObjectProperty) Logger(org.slf4j.Logger) BufferedImage(java.awt.image.BufferedImage) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) Collectors(java.util.stream.Collectors) File(java.io.File) Executors(java.util.concurrent.Executors) PathObject(qupath.lib.objects.PathObject) ClosedByInterruptException(java.nio.channels.ClosedByInterruptException) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) ThreadTools(qupath.lib.common.ThreadTools) PathPrefs(qupath.lib.gui.prefs.PathPrefs) ImageRegion(qupath.lib.regions.ImageRegion) BufferedImage(java.awt.image.BufferedImage) PathObject(qupath.lib.objects.PathObject) OMEPyramidSeries(qupath.lib.images.writers.ome.OMEPyramidWriter.OMEPyramidSeries) ParameterList(qupath.lib.plugins.parameters.ParameterList) CompressionType(qupath.lib.images.writers.ome.OMEPyramidWriter.CompressionType) File(java.io.File) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer)

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