Search in sources :

Example 11 with ImageData

use of qupath.lib.images.ImageData in project qupath by qupath.

the class DistanceTools method detectionToAnnotationDistances.

/**
 * Compute the distance for all detection object centroids to the closest annotation with each valid, not-ignored classification and add
 * the result to the detection measurement list.
 * @param imageData
 * @param splitClassNames if true, split the classification name. For example, if an image contains classifications for both "CD3: CD4" and "CD3: CD8",
 *                        distances will be calculated for all components (e.g. "CD3", "CD4" and "CD8").
 */
public static void detectionToAnnotationDistances(ImageData<?> imageData, boolean splitClassNames) {
    var server = imageData.getServer();
    var hierarchy = imageData.getHierarchy();
    var annotations = hierarchy.getAnnotationObjects();
    var detections = hierarchy.getCellObjects();
    if (detections.isEmpty())
        detections = hierarchy.getDetectionObjects();
    // TODO: Support TMA cores
    if (hierarchy.getTMAGrid() != null)
        logger.warn("Detection to annotation distances command currently ignores TMA grid information!");
    var pathClasses = annotations.stream().map(p -> p.getPathClass()).filter(p -> p != null && p.isValid() && !PathClassTools.isIgnoredClass(p)).collect(Collectors.toSet());
    var cal = server.getPixelCalibration();
    String xUnit = cal.getPixelWidthUnit();
    String yUnit = cal.getPixelHeightUnit();
    double pixelWidth = cal.getPixelWidth().doubleValue();
    double pixelHeight = cal.getPixelHeight().doubleValue();
    if (!xUnit.equals(yUnit))
        throw new IllegalArgumentException("Pixel width & height units do not match! Width " + xUnit + ", height " + yUnit);
    String unit = xUnit;
    for (PathClass pathClass : pathClasses) {
        if (splitClassNames) {
            var names = PathClassTools.splitNames(pathClass);
            for (var name : names) {
                logger.debug("Computing distances for {}", pathClass);
                var filteredAnnotations = annotations.stream().filter(a -> PathClassTools.containsName(a.getPathClass(), name)).collect(Collectors.toList());
                if (!filteredAnnotations.isEmpty()) {
                    String measurementName = "Distance to annotation with " + name + " " + unit;
                    centroidToBoundsDistance2D(detections, filteredAnnotations, pixelWidth, pixelHeight, measurementName);
                }
            }
        } else {
            logger.debug("Computing distances for {}", pathClass);
            var filteredAnnotations = annotations.stream().filter(a -> a.getPathClass() == pathClass).collect(Collectors.toList());
            if (!filteredAnnotations.isEmpty()) {
                String name = "Distance to annotation " + pathClass + " " + unit;
                centroidToBoundsDistance2D(detections, filteredAnnotations, pixelWidth, pixelHeight, name);
            }
        }
    }
    hierarchy.fireObjectMeasurementsChangedEvent(DistanceTools.class, detections);
}
Also used : PointOnGeometryLocator(org.locationtech.jts.algorithm.locate.PointOnGeometryLocator) PathClassTools(qupath.lib.objects.classes.PathClassTools) PointPairDistance(org.locationtech.jts.algorithm.distance.PointPairDistance) LoggerFactory(org.slf4j.LoggerFactory) Coordinate(org.locationtech.jts.geom.Coordinate) TreeSet(java.util.TreeSet) ArrayList(java.util.ArrayList) Location(org.locationtech.jts.geom.Location) Puntal(org.locationtech.jts.geom.Puntal) DistanceToPoint(org.locationtech.jts.algorithm.distance.DistanceToPoint) GeometryTools(qupath.lib.roi.GeometryTools) AffineTransformation(org.locationtech.jts.geom.util.AffineTransformation) GeometryCombiner(org.locationtech.jts.geom.util.GeometryCombiner) ImageData(qupath.lib.images.ImageData) ItemBoundable(org.locationtech.jts.index.strtree.ItemBoundable) Logger(org.slf4j.Logger) Collection(java.util.Collection) PathClass(qupath.lib.objects.classes.PathClass) IndexedPointInAreaLocator(org.locationtech.jts.algorithm.locate.IndexedPointInAreaLocator) Polygonal(org.locationtech.jts.geom.Polygonal) Collectors(java.util.stream.Collectors) Lineal(org.locationtech.jts.geom.Lineal) PathObjectTools(qupath.lib.objects.PathObjectTools) PathObject(qupath.lib.objects.PathObject) List(java.util.List) Geometry(org.locationtech.jts.geom.Geometry) PrecisionModel(org.locationtech.jts.geom.PrecisionModel) Envelope(org.locationtech.jts.geom.Envelope) ItemDistance(org.locationtech.jts.index.strtree.ItemDistance) STRtree(org.locationtech.jts.index.strtree.STRtree) PathClass(qupath.lib.objects.classes.PathClass)

Example 12 with ImageData

use of qupath.lib.images.ImageData in project qupath by qupath.

the class ImageJMacroRunner method getParentObjects.

@Override
protected Collection<? extends PathObject> getParentObjects(final PluginRunner<BufferedImage> runner) {
    // Try to get currently-selected objects
    PathObjectHierarchy hierarchy = getHierarchy(runner);
    List<PathObject> pathObjects = hierarchy.getSelectionModel().getSelectedObjects().stream().filter(p -> p.isAnnotation() || p.isTMACore()).collect(Collectors.toList());
    if (pathObjects.isEmpty()) {
        if (GuiTools.promptForParentObjects(this.getName(), runner.getImageData(), false, getSupportedParentObjectClasses()))
            pathObjects = new ArrayList<>(hierarchy.getSelectionModel().getSelectedObjects());
    }
    return pathObjects;
// // TODO: Give option to analyse annotations, even when TMA grid is present
// ImageData<BufferedImage> imageData = runner.getImageData();
// TMAGrid tmaGrid = imageData.getHierarchy().getTMAGrid();
// if (tmaGrid != null && tmaGrid.nCores() > 0)
// return PathObjectTools.getTMACoreObjects(imageData.getHierarchy(), false);
// else
// return imageData.getHierarchy().getObjects(null, PathAnnotationObject.class);
}
Also used : Button(javafx.scene.control.Button) Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) CombineOp(qupath.lib.roi.RoiTools.CombineOp) IJTools(qupath.imagej.tools.IJTools) LoggerFactory(org.slf4j.LoggerFactory) PathImage(qupath.lib.images.PathImage) ParameterList(qupath.lib.plugins.parameters.ParameterList) PluginRunner(qupath.lib.plugins.PluginRunner) QuPathGUI(qupath.lib.gui.QuPathGUI) BufferedImage(java.awt.image.BufferedImage) Collection(java.util.Collection) Font(javafx.scene.text.Font) Collectors(java.util.stream.Collectors) InvocationTargetException(java.lang.reflect.InvocationTargetException) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathObject(qupath.lib.objects.PathObject) Platform(javafx.application.Platform) ImagePlus(ij.ImagePlus) List(java.util.List) GuiTools(qupath.lib.gui.tools.GuiTools) BorderPane(javafx.scene.layout.BorderPane) Roi(ij.gui.Roi) RectangleROI(qupath.lib.roi.RectangleROI) Scene(javafx.scene.Scene) TextArea(javafx.scene.control.TextArea) WindowManager(ij.WindowManager) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ImageDisplay(qupath.lib.display.ImageDisplay) ArrayList(java.util.ArrayList) Dialogs(qupath.lib.gui.dialogs.Dialogs) SwingUtilities(javax.swing.SwingUtilities) Insets(javafx.geometry.Insets) AbstractPlugin(qupath.lib.plugins.AbstractPlugin) GridPane(javafx.scene.layout.GridPane) LineROI(qupath.lib.roi.LineROI) ImageData(qupath.lib.images.ImageData) RoiTools(qupath.lib.roi.RoiTools) Logger(org.slf4j.Logger) Interpreter(ij.macro.Interpreter) RegionRequest(qupath.lib.regions.RegionRequest) Calibration(ij.measure.Calibration) ChannelDisplayTransformServer(qupath.lib.gui.images.servers.ChannelDisplayTransformServer) IOException(java.io.IOException) TMACoreObject(qupath.lib.objects.TMACoreObject) ROI(qupath.lib.roi.interfaces.ROI) Stage(javafx.stage.Stage) ParameterPanelFX(qupath.lib.gui.dialogs.ParameterPanelFX) QuPath_Send_Overlay_to_QuPath(qupathj.QuPath_Send_Overlay_to_QuPath) IJ(ij.IJ) PaneTools(qupath.lib.gui.tools.PaneTools) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PathObject(qupath.lib.objects.PathObject) ArrayList(java.util.ArrayList)

Example 13 with ImageData

use of qupath.lib.images.ImageData in project qupath by qupath.

the class PixelClassifierUI method promptToCreateObjects.

/**
 * Prompt the user to create objects directly from the pixels of an {@link ImageServer}.
 * Often, the {@link ImageServer} has been created by applying a {@link PixelClassifier}.
 *
 * @param imageData the {@link ImageData} to which objects should be added
 * @param classifier the {@link ImageServer} used to generate objects
 * @param classifierName the name of the classifier; if not null and the command runs to completion, it will be logged in the history
 * 						 workflow of the {@link ImageData} for later scripting.
 * @return true if changes were made, false otherwise
 */
public static boolean promptToCreateObjects(ImageData<BufferedImage> imageData, PixelClassifier classifier, String classifierName) {
    Objects.requireNonNull(imageData);
    Objects.requireNonNull(classifier);
    // Check what is selected
    List<SelectionChoice> choices = buildChoiceList(imageData.getHierarchy(), SelectionChoice.FULL_IMAGE, SelectionChoice.CURRENT_SELECTION, SelectionChoice.ANNOTATIONS, SelectionChoice.TMA);
    SelectionChoice defaultChoice;
    if (choices.contains(SelectionChoice.CURRENT_SELECTION))
        defaultChoice = SelectionChoice.CURRENT_SELECTION;
    else if (choices.contains(SelectionChoice.ANNOTATIONS))
        defaultChoice = SelectionChoice.ANNOTATIONS;
    else
        defaultChoice = choices.get(0);
    var parentChoice = Dialogs.showChoiceDialog("Pixel classifier", "Choose parent objects", choices, defaultChoice);
    if (parentChoice == null)
        return false;
    var outputObjectTypes = Arrays.asList("Annotation", "Detection");
    // To avoid confusing the user unnecessarily, if we *only* have ignored classes then set default for includeIgnored to true
    var labels = classifier.getMetadata().getClassificationLabels();
    boolean allIgnored = !labels.isEmpty() && labels.values().stream().allMatch(p -> p == null || PathClassTools.isIgnoredClass(p));
    boolean includeIgnored = allIgnored;
    var cal = imageData.getServer().getPixelCalibration();
    var units = cal.unitsMatch2D() ? cal.getPixelWidthUnit() + "^2" : cal.getPixelWidthUnit() + "x" + cal.getPixelHeightUnit();
    ParameterList params;
    if (lastCreateObjectParams != null) {
        params = lastCreateObjectParams.duplicate();
        params.setHiddenParameters(false, params.getKeyValueParameters(true).keySet().toArray(String[]::new));
        ((BooleanParameter) params.getParameters().get("includeIgnored")).setValue(includeIgnored);
    } else {
        params = new ParameterList().addChoiceParameter("objectType", "New object type", "Annotation", outputObjectTypes, "Define the type of objects that will be created").addDoubleParameter("minSize", "Minimum object size", 0, units, "Minimum size of a region to keep (smaller regions will be dropped)").addDoubleParameter("minHoleSize", "Minimum hole size", 0, units, "Minimum size of a hole to keep (smaller holes will be filled)").addBooleanParameter("doSplit", "Split objects", false, "Split multi-part regions into separate objects").addBooleanParameter("clearExisting", "Delete existing objects", false, "Delete any existing objects within the selected object before adding new objects (or entire image if no object is selected)").addBooleanParameter("includeIgnored", "Create objects for ignored classes", includeIgnored, "Create objects for classifications that are usually ignored (e.g. \"Ignore*\", \"Region*\")").addBooleanParameter("selectNew", "Set new objects to selected", false, "Set the newly-created objects to be selected");
    }
    if (!Dialogs.showParameterDialog("Create objects", params))
        return false;
    boolean createDetections = params.getChoiceParameterValue("objectType").equals("Detection");
    boolean doSplit = params.getBooleanParameterValue("doSplit");
    includeIgnored = params.getBooleanParameterValue("includeIgnored");
    double minSize = params.getDoubleParameterValue("minSize");
    double minHoleSize = params.getDoubleParameterValue("minHoleSize");
    boolean clearExisting = params.getBooleanParameterValue("clearExisting");
    boolean selectNew = params.getBooleanParameterValue("selectNew");
    lastCreateObjectParams = params;
    parentChoice.handleSelection(imageData);
    List<CreateObjectOptions> options = new ArrayList<>();
    if (doSplit)
        options.add(CreateObjectOptions.SPLIT);
    if (clearExisting)
        options.add(CreateObjectOptions.DELETE_EXISTING);
    if (includeIgnored)
        options.add(CreateObjectOptions.INCLUDE_IGNORED);
    else if (allIgnored) {
        Dialogs.showErrorMessage(title, "Cannot create objects - all class names have an asterisk to show they should be 'ignored'!");
        return false;
    }
    if (selectNew)
        options.add(CreateObjectOptions.SELECT_NEW);
    var optionsArray = options.toArray(CreateObjectOptions[]::new);
    String optionsString = "";
    if (!options.isEmpty())
        optionsString = ", " + options.stream().map(o -> "\"" + o.name() + "\"").collect(Collectors.joining(", "));
    try {
        if (createDetections) {
            if (PixelClassifierTools.createDetectionsFromPixelClassifier(imageData, classifier, minSize, minHoleSize, optionsArray)) {
                if (classifierName != null) {
                    imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Pixel classifier create detections", String.format("createDetectionsFromPixelClassifier(\"%s\", %s, %s)", classifierName, minSize, minHoleSize + optionsString)));
                }
                return true;
            }
        } else {
            if (PixelClassifierTools.createAnnotationsFromPixelClassifier(imageData, classifier, minSize, minHoleSize, optionsArray)) {
                if (classifierName != null) {
                    imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Pixel classifier create annotations", String.format("createAnnotationsFromPixelClassifier(\"%s\", %s, %s)", classifierName, minSize, minHoleSize + optionsString)));
                }
                return true;
            }
        }
    } catch (IOException e) {
        Dialogs.showErrorMessage(title, e);
    }
    return false;
}
Also used : Button(javafx.scene.control.Button) CreateObjectOptions(qupath.opencv.ml.pixel.PixelClassifierTools.CreateObjectOptions) Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) PathTileObject(qupath.lib.objects.PathTileObject) BooleanBinding(javafx.beans.binding.BooleanBinding) CheckMenuItem(javafx.scene.control.CheckMenuItem) LoggerFactory(org.slf4j.LoggerFactory) Side(javafx.geometry.Side) ImageWriter(qupath.lib.images.writers.ImageWriter) ParameterList(qupath.lib.plugins.parameters.ParameterList) ComboBox(javafx.scene.control.ComboBox) ContextMenu(javafx.scene.control.ContextMenu) Map(java.util.Map) PixelClassifierTools(qupath.opencv.ml.pixel.PixelClassifierTools) Pane(javafx.scene.layout.Pane) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) Collectors(java.util.stream.Collectors) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathDetectionObject(qupath.lib.objects.PathDetectionObject) PathObject(qupath.lib.objects.PathObject) Objects(java.util.Objects) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) Project(qupath.lib.projects.Project) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) GuiTools(qupath.lib.gui.tools.GuiTools) RegionFilter(qupath.lib.gui.viewer.RegionFilter) BorderPane(javafx.scene.layout.BorderPane) StringProperty(javafx.beans.property.StringProperty) PathCellObject(qupath.lib.objects.PathCellObject) SaveResourcePaneBuilder(qupath.process.gui.commands.ui.SaveResourcePaneBuilder) ObjectExpression(javafx.beans.binding.ObjectExpression) PathClassTools(qupath.lib.objects.classes.PathClassTools) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Bindings(javafx.beans.binding.Bindings) ArrayList(java.util.ArrayList) StandardRegionFilters(qupath.lib.gui.viewer.RegionFilter.StandardRegionFilters) LinkedHashMap(java.util.LinkedHashMap) Dialogs(qupath.lib.gui.dialogs.Dialogs) Tooltip(javafx.scene.control.Tooltip) ImageData(qupath.lib.images.ImageData) Logger(org.slf4j.Logger) StringExpression(javafx.beans.binding.StringExpression) ImageWriterTools(qupath.lib.images.writers.ImageWriterTools) Commands(qupath.lib.gui.commands.Commands) IOException(java.io.IOException) TMACoreObject(qupath.lib.objects.TMACoreObject) OverlayOptions(qupath.lib.gui.viewer.OverlayOptions) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) PixelClassifier(qupath.lib.classifiers.pixel.PixelClassifier) BooleanParameter(qupath.lib.plugins.parameters.BooleanParameter) Collections(java.util.Collections) PaneTools(qupath.lib.gui.tools.PaneTools) ArrayList(java.util.ArrayList) IOException(java.io.IOException) BooleanParameter(qupath.lib.plugins.parameters.BooleanParameter) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) ParameterList(qupath.lib.plugins.parameters.ParameterList) CreateObjectOptions(qupath.opencv.ml.pixel.PixelClassifierTools.CreateObjectOptions)

Example 14 with ImageData

use of qupath.lib.images.ImageData in project qupath by qupath.

the class QP method makeInverseAnnotation.

/**
 * Make an annotation, for which the ROI is obtained by subtracting the ROIs of the specified objects from the closest
 * common ancestor ROI (or entire image if the closest ancestor is the root).
 * <p>
 * In an inverted annotation can be created, it is added to the hierarchy and set as selected.
 *
 * @param imageData the image containing the annotation
 * @param pathObjects the annotation to invert
 * @return true if an inverted annotation is added to the hierarchy, false otherwise.
 */
public static boolean makeInverseAnnotation(final ImageData<?> imageData, Collection<PathObject> pathObjects) {
    if (imageData == null)
        return false;
    var map = pathObjects.stream().filter(p -> p.hasROI() && p.getROI().isArea()).collect(Collectors.groupingBy(p -> p.getROI().getImagePlane()));
    if (map.isEmpty()) {
        logger.warn("No area annotations available - cannot created inverse ROI!");
        return false;
    }
    if (map.size() > 1) {
        logger.error("Cannot merge annotations from different image planes!");
        return false;
    }
    ImagePlane plane = map.keySet().iterator().next();
    List<PathObject> pathObjectList = map.get(plane);
    PathObjectHierarchy hierarchy = imageData.getHierarchy();
    // Try to get the best candidate parent
    Collection<PathObject> parentSet = pathObjectList.stream().map(p -> p.getParent()).collect(Collectors.toCollection(HashSet::new));
    PathObject parent;
    if (parentSet.size() > 1) {
        parentSet.clear();
        boolean firstTime = true;
        for (PathObject temp : pathObjectList) {
            if (firstTime)
                parentSet.addAll(PathObjectTools.getAncestorList(temp));
            else
                parentSet.retainAll(PathObjectTools.getAncestorList(temp));
            firstTime = false;
        }
        List<PathObject> parents = new ArrayList<>(parentSet);
        Collections.sort(parents, Comparator.comparingInt(PathObject::getLevel).reversed().thenComparingDouble(p -> p.hasROI() ? p.getROI().getArea() : Double.MAX_VALUE));
        parent = parents.get(0);
    } else
        parent = parentSet.iterator().next();
    // Get the parent area
    Geometry geometryParent;
    if (parent == null || parent.isRootObject() || !parent.hasROI())
        geometryParent = GeometryTools.createRectangle(0, 0, imageData.getServer().getWidth(), imageData.getServer().getHeight());
    else
        geometryParent = parent.getROI().getGeometry();
    // Get the parent area to use
    var union = GeometryTools.union(pathObjectList.stream().map(p -> p.getROI().getGeometry()).collect(Collectors.toList()));
    var geometry = geometryParent.difference(union);
    // Create the new ROI
    ROI shapeNew = GeometryTools.geometryToROI(geometry, plane);
    PathObject pathObjectNew = PathObjects.createAnnotationObject(shapeNew);
    parent.addPathObject(pathObjectNew);
    hierarchy.fireHierarchyChangedEvent(parent);
    hierarchy.getSelectionModel().setSelectedObject(pathObjectNew);
    return true;
}
Also used : FeatureExtractors(qupath.opencv.ml.objects.features.FeatureExtractors) Arrays(java.util.Arrays) ServerTools(qupath.lib.images.servers.ServerTools) PathTileObject(qupath.lib.objects.PathTileObject) IJTools(qupath.imagej.tools.IJTools) GroovyCV(qupath.opencv.tools.GroovyCV) CommandLinePluginRunner(qupath.lib.plugins.CommandLinePluginRunner) ImageRegion(qupath.lib.regions.ImageRegion) Map(java.util.Map) PixelClassifierTools(qupath.opencv.ml.pixel.PixelClassifierTools) Path(java.nio.file.Path) ColorTools(qupath.lib.common.ColorTools) Member(java.lang.reflect.Member) PathObjects(qupath.lib.objects.PathObjects) Set(java.util.Set) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) Project(qupath.lib.projects.Project) Stream(java.util.stream.Stream) ColorModels(qupath.lib.analysis.heatmaps.ColorModels) ShapeFeatures(qupath.lib.analysis.features.ObjectMeasurements.ShapeFeatures) DensityMaps(qupath.lib.analysis.heatmaps.DensityMaps) PathObjectPredicates(qupath.lib.objects.PathObjectPredicates) GeoJsonExportOptions(qupath.lib.io.PathIO.GeoJsonExportOptions) GsonTools(qupath.lib.io.GsonTools) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Constructor(java.lang.reflect.Constructor) PathPlugin(qupath.lib.plugins.PathPlugin) Projects(qupath.lib.projects.Projects) ArrayList(java.util.ArrayList) ROIs(qupath.lib.roi.ROIs) ImageOps(qupath.opencv.ops.ImageOps) LinkedHashSet(java.util.LinkedHashSet) PathClassifierTools(qupath.lib.classifiers.PathClassifierTools) ObjectArrays(com.google.common.collect.ObjectArrays) Files(java.nio.file.Files) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) TileExporter(qupath.lib.images.writers.TileExporter) DistanceTools(qupath.lib.analysis.DistanceTools) IOException(java.io.IOException) Padding(qupath.lib.regions.Padding) Field(java.lang.reflect.Field) DelaunayTools(qupath.lib.analysis.DelaunayTools) File(java.io.File) PathObjectTools(qupath.lib.objects.PathObjectTools) ROI(qupath.lib.roi.interfaces.ROI) PixelClassifier(qupath.lib.classifiers.pixel.PixelClassifier) Paths(java.nio.file.Paths) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) UriResource(qupath.lib.io.UriResource) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) PathIO(qupath.lib.io.PathIO) RunSavedClassifierWorkflowStep(qupath.lib.plugins.workflow.RunSavedClassifierWorkflowStep) CreateObjectOptions(qupath.opencv.ml.pixel.PixelClassifierTools.CreateObjectOptions) ImageServer(qupath.lib.images.servers.ImageServer) LoggerFactory(org.slf4j.LoggerFactory) Scanner(java.util.Scanner) PathObjectFilter(qupath.lib.objects.PathObjectFilter) OpenCVMLClassifier(qupath.opencv.ml.objects.OpenCVMLClassifier) BufferedImageTools(qupath.lib.awt.common.BufferedImageTools) URI(java.net.URI) ImageServers(qupath.lib.images.servers.ImageServers) Method(java.lang.reflect.Method) ImageType(qupath.lib.images.ImageData.ImageType) ObjectMeasurements(qupath.lib.analysis.features.ObjectMeasurements) BufferedImage(java.awt.image.BufferedImage) PixelClassifiers(qupath.opencv.ml.pixel.PixelClassifiers) Predicate(java.util.function.Predicate) ImageServerProvider(qupath.lib.images.servers.ImageServerProvider) Collection(java.util.Collection) DensityMapBuilder(qupath.lib.analysis.heatmaps.DensityMaps.DensityMapBuilder) UriUpdater(qupath.lib.io.UriUpdater) Collectors(java.util.stream.Collectors) FileNotFoundException(java.io.FileNotFoundException) PathObject(qupath.lib.objects.PathObject) PathDetectionObject(qupath.lib.objects.PathDetectionObject) List(java.util.List) ProjectIO(qupath.lib.projects.ProjectIO) ContourTracing(qupath.lib.analysis.images.ContourTracing) PathObjectClassifier(qupath.lib.classifiers.PathObjectClassifier) Modifier(java.lang.reflect.Modifier) DnnTools(qupath.opencv.dnn.DnnTools) ImagePlane(qupath.lib.regions.ImagePlane) Geometry(org.locationtech.jts.geom.Geometry) Pattern(java.util.regex.Pattern) PathCellObject(qupath.lib.objects.PathCellObject) PathClassTools(qupath.lib.objects.classes.PathClassTools) ImageChannel(qupath.lib.images.servers.ImageChannel) OpenCVTools(qupath.opencv.tools.OpenCVTools) HashMap(java.util.HashMap) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) ColorTransforms(qupath.lib.images.servers.ColorTransforms) CellTools(qupath.lib.objects.CellTools) HashSet(java.util.HashSet) ColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains) ObjectClassifiers(qupath.lib.classifiers.object.ObjectClassifiers) GeometryTools(qupath.lib.roi.GeometryTools) NoSuchElementException(java.util.NoSuchElementException) WeakHashMap(java.util.WeakHashMap) ImageData(qupath.lib.images.ImageData) RoiTools(qupath.lib.roi.RoiTools) Logger(org.slf4j.Logger) ProjectImageEntry(qupath.lib.projects.ProjectImageEntry) ImageWriterTools(qupath.lib.images.writers.ImageWriterTools) PixelType(qupath.lib.images.servers.PixelType) PathClass(qupath.lib.objects.classes.PathClass) PointIO(qupath.lib.io.PointIO) TMACoreObject(qupath.lib.objects.TMACoreObject) ObjectClassifier(qupath.lib.classifiers.object.ObjectClassifier) Comparator(java.util.Comparator) Collections(java.util.Collections) Geometry(org.locationtech.jts.geom.Geometry) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PathObject(qupath.lib.objects.PathObject) ArrayList(java.util.ArrayList) ImagePlane(qupath.lib.regions.ImagePlane) ROI(qupath.lib.roi.interfaces.ROI)

Example 15 with ImageData

use of qupath.lib.images.ImageData in project qupath by qupath.

the class ImageDetailsPane method promptToSetImageType.

// /**
// * Prompt the user to set the {@link ImageType} for the image.
// * @param imageData
// * @return
// */
// public static boolean promptToSetImageType(ImageData<BufferedImage> imageData) {
// List<ImageType> values = Arrays.asList(ImageType.values());
// if (!imageData.getServer().isRGB()) {
// values = new ArrayList<>(values);
// values.remove(ImageType.BRIGHTFIELD_H_DAB);
// values.remove(ImageType.BRIGHTFIELD_H_E);
// values.remove(ImageType.BRIGHTFIELD_OTHER);
// }
// 
// var dialog = new ChoiceDialog<>(imageData.getImageType(), values);
// dialog.setTitle("Image type");
// if (QuPathGUI.getInstance() != null)
// dialog.initOwner(QuPathGUI.getInstance().getStage());
// dialog.getDialogPane().setHeaderText(null);
// dialog.getDialogPane().setContentText("Set image type");
// dialog.getDialogPane().setPrefWidth(400);
// 
// var labelExplain = new Label("The image type influences some commands (e.g. cell detection) and should be set for every image. "
// + "\n\nSelect an option below or in the preferences to customize how QuPath handles setting the image type when opening an image."
// + "\n\n'Auto-estimate' is convenient to reduce annoying prompts, but the estimates are sometimes wrong. "
// + "When this happens you can correct them by double-clicking "
// + "the type under the 'Image' tab.");
// labelExplain.setWrapText(true);
// labelExplain.setPrefWidth(400);
// labelExplain.setMinHeight(Label.USE_PREF_SIZE);
// 
// var comboSetType = new ComboBox<ImageTypeSetting>();
// comboSetType.getItems().setAll(ImageTypeSetting.values());
// comboSetType.getSelectionModel().select(PathPrefs.imageTypeSettingProperty().get());
// comboSetType.setMaxWidth(Double.MAX_VALUE);
// labelExplain.setPadding(new Insets(0, 0, 10, 0));
// var expandablePane = new BorderPane(labelExplain);
// expandablePane.setBottom(comboSetType);
// 
// dialog.getDialogPane().setExpandableContent(expandablePane);
// 
// var result = dialog.showAndWait();
// ImageType type = result.orElse(null);
// if (type == null)
// return false;
// 
// if (comboSetType.getSelectionModel().getSelectedItem() != null)
// PathPrefs.imageTypeSettingProperty().set(comboSetType.getSelectionModel().getSelectedItem());
// 
// if (type != imageData.getImageType()) {
// imageData.setImageType(type);
// return true;
// }
// return false;
// }
/**
 * Prompt the user to set the {@link ImageType} for the image.
 * @param imageData the image data for which the type should be set
 * @param defaultType the default type (selected when the dialog is shown)
 * @return true if the type was changed, false otherwise
 */
public static boolean promptToSetImageType(ImageData<BufferedImage> imageData, ImageType defaultType) {
    double size = 32;
    var group = new ToggleGroup();
    boolean isRGB = imageData.getServer().isRGB();
    if (defaultType == null)
        defaultType = ImageType.UNSET;
    var buttonMap = new LinkedHashMap<ImageType, ToggleButton>();
    // TODO: Create a nicer icon for unspecified type
    var iconUnspecified = (Group) createImageTypeCell(Color.GRAY, null, null, size);
    if (isRGB) {
        buttonMap.put(ImageType.BRIGHTFIELD_H_E, createImageTypeButton(ImageType.BRIGHTFIELD_H_E, "Brightfield\nH&E", createImageTypeCell(Color.WHITE, Color.PINK, Color.DARKBLUE, size), "Brightfield image with hematoylin & eosin stains\n(8-bit RGB only)", isRGB));
        buttonMap.put(ImageType.BRIGHTFIELD_H_DAB, createImageTypeButton(ImageType.BRIGHTFIELD_H_DAB, "Brightfield\nH-DAB", createImageTypeCell(Color.WHITE, Color.rgb(200, 200, 220), Color.rgb(120, 50, 20), size), "Brightfield image with hematoylin & DAB stains\n(8-bit RGB only)", isRGB));
        buttonMap.put(ImageType.BRIGHTFIELD_OTHER, createImageTypeButton(ImageType.BRIGHTFIELD_OTHER, "Brightfield\nOther", createImageTypeCell(Color.WHITE, Color.ORANGE, Color.FIREBRICK, size), "Brightfield image with other chromogenic stains\n(8-bit RGB only)", isRGB));
    }
    buttonMap.put(ImageType.FLUORESCENCE, createImageTypeButton(ImageType.FLUORESCENCE, "Fluorescence", createImageTypeCell(Color.BLACK, Color.LIGHTGREEN, Color.BLUE, size), "Fluorescence or fluorescence-like image with a dark background\n" + "Also suitable for imaging mass cytometry", true));
    buttonMap.put(ImageType.OTHER, createImageTypeButton(ImageType.OTHER, "Other", createImageTypeCell(Color.BLACK, Color.WHITE, Color.GRAY, size), "Any other image type", true));
    buttonMap.put(ImageType.UNSET, createImageTypeButton(ImageType.UNSET, "Unspecified", iconUnspecified, "Do not set the image type (not recommended for analysis)", true));
    var buttons = buttonMap.values().toArray(ToggleButton[]::new);
    for (var btn : buttons) {
        if (btn.isDisabled()) {
            btn.getTooltip().setText("Image type is not supported because image is not RGB");
        }
    }
    var buttonList = Arrays.asList(buttons);
    group.getToggles().setAll(buttons);
    group.selectedToggleProperty().addListener((v, o, n) -> {
        // Ensure that we can't deselect all buttons
        if (n == null)
            o.setSelected(true);
    });
    PaneTools.setMaxWidth(Double.MAX_VALUE, buttons);
    PaneTools.setMaxHeight(Double.MAX_VALUE, buttons);
    var selectedButton = buttonMap.get(defaultType);
    group.selectToggle(selectedButton);
    var grid = new GridPane();
    int nHorizontal = 3;
    int nVertical = (int) Math.ceil(buttons.length / (double) nHorizontal);
    grid.getColumnConstraints().setAll(IntStream.range(0, nHorizontal).mapToObj(i -> {
        var c = new ColumnConstraints();
        c.setPercentWidth(100.0 / nHorizontal);
        return c;
    }).collect(Collectors.toList()));
    grid.getRowConstraints().setAll(IntStream.range(0, nVertical).mapToObj(i -> {
        var c = new RowConstraints();
        c.setPercentHeight(100.0 / nVertical);
        return c;
    }).collect(Collectors.toList()));
    grid.setVgap(5);
    // grid.setHgap(5);
    grid.setMaxWidth(Double.MAX_VALUE);
    for (int i = 0; i < buttons.length; i++) {
        grid.add(buttons[i], i % nHorizontal, i / nHorizontal);
    }
    // grid.getChildren().setAll(buttons);
    var content = new BorderPane(grid);
    var comboOptions = new ComboBox<ImageTypeSetting>();
    comboOptions.getItems().setAll(ImageTypeSetting.values());
    var prompts = Map.of(ImageTypeSetting.AUTO_ESTIMATE, "Always auto-estimate type (don't prompt)", ImageTypeSetting.PROMPT, "Always prompt me to set type", ImageTypeSetting.NONE, "Don't set the image type");
    comboOptions.setButtonCell(GuiTools.createCustomListCell(p -> prompts.get(p)));
    comboOptions.setCellFactory(c -> GuiTools.createCustomListCell(p -> prompts.get(p)));
    comboOptions.setTooltip(new Tooltip("Choose whether you want to see these prompts " + "when opening an image for the first time"));
    comboOptions.setMaxWidth(Double.MAX_VALUE);
    // comboOptions.prefWidthProperty().bind(grid.widthProperty().subtract(100));
    comboOptions.getSelectionModel().select(PathPrefs.imageTypeSettingProperty().get());
    if (nVertical > 1)
        BorderPane.setMargin(comboOptions, new Insets(5, 0, 0, 0));
    else
        BorderPane.setMargin(comboOptions, new Insets(10, 0, 0, 0));
    content.setBottom(comboOptions);
    var labelDetails = new Label("The image type is used for stain separation " + "by some commands, e.g. 'Cell detection'.\n" + "Brightfield types are only available for 8-bit RGB images.");
    // + "For 'Brightfield' images you can set the color stain vectors.");
    labelDetails.setWrapText(true);
    labelDetails.prefWidthProperty().bind(grid.widthProperty().subtract(10));
    labelDetails.setMaxHeight(Double.MAX_VALUE);
    labelDetails.setPrefHeight(Label.USE_COMPUTED_SIZE);
    labelDetails.setPrefHeight(100);
    labelDetails.setAlignment(Pos.CENTER);
    labelDetails.setTextAlignment(TextAlignment.CENTER);
    var dialog = Dialogs.builder().title("Set image type").headerText("What type of image is this?").content(content).buttons(ButtonType.APPLY, ButtonType.CANCEL).expandableContent(labelDetails).build();
    // Try to make it easier to dismiss the dialog in a variety of ways
    var btnApply = dialog.getDialogPane().lookupButton(ButtonType.APPLY);
    Platform.runLater(() -> selectedButton.requestFocus());
    for (var btn : buttons) {
        btn.setOnMouseClicked(e -> {
            if (!btn.isDisabled() && e.getClickCount() == 2) {
                btnApply.fireEvent(new ActionEvent());
                e.consume();
            }
        });
    }
    var enterPressed = new KeyCodeCombination(KeyCode.ENTER);
    var spacePressed = new KeyCodeCombination(KeyCode.SPACE);
    dialog.getDialogPane().addEventFilter(KeyEvent.KEY_PRESSED, e -> {
        if (enterPressed.match(e) || spacePressed.match(e)) {
            btnApply.fireEvent(new ActionEvent());
            e.consume();
        } else if (e.getCode() == KeyCode.UP || e.getCode() == KeyCode.DOWN || e.getCode() == KeyCode.LEFT || e.getCode() == KeyCode.RIGHT) {
            var selected = (ToggleButton) group.getSelectedToggle();
            var ind = buttonList.indexOf(selected);
            var newSelected = selected;
            if (e.getCode() == KeyCode.UP && ind >= nHorizontal) {
                newSelected = buttonList.get(ind - nHorizontal);
            }
            if (e.getCode() == KeyCode.LEFT && ind > 0) {
                newSelected = buttonList.get(ind - 1);
            }
            if (e.getCode() == KeyCode.RIGHT && ind < buttonList.size() - 1) {
                newSelected = buttonList.get(ind + 1);
            }
            if (e.getCode() == KeyCode.DOWN && ind < buttonList.size() - nHorizontal) {
                newSelected = buttonList.get(ind + nHorizontal);
            }
            newSelected.requestFocus();
            group.selectToggle(newSelected);
            e.consume();
        }
    });
    var response = dialog.showAndWait();
    if (response.orElse(ButtonType.CANCEL) == ButtonType.APPLY) {
        PathPrefs.imageTypeSettingProperty().set(comboOptions.getSelectionModel().getSelectedItem());
        var selectedType = (ImageType) group.getSelectedToggle().getUserData();
        if (selectedType != imageData.getImageType()) {
            imageData.setImageType(selectedType);
            return true;
        }
    }
    return false;
}
Also used : Arrays(java.util.Arrays) ServerTools(qupath.lib.images.servers.ServerTools) StackPane(javafx.scene.layout.StackPane) Category(java.util.Locale.Category) ParameterList(qupath.lib.plugins.parameters.ParameterList) MasterDetailPane(org.controlsfx.control.MasterDetailPane) ReadOnlyObjectWrapper(javafx.beans.property.ReadOnlyObjectWrapper) Map(java.util.Map) ColorTools(qupath.lib.common.ColorTools) Rectangle(javafx.scene.shape.Rectangle) KeyEvent(javafx.scene.input.KeyEvent) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) Group(javafx.scene.Group) StandardCharsets(java.nio.charset.StandardCharsets) Platform(javafx.application.Platform) PropertyChangeListener(java.beans.PropertyChangeListener) Clipboard(javafx.scene.input.Clipboard) BorderPane(javafx.scene.layout.BorderPane) RectangleROI(qupath.lib.roi.RectangleROI) ColumnConstraints(javafx.scene.layout.ColumnConstraints) Bindings(javafx.beans.binding.Bindings) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) TextAlignment(javafx.scene.text.TextAlignment) GridPane(javafx.scene.layout.GridPane) Color(javafx.scene.paint.Color) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) Node(javafx.scene.Node) IOException(java.io.IOException) Background(javafx.scene.layout.Background) File(java.io.File) Menu(javafx.scene.control.Menu) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) ROI(qupath.lib.roi.interfaces.ROI) ParameterPanelFX(qupath.lib.gui.dialogs.ParameterPanelFX) ImageView(javafx.scene.image.ImageView) PixelCalibration(qupath.lib.images.servers.PixelCalibration) ObservableValue(javafx.beans.value.ObservableValue) QP(qupath.lib.scripting.QP) Image(javafx.scene.image.Image) PathPrefs(qupath.lib.gui.prefs.PathPrefs) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) PaneTools(qupath.lib.gui.tools.PaneTools) Pos(javafx.geometry.Pos) ImageServer(qupath.lib.images.servers.ImageServer) URLDecoder(java.net.URLDecoder) LoggerFactory(org.slf4j.LoggerFactory) Side(javafx.geometry.Side) KeyCombination(javafx.scene.input.KeyCombination) ComboBox(javafx.scene.control.ComboBox) Locale(java.util.Locale) ImageIO(javax.imageio.ImageIO) BufferedImageTools(qupath.lib.awt.common.BufferedImageTools) URI(java.net.URI) TableView(javafx.scene.control.TableView) ImageType(qupath.lib.images.ImageData.ImageType) QuPathGUI(qupath.lib.gui.QuPathGUI) Pane(javafx.scene.layout.Pane) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) Ellipse(javafx.scene.shape.Ellipse) Collection(java.util.Collection) ChannelDisplayInfo(qupath.lib.display.ChannelDisplayInfo) Collectors(java.util.stream.Collectors) StainVector(qupath.lib.color.StainVector) Objects(java.util.Objects) List(java.util.List) ImageTypeSetting(qupath.lib.gui.prefs.PathPrefs.ImageTypeSetting) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) ToggleButton(javafx.scene.control.ToggleButton) GuiTools(qupath.lib.gui.tools.GuiTools) ClipboardContent(javafx.scene.input.ClipboardContent) IntStream(java.util.stream.IntStream) Scene(javafx.scene.Scene) ListView(javafx.scene.control.ListView) ButtonType(javafx.scene.control.ButtonType) RowConstraints(javafx.scene.layout.RowConstraints) ColorDeconvolutionHelper(qupath.lib.color.ColorDeconvolutionHelper) ImageDisplay(qupath.lib.display.ImageDisplay) TableColumn(javafx.scene.control.TableColumn) Dialogs(qupath.lib.gui.dialogs.Dialogs) ColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains) TableCell(javafx.scene.control.TableCell) Insets(javafx.geometry.Insets) BackgroundFill(javafx.scene.layout.BackgroundFill) Callback(javafx.util.Callback) Tooltip(javafx.scene.control.Tooltip) PropertyChangeEvent(java.beans.PropertyChangeEvent) ImageData(qupath.lib.images.ImageData) KeyCode(javafx.scene.input.KeyCode) Modality(javafx.stage.Modality) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) MenuBar(javafx.scene.control.MenuBar) ReadOnlyStringWrapper(javafx.beans.property.ReadOnlyStringWrapper) DropShadow(javafx.scene.effect.DropShadow) ActionEvent(javafx.event.ActionEvent) ToggleGroup(javafx.scene.control.ToggleGroup) Stage(javafx.stage.Stage) SwingFXUtils(javafx.embed.swing.SwingFXUtils) WrappedBufferedImageServer(qupath.lib.images.servers.WrappedBufferedImageServer) ChangeListener(javafx.beans.value.ChangeListener) Collections(java.util.Collections) ContentDisplay(javafx.scene.control.ContentDisplay) Group(javafx.scene.Group) ToggleGroup(javafx.scene.control.ToggleGroup) BorderPane(javafx.scene.layout.BorderPane) GridPane(javafx.scene.layout.GridPane) Insets(javafx.geometry.Insets) ColumnConstraints(javafx.scene.layout.ColumnConstraints) ComboBox(javafx.scene.control.ComboBox) ActionEvent(javafx.event.ActionEvent) Tooltip(javafx.scene.control.Tooltip) Label(javafx.scene.control.Label) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) LinkedHashMap(java.util.LinkedHashMap) RowConstraints(javafx.scene.layout.RowConstraints) ImageType(qupath.lib.images.ImageData.ImageType) ToggleGroup(javafx.scene.control.ToggleGroup)

Aggregations

ImageData (qupath.lib.images.ImageData)32 BufferedImage (java.awt.image.BufferedImage)27 Collectors (java.util.stream.Collectors)26 Logger (org.slf4j.Logger)26 LoggerFactory (org.slf4j.LoggerFactory)26 List (java.util.List)25 ArrayList (java.util.ArrayList)23 IOException (java.io.IOException)21 PathObject (qupath.lib.objects.PathObject)21 File (java.io.File)19 Collection (java.util.Collection)19 Dialogs (qupath.lib.gui.dialogs.Dialogs)19 ImageServer (qupath.lib.images.servers.ImageServer)19 Collections (java.util.Collections)17 Map (java.util.Map)17 GeneralTools (qupath.lib.common.GeneralTools)17 QuPathGUI (qupath.lib.gui.QuPathGUI)17 Arrays (java.util.Arrays)16 PathPrefs (qupath.lib.gui.prefs.PathPrefs)15 Bindings (javafx.beans.binding.Bindings)14