Search in sources :

Example 1 with PathTileObject

use of qupath.lib.objects.PathTileObject in project qupath by qupath.

the class PathHierarchyPaintingHelper method paintObject.

/**
 * Paint an object (or, more precisely, its ROI), optionally along with the ROIs of any child objects.
 *
 * This is subject to the OverlayOptions, and therefore may not actually end up painting anything
 * (if the settings are such that objects of the class provided are not to be displayed)
 *
 * @param pathObject
 * @param paintChildren
 * @param g
 * @param boundsDisplayed
 * @param overlayOptions
 * @param selectionModel
 * @param downsample
 * @return true if anything was painted, false otherwise
 */
public static boolean paintObject(PathObject pathObject, boolean paintChildren, Graphics2D g, Rectangle boundsDisplayed, OverlayOptions overlayOptions, PathObjectSelectionModel selectionModel, double downsample) {
    if (pathObject == null)
        return false;
    // g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
    // g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
    // Always paint the selected object
    // Note: this makes the assumption that child ROIs are completely contained within their parents;
    // this probably should be the case, but isn't guaranteed
    boolean isSelected = (selectionModel != null && selectionModel.isSelected(pathObject)) && (PathPrefs.useSelectedColorProperty().get() || !PathObjectTools.hasPointROI(pathObject));
    boolean isDetectedObject = pathObject.isDetection() || (pathObject.isTile() && pathObject.hasMeasurements());
    // Check if the PathClass isn't being shown
    PathClass pathClass = pathObject.getPathClass();
    if (!isSelected && overlayOptions != null && overlayOptions.isPathClassHidden(pathClass))
        return false;
    boolean painted = false;
    // See if we need to check the children
    ROI pathROI = pathObject.getROI();
    if (pathROI != null) {
        double roiBoundsX = pathROI.getBoundsX();
        double roiBoundsY = pathROI.getBoundsY();
        double roiBoundsWidth = pathROI.getBoundsWidth();
        double roiBoundsHeight = pathROI.getBoundsHeight();
        if (PathObjectTools.hasPointROI(pathObject) || boundsDisplayed == null || pathROI instanceof LineROI || boundsDisplayed.intersects(roiBoundsX, roiBoundsY, Math.max(roiBoundsWidth, 1), Math.max(roiBoundsHeight, 1))) {
            // Paint the ROI, if necessary
            if (isSelected || (overlayOptions.getShowDetections() && isDetectedObject) || (overlayOptions.getShowAnnotations() && pathObject.isAnnotation()) || (overlayOptions.getShowTMAGrid() && pathObject.isTMACore())) {
                boolean doFill = overlayOptions.getFillDetections() || pathObject instanceof ParallelTileObject;
                boolean doOutline = true;
                Color color = null;
                boolean useMapper = false;
                double fillOpacity = .75;
                if (isSelected && PathPrefs.useSelectedColorProperty().get() && PathPrefs.colorSelectedObjectProperty().getValue() != null)
                    color = ColorToolsAwt.getCachedColor(PathPrefs.colorSelectedObjectProperty().get());
                else {
                    MeasurementMapper mapper = overlayOptions.getMeasurementMapper();
                    useMapper = mapper != null && mapper.isValid() && pathObject.isDetection();
                    if (useMapper) {
                        if (pathObject.hasMeasurements()) {
                            Integer rgb = mapper.getColorForObject(pathObject);
                            // If the mapper returns null, the object shouldn't be painted
                            if (rgb == null)
                                return false;
                            // , mapper.getColorMapper().hasAlpha());
                            color = ColorToolsAwt.getCachedColor(rgb);
                        } else
                            color = null;
                        // System.out.println(color + " - " + pathObject.getMeasurementList().getMeasurementValue(mapper.));
                        fillOpacity = 1.0;
                        // Outlines are not so helpful with the measurement mapper
                        if (doFill)
                            doOutline = doOutline && !pathObject.isTile();
                    } else {
                        Integer rgb = ColorToolsFX.getDisplayedColorARGB(pathObject);
                        color = ColorToolsAwt.getCachedColor(rgb);
                    }
                // color = PathObjectHelpers.getDisplayedColor(pathObject);
                }
                // Check if we have only one or two pixels to draw - if so, we can be done quickly
                if (isDetectedObject && downsample > 4 && roiBoundsWidth / downsample < 3 && roiBoundsHeight / downsample < 3) {
                    int x = (int) roiBoundsX;
                    int y = (int) roiBoundsY;
                    // Prefer rounding up, lest we lose a lot of regions unnecessarily
                    int w = (int) (roiBoundsWidth + .9);
                    int h = (int) (roiBoundsHeight + .9);
                    if (w > 0 && h > 0) {
                        g.setColor(color);
                        // g.setColor(DisplayHelpers.getMoreTranslucentColor(color));
                        // g.setStroke(getCachedStroke(overlayOptions.strokeThinThicknessProperty().get()));
                        g.fillRect(x, y, w, h);
                    }
                    painted = true;
                } else {
                    Stroke stroke = null;
                    // Decide whether to fill or not
                    Color colorFill = doFill && (isDetectedObject || PathObjectTools.hasPointROI(pathObject)) ? color : null;
                    if (colorFill != null && fillOpacity != 1) {
                        if (pathObject instanceof ParallelTileObject)
                            colorFill = ColorToolsAwt.getMoreTranslucentColor(colorFill);
                        else if (pathObject instanceof PathCellObject && overlayOptions.getShowCellBoundaries() && overlayOptions.getShowCellNuclei()) {
                            // if (isSelected)
                            // colorFill = ColorToolsAwt.getTranslucentColor(colorFill);
                            // else
                            colorFill = ColorToolsAwt.getMoreTranslucentColor(colorFill);
                        } else if (pathObject.getParent() instanceof PathDetectionObject) {
                            colorFill = ColorToolsAwt.getTranslucentColor(colorFill);
                        } else if (pathObject instanceof PathTileObject && pathClass == null && color != null && color.getRGB() == PathPrefs.colorTileProperty().get()) {
                            // Don't fill in empty, unclassified tiles
                            // DisplayHelpers.getMoreTranslucentColor(colorFill);
                            colorFill = null;
                        }
                    }
                    // Color colorStroke = doOutline ? (colorFill == null ? color : (downsample > overlayOptions.strokeThinThicknessProperty().get() ? null : DisplayHelpers.darkenColor(color))) : null;
                    Color colorStroke = doOutline ? (colorFill == null ? color : ColorToolsAwt.darkenColor(color)) : null;
                    // For thick lines, antialiasing is very noticeable... less so for thin lines (of which there may be a huge number)
                    if (isDetectedObject) {
                        // Detections inside detections get half the line width
                        if (pathObject.getParent() instanceof PathDetectionObject)
                            stroke = getCachedStroke(PathPrefs.detectionStrokeThicknessProperty().get() / 2.0);
                        else
                            stroke = getCachedStroke(PathPrefs.detectionStrokeThicknessProperty().get());
                    } else {
                        double thicknessScale = downsample * (isSelected && !PathPrefs.useSelectedColorProperty().get() ? 1.6 : 1);
                        float thickness = (float) (PathPrefs.annotationStrokeThicknessProperty().get() * thicknessScale);
                        if (isSelected && pathObject.getParent() == null && PathPrefs.selectionModeProperty().get()) {
                            stroke = getCachedStrokeDashed(thickness);
                        } else {
                            stroke = getCachedStroke(thickness);
                        }
                    }
                    g.setStroke(stroke);
                    boolean paintSymbols = overlayOptions.getDetectionDisplayMode() == DetectionDisplayMode.CENTROIDS && pathObject.isDetection() && !pathObject.isTile();
                    if (paintSymbols) {
                        pathROI = PathObjectTools.getROI(pathObject, true);
                        double x = pathROI.getCentroidX();
                        double y = pathROI.getCentroidY();
                        double radius = PathPrefs.detectionStrokeThicknessProperty().get() * 2.0;
                        if (pathObject.getParent() instanceof PathDetectionObject)
                            radius /= 2.0;
                        Shape shape;
                        int nSubclasses = 0;
                        if (pathClass != null) {
                            nSubclasses = PathClassTools.splitNames(pathClass).size();
                        }
                        switch(nSubclasses) {
                            case 0:
                                var ellipse = localEllipse2D.get();
                                ellipse.setFrame(x - radius, y - radius, radius * 2, radius * 2);
                                shape = ellipse;
                                break;
                            case 1:
                                var rect = localRect2D.get();
                                rect.setFrame(x - radius, y - radius, radius * 2, radius * 2);
                                shape = rect;
                                break;
                            case 2:
                                var triangle = localPath2D.get();
                                double sqrt3 = Math.sqrt(3.0);
                                triangle.reset();
                                triangle.moveTo(x, y - radius * 2.0 / sqrt3);
                                triangle.lineTo(x - radius, y + radius / sqrt3);
                                triangle.lineTo(x + radius, y + radius / sqrt3);
                                triangle.closePath();
                                shape = triangle;
                                break;
                            case 3:
                                var plus = localPath2D.get();
                                plus.reset();
                                plus.moveTo(x, y - radius);
                                plus.lineTo(x, y + radius);
                                plus.moveTo(x - radius, y);
                                plus.lineTo(x + radius, y);
                                shape = plus;
                                break;
                            default:
                                var cross = localPath2D.get();
                                cross.reset();
                                radius /= Math.sqrt(2);
                                cross.moveTo(x - radius, y - radius);
                                cross.lineTo(x + radius, y + radius);
                                cross.moveTo(x + radius, y - radius);
                                cross.lineTo(x - radius, y + radius);
                                shape = cross;
                                break;
                        }
                        paintShape(shape, g, colorStroke, stroke, colorFill);
                    } else if (pathObject instanceof PathCellObject) {
                        PathCellObject cell = (PathCellObject) pathObject;
                        if (overlayOptions.getShowCellBoundaries())
                            paintROI(pathROI, g, colorStroke, stroke, colorFill, downsample);
                        if (overlayOptions.getShowCellNuclei())
                            paintROI(cell.getNucleusROI(), g, colorStroke, stroke, colorFill, downsample);
                        painted = true;
                    } else {
                        if ((overlayOptions.getFillAnnotations() && pathObject.isAnnotation() && pathObject.getPathClass() != PathClassFactory.getPathClass(StandardPathClasses.REGION) && (pathObject.getPathClass() != null || !pathObject.hasChildren())) || (pathObject.isTMACore() && overlayOptions.getShowTMACoreLabels()))
                            paintROI(pathROI, g, colorStroke, stroke, ColorToolsAwt.getMoreTranslucentColor(colorStroke), downsample);
                        else
                            paintROI(pathROI, g, colorStroke, stroke, colorFill, downsample);
                        painted = true;
                    }
                }
            }
        }
    }
    // Paint the children, if necessary
    if (paintChildren) {
        for (PathObject childObject : pathObject.getChildObjectsAsArray()) {
            // Only call the painting method if required
            ROI childROI = childObject.getROI();
            if ((childROI != null && boundsDisplayed.intersects(childROI.getBoundsX(), childROI.getBoundsY(), childROI.getBoundsWidth(), childROI.getBoundsHeight())) || childObject.hasChildren())
                painted = paintObject(childObject, paintChildren, g, boundsDisplayed, overlayOptions, selectionModel, downsample) | painted;
        }
    }
    return painted;
}
Also used : BasicStroke(java.awt.BasicStroke) Stroke(java.awt.Stroke) PathDetectionObject(qupath.lib.objects.PathDetectionObject) Shape(java.awt.Shape) RectangularShape(java.awt.geom.RectangularShape) Color(java.awt.Color) EllipseROI(qupath.lib.roi.EllipseROI) PointsROI(qupath.lib.roi.PointsROI) RectangleROI(qupath.lib.roi.RectangleROI) LineROI(qupath.lib.roi.LineROI) ROI(qupath.lib.roi.interfaces.ROI) ParallelTileObject(qupath.lib.plugins.ParallelTileObject) PathClass(qupath.lib.objects.classes.PathClass) PathTileObject(qupath.lib.objects.PathTileObject) PathObject(qupath.lib.objects.PathObject) MeasurementMapper(qupath.lib.gui.tools.MeasurementMapper) LineROI(qupath.lib.roi.LineROI) PathCellObject(qupath.lib.objects.PathCellObject)

Example 2 with PathTileObject

use of qupath.lib.objects.PathTileObject 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 3 with PathTileObject

use of qupath.lib.objects.PathTileObject in project qupath by qupath.

the class SmoothFeaturesPlugin method addRunnableTasks.

@Override
protected void addRunnableTasks(final ImageData<T> imageData, final PathObject parentObject, List<Runnable> tasks) {
    double fwhm;
    ImageServer<T> server = imageData.getServer();
    String fwhmStringTemp;
    PixelCalibration cal = server == null ? null : server.getPixelCalibration();
    if (cal != null && cal.hasPixelSizeMicrons()) {
        fwhm = getParameterList(imageData).getDoubleParameterValue("fwhmMicrons");
        fwhmStringTemp = GeneralTools.createFormatter(2).format(fwhm) + " " + GeneralTools.micrometerSymbol();
        fwhm /= cal.getAveragedPixelSizeMicrons();
    // params.addDoubleParameter("fwhmPixels", "Radius (FWHM)", fwhm, "pixels"); // Set the FWHM in pixels too
    } else {
        fwhm = getParameterList(imageData).getDoubleParameterValue("fwhmPixels");
        fwhmStringTemp = GeneralTools.createFormatter(2).format(fwhm) + " px";
    }
    // sigma = 50;
    final String fwhmString = fwhmStringTemp;
    final double fwhmPixels = fwhm;
    final boolean withinClass = params.getBooleanParameterValue("smoothWithinClasses");
    final boolean useLegacyNames = params.containsKey("useLegacyNames") && Boolean.TRUE.equals(params.getBooleanParameterValue("useLegacyNames"));
    tasks.add(new Runnable() {

        @Override
        public void run() {
            try {
                if (!parentObject.hasChildren())
                    return;
                // System.out.println("Smoothing with FWHM " +fwhmPixels);
                // TODO: MAKE A MORE ELEGANT LIST!!!!
                List<PathObject> pathObjects = PathObjectTools.getFlattenedObjectList(parentObject, null, false);
                Iterator<PathObject> iterObjects = pathObjects.iterator();
                while (iterObjects.hasNext()) {
                    PathObject temp = iterObjects.next();
                    if (!(temp instanceof PathDetectionObject || temp instanceof PathTileObject))
                        iterObjects.remove();
                }
                if (pathObjects.isEmpty())
                    return;
                // TODO: ACCESS & USE THE CLASSIFIER DATA!!!!
                List<String> measurements = new ArrayList<>(PathClassifierTools.getAvailableFeatures(pathObjects));
                Iterator<String> iter = measurements.iterator();
                while (iter.hasNext()) {
                    String name = iter.next().toLowerCase();
                    if (name.endsWith("smoothed") || name.startsWith("smoothed") || name.contains(" - smoothed (fwhm ") || name.startsWith("smoothed denominator (local density, ") || name.startsWith("nearby detection counts"))
                        iter.remove();
                }
                logger.debug(String.format("Smooth features: %s (FWHM: %.2f px)", parentObject.getDisplayedName(), fwhmPixels));
                smoothMeasurements(pathObjects, measurements, fwhmPixels, fwhmString, withinClass, useLegacyNames);
            // // REMOVE - the purpose was to test a 'difference of Gaussians' type of thing
            // List<String> namesAdded1 = new ArrayList<>(smoothMeasurements(pathObjects, measurements, fwhmPixels));
            // List<String> namesAdded2 = new ArrayList<>(smoothMeasurements(pathObjects, measurements, fwhmPixels * 2));
            // for (PathObject pathObject : pathObjects) {
            // MeasurementList ml = pathObject.getMeasurementList();
            // ml.ensureListOpen();
            // for (int i = 0; i < namesAdded1.size(); i++) {
            // String name1 = namesAdded1.get(i);
            // String name2 = namesAdded2.get(i);
            // double m1 = ml.getMeasurementValue(name1);
            // double m2 = ml.getMeasurementValue(name2);
            // ml.addMeasurement(name1 + " - " + name2, m1 - m2);
            // }
            // ml.closeList();
            // }
            } catch (Exception e) {
                e.printStackTrace();
                throw (e);
            }
        }
    });
}
Also used : PathDetectionObject(qupath.lib.objects.PathDetectionObject) PixelCalibration(qupath.lib.images.servers.PixelCalibration) PathTileObject(qupath.lib.objects.PathTileObject) PathObject(qupath.lib.objects.PathObject) Iterator(java.util.Iterator) ArrayList(java.util.ArrayList) MeasurementList(qupath.lib.measurements.MeasurementList) ParameterList(qupath.lib.plugins.parameters.ParameterList) List(java.util.List)

Example 4 with PathTileObject

use of qupath.lib.objects.PathTileObject in project qupath by qupath.

the class ColorToolsFX method getDisplayedColorARGB.

/**
 * Get the color with which an object should be displayed, as a packaged ARGB integer.
 *
 * This could be stored internally, or obtained from its PathClass.
 *
 * If neither of these produces a result, a default color will be returned based on PathPrefs
 * for the specific (Java) class of the PathObject.
 *
 * Assuming PathPrefs does not contain any nulls, this will therefore not return nulls either.
 *
 * @param pathObject
 * @return
 */
public static Integer getDisplayedColorARGB(final PathObject pathObject) {
    // Check if any color has been set - if so, return it
    Integer color = pathObject.getColorRGB();
    if (color != null)
        return color;
    // Check if any class has been set, if so then use its color
    PathClass pathClass = pathObject.getPathClass();
    if (pathClass != null)
        color = pathClass.getColor();
    if (color != null)
        return color;
    if (pathObject instanceof PathTileObject)
        return PathPrefs.colorTileProperty().getValue();
    if (pathObject instanceof TMACoreObject) {
        if (((TMACoreObject) pathObject).isMissing())
            return PathPrefs.colorTMAMissingProperty().getValue();
        else
            return PathPrefs.colorTMAProperty().getValue();
    }
    return PathPrefs.colorDefaultObjectsProperty().getValue();
}
Also used : PathClass(qupath.lib.objects.classes.PathClass) PathTileObject(qupath.lib.objects.PathTileObject) TMACoreObject(qupath.lib.objects.TMACoreObject)

Aggregations

PathTileObject (qupath.lib.objects.PathTileObject)4 PathObject (qupath.lib.objects.PathObject)3 PathDetectionObject (qupath.lib.objects.PathDetectionObject)2 PathClass (qupath.lib.objects.classes.PathClass)2 RectangleROI (qupath.lib.roi.RectangleROI)2 BasicStroke (java.awt.BasicStroke)1 Color (java.awt.Color)1 Shape (java.awt.Shape)1 Stroke (java.awt.Stroke)1 RectangularShape (java.awt.geom.RectangularShape)1 ArrayList (java.util.ArrayList)1 Iterator (java.util.Iterator)1 List (java.util.List)1 Coordinate (org.locationtech.jts.geom.Coordinate)1 Geometry (org.locationtech.jts.geom.Geometry)1 GeometricShapeFactory (org.locationtech.jts.util.GeometricShapeFactory)1 MeasurementMapper (qupath.lib.gui.tools.MeasurementMapper)1 PixelCalibration (qupath.lib.images.servers.PixelCalibration)1 MeasurementList (qupath.lib.measurements.MeasurementList)1 PathCellObject (qupath.lib.objects.PathCellObject)1