use of qupath.lib.roi.ROIs in project qupath by qupath.
the class QP method makeInverseAnnotation.
/**
* Make an annotation, for which the ROI is obtained by subtracting the ROIs of the specified objects from the closest
* common ancestor ROI (or entire image if the closest ancestor is the root).
* <p>
* In an inverted annotation can be created, it is added to the hierarchy and set as selected.
*
* @param imageData the image containing the annotation
* @param pathObjects the annotation to invert
* @return true if an inverted annotation is added to the hierarchy, false otherwise.
*/
public static boolean makeInverseAnnotation(final ImageData<?> imageData, Collection<PathObject> pathObjects) {
if (imageData == null)
return false;
var map = pathObjects.stream().filter(p -> p.hasROI() && p.getROI().isArea()).collect(Collectors.groupingBy(p -> p.getROI().getImagePlane()));
if (map.isEmpty()) {
logger.warn("No area annotations available - cannot created inverse ROI!");
return false;
}
if (map.size() > 1) {
logger.error("Cannot merge annotations from different image planes!");
return false;
}
ImagePlane plane = map.keySet().iterator().next();
List<PathObject> pathObjectList = map.get(plane);
PathObjectHierarchy hierarchy = imageData.getHierarchy();
// Try to get the best candidate parent
Collection<PathObject> parentSet = pathObjectList.stream().map(p -> p.getParent()).collect(Collectors.toCollection(HashSet::new));
PathObject parent;
if (parentSet.size() > 1) {
parentSet.clear();
boolean firstTime = true;
for (PathObject temp : pathObjectList) {
if (firstTime)
parentSet.addAll(PathObjectTools.getAncestorList(temp));
else
parentSet.retainAll(PathObjectTools.getAncestorList(temp));
firstTime = false;
}
List<PathObject> parents = new ArrayList<>(parentSet);
Collections.sort(parents, Comparator.comparingInt(PathObject::getLevel).reversed().thenComparingDouble(p -> p.hasROI() ? p.getROI().getArea() : Double.MAX_VALUE));
parent = parents.get(0);
} else
parent = parentSet.iterator().next();
// Get the parent area
Geometry geometryParent;
if (parent == null || parent.isRootObject() || !parent.hasROI())
geometryParent = GeometryTools.createRectangle(0, 0, imageData.getServer().getWidth(), imageData.getServer().getHeight());
else
geometryParent = parent.getROI().getGeometry();
// Get the parent area to use
var union = GeometryTools.union(pathObjectList.stream().map(p -> p.getROI().getGeometry()).collect(Collectors.toList()));
var geometry = geometryParent.difference(union);
// Create the new ROI
ROI shapeNew = GeometryTools.geometryToROI(geometry, plane);
PathObject pathObjectNew = PathObjects.createAnnotationObject(shapeNew);
parent.addPathObject(pathObjectNew);
hierarchy.fireHierarchyChangedEvent(parent);
hierarchy.getSelectionModel().setSelectedObject(pathObjectNew);
return true;
}
use of qupath.lib.roi.ROIs in project qupath by qupath.
the class PathObjectTools method mergePointsForSelectedObjectClasses.
// private static void addPathObjectsRecursively(Collection<PathObject> pathObjectsInput, Collection<PathObject> pathObjects, Class<? extends PathObject> cls) {
// Collection<PathObject> buffer = null;
// for (PathObject childObject : pathObjectsInput) {
// if (cls == null || cls.isInstance(childObject)) {
// pathObjects.add(childObject);
// }
// if (childObject.hasChildren()) {
// if (buffer == null)
// buffer = new ArrayList<>();
// else
// buffer.clear();
// childObject.getChildObjects(buffer);
// addPathObjectsRecursively(buffer, pathObjects, cls);
// }
// }
// }
// /**
// * Split annotations containing multi-point ROIs into separate single-point ROIs.
// *
// * @param hierarchy the object hierarchy
// * @param selectedOnly if true, consider only annotations that are currently selected; if false, consider all point annotations in the hierarchy
// * @return true if changes are made to the hierarchy, false otherwise
// */
// public static boolean splitPoints(PathObjectHierarchy hierarchy, boolean selectedOnly) {
// if (hierarchy == null) {
// logger.debug("No hierarchy available, cannot split points!");
// return false;
// }
// return splitPoints(hierarchy, selectedOnly ? hierarchy.getSelectionModel().getSelectedObjects() : hierarchy.getAnnotationObjects());
// }
//
// /**
// * Split annotations containing multi-point ROIs into separate single-point ROIs.
// *
// * @param hierarchy the object hierarchy
// * @param pathObjects a collection of point annotations to split; non-points and non-annotations will be ignored
// * @return pathObjects if changes are made to the hierarchy, false otherwise
// */
// public static boolean splitPoints(PathObjectHierarchy hierarchy, Collection<PathObject> pathObjects) {
// var points = pathObjects.stream().filter(p -> p.isAnnotation() && p.getROI().isPoint() && p.getROI().getNumPoints() > 1).collect(Collectors.toList());
// if (points.isEmpty()) {
// logger.debug("No (multi)point ROIs available to split!");
// return false;
// }
// List<PathObject> newObjects = new ArrayList<>();
// for (PathObject pathObject : points) {
// ROI p = pathObject.getROI();
// ImagePlane plane = p.getImagePlane();
// PathClass pathClass = pathObject.getPathClass();
// for (Point2 p2 : p.getAllPoints()) {
// PathObject temp = PathObjects.createAnnotationObject(ROIs.createPointsROI(p2.getX(), p2.getY(), plane), pathClass);
// newObjects.add(temp);
// }
// }
// hierarchy.removeObjects(points, true);
// hierarchy.addPathObjects(newObjects);
// // Reset the selection
// hierarchy.getSelectionModel().clearSelection();
// return true;
// }
/**
* Merge point annotations sharing the same {@link PathClass} and {@link ImagePlane} as the selected annotations,
* creating multi-point annotations for all matching points and removing the (previously-separated) annotations.
*
* @param hierarchy object hierarchy to modify
* @return true if changes are made to the hierarchy, false otherwise
*/
public static boolean mergePointsForSelectedObjectClasses(PathObjectHierarchy hierarchy) {
var pathClasses = hierarchy.getSelectionModel().getSelectedObjects().stream().filter(p -> p.isAnnotation() && p.getROI().isPoint()).map(p -> p.getPathClass()).collect(Collectors.toSet());
boolean changes = false;
for (PathClass pathClass : pathClasses) changes = changes || mergePointsForClass(hierarchy, pathClass);
return changes;
}
use of qupath.lib.roi.ROIs in project qupath by qupath.
the class Commands method combineAnnotations.
/**
* Combine all the annotations that overlap with a selected object.
* <p>
* The selected object should itself be an annotation.
*
* @param hierarchy
* @param pathObjects
* @param op
* @return true if any changes were made, false otherwise
*/
static boolean combineAnnotations(PathObjectHierarchy hierarchy, List<PathObject> pathObjects, RoiTools.CombineOp op) {
if (hierarchy == null || hierarchy.isEmpty() || pathObjects.isEmpty()) {
logger.warn("Combine annotations: Cannot combine - no annotations found");
return false;
}
pathObjects = new ArrayList<>(pathObjects);
PathObject pathObject = pathObjects.get(0);
if (!pathObject.isAnnotation()) {
// || !RoiTools.isShapeROI(pathObject.getROI())) {
logger.warn("Combine annotations: No annotation with ROI selected");
return false;
}
var plane = pathObject.getROI().getImagePlane();
// pathObjects.removeIf(p -> !RoiTools.isShapeROI(p.getROI())); // Remove any null or point ROIs, TODO: Consider supporting points
// Remove any null or point ROIs, TODO: Consider supporting points
pathObjects.removeIf(p -> !p.hasROI() || !p.getROI().getImagePlane().equals(plane));
if (pathObjects.isEmpty()) {
logger.warn("Cannot combine annotations - only one suitable annotation found");
return false;
}
var allROIs = pathObjects.stream().map(p -> p.getROI()).collect(Collectors.toCollection(() -> new ArrayList<>()));
ROI newROI;
switch(op) {
case ADD:
newROI = RoiTools.union(allROIs);
break;
case INTERSECT:
newROI = RoiTools.intersection(allROIs);
break;
case SUBTRACT:
var first = allROIs.remove(0);
newROI = RoiTools.combineROIs(first, RoiTools.union(allROIs), op);
break;
default:
throw new IllegalArgumentException("Unknown combine op " + op);
}
if (newROI == null) {
logger.debug("No changes were made");
return false;
}
PathObject newObject = null;
if (!newROI.isEmpty()) {
newObject = PathObjects.createAnnotationObject(newROI, pathObject.getPathClass());
newObject.setName(pathObject.getName());
newObject.setColorRGB(pathObject.getColorRGB());
}
// Remove previous objects
hierarchy.removeObjects(pathObjects, true);
if (newObject != null)
hierarchy.addPathObject(newObject);
return true;
}
Aggregations