use of qupath.lib.objects.TMACoreObject in project qupath by qupath.
the class FindConvexHullDetectionsPlugin method getParentObjects.
@Override
protected Collection<? extends PathObject> getParentObjects(PluginRunner<T> runner) {
PathObjectHierarchy hierarchy = getHierarchy(runner);
PathObject selected = hierarchy.getSelectionModel().getSelectedObject();
if (selected instanceof TMACoreObject)
return Collections.singleton(selected);
if (hierarchy.getTMAGrid() != null)
return hierarchy.getTMAGrid().getTMACoreList();
else
return Collections.emptyList();
}
use of qupath.lib.objects.TMACoreObject in project qupath by qupath.
the class TMACommands method createAugmentedTMAGrid.
/**
* Add a new row or column to a TMA grid.
*
* @param hierarchy
* @param selectedCore
* @param type
* @return
*/
private static TMAGrid createAugmentedTMAGrid(final PathObjectHierarchy hierarchy, final TMACoreObject selectedCore, final TMAAddType type) {
TMAGrid grid = hierarchy.getTMAGrid();
// Convert to easier form
boolean addAfter = type == TMAAddType.COLUMN_AFTER || type == TMAAddType.ROW_AFTER;
boolean addColumn = type == TMAAddType.COLUMN_AFTER || type == TMAAddType.COLUMN_BEFORE;
// Try to identify the row/column that we want
int row = -1;
int col = -1;
if (selectedCore != null) {
for (int y = 0; y < grid.getGridHeight(); y++) {
for (int x = 0; x < grid.getGridWidth(); x++) {
if (grid.getTMACore(y, x) == selectedCore) {
if (addAfter) {
row = y + 1;
col = x + 1;
} else {
row = y;
col = x;
}
break;
}
}
}
}
// If we don't have a row or column, choose based on the add type
if (row < 0) {
switch(type) {
case COLUMN_AFTER:
col = grid.getGridWidth();
break;
case COLUMN_BEFORE:
col = 0;
break;
case ROW_AFTER:
row = grid.getGridHeight();
break;
case ROW_BEFORE:
row = 0;
break;
default:
break;
}
}
// Compute the width of the new grid
int newWidth = addColumn ? grid.getGridWidth() + 1 : grid.getGridWidth();
int newHeight = addColumn ? grid.getGridHeight() : grid.getGridHeight() + 1;
// Loop through cores, getting mean widths, heights & displacements
RunningStatistics statsWidth = new RunningStatistics();
RunningStatistics statsHeight = new RunningStatistics();
RunningStatistics statsDx = new RunningStatistics();
RunningStatistics statsDy = new RunningStatistics();
for (int r = 0; r < grid.getGridHeight(); r++) {
TMACoreObject coreColBefore = null;
for (int c = 0; c < grid.getGridWidth(); c++) {
TMACoreObject core = grid.getTMACore(r, c);
if (!core.hasROI())
continue;
statsWidth.addValue(core.getROI().getBoundsWidth());
statsHeight.addValue(core.getROI().getBoundsHeight());
if (coreColBefore != null && coreColBefore.hasROI()) {
statsDx.addValue(core.getROI().getCentroidX() - coreColBefore.getROI().getCentroidX());
}
if (r > 0) {
TMACoreObject coreRowBefore = grid.getTMACore(r - 1, c);
if (coreRowBefore != null && coreRowBefore.hasROI()) {
statsDy.addValue(core.getROI().getCentroidY() - coreRowBefore.getROI().getCentroidY());
}
}
coreColBefore = core;
}
}
double meanWidth = statsWidth.getMean();
double meanHeight = statsHeight.getMean();
double meanDx = statsDx.getMean();
double meanDy = statsDy.getMean();
double diameter = (meanWidth + meanHeight) / 2;
// Create a new list of cores, adding where necessary
List<TMACoreObject> coresNew = new ArrayList<>();
for (int r = 0; r < newHeight; r++) {
for (int c = 0; c < newWidth; c++) {
// Copy existing rows & columns, or create new cores if required
if (addColumn) {
if (c < col) {
coresNew.add(grid.getTMACore(r, c));
} else if (c > col) {
coresNew.add(grid.getTMACore(r, c - 1));
} else if (c == col) {
// Try to get average x & y coordinates between surrounding columns
double x1, x2, y;
if (col == 0) {
x2 = grid.getTMACore(r, c).getROI().getCentroidX();
x1 = x2 - meanDx * 2;
y = grid.getTMACore(r, c).getROI().getCentroidY();
} else if (col == grid.getGridWidth()) {
x1 = grid.getTMACore(r, c - 1).getROI().getCentroidX();
x2 = x1 + meanDx * 2;
y = grid.getTMACore(r, c - 1).getROI().getCentroidY();
} else {
x1 = grid.getTMACore(r, c - 1).getROI().getCentroidX();
x2 = grid.getTMACore(r, c).getROI().getCentroidX();
y = (grid.getTMACore(r, c - 1).getROI().getCentroidY() + grid.getTMACore(r, c).getROI().getCentroidY()) / 2;
}
TMACoreObject coreNew = PathObjects.createTMACoreObject((x1 + x2) / 2, y, diameter, true);
coresNew.add(coreNew);
}
} else {
if (r < row) {
coresNew.add(grid.getTMACore(r, c));
} else if (r > row) {
coresNew.add(grid.getTMACore(r - 1, c));
} else if (r == row) {
// Try to get average x & y coordinates between surrounding columns
double x, y1, y2;
if (row == 0) {
y2 = grid.getTMACore(r, c).getROI().getCentroidY();
y1 = y2 - meanDy * 2;
x = grid.getTMACore(r, c).getROI().getCentroidX();
} else if (row == grid.getGridHeight()) {
y1 = grid.getTMACore(r - 1, c).getROI().getCentroidY();
y2 = y1 + meanDy * 2;
x = grid.getTMACore(r - 1, c).getROI().getCentroidX();
} else {
y1 = grid.getTMACore(r - 1, c).getROI().getCentroidY();
y2 = grid.getTMACore(r, c).getROI().getCentroidY();
x = (grid.getTMACore(r - 1, c).getROI().getCentroidX() + grid.getTMACore(r, c).getROI().getCentroidX()) / 2;
}
TMACoreObject coreNew = PathObjects.createTMACoreObject(x, (y1 + y2) / 2, diameter, true);
coresNew.add(coreNew);
}
}
}
}
// Update with a new TMAGrid
return DefaultTMAGrid.create(coresNew, newWidth);
}
use of qupath.lib.objects.TMACoreObject in project qupath by qupath.
the class TMACommands method promptToAddTMARowOrColumn.
/**
* Add a new row or column to a TMA grid.
*
* @param imageData
* @param type
* @return
*/
private static boolean promptToAddTMARowOrColumn(final ImageData<?> imageData, final TMAAddType type) {
String NAME = type.commandName();
if (imageData == null) {
Dialogs.showNoImageError(NAME);
return false;
}
if (imageData.getHierarchy().getTMAGrid() == null) {
Dialogs.showErrorMessage(NAME, "No image with dearrayed TMA cores selected!");
return false;
}
PathObjectHierarchy hierarchy = imageData.getHierarchy();
PathObject selected = hierarchy.getSelectionModel().getSelectedObject();
TMACoreObject selectedCore = null;
if (selected != null)
selectedCore = PathObjectTools.getAncestorTMACore(selected);
TMAGrid gridNew = createAugmentedTMAGrid(hierarchy, selectedCore, type);
double w = imageData.getServer().getWidth();
double h = imageData.getServer().getHeight();
// Check if the core centroids all fall within the image or not
int outsideCores = 0;
for (TMACoreObject core : gridNew.getTMACoreList()) {
// Shouldn't happen...
if (!core.hasROI())
continue;
// Check if the centroid for any *new* core falls within the image
// (don't fail if an existing core is outside)
double x = core.getROI().getCentroidX();
double y = core.getROI().getCentroidY();
if (x < 0 || x >= w || y < 0 || y >= h) {
if (!hierarchy.getTMAGrid().getTMACoreList().contains(core)) {
outsideCores++;
}
// throw new IllegalArgumentException("Cannot update TMA grid - not enough space within image");
}
}
if (outsideCores > 0) {
String label = outsideCores == 1 ? "core" : "cores";
if (!Dialogs.showConfirmDialog("Add to TMA Grid", "Not enough space within image to store " + outsideCores + " new " + label + " - proceed anyway?"))
return false;
}
// If we got this far, update the grid
hierarchy.setTMAGrid(gridNew);
// Prompt for relabelling
TMACommands.promptToRelabelTMAGrid(imageData);
return true;
}
use of qupath.lib.objects.TMACoreObject in project qupath by qupath.
the class ColorToolsFX method getDisplayedColorARGB.
/**
* Get the color with which an object should be displayed, as a packaged ARGB integer.
*
* This could be stored internally, or obtained from its PathClass.
*
* If neither of these produces a result, a default color will be returned based on PathPrefs
* for the specific (Java) class of the PathObject.
*
* Assuming PathPrefs does not contain any nulls, this will therefore not return nulls either.
*
* @param pathObject
* @return
*/
public static Integer getDisplayedColorARGB(final PathObject pathObject) {
// Check if any color has been set - if so, return it
Integer color = pathObject.getColorRGB();
if (color != null)
return color;
// Check if any class has been set, if so then use its color
PathClass pathClass = pathObject.getPathClass();
if (pathClass != null)
color = pathClass.getColor();
if (color != null)
return color;
if (pathObject instanceof PathTileObject)
return PathPrefs.colorTileProperty().getValue();
if (pathObject instanceof TMACoreObject) {
if (((TMACoreObject) pathObject).isMissing())
return PathPrefs.colorTMAMissingProperty().getValue();
else
return PathPrefs.colorTMAProperty().getValue();
}
return PathPrefs.colorDefaultObjectsProperty().getValue();
}
use of qupath.lib.objects.TMACoreObject in project qupath by qupath.
the class TMAExplorer method createAndShowStage.
private void createAndShowStage() {
Project<BufferedImage> project = qupath.getProject();
entries.clear();
if (project != null) {
// Create an output directory for the images
File dirBaseImageOutput = Projects.getBaseDirectory(project);
dirBaseImageOutput = new File(dirBaseImageOutput, "TMA");
dirBaseImageOutput = new File(dirBaseImageOutput, "images");
if (!dirBaseImageOutput.exists())
dirBaseImageOutput.mkdirs();
Map<String, RunningStatistics> statsMap = new HashMap<>();
for (ProjectImageEntry<BufferedImage> imageEntry : project.getImageList()) {
// Look for data file
if (!imageEntry.hasImageData())
continue;
File dirImageOutput = new File(dirBaseImageOutput, imageEntry.getImageName());
if (!dirImageOutput.exists())
dirImageOutput.mkdirs();
// Read data
ImageData<BufferedImage> imageData;
try {
imageData = imageEntry.readImageData();
} catch (IOException e) {
logger.error("Error reading ImageData for " + imageEntry.getImageName(), e);
continue;
}
TMAGrid tmaGrid = imageData.getHierarchy().getTMAGrid();
if (tmaGrid == null) {
logger.warn("No TMA data for {}", imageEntry.getImageName());
continue;
}
// Figure out downsample value
ImageServer<BufferedImage> server = imageData.getServer();
double downsample = Math.round(5 / server.getPixelCalibration().getAveragedPixelSizeMicrons());
// Read the TMA entries
int counter = 0;
for (TMACoreObject core : tmaGrid.getTMACoreList()) {
counter++;
String name = core.getName();
if (name == null)
name = Integer.toString(counter);
File fileOutput = new File(dirImageOutput, name + ".jpg");
if (!fileOutput.exists()) {
try {
RegionRequest request = RegionRequest.createInstance(server.getPath(), downsample, core.getROI());
BufferedImage img = server.readBufferedImage(request);
ImageIO.write(img, "jpg", fileOutput);
} catch (Exception e) {
logger.error("Unable to write {}", fileOutput.getAbsolutePath());
}
}
var entry = TMAEntries.createDefaultTMAEntry(imageEntry.getImageName(), fileOutput.getAbsolutePath(), null, core.getName(), core.isMissing());
MeasurementList ml = core.getMeasurementList();
for (int i = 0; i < ml.size(); i++) {
String measurement = ml.getMeasurementName(i);
double val = ml.getMeasurementValue(i);
entry.putMeasurement(measurement, val);
if (!Double.isNaN(val)) {
RunningStatistics stats = statsMap.get(measurement);
if (stats == null) {
stats = new RunningStatistics();
statsMap.put(measurement, stats);
}
stats.addValue(val);
}
}
entries.add(entry);
}
try {
server.close();
} catch (Exception e) {
logger.warn("Problem closing server", e);
}
}
// Loop through all entries and perform outlier count
double k = 3;
for (TMAEntry entry : entries) {
int outlierCount = 0;
for (Entry<String, RunningStatistics> statsEntry : statsMap.entrySet()) {
RunningStatistics stats = statsEntry.getValue();
double val = entry.getMeasurementAsDouble(statsEntry.getKey());
if (!(val >= stats.getMean() - stats.getStdDev() * k && val <= stats.getMean() + stats.getStdDev() * k))
outlierCount++;
}
entry.putMeasurement("Outlier count", outlierCount);
}
}
Stage stage = new Stage();
stage.initOwner(qupath.getStage());
TMASummaryViewer summaryViewer = new TMASummaryViewer(stage);
summaryViewer.setTMAEntries(entries);
summaryViewer.getStage().show();
}
Aggregations