use of qupath.lib.objects.PathObject in project qupath by qupath.
the class PointIO method writePoints.
/**
* Write a list of point annotations to a stream.
* @param stream
* @param pathObjects
* @throws IOException
*/
public static void writePoints(OutputStream stream, Collection<? extends PathObject> pathObjects) throws IOException {
// Check that all PathObjects contain only point annotations
int unfilteredSize = pathObjects.size();
pathObjects = pathObjects.stream().filter(p -> p.getROI() instanceof PointsROI).collect(Collectors.toList());
int filteredSize = pathObjects.size();
if (unfilteredSize != filteredSize)
logger.warn(unfilteredSize - filteredSize + " of the " + filteredSize + " elements in list is/are not point annotations. These will be skipped.");
try (Writer writer = new BufferedWriter(new OutputStreamWriter(stream, StandardCharsets.UTF_8))) {
List<String> cols = new ArrayList<>();
cols.addAll(Arrays.asList("x", "y"));
String sep = "\t";
ImagePlane defaultPlane = ImagePlane.getDefaultPlane();
boolean hasClass = pathObjects.stream().anyMatch(p -> p.getPathClass() != null);
boolean hasName = pathObjects.stream().anyMatch(p -> p.getName() != null);
boolean hasColor = pathObjects.stream().anyMatch(p -> p.getColorRGB() != null);
boolean hasC = pathObjects.stream().anyMatch(p -> p.getROI().getC() > defaultPlane.getC());
boolean hasZ = pathObjects.stream().anyMatch(p -> p.getROI().getZ() > defaultPlane.getZ());
boolean hasT = pathObjects.stream().anyMatch(p -> p.getROI().getT() > defaultPlane.getT());
if (hasC)
cols.add("c");
if (hasZ)
cols.add("z");
if (hasT)
cols.add("t");
if (hasClass)
cols.add("class");
if (hasName)
cols.add("name");
if (hasColor)
cols.add("color");
for (String col : cols) writer.write(col + sep);
writer.write(System.lineSeparator());
for (PathObject pathObject : pathObjects) {
if (!PathObjectTools.hasPointROI(pathObject))
continue;
PointsROI points = (PointsROI) pathObject.getROI();
for (Point2 point : points.getAllPoints()) {
String[] row = new String[cols.size()];
row[cols.indexOf("x")] = point.getX() + "";
row[cols.indexOf("y")] = sep + point.getY();
if (hasC)
row[cols.indexOf("c")] = sep + points.getC();
if (hasZ)
row[cols.indexOf("z")] = sep + points.getZ();
if (hasT)
row[cols.indexOf("t")] = sep + points.getT();
if (hasClass)
row[cols.indexOf("class")] = pathObject.getPathClass() != null ? sep + pathObject.getPathClass() : sep;
if (hasName)
row[cols.indexOf("name")] = pathObject.getName() != null ? sep + pathObject.getName() : sep;
if (hasColor)
row[cols.indexOf("color")] = pathObject.getColorRGB() != null ? sep + pathObject.getColorRGB() : sep;
for (String val : row) writer.write(val);
writer.write(System.lineSeparator());
}
}
}
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class TMAScoreImporter method importFromCSV.
private static int importFromCSV(final Map<String, List<String>> map, final PathObjectHierarchy hierarchy) {
TMAGrid tmaGrid = hierarchy.getTMAGrid();
if (tmaGrid == null || tmaGrid.nCores() == 0) {
logger.error("No TMA grid found!");
return 0;
}
// Try to get a 'core' column
String coreKey = null;
for (String key : map.keySet()) {
if (key.trim().toLowerCase().equals("core")) {
coreKey = key;
break;
}
}
List<String> coreNames = coreKey == null ? null : map.remove(coreKey);
// Try to get a unique ID column
List<String> coreIDs = map.remove(TMACoreObject.KEY_UNIQUE_ID);
// If we don't have a core column OR a unique ID column, we can't do anything
if (coreNames == null && coreIDs == null) {
logger.error("No column with header 'core' or '" + TMACoreObject.KEY_UNIQUE_ID + "' found");
return 0;
}
// int n = coreNames == null ? coreIDs.size() : coreNames.size();
// Get a list of cores ordered by whatever info we have
Map<Integer, List<TMACoreObject>> cores = new HashMap<>();
boolean coresFound = false;
// If we have any unique IDs, use these and change names accordingly - if possible, and necessary
if (coreIDs != null) {
int i = 0;
for (String id : coreIDs) {
List<TMACoreObject> coresByID = new ArrayList<>();
for (TMACoreObject coreTemp : tmaGrid.getTMACoreList()) if (id != null && id.equals(coreTemp.getUniqueID()))
coresByID.add(coreTemp);
if (!coresByID.isEmpty()) {
cores.put(i, coresByID);
coresFound = true;
if (coreNames != null && coresByID.size() == 1) {
String currentName = coresByID.get(0).getName();
String newName = coreNames.get(i);
if (!newName.equals(currentName)) {
coresByID.get(0).setName(newName);
if (currentName != null)
logger.warn("Core name changed from {} to {}", currentName, newName);
}
}
}
i++;
}
}
// If we didn't have any unique IDs, we need to work with core names instead
if (!coresFound && coreNames != null) {
int i = 0;
for (String name : coreNames) {
TMACoreObject core = tmaGrid.getTMACore(name);
if (core != null) {
cores.put(i, Collections.singletonList(core));
coresFound = true;
if (coreIDs != null) {
String currentID = core.getUniqueID();
String newID = coreIDs.get(i);
if (newID != null && !newID.equals(currentID)) {
core.setUniqueID(newID);
// It shouldn't occur that an existing ID is changed... although it's possible if there are duplicates
if (currentID != null)
logger.warn("Core unique ID changed from {} to {}", currentID, newID);
}
}
}
i++;
}
}
// Add extra columns from the map, either as metadata or measurements
for (Entry<String, List<String>> entry : map.entrySet()) {
// Skip columns without headers
if (entry.getKey() == null || entry.getKey().trim().length() == 0)
continue;
// If we have survival data, or else can't parse numeric values, add as metadata
boolean isOverallSurvival = entry.getKey().equalsIgnoreCase(TMACoreObject.KEY_OVERALL_SURVIVAL);
boolean isRecurrenceFreeSurvival = entry.getKey().equalsIgnoreCase(TMACoreObject.KEY_RECURRENCE_FREE_SURVIVAL);
boolean isOSCensored = entry.getKey().equalsIgnoreCase(TMACoreObject.KEY_OS_CENSORED);
boolean isRFSCensored = entry.getKey().equalsIgnoreCase(TMACoreObject.KEY_RFS_CENSORED);
// Try to parse numeric data, if we can
boolean isSurvivalRelated = isOverallSurvival || isRecurrenceFreeSurvival || isOSCensored || isRFSCensored;
double[] vals = parseNumeric(entry.getValue(), !isSurvivalRelated);
if (isSurvivalRelated || vals == null || vals.length == GeneralTools.numNaNs(vals)) {
for (int i : cores.keySet()) {
for (TMACoreObject core : cores.get(i)) {
if (core == null)
continue;
if (isOverallSurvival)
core.getMeasurementList().putMeasurement(TMACoreObject.KEY_OVERALL_SURVIVAL, vals[i]);
else if (isRecurrenceFreeSurvival)
core.getMeasurementList().putMeasurement(TMACoreObject.KEY_RECURRENCE_FREE_SURVIVAL, vals[i]);
else if (isOSCensored)
core.getMeasurementList().putMeasurement(TMACoreObject.KEY_OS_CENSORED, vals[i] > 0 ? 1 : 0);
else if (isRFSCensored)
core.getMeasurementList().putMeasurement(TMACoreObject.KEY_RFS_CENSORED, vals[i] > 0 ? 1 : 0);
else
core.putMetadataValue(entry.getKey(), entry.getValue().get(i));
}
}
} else {
// If we have a numeric column, add to measurement list
for (int i : cores.keySet()) {
for (TMACoreObject core : cores.get(i)) {
core.getMeasurementList().addMeasurement(entry.getKey(), vals[i]);
}
}
}
}
// Loop through and close any measurement lists, recording anywhere changes were made
Set<PathObject> changed = new HashSet<>();
for (List<TMACoreObject> coreList : cores.values()) {
for (TMACoreObject core : coreList) {
if (core == null)
continue;
core.getMeasurementList().close();
changed.add(core);
}
}
hierarchy.fireObjectsChangedEvent(null, changed);
return changed.size();
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class GuiTools method promptToClearAllSelectedObjects.
/**
* Prompt user to select all currently-selected objects (except TMA core objects).
*
* @param imageData
* @return
*/
public static boolean promptToClearAllSelectedObjects(final ImageData<?> imageData) {
// Get all non-TMA core objects
PathObjectHierarchy hierarchy = imageData.getHierarchy();
Collection<PathObject> selectedRaw = hierarchy.getSelectionModel().getSelectedObjects();
List<PathObject> selected = selectedRaw.stream().filter(p -> !(p instanceof TMACoreObject)).collect(Collectors.toList());
if (selected.isEmpty()) {
if (selectedRaw.size() > selected.size())
Dialogs.showErrorMessage("Delete selected objects", "No valid objects selected! \n\nNote: Individual TMA cores cannot be deleted with this method.");
else
Dialogs.showErrorMessage("Delete selected objects", "No objects selected!");
return false;
}
int n = selected.size();
String message;
if (n == 1)
message = "Delete selected object?";
else
message = "Delete " + n + " selected objects?";
if (Dialogs.showYesNoDialog("Delete objects", message)) {
// Check for descendants
List<PathObject> children = new ArrayList<>();
for (PathObject temp : selected) {
children.addAll(temp.getChildObjects());
}
children.removeAll(selected);
boolean keepChildren = true;
if (!children.isEmpty()) {
Dialogs.DialogButton response = Dialogs.showYesNoCancelDialog("Delete objects", "Keep descendant objects?");
if (response == Dialogs.DialogButton.CANCEL)
return false;
keepChildren = response == Dialogs.DialogButton.YES;
}
hierarchy.removeObjects(selected, keepChildren);
hierarchy.getSelectionModel().clearSelection();
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Delete selected objects", "clearSelectedObjects(" + keepChildren + ");"));
if (keepChildren)
logger.info(selected.size() + " object(s) deleted");
else
logger.info(selected.size() + " object(s) deleted with descendants");
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Delete selected objects", "clearSelectedObjects();"));
logger.info(selected.size() + " object(s) deleted");
return true;
} else
return false;
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class GuiTools method promptToSetActiveAnnotationProperties.
/**
* Prompt the user to set properties for the currently-selected annotation(s).
*
* @param hierarchy current hierarchy
* @return true if changes to annotation properties were made, false otherwise.
*/
public static boolean promptToSetActiveAnnotationProperties(final PathObjectHierarchy hierarchy) {
PathObject currentObject = hierarchy.getSelectionModel().getSelectedObject();
if (currentObject == null || !currentObject.isAnnotation())
return false;
ROI roi = currentObject.getROI();
if (roi == null)
return false;
Collection<PathAnnotationObject> otherAnnotations = hierarchy.getSelectionModel().getSelectedObjects().stream().filter(p -> p.isAnnotation() && p != currentObject).map(p -> (PathAnnotationObject) p).collect(Collectors.toList());
if (promptToSetAnnotationProperties((PathAnnotationObject) currentObject, otherAnnotations)) {
hierarchy.fireObjectsChangedEvent(null, Collections.singleton(currentObject));
// Ensure the object is still selected
hierarchy.getSelectionModel().setSelectedObject(currentObject);
return true;
}
return false;
}
use of qupath.lib.objects.PathObject in project qupath by qupath.
the class GuiTools method setSelectedAnnotationLock.
/**
* Set selected TMA cores to have the specified 'locked' status.
*
* @param hierarchy
* @param setToLocked
*/
private static void setSelectedAnnotationLock(final PathObjectHierarchy hierarchy, final boolean setToLocked) {
if (hierarchy == null)
return;
PathObject pathObject = hierarchy.getSelectionModel().getSelectedObject();
List<PathObject> changed = new ArrayList<>();
if (pathObject instanceof PathAnnotationObject) {
PathAnnotationObject annotation = (PathAnnotationObject) pathObject;
annotation.setLocked(setToLocked);
changed.add(annotation);
// Update any other selected cores to have the same status
for (PathObject pathObject2 : hierarchy.getSelectionModel().getSelectedObjects()) {
if (pathObject2 instanceof PathAnnotationObject) {
annotation = (PathAnnotationObject) pathObject2;
if (annotation.isLocked() != setToLocked) {
annotation.setLocked(setToLocked);
changed.add(annotation);
}
}
}
}
if (!changed.isEmpty())
hierarchy.fireObjectsChangedEvent(GuiTools.class, changed);
}
Aggregations