use of qupath.lib.geom.Point2 in project qupath by qupath.
the class WindingTest method getWindingNumber.
public static int getWindingNumber(final List<Point2> points, final double x, final double y) {
if (points.size() <= 2)
return 0;
int nPoints = points.size();
int wn = 0;
for (int i = 0; i < nPoints; i++) {
Point2 p = points.get(i);
Point2 p2 = points.get((i + 1) % nPoints);
if (p.getY() <= y) {
// start y <= P.y
if (// an upward crossing
p2.getY() > y)
if (// P left of edge
isLeft(p.getX(), p.getY(), p2.getX(), p2.getY(), x, y) > 0)
// have a valid up intersect
wn++;
} else {
// start y > P.y (no test needed)
if (// a downward crossing
p2.getY() <= y)
if (// P right of edge
isLeft(p.getX(), p.getY(), p2.getX(), p2.getY(), x, y) < 0)
// have a valid down intersect
wn--;
}
}
return wn;
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class WindingTest method getWindingNumber.
public static int getWindingNumber(final Vertices vertices, final double x, final double y) {
if (vertices.size() <= 2)
return 0;
int nPoints = vertices.size();
int wn = 0;
for (int i = 0; i < nPoints; i++) {
Point2 p = vertices.get(i);
Point2 p2 = vertices.get((i + 1) % nPoints);
if (p.getY() <= y) {
// start y <= P.y
if (// an upward crossing
p2.getY() > y)
if (// P left of edge
isLeft(p.getX(), p.getY(), p2.getX(), p2.getY(), x, y) > 0)
// have a valid up intersect
wn++;
} else {
// start y > P.y (no test needed)
if (// a downward crossing
p2.getY() <= y)
if (// P right of edge
isLeft(p.getX(), p.getY(), p2.getX(), p2.getY(), x, y) < 0)
// have a valid down intersect
wn--;
}
}
return wn;
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class ShapeSimplifier method smoothPoints.
/**
* Apply a simple 3-point moving average to a list of points.
*
* @param points
* @return
*/
public static List<Point2> smoothPoints(List<Point2> points) {
List<Point2> points2 = new ArrayList<>(points.size());
for (int i = 0; i < points.size(); i++) {
Point2 p1 = points.get((i + points.size() - 1) % points.size());
Point2 p2 = points.get(i);
Point2 p3 = points.get((i + 1) % points.size());
points2.add(new Point2((p1.getX() + p2.getX() + p3.getX()) / 3, (p1.getY() + p2.getY() + p3.getY()) / 3));
}
return points2;
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class PathClassificationLabellingHelper method getClassificationMap.
/**
* Get a map of training data, based on the child objects of some classified annotations.
*
* @param hierarchy the hierarchy containing all the objects and annotations.
* @param pointsOnly if true, only Point annotations will be used for training.
*
* @return
*/
public static Map<PathClass, List<PathObject>> getClassificationMap(final PathObjectHierarchy hierarchy, final boolean pointsOnly) {
Map<PathClass, List<PathObject>> classifications = new TreeMap<>();
// Get the annotations & filter out those that are useful
List<PathObject> annotations = new ArrayList<>(getAnnotations(hierarchy));
Iterator<PathObject> iter = annotations.iterator();
while (iter.hasNext()) {
PathObject pathObject = iter.next();
// We need a PathClass, and may need to only include points
if (pathObject.getPathClass() == null || pathObject.getPathClass() == PathClassFactory.getPathClass(StandardPathClasses.REGION) || (pointsOnly && !PathObjectTools.hasPointROI(pathObject)))
iter.remove();
else
classifications.put(pathObject.getPathClass(), new ArrayList<>());
}
// from the hierarchy
if (annotations.size() > 1) {
annotations.sort(new Comparator<PathObject>() {
@Override
public int compare(PathObject o1, PathObject o2) {
PathAnnotationObject p1 = (PathAnnotationObject) o1;
PathAnnotationObject p2 = (PathAnnotationObject) o2;
int comp = 0;
if (p1.hasROI()) {
if (p2.hasROI()) {
comp = Double.compare(p1.getROI().getCentroidY(), p2.getROI().getCentroidY());
if (comp == 0)
comp = Double.compare(p1.getROI().getCentroidX(), p2.getROI().getCentroidX());
if (comp == 0)
comp = p1.getROI().toString().compareTo(p2.getROI().toString());
}
}
if (comp == 0)
return Integer.compare(o1.hashCode(), o2.hashCode());
else
return comp;
}
});
}
// StringBuilder sb = new StringBuilder("DETECTIONS:\t");
for (PathObject pathObject : annotations) {
PathClass pathClass = pathObject.getPathClass();
List<PathObject> list = classifications.get(pathClass);
// sb.append(list.size() + ", ");
if (PathObjectTools.hasPointROI(pathObject)) {
for (Point2 p : ((PointsROI) pathObject.getROI()).getAllPoints()) {
// TODO: Pay attention to z & t position!
Collection<PathObject> pathObjectsTemp = PathObjectTools.getObjectsForLocation(hierarchy, p.getX(), p.getY(), 0, 0, -1);
pathObjectsTemp = PathObjectTools.getObjectsOfClass(pathObjectsTemp, PathDetectionObject.class);
// Clumsy way to avoid duplicates...
list.removeAll(pathObjectsTemp);
list.addAll(pathObjectsTemp);
}
} else
list.addAll(hierarchy.getObjectsForROI(PathDetectionObject.class, pathObject.getROI()));
}
for (Entry<PathClass, List<PathObject>> entry : classifications.entrySet()) {
logger.info(entry.getKey() + ": " + entry.getValue().size());
}
return classifications;
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class PixelClassificationOverlay method paintOverlay.
@Override
public void paintOverlay(Graphics2D g2d, ImageRegion imageRegion, double downsampleFactor, ImageData<BufferedImage> imageData, boolean paintCompletely) {
// For now, bind the display to the display of detections
if (!showOverlay.get())
return;
if (imageData == null)
return;
// ImageServer<BufferedImage> server = imageData.getServer();
var server = getPixelClassificationServer(imageData);
if (server == null)
return;
// Show classified tiles. Without this, opacity can make it hard to see which regions have been processed.
// Note that if the alpha value is too large, tile boundaries can appear at some viewing magnifications (previous default was 32)
var colorComplete = imageData.getImageType() == ImageData.ImageType.FLUORESCENCE ? ColorToolsAwt.getCachedColor(255, 255, 255, 1) : ColorToolsAwt.getCachedColor(0, 0, 0, 1);
// Get the displayed clip bounds for fast checking if ROIs need to be drawn
RegionRequest fullRequest;
Shape shapeRegion = g2d.getClip();
if (shapeRegion == null)
fullRequest = RegionRequest.createInstance(server.getPath(), downsampleFactor, imageRegion);
else
fullRequest = RegionRequest.createInstance(server.getPath(), downsampleFactor, AwtTools.getImageRegion(shapeRegion, imageRegion.getZ(), imageRegion.getT()));
// If we have a filter, we might not need to do anything
var filter = getOverlayOptions().getPixelClassificationRegionFilter();
// Avoid this check; it causes confusion when zoomed in
// if (!filter.test(imageData, fullRequest))
// return;
var renderer = this.renderer.get();
if (renderer != null && rendererLastTimestamp != renderer.getLastChangeTimestamp()) {
clearCache();
rendererLastTimestamp = renderer.getLastChangeTimestamp();
}
// double requestedDownsample = classifier.getMetadata().getInputPixelSizeMicrons() / server.getAveragedPixelSizeMicrons();
double requestedDownsample = ServerTools.getPreferredDownsampleFactor(server, downsampleFactor);
var gCopy = (Graphics2D) g2d.create();
if (requestedDownsample > server.getDownsampleForResolution(0))
gCopy.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
else
// Only use specified interpolation when upsampling
setInterpolation(gCopy);
// gCopy.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
var comp = getAlphaComposite();
var previousComposite = gCopy.getComposite();
if (comp != null) {
if (previousComposite instanceof AlphaComposite)
gCopy.setComposite(comp.derive(((AlphaComposite) previousComposite).getAlpha() * comp.getAlpha()));
else
gCopy.setComposite(comp);
}
Collection<TileRequest> tiles = server.getTileRequestManager().getTileRequests(fullRequest);
if (fullRequest != null) {
double x = (Math.max(0, fullRequest.getMinX()) + Math.min(server.getWidth(), fullRequest.getMaxX())) / 2.0;
double y = (Math.max(0, fullRequest.getMinY()) + Math.min(server.getHeight(), fullRequest.getMaxY())) / 2.0;
var p = new Point2(x, y);
tiles = new ArrayList<>(tiles);
((List<TileRequest>) tiles).sort(Comparator.comparingDouble((TileRequest t) -> p.distanceSq(t.getImageX() + t.getImageWidth() / 2.0, t.getImageY() + t.getImageHeight() / 2.0)));
}
// Clear pending requests, since we'll insert new ones (perhaps in a different order)
this.pendingRequests.clear();
// Loop through & paint classified tiles if we have them, or request tiles if we don't
for (TileRequest tile : tiles) {
var request = tile.getRegionRequest();
if (filter != null && !filter.test(imageData, request))
continue;
// Try to get an RGB image, supplying a server that can be queried for a corresponding non-RGB cached tile if needed
BufferedImage imgRGB = getCachedTileRGB(tile, server);
if (imgRGB != null) {
gCopy.setColor(colorComplete);
gCopy.fillRect(request.getX(), request.getY(), request.getWidth(), request.getHeight());
// Get the cached RGB painted version (since painting can be a fairly expensive operation)
gCopy.drawImage(imgRGB, request.getX(), request.getY(), request.getWidth(), request.getHeight(), null);
// System.err.println(request.getHeight() == imgRGB.getHeight());
continue;
}
// Request a tile
if (livePrediction) {
requestTile(tile, imageData, server);
}
}
gCopy.dispose();
}
Aggregations