Search in sources :

Example 26 with Point2

use of qupath.lib.geom.Point2 in project qupath by qupath.

the class RoiTools method getShapeROI.

/**
 * Create a {@link ROI} from an Shape with a specified 'flatness'.
 * This will try to return a RectangleROI or PolygonROI if possible,
 * or AreaROI if neither of the other classes can adequately represent the area.
 *
 * In the input shape is an Ellipse2D then an EllipseROI will be returned.
 *
 * @param shape
 * @param plane
 * @param flatness - can be used to prefer polygons, see {@code Shape.getPathIterator(AffineTransform, double)}
 * @return
 */
public static ROI getShapeROI(Shape shape, ImagePlane plane, double flatness) {
    if (shape instanceof Rectangle2D) {
        Rectangle2D bounds = shape.getBounds2D();
        return ROIs.createRectangleROI(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), plane);
    }
    if (shape instanceof Ellipse2D) {
        Rectangle2D bounds = shape.getBounds2D();
        return ROIs.createEllipseROI(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), plane);
    }
    if (shape instanceof Line2D) {
        Line2D line = (Line2D) shape;
        return ROIs.createLineROI(line.getX1(), line.getY1(), line.getX2(), line.getY2(), plane);
    }
    boolean isClosed = false;
    List<Point2> points = null;
    if (!(shape instanceof Area)) {
        PathIterator iterator = shape.getPathIterator(null, flatness);
        double[] coords = new double[6];
        points = new ArrayList<>();
        while (!iterator.isDone()) {
            int type = iterator.currentSegment(coords);
            if (type == PathIterator.SEG_CLOSE) {
                isClosed = true;
                break;
            } else
                points.add(new Point2(coords[0], coords[1]));
            iterator.next();
        }
    }
    // (e.g. by checking isRectangular, isPolygonal)
    if (isClosed) {
        Area area;
        if (shape instanceof Area) {
            area = (Area) shape;
        } else
            area = new Area(shape);
        return getShapeROI(area, plane, flatness);
    } else if (points.size() == 2) {
        // Handle straight lines, with only two end points
        Point2 p1 = points.get(0);
        Point2 p2 = points.get(1);
        return ROIs.createLineROI(p1.getX(), p1.getY(), p2.getX(), p2.getY(), plane);
    } else
        // Handle polylines
        return ROIs.createPolylineROI(points, plane);
}
Also used : Area(java.awt.geom.Area) Point2(qupath.lib.geom.Point2) PathIterator(java.awt.geom.PathIterator) Rectangle2D(java.awt.geom.Rectangle2D) Line2D(java.awt.geom.Line2D) Ellipse2D(java.awt.geom.Ellipse2D)

Example 27 with Point2

use of qupath.lib.geom.Point2 in project qupath by qupath.

the class ShapeSimplifier method simplifyPath.

/**
 * Create a simplified path (fewer coordinates) using method based on Visvalingam’s Algorithm.
 * <p>
 * See references:
 * https://hydra.hull.ac.uk/resources/hull:8338
 * https://www.jasondavies.com/simplify/
 * http://bost.ocks.org/mike/simplify/
 *
 * @param path
 * @param altitudeThreshold
 * @return
 */
public static Path2D simplifyPath(Path2D path, double altitudeThreshold) {
    List<Point2> points = new ArrayList<>();
    PathIterator iter = path.getPathIterator(null, 0.5);
    // int nVerticesBefore = 0;
    // int nVerticesAfter = 0;
    Path2D pathNew = new Path2D.Float();
    while (!iter.isDone()) {
        points.clear();
        getNextClosedSegment(iter, points);
        // nVerticesBefore += points.size();
        if (points.isEmpty())
            break;
        ShapeSimplifier.simplifyPolygonPoints(points, altitudeThreshold);
        // nVerticesAfter += points.size();
        boolean firstPoint = true;
        for (Point2 p : points) {
            double xx = p.getX();
            double yy = p.getY();
            if (firstPoint) {
                pathNew.moveTo(xx, yy);
                firstPoint = false;
            } else
                pathNew.lineTo(xx, yy);
        }
        pathNew.closePath();
    }
    return pathNew;
}
Also used : Point2(qupath.lib.geom.Point2) PathIterator(java.awt.geom.PathIterator) Path2D(java.awt.geom.Path2D) ArrayList(java.util.ArrayList)

Example 28 with Point2

use of qupath.lib.geom.Point2 in project qupath by qupath.

the class RoiTools method splitAreaToPolygons.

/**
 * Split Area into PolygonROIs for the exterior and the holes.
 * <p>
 * The first array returned gives the <i>holes</i> and the second the positive regions (admittedly, it might have
 * been more logical the other way around).
 *
 * <pre>
 * {@code
 * var polygons = splitAreaToPolygons(area, -1, 0, 0);
 * var holes = polygons[0];
 * var regions = polygons[1];
 * }
 * </pre>
 *
 * @param area
 * @param c
 * @param z
 * @param t
 * @return
 */
public static PolygonROI[][] splitAreaToPolygons(final Area area, int c, int z, int t) {
    Map<Boolean, List<PolygonROI>> map = new HashMap<>();
    map.put(Boolean.TRUE, new ArrayList<>());
    map.put(Boolean.FALSE, new ArrayList<>());
    PathIterator iter = area.getPathIterator(null, 0.5);
    var plane = ImagePlane.getPlaneWithChannel(c, z, t);
    List<Point2> points = new ArrayList<>();
    double areaTempSigned = 0;
    double areaCached = 0;
    double[] seg = new double[6];
    double startX = Double.NaN, startY = Double.NaN;
    double x0 = 0, y0 = 0, x1 = 0, y1 = 0;
    boolean closed = false;
    while (!iter.isDone()) {
        switch(iter.currentSegment(seg)) {
            case PathIterator.SEG_MOVETO:
                // Log starting positions - need them again for closing the path
                startX = seg[0];
                startY = seg[1];
                x0 = startX;
                y0 = startY;
                iter.next();
                areaCached += areaTempSigned;
                areaTempSigned = 0;
                points.clear();
                points.add(new Point2(startX, startY));
                closed = false;
                continue;
            case PathIterator.SEG_CLOSE:
                x1 = startX;
                y1 = startY;
                closed = true;
                break;
            case PathIterator.SEG_LINETO:
                x1 = seg[0];
                y1 = seg[1];
                points.add(new Point2(x1, y1));
                closed = false;
                break;
            default:
                // Shouldn't happen because of flattened PathIterator
                throw new RuntimeException("Invalid area computation!");
        }
        ;
        areaTempSigned += 0.5 * (x0 * y1 - x1 * y0);
        // Add polygon if it has just been closed
        if (closed) {
            if (areaTempSigned < 0)
                map.get(Boolean.FALSE).add(ROIs.createPolygonROI(points, plane));
            else if (areaTempSigned > 0)
                map.get(Boolean.TRUE).add(ROIs.createPolygonROI(points, plane));
        // Zero indicates the shape is empty...
        }
        // Update the coordinates
        x0 = x1;
        y0 = y1;
        iter.next();
    }
    // TODO: Decide which is positive and which is negative
    areaCached += areaTempSigned;
    PolygonROI[][] polyOutput = new PolygonROI[2][];
    if (areaCached < 0) {
        polyOutput[0] = map.get(Boolean.TRUE).toArray(PolygonROI[]::new);
        polyOutput[1] = map.get(Boolean.FALSE).toArray(PolygonROI[]::new);
    } else {
        polyOutput[0] = map.get(Boolean.FALSE).toArray(PolygonROI[]::new);
        polyOutput[1] = map.get(Boolean.TRUE).toArray(PolygonROI[]::new);
    }
    return polyOutput;
}
Also used : HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) PathIterator(java.awt.geom.PathIterator) ArrayList(java.util.ArrayList) Point2(qupath.lib.geom.Point2) ArrayList(java.util.ArrayList) List(java.util.List)

Example 29 with Point2

use of qupath.lib.geom.Point2 in project qupath by qupath.

the class RoiTools method getLinearPathPoints.

static List<Point2> getLinearPathPoints(final Path2D path, final PathIterator iter) {
    List<Point2> points = new ArrayList<>();
    double[] seg = new double[6];
    while (!iter.isDone()) {
        switch(iter.currentSegment(seg)) {
            case PathIterator.SEG_MOVETO:
            // Fall through
            case PathIterator.SEG_LINETO:
                points.add(new Point2(seg[0], seg[1]));
                break;
            case PathIterator.SEG_CLOSE:
                // points.add(points.get(0));
                break;
            default:
                throw new IllegalArgumentException("Invalid polygon " + path + " - only line connections are allowed");
        }
        ;
        iter.next();
    }
    return points;
}
Also used : Point2(qupath.lib.geom.Point2) ArrayList(java.util.ArrayList)

Example 30 with Point2

use of qupath.lib.geom.Point2 in project qupath by qupath.

the class ConvexHull method getConvexHull.

/**
 * TODO: Consider a more efficient convex hull calculation.
 *
 * For implementation details, see
 * <ul>
 * 	<li>http://en.wikipedia.org/wiki/Gift_wrapping_algorithm</li>
 * 	<li>http://en.wikipedia.org/wiki/Graham_scan</li>
 * </ul>
 *
 * @param points
 * @return
 */
public static List<Point2> getConvexHull(List<Point2> points) {
    if (points == null || points.isEmpty())
        return null;
    // Find the left-most point
    Point2 pointOnHull = points.get(0);
    for (Point2 p : points) {
        if (p.getX() < pointOnHull.getX())
            pointOnHull = p;
    }
    List<Point2> hull = new ArrayList<>(points.size());
    while (true) {
        // logger.info("Adding: " + pointOnHull);
        hull.add(pointOnHull);
        Point2 endPoint = points.get(0);
        double dx = endPoint.getX() - pointOnHull.getX();
        double dy = endPoint.getY() - pointOnHull.getY();
        for (Point2 s : points) {
            double sx = s.getX() - pointOnHull.getX();
            double sy = s.getY() - pointOnHull.getY();
            if ((endPoint.equals(pointOnHull)) || sx * dy - sy * dx > 0) {
                endPoint = s;
                dx = sx;
                dy = sy;
            }
        }
        pointOnHull = endPoint;
        if (endPoint.equals(hull.get(0))) {
            logger.trace("Original points: {}, Convex hull points: {}", points.size(), hull.size());
            return hull;
        }
    // if (endPoint == hull.get(0) || endPoint.distanceSq(hull.get(0)) < 0.00000001)
    // return hull;
    }
}
Also used : Point2(qupath.lib.geom.Point2) ArrayList(java.util.ArrayList)

Aggregations

Point2 (qupath.lib.geom.Point2)33 ArrayList (java.util.ArrayList)19 PathObject (qupath.lib.objects.PathObject)7 List (java.util.List)6 PointsROI (qupath.lib.roi.PointsROI)6 ROI (qupath.lib.roi.interfaces.ROI)6 HashMap (java.util.HashMap)5 ImagePlane (qupath.lib.regions.ImagePlane)5 Map (java.util.Map)4 PathClass (qupath.lib.objects.classes.PathClass)4 PathClassFactory (qupath.lib.objects.classes.PathClassFactory)4 ROIs (qupath.lib.roi.ROIs)4 PathIterator (java.awt.geom.PathIterator)3 IOException (java.io.IOException)3 Arrays (java.util.Arrays)3 HashSet (java.util.HashSet)3 Collectors (java.util.stream.Collectors)3 Test (org.junit.jupiter.api.Test)3 PathObjects (qupath.lib.objects.PathObjects)3 AlphaComposite (java.awt.AlphaComposite)2