use of qupath.lib.objects.TMACoreObject in project qupath by qupath.
the class TMADataIO method importDearrayedTMAData.
/**
* Import a TMA grid from an exported TMA analysis file, i.e. with extension ".qptma"
*
* @param file
* @return
*/
public static TMAGrid importDearrayedTMAData(final File file) {
try (Scanner scanner = new Scanner(file)) {
int gridWidth = -1;
int gridHeight = -1;
String line = null;
while (scanner.hasNextLine()) {
line = scanner.nextLine();
if (line.startsWith("TMA grid width:"))
gridWidth = Integer.parseInt(line.replace("TMA grid width:", "").trim());
else if (line.startsWith("TMA grid height:"))
gridHeight = Integer.parseInt(line.replace("TMA grid height:", "").trim());
if (gridWidth >= 0 && gridHeight >= 0)
break;
}
if (gridWidth <= 0 || gridHeight <= 0) {
logger.error("No grid dimensions available!");
return null;
}
Map<String, List<String>> csvData = TMAScoreImporter.readCSV(scanner);
List<String> colNames = csvData.get("Core name");
List<String> colX = csvData.get("X");
List<String> colY = csvData.get("Y");
List<String> colWidth = csvData.get("Width");
List<String> colHeight = csvData.get("Height");
List<String> colPresent = csvData.get("Present");
List<String> colID = csvData.get(TMACoreObject.KEY_UNIQUE_ID);
if (colX == null || colY == null || colWidth == null || colHeight == null) {
logger.error("No core locations available!");
return null;
}
// int count = 0;
List<TMACoreObject> cores = new ArrayList<>();
for (int i = 0; i < colX.size(); i++) {
double x = Double.parseDouble(colX.get(i));
double y = Double.parseDouble(colY.get(i));
double w = Double.parseDouble(colWidth.get(i));
double h = Double.parseDouble(colHeight.get(i));
boolean present = colPresent == null ? true : Boolean.parseBoolean(colPresent.get(i));
String name = colNames == null ? null : colNames.get(i);
String id = colID == null ? null : colID.get(i);
TMACoreObject core = PathObjects.createTMACoreObject(x, y, w, h, !present);
if (name != null)
core.setName(name);
if (id != null && !id.isEmpty())
core.setUniqueID(id);
cores.add(core);
}
return DefaultTMAGrid.create(cores, gridWidth);
} catch (FileNotFoundException e) {
logger.error("Cannot find file: {}", file);
return null;
}
}
use of qupath.lib.objects.TMACoreObject in project qupath by qupath.
the class TMASummaryViewer method getEntriesForTMAData.
private static List<TMAEntry> getEntriesForTMAData(final ImageData<BufferedImage> imageData) {
List<TMAEntry> entriesNew = new ArrayList<>();
if (imageData.getHierarchy().getTMAGrid() == null)
return entriesNew;
ObservableMeasurementTableData data = new ObservableMeasurementTableData();
data.setImageData(imageData, imageData.getHierarchy().getTMAGrid().getTMACoreList());
for (TMACoreObject core : imageData.getHierarchy().getTMAGrid().getTMACoreList()) {
entriesNew.add(TMAEntries.createTMAObjectEntry(imageData, data, core));
}
return entriesNew;
}
use of qupath.lib.objects.TMACoreObject 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.TMACoreObject 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.TMACoreObject in project qupath by qupath.
the class QuPathViewer method getImageLocationString.
/**
* Get a string representing the image coordinates for a particular x & y location.
* @param xx x-coordinate in the image space (not the component/viewer space)
* @param yy y-coordinate in the image space (not the component/viewer space)
* @param useCalibratedUnits
* @return
*/
private String getImageLocationString(double xx, double yy, boolean useCalibratedUnits) {
ImageServer<BufferedImage> server = getServer();
if (server == null)
return "";
String units;
if (xx < 0 || yy < 0 || xx > server.getWidth() - 1 || yy > server.getHeight() - 1)
return "";
double xDisplay = xx;
double yDisplay = yy;
PixelCalibration cal = server.getPixelCalibration();
if (useCalibratedUnits && cal.hasPixelSizeMicrons()) {
units = GeneralTools.micrometerSymbol();
xDisplay *= cal.getPixelWidthMicrons();
yDisplay *= cal.getPixelHeightMicrons();
} else {
units = "px";
}
// See if we're on top of a TMA core
String prefix = "";
TMAGrid tmaGrid = getHierarchy().getTMAGrid();
if (tmaGrid != null) {
TMACoreObject core = PathObjectTools.getTMACoreForPixel(tmaGrid, xx, yy);
if (core != null && core.getName() != null)
prefix = "Core: " + core.getName() + "\n";
}
String s = null;
RegionRequest request = ImageRegionStoreHelpers.getTileRequest(server, xx, yy, downsampleFactor.get(), getZPosition(), getTPosition());
if (request != null) {
BufferedImage img = regionStore.getCachedTile(server, request);
int xi = 0, yi = 0;
if (img == null) {
// Try getting a value from the thumbnail for the whole image
BufferedImage imgThumbnail = regionStore.getCachedThumbnail(server, getZPosition(), getTPosition());
if (imgThumbnail != null) {
img = imgThumbnail;
double downsample = (double) server.getWidth() / imgThumbnail.getWidth();
xi = (int) (xx / downsample + .5);
yi = (int) (yy / downsample + .5);
}
} else {
xi = (int) ((xx - request.getX()) / request.getDownsample());
yi = (int) ((yy - request.getY()) / request.getDownsample());
}
if (img != null) {
// Make sure we are within range
xi = Math.min(xi, img.getWidth() - 1);
yi = Math.min(yi, img.getHeight() - 1);
// Get the value, having applied any required color transforms
if (imageDisplay != null)
s = imageDisplay.getTransformedValueAsString(img, xi, yi);
}
}
// Append z, t position if required
String zString = null;
if (server.nZSlices() > 1) {
double zSpacing = server.getPixelCalibration().getZSpacingMicrons();
if (!useCalibratedUnits || Double.isNaN(zSpacing))
zString = "z = " + getZPosition();
else
zString = String.format("z = %.2f %s", getZPosition() * zSpacing, GeneralTools.micrometerSymbol());
}
String tString = null;
if (server.nTimepoints() > 1) {
// TODO: Consider use of TimeUnit
// TimeUnit timeUnit = server.getTimeUnit();
// if (!useMicrons || timeUnit == null)
tString = "t = " + getTPosition();
// else
// tString = String.format("z = %.2f %s", getTPosition(), timeUnit.toString());
}
String dimensionString;
if (tString == null && zString == null)
dimensionString = "";
else {
dimensionString = "\n";
if (zString != null) {
dimensionString += zString;
if (tString != null)
dimensionString += ", " + tString;
} else
dimensionString += tString;
}
if (s != null)
return String.format("%s%.2f, %.2f %s\n%s%s", prefix, xDisplay, yDisplay, units, s, dimensionString);
else
return String.format("%s%.2f, %.2f %s%s", prefix, xDisplay, yDisplay, units, dimensionString);
// if (s != null)
// return String.format("<html><center>%.2f, %.2f %s<br>%s", xDisplay, yDisplay, units, s);
// else
// return String.format("<html><center>%.2f, %.2f %s", xDisplay, yDisplay, units);
}
Aggregations