Search in sources :

Example 1 with RectangleROI

use of qupath.lib.roi.RectangleROI in project qupath by qupath.

the class IconFactory method createROIIcon.

/**
 * Create an icon depicting a ROI.
 * @param roi the region of interest
 * @param width the preferred icon width
 * @param height the preferred icon height
 * @param color the icon (line) color
 * @return a node that may be used as an icon resembling the shape of the ROI
 */
public static Node createROIIcon(ROI roi, int width, int height, Color color) {
    double scale = Math.min(width / roi.getBoundsWidth(), height / roi.getBoundsHeight());
    if (roi instanceof RectangleROI) {
        Rectangle rect = new Rectangle(0, 0, roi.getBoundsWidth() * scale, roi.getBoundsHeight() * scale);
        rect.setStroke(color);
        rect.setFill(null);
        return rect;
    } else if (roi instanceof EllipseROI) {
        double w = roi.getBoundsWidth() * scale;
        double h = roi.getBoundsHeight() * scale;
        Ellipse ellipse = new Ellipse(w / 2, height / 2, w / 2, h / 2);
        ellipse.setStroke(color);
        ellipse.setFill(null);
        return ellipse;
    } else if (roi instanceof LineROI) {
        LineROI l = (LineROI) roi;
        double xMin = Math.min(l.getX1(), l.getX2());
        double yMin = Math.min(l.getY1(), l.getY2());
        Line line = new Line((l.getX1() - xMin) * scale, (l.getY1() - yMin) * scale, (l.getX2() - xMin) * scale, (l.getY2() - yMin) * scale);
        line.setStroke(color);
        line.setFill(null);
        return line;
    } else if (roi.isPoint()) {
        // Just show generic points
        Node node = IconFactory.createNode(Math.min(width, height), Math.min(width, height), IconFactory.PathIcons.POINTS_TOOL);
        if (node instanceof Glyph) {
            var glyph = (Glyph) node;
            glyph.textFillProperty().unbind();
            glyph.setColor(color);
        }
        return node;
    } else {
        var path = pathCache.getOrDefault(roi, null);
        if (path == null) {
            var shape = roi.isArea() ? RoiTools.getArea(roi) : RoiTools.getShape(roi);
            if (shape != null) {
                var transform = new AffineTransform();
                transform.translate(-roi.getBoundsX(), -roi.getBoundsY());
                transform.scale(scale, scale);
                PathIterator iterator = shape.getPathIterator(transform, Math.max(0.5, 1.0 / scale));
                path = createShapeIcon(iterator, color);
                pathCache.put(roi, path);
            }
        } else {
            path = new Path(path.getElements());
            path.setStroke(color);
        }
        if (path != null)
            return path;
    }
    logger.warn("Unable to create icon for ROI: {}", roi);
    return null;
}
Also used : Path(javafx.scene.shape.Path) ClosePath(javafx.scene.shape.ClosePath) Ellipse(javafx.scene.shape.Ellipse) PathIterator(java.awt.geom.PathIterator) Node(javafx.scene.Node) Rectangle(javafx.scene.shape.Rectangle) Line(javafx.scene.shape.Line) RectangleROI(qupath.lib.roi.RectangleROI) EllipseROI(qupath.lib.roi.EllipseROI) Glyph(org.controlsfx.glyphfont.Glyph) AffineTransform(java.awt.geom.AffineTransform) LineROI(qupath.lib.roi.LineROI)

Example 2 with RectangleROI

use of qupath.lib.roi.RectangleROI in project qupath by qupath.

the class IJTools method convertToIJRoi.

/**
 * Convert a QuPath ROI to an ImageJ Roi.
 * @param <T>
 * @param pathROI
 * @param xOrigin x-origin indicating relationship of ImagePlus to the original image, as stored in ImageJ Calibration object
 * @param yOrigin y-origin indicating relationship of ImagePlus to the original image, as stored in ImageJ Calibration object
 * @param downsampleFactor downsample factor at which the ImagePlus was extracted from the full-resolution image
 * @return
 */
public static <T extends PathImage<ImagePlus>> Roi convertToIJRoi(ROI pathROI, double xOrigin, double yOrigin, double downsampleFactor) {
    if (pathROI instanceof PolygonROI)
        return ROIConverterIJ.convertToPolygonROI((PolygonROI) pathROI, xOrigin, yOrigin, downsampleFactor);
    if (pathROI instanceof RectangleROI)
        return ROIConverterIJ.getRectangleROI((RectangleROI) pathROI, xOrigin, yOrigin, downsampleFactor);
    if (pathROI instanceof EllipseROI)
        return ROIConverterIJ.convertToOvalROI((EllipseROI) pathROI, xOrigin, yOrigin, downsampleFactor);
    if (pathROI instanceof LineROI)
        return ROIConverterIJ.convertToLineROI((LineROI) pathROI, xOrigin, yOrigin, downsampleFactor);
    if (pathROI instanceof PolylineROI)
        return ROIConverterIJ.convertToPolygonROI((PolylineROI) pathROI, xOrigin, yOrigin, downsampleFactor);
    if (pathROI instanceof PointsROI)
        return ROIConverterIJ.convertToPointROI((PointsROI) pathROI, xOrigin, yOrigin, downsampleFactor);
    // If we have any other kind of shape, create a general shape roi
    if (pathROI != null && pathROI.isArea()) {
        // TODO: Deal with non-AWT area ROIs!
        Shape shape = RoiTools.getArea(pathROI);
        // "scaleX", "shearY", "shearX", "scaleY", "translateX", "translateY"
        shape = new AffineTransform(1.0 / downsampleFactor, 0, 0, 1.0 / downsampleFactor, xOrigin, yOrigin).createTransformedShape(shape);
        return ROIConverterIJ.setIJRoiProperties(new ShapeRoi(shape), pathROI);
    }
    // TODO: Integrate ROI not supported exception...?
    return null;
}
Also used : PolygonROI(qupath.lib.roi.PolygonROI) ShapeRoi(ij.gui.ShapeRoi) RectangleROI(qupath.lib.roi.RectangleROI) Shape(java.awt.Shape) EllipseROI(qupath.lib.roi.EllipseROI) PolylineROI(qupath.lib.roi.PolylineROI) PointsROI(qupath.lib.roi.PointsROI) AffineTransform(java.awt.geom.AffineTransform) LineROI(qupath.lib.roi.LineROI)

Example 3 with RectangleROI

use of qupath.lib.roi.RectangleROI in project qupath by qupath.

the class BrushTool method createShape.

/**
 * Create a new Geometry using the specified tool, assuming a user click/drag at the provided x &amp; y coordinates.
 * @param e
 *
 * @param x
 * @param y
 * @param useTiles If true, request generating a shape from existing tile objects.
 * @param addToShape If provided, it can be assumed that any new shape ought to be added to this one.
 *                   The purpose is that this method may (optionally) use the shape to refine the one it will generate,
 *                   e.g. to avoid having isolated or jagged boundaries.
 * @return
 */
protected Geometry createShape(MouseEvent e, double x, double y, boolean useTiles, Geometry addToShape) {
    // See if we're on top of a tile
    if (useTiles) {
        List<PathObject> listSelectable = getSelectableObjectList(x, y);
        for (PathObject temp : listSelectable) {
            // if ((temp instanceof PathDetectionObject) && temp.getROI() instanceof PathArea)
            if (temp instanceof PathTileObject && temp.hasROI() && temp.getROI().isArea() && !(temp.getROI() instanceof RectangleROI)) {
                creatingTiledROI = true;
                return temp.getROI().getGeometry();
            }
        }
        // If we're currently creating a tiled, ROI, but now not clicked on a tile, just return
        if (creatingTiledROI)
            return null;
    }
    // Compute a diameter scaled according to the pressure being applied
    double diameter = Math.max(1, getBrushDiameter());
    Geometry geometry;
    if (lastPoint == null) {
        var shapeFactory = new GeometricShapeFactory(getGeometryFactory());
        shapeFactory.setCentre(new Coordinate(x, y));
        shapeFactory.setSize(diameter);
        // shapeFactory.setCentre(new Coordinate(x-diameter/2, y-diameter/2));
        geometry = shapeFactory.createEllipse();
    } else {
        if (lastPoint.distanceSq(x, y) == 0)
            return null;
        var factory = getGeometryFactory();
        geometry = factory.createLineString(new Coordinate[] { new Coordinate(lastPoint.getX(), lastPoint.getY()), new Coordinate(x, y) }).buffer(diameter / 2.0);
    }
    return geometry;
}
Also used : Geometry(org.locationtech.jts.geom.Geometry) PathTileObject(qupath.lib.objects.PathTileObject) PathObject(qupath.lib.objects.PathObject) RectangleROI(qupath.lib.roi.RectangleROI) GeometricShapeFactory(org.locationtech.jts.util.GeometricShapeFactory) Coordinate(org.locationtech.jts.geom.Coordinate)

Example 4 with RectangleROI

use of qupath.lib.roi.RectangleROI in project qupath by qupath.

the class ImageDetailsPane method editStainVector.

void editStainVector(Object value) {
    if (!(value instanceof StainVector || value instanceof double[]))
        return;
    // JOptionPane.showMessageDialog(null, "Modifying stain vectors not yet implemented...");
    ImageData<BufferedImage> imageData = qupath.getImageData();
    if (imageData == null)
        return;
    ColorDeconvolutionStains stains = imageData.getColorDeconvolutionStains();
    // Default to background values
    int num = -1;
    String name = null;
    String message = null;
    if (value instanceof StainVector) {
        StainVector stainVector = (StainVector) value;
        if (stainVector.isResidual() && imageData.getImageType() != ImageType.BRIGHTFIELD_OTHER) {
            logger.warn("Cannot set residual stain vector - this is computed from the known vectors");
            return;
        }
        num = stains.getStainNumber(stainVector);
        if (num <= 0) {
            logger.error("Could not identify stain vector " + stainVector + " inside " + stains);
            return;
        }
        name = stainVector.getName();
        message = "Set stain vector from ROI?";
    } else
        message = "Set color deconvolution background values from ROI?";
    ROI pathROI = imageData.getHierarchy().getSelectionModel().getSelectedROI();
    boolean wasChanged = false;
    String warningMessage = null;
    boolean editableName = imageData.getImageType() == ImageType.BRIGHTFIELD_OTHER;
    if (pathROI != null) {
        if ((pathROI instanceof RectangleROI) && !pathROI.isEmpty() && ((RectangleROI) pathROI).getArea() < 500 * 500) {
            if (Dialogs.showYesNoDialog("Color deconvolution stains", message)) {
                ImageServer<BufferedImage> server = imageData.getServer();
                BufferedImage img = null;
                try {
                    img = server.readBufferedImage(RegionRequest.createInstance(server.getPath(), 1, pathROI));
                } catch (IOException e) {
                    Dialogs.showErrorMessage("Set stain vector", "Unable to read image region");
                    logger.error("Unable to read region", e);
                }
                int rgb = ColorDeconvolutionHelper.getMedianRGB(img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth()));
                if (num >= 0) {
                    StainVector vectorValue = ColorDeconvolutionHelper.generateMedianStainVectorFromPixels(name, img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth()), stains.getMaxRed(), stains.getMaxGreen(), stains.getMaxBlue());
                    if (!Double.isFinite(vectorValue.getRed() + vectorValue.getGreen() + vectorValue.getBlue())) {
                        Dialogs.showErrorMessage("Set stain vector", "Cannot set stains for the current ROI!\n" + "It might be too close to the background color.");
                        return;
                    }
                    value = vectorValue;
                } else {
                    // Update the background
                    value = new double[] { ColorTools.red(rgb), ColorTools.green(rgb), ColorTools.blue(rgb) };
                }
                wasChanged = true;
            }
        } else {
            warningMessage = "Note: To set stain values from an image region, draw a small, rectangular ROI first";
        }
    }
    // Prompt to set the name / verify stains
    ParameterList params = new ParameterList();
    String title;
    String nameBefore = null;
    String valuesBefore = null;
    String collectiveNameBefore = stains.getName();
    String suggestedName;
    if (collectiveNameBefore.endsWith("default"))
        suggestedName = collectiveNameBefore.substring(0, collectiveNameBefore.lastIndexOf("default")) + "modified";
    else
        suggestedName = collectiveNameBefore;
    params.addStringParameter("collectiveName", "Collective name", suggestedName, "Enter collective name for all 3 stains (e.g. H-DAB Scanner A, H&E Scanner B)");
    if (value instanceof StainVector) {
        nameBefore = ((StainVector) value).getName();
        valuesBefore = ((StainVector) value).arrayAsString(Locale.getDefault(Category.FORMAT));
        params.addStringParameter("name", "Name", nameBefore, "Enter stain name").addStringParameter("values", "Values", valuesBefore, "Enter 3 values (red, green, blue) defining color deconvolution stain vector, separated by spaces");
        title = "Set stain vector";
    } else {
        nameBefore = "Background";
        valuesBefore = GeneralTools.arrayToString(Locale.getDefault(Category.FORMAT), (double[]) value, 2);
        params.addStringParameter("name", "Stain name", nameBefore);
        params.addStringParameter("values", "Stain values", valuesBefore, "Enter 3 values (red, green, blue) defining background, separated by spaces");
        params.setHiddenParameters(true, "name");
        title = "Set background";
    }
    if (warningMessage != null)
        params.addEmptyParameter(warningMessage);
    // Disable editing the name if it should be fixed
    ParameterPanelFX parameterPanel = new ParameterPanelFX(params);
    parameterPanel.setParameterEnabled("name", editableName);
    ;
    if (!Dialogs.showConfirmDialog(title, parameterPanel.getPane()))
        return;
    // Check if anything changed
    String collectiveName = params.getStringParameterValue("collectiveName");
    String nameAfter = params.getStringParameterValue("name");
    String valuesAfter = params.getStringParameterValue("values");
    if (collectiveName.equals(collectiveNameBefore) && nameAfter.equals(nameBefore) && valuesAfter.equals(valuesBefore) && !wasChanged)
        return;
    double[] valuesParsed = ColorDeconvolutionStains.parseStainValues(Locale.getDefault(Category.FORMAT), valuesAfter);
    if (valuesParsed == null) {
        logger.error("Input for setting color deconvolution information invalid! Cannot parse 3 numbers from {}", valuesAfter);
        return;
    }
    if (num >= 0) {
        try {
            stains = stains.changeStain(StainVector.createStainVector(nameAfter, valuesParsed[0], valuesParsed[1], valuesParsed[2]), num);
        } catch (Exception e) {
            logger.error("Error setting stain vectors", e);
            Dialogs.showErrorMessage("Set stain vectors", "Requested stain vectors are not valid!\nAre two stains equal?");
        }
    } else {
        // Update the background
        stains = stains.changeMaxValues(valuesParsed[0], valuesParsed[1], valuesParsed[2]);
    }
    // Set the collective name
    stains = stains.changeName(collectiveName);
    imageData.setColorDeconvolutionStains(stains);
    qupath.getViewer().repaintEntireImage();
}
Also used : StainVector(qupath.lib.color.StainVector) IOException(java.io.IOException) RectangleROI(qupath.lib.roi.RectangleROI) ROI(qupath.lib.roi.interfaces.ROI) BufferedImage(java.awt.image.BufferedImage) ParameterPanelFX(qupath.lib.gui.dialogs.ParameterPanelFX) IOException(java.io.IOException) RectangleROI(qupath.lib.roi.RectangleROI) ParameterList(qupath.lib.plugins.parameters.ParameterList) ColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains)

Example 5 with RectangleROI

use of qupath.lib.roi.RectangleROI in project qupath by qupath.

the class PixelClassifierPane method showOutput.

private boolean showOutput() {
    if (overlay == null) {
        Dialogs.showErrorMessage("Show output", "No pixel classifier has been trained yet!");
        return false;
    }
    var viewer = qupath.getViewer();
    var imageData = viewer.getImageData();
    var server = imageData == null ? null : overlay.getPixelClassificationServer(imageData);
    if (server == null)
        return false;
    var selected = viewer.getSelectedObject();
    var roi = selected == null ? null : selected.getROI();
    double downsample = server.getDownsampleForResolution(0);
    RegionRequest request;
    if (roi == null) {
        request = RegionRequest.createInstance(server.getPath(), downsample, 0, 0, server.getWidth(), server.getHeight(), viewer.getZPosition(), viewer.getTPosition());
    } else {
        request = RegionRequest.createInstance(server.getPath(), downsample, selected.getROI());
    }
    long estimatedPixels = (long) Math.ceil(request.getWidth() / request.getDownsample()) * (long) Math.ceil(request.getHeight() / request.getDownsample());
    double estimatedMB = (estimatedPixels * server.nChannels() * (server.getPixelType().getBytesPerPixel())) / (1024.0 * 1024.0);
    if (estimatedPixels >= Integer.MAX_VALUE - 16) {
        Dialogs.showErrorMessage("Extract output", "Requested region is too big! Try selecting a smaller region.");
        return false;
    } else if (estimatedMB >= 200.0) {
        if (!Dialogs.showConfirmDialog("Extract output", String.format("Extracting this region will require approximately %.1f MB - are you sure you want to try this?", estimatedMB)))
            return false;
    }
    try {
        // var imp = IJExtension.extractROI(server, selected, request, true, null).getImage();
        var pathImage = IJTools.convertToImagePlus(server, request);
        var imp = pathImage.getImage();
        if (imp instanceof CompositeImage && server.getMetadata().getChannelType() != ChannelType.CLASSIFICATION)
            ((CompositeImage) imp).setDisplayMode(CompositeImage.GRAYSCALE);
        if (roi != null && !(roi instanceof RectangleROI)) {
            imp.setRoi(IJTools.convertToIJRoi(roi, pathImage));
        }
        IJExtension.getImageJInstance();
        imp.show();
        return true;
    } catch (IOException e) {
        logger.error("Error showing output", e);
    }
    return false;
}
Also used : RectangleROI(qupath.lib.roi.RectangleROI) CompositeImage(ij.CompositeImage) IOException(java.io.IOException) RegionRequest(qupath.lib.regions.RegionRequest)

Aggregations

RectangleROI (qupath.lib.roi.RectangleROI)8 PathObject (qupath.lib.objects.PathObject)4 ROI (qupath.lib.roi.interfaces.ROI)4 BufferedImage (java.awt.image.BufferedImage)3 IOException (java.io.IOException)3 LineROI (qupath.lib.roi.LineROI)3 Shape (java.awt.Shape)2 AffineTransform (java.awt.geom.AffineTransform)2 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)2 RegionRequest (qupath.lib.regions.RegionRequest)2 EllipseROI (qupath.lib.roi.EllipseROI)2 PolygonROI (qupath.lib.roi.PolygonROI)2 CompositeImage (ij.CompositeImage)1 ImagePlus (ij.ImagePlus)1 Roi (ij.gui.Roi)1 ShapeRoi (ij.gui.ShapeRoi)1 Interpreter (ij.macro.Interpreter)1 Calibration (ij.measure.Calibration)1 AlphaComposite (java.awt.AlphaComposite)1 Color (java.awt.Color)1