use of qupath.lib.geom.Point2 in project qupath by qupath.
the class ROIConverterIJ method getTransformedPoints.
/**
* Convert a collection of points from a ROI into the coordinate space determined from the calibration information.
*
* @param points
* @param xOrigin
* @param yOrigin
* @param downsampleFactor
* @return float arrays, where result[0] gives the x coordinates and result[1] the y coordinates
*/
private static float[][] getTransformedPoints(Collection<Point2> points, double xOrigin, double yOrigin, double downsampleFactor) {
float[] xPoints = new float[points.size()];
float[] yPoints = new float[points.size()];
int i = 0;
for (Point2 p : points) {
xPoints[i] = (float) convertXtoIJ(p.getX(), xOrigin, downsampleFactor);
yPoints[i] = (float) convertYtoIJ(p.getY(), yOrigin, downsampleFactor);
i++;
}
return new float[][] { xPoints, yPoints };
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class AbstractPathTool method setConstrainedAreaParent.
/**
* Set the parent that may be used to constrain a new ROI, if possible.
*
* @param hierarchy object hierarchy containing potential constraining objects
* @param xx x-coordinate in the image space of the starting point for the new object
* @param yy y-coordinate in the image space of the starting point for the new object
* @param exclusions objects not to consider (e.g. the new ROI being created)
*/
void setConstrainedAreaParent(final PathObjectHierarchy hierarchy, double xx, double yy, Collection<PathObject> exclusions) {
if (!Platform.isFxApplicationThread())
throw new IllegalStateException("Not on the FxApplication thread!");
// Reset parent area & its descendant annotation areas
constrainedParentGeometry = null;
constrainedRemoveGeometries = null;
// Identify the smallest area annotation that contains the specified point
constrainedParentObject = getSelectableObjectList(xx, yy).stream().filter(p -> !p.isDetection() && p.hasROI() && p.getROI().isArea() && !exclusions.contains(p)).sorted(Comparator.comparing(p -> p.getROI().getArea())).findFirst().orElseGet(() -> null);
// Check the parent is a valid potential parent
if (constrainedParentObject == null || !(constrainedParentObject.hasROI() && constrainedParentObject.getROI().isArea())) {
constrainedParentObject = hierarchy.getRootObject();
}
// Get the parent Geometry
if (constrainedParentObject.hasROI() && constrainedParentObject.getROI().isArea())
constrainedParentGeometry = constrainedParentObject.getROI().getGeometry();
constrainedStartPoint = new Point2(xx, yy);
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class DensityMaps method findHotspots.
/**
* Find hotspots in a density map.
*
* @param hierarchy hierarchy used to obtain selected objects and add hotspots
* @param densityServer the density map to query
* @param channel channel in which to find hotspots (usually 0)
* @param nHotspots maximum number of hotspots to find per selected annotation
* @param radius hotspot radius, in calibrated units
* @param minCount minimum value required in the 'count' channel (the last channel)
* @param hotspotClass the classification to apply to hotspots
* @param deleteExisting optionally delete existing annotations identified as hotspots
* @param peaksOnly optionally restrict hotspots to only include intensity peaks
* @throws IOException
*/
public static void findHotspots(PathObjectHierarchy hierarchy, ImageServer<BufferedImage> densityServer, int channel, int nHotspots, double radius, double minCount, PathClass hotspotClass, boolean deleteExisting, boolean peaksOnly) throws IOException {
if (nHotspots <= 0) {
logger.warn("Number of hotspots requested is {}!", nHotspots);
return;
}
logger.debug("Finding {} hotspots in {} for channel {}, radius {}", nHotspots, densityServer, channel, radius);
Collection<PathObject> parents = new ArrayList<>(hierarchy.getSelectionModel().getSelectedObjects());
if (parents.isEmpty())
parents = Collections.singleton(hierarchy.getRootObject());
double downsample = densityServer.getDownsampleForResolution(0);
var toDelete = new HashSet<PathObject>();
// Handle deleting existing hotspots
if (deleteExisting) {
toDelete.addAll(hierarchy.getAnnotationObjects().stream().filter(p -> p.getPathClass() == hotspotClass && p.isAnnotation() && p.getName() != null && p.getName().startsWith("Hotspot")).collect(Collectors.toList()));
}
// Convert radius to pixels
double radiusPixels = radius / densityServer.getPixelCalibration().getAveragedPixelSize().doubleValue();
try (@SuppressWarnings("unchecked") var scope = new PointerScope()) {
for (var parent : parents) {
ROI roi = parent.getROI();
// We need a ROI to define the area of interest
if (roi == null) {
if (densityServer.nTimepoints() > 1 || densityServer.nZSlices() > 1) {
logger.warn("Hotspot detection without a parent object not supported for images with multiple z-slices/timepoints.");
logger.warn("I will apply detection to the first plane only. If you need hotspots elsewhere, create an annotation first and use it to define the ROI.");
}
roi = ROIs.createRectangleROI(0, 0, densityServer.getWidth(), densityServer.getHeight(), ImagePlane.getDefaultPlane());
}
// Erode the ROI & see if any hotspot could fit
var roiEroded = RoiTools.buffer(roi, -radiusPixels);
if (roiEroded.isEmpty() || roiEroded.getArea() == 0) {
logger.warn("ROI is too small! Cannot detected hotspots with radius {} in {}", radius, parent);
continue;
}
// Read the image
var plane = roi.getImagePlane();
RegionRequest request = RegionRequest.createInstance(densityServer.getPath(), downsample, 0, 0, densityServer.getWidth(), densityServer.getHeight(), plane.getZ(), plane.getT());
var img = densityServer.readBufferedImage(request);
// Create a mask
var imgMask = BufferedImageTools.createROIMask(img.getWidth(), img.getHeight(), roiEroded, request);
// Switch to OpenCV
var mat = OpenCVTools.imageToMat(img);
var matMask = OpenCVTools.imageToMat(imgMask);
// Find hotspots
var channels = OpenCVTools.splitChannels(mat);
var density = channels.get(channel);
if (minCount > 0) {
var thresholdMask = opencv_core.greaterThan(channels.get(channels.size() - 1), minCount).asMat();
opencv_core.bitwise_and(matMask, thresholdMask, matMask);
thresholdMask.close();
}
// TODO: Limit to peaks
if (peaksOnly) {
var matMaxima = OpenCVTools.findRegionalMaxima(density);
var matPeaks = OpenCVTools.shrinkLabels(matMaxima);
matPeaks.put(opencv_core.greaterThan(matPeaks, 0));
opencv_core.bitwise_and(matMask, matPeaks, matMask);
matPeaks.close();
matMaxima.close();
}
// Sort in descending order
var maxima = new ArrayList<>(OpenCVTools.getMaskedPixels(density, matMask));
Collections.sort(maxima, Comparator.comparingDouble((IndexedPixel p) -> p.getValue()).reversed());
// Try to get as many maxima as we need
// Impose minimum separation
var points = maxima.stream().map(p -> new Point2(p.getX() * downsample, p.getY() * downsample)).collect(Collectors.toList());
var hotspotCentroids = new ArrayList<Point2>();
double distSqThreshold = radiusPixels * radiusPixels * 4;
for (var p : points) {
// Check not too close to an existing hotspot
boolean skip = false;
for (var p2 : hotspotCentroids) {
if (p.distanceSq(p2) < distSqThreshold) {
skip = true;
break;
}
}
if (!skip) {
hotspotCentroids.add(p);
if (hotspotCentroids.size() == nHotspots)
break;
}
}
var hotspots = new ArrayList<PathObject>();
int i = 0;
for (var p : hotspotCentroids) {
i++;
var ellipse = ROIs.createEllipseROI(p.getX() - radiusPixels, p.getY() - radiusPixels, radiusPixels * 2, radiusPixels * 2, roi.getImagePlane());
var hotspot = PathObjects.createAnnotationObject(ellipse, hotspotClass);
hotspot.setName("Hotspot " + i);
hotspots.add(hotspot);
}
if (hotspots.isEmpty())
logger.warn("No hotspots found in {}", parent);
else if (hotspots.size() < nHotspots) {
logger.warn("Only {}/{} hotspots could be found in {}", hotspots.size(), nHotspots, parent);
}
parent.addPathObjects(hotspots);
}
hierarchy.fireHierarchyChangedEvent(DensityMaps.class);
if (!toDelete.isEmpty())
hierarchy.removeObjects(toDelete, true);
}
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class PointsROI method recomputeBounds.
// @Deprecated
// public boolean startAdjusting(double x, double y, int modifiers) {
// Point2 pNearest = getNearest(x, y, PointsROI.getDefaultPointRadius());
// if (pNearest == null)
// return false;
// // logger.info("STARTING: " + p);
// pointAdjusting = pNearest;
// pNearest.setLocation(x, y);
// isAdjusting = true;
// return true;
// }
//
// @Override
// public void finishAdjusting(double x, double y, boolean shiftDown) {
// super.finishAdjusting(x, y, shiftDown);
// pointAdjusting = null;
// recomputeBounds();
// }
//
//
// @Override
// public void updateAdjustment(double x, double y, boolean shiftDown) {
// if (pointAdjusting != null) {
// pointAdjusting.setLocation(x, y);
// }
// }
protected void recomputeBounds() {
if (points.isEmpty()) {
resetBounds();
return;
}
xMin = Double.POSITIVE_INFINITY;
yMin = Double.POSITIVE_INFINITY;
xMax = Double.NEGATIVE_INFINITY;
yMax = Double.NEGATIVE_INFINITY;
for (Point2 p : points) {
updateBounds(p.getX(), p.getY());
}
}
use of qupath.lib.geom.Point2 in project qupath by qupath.
the class PointsROI method computeCentroid.
private void computeCentroid() {
double xSum = 0;
double ySum = 0;
int n = points.size();
for (Point2 p : points) {
xSum += p.getX() / n;
ySum += p.getY() / n;
}
xCentroid = xSum;
yCentroid = ySum;
}
Aggregations