use of qupath.lib.objects.PathDetectionObject in project qupath by qupath.
the class PathHierarchyPaintingHelper method paintObject.
/**
* Paint an object (or, more precisely, its ROI), optionally along with the ROIs of any child objects.
*
* This is subject to the OverlayOptions, and therefore may not actually end up painting anything
* (if the settings are such that objects of the class provided are not to be displayed)
*
* @param pathObject
* @param paintChildren
* @param g
* @param boundsDisplayed
* @param overlayOptions
* @param selectionModel
* @param downsample
* @return true if anything was painted, false otherwise
*/
public static boolean paintObject(PathObject pathObject, boolean paintChildren, Graphics2D g, Rectangle boundsDisplayed, OverlayOptions overlayOptions, PathObjectSelectionModel selectionModel, double downsample) {
if (pathObject == null)
return false;
// g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
// g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
// Always paint the selected object
// Note: this makes the assumption that child ROIs are completely contained within their parents;
// this probably should be the case, but isn't guaranteed
boolean isSelected = (selectionModel != null && selectionModel.isSelected(pathObject)) && (PathPrefs.useSelectedColorProperty().get() || !PathObjectTools.hasPointROI(pathObject));
boolean isDetectedObject = pathObject.isDetection() || (pathObject.isTile() && pathObject.hasMeasurements());
// Check if the PathClass isn't being shown
PathClass pathClass = pathObject.getPathClass();
if (!isSelected && overlayOptions != null && overlayOptions.isPathClassHidden(pathClass))
return false;
boolean painted = false;
// See if we need to check the children
ROI pathROI = pathObject.getROI();
if (pathROI != null) {
double roiBoundsX = pathROI.getBoundsX();
double roiBoundsY = pathROI.getBoundsY();
double roiBoundsWidth = pathROI.getBoundsWidth();
double roiBoundsHeight = pathROI.getBoundsHeight();
if (PathObjectTools.hasPointROI(pathObject) || boundsDisplayed == null || pathROI instanceof LineROI || boundsDisplayed.intersects(roiBoundsX, roiBoundsY, Math.max(roiBoundsWidth, 1), Math.max(roiBoundsHeight, 1))) {
// Paint the ROI, if necessary
if (isSelected || (overlayOptions.getShowDetections() && isDetectedObject) || (overlayOptions.getShowAnnotations() && pathObject.isAnnotation()) || (overlayOptions.getShowTMAGrid() && pathObject.isTMACore())) {
boolean doFill = overlayOptions.getFillDetections() || pathObject instanceof ParallelTileObject;
boolean doOutline = true;
Color color = null;
boolean useMapper = false;
double fillOpacity = .75;
if (isSelected && PathPrefs.useSelectedColorProperty().get() && PathPrefs.colorSelectedObjectProperty().getValue() != null)
color = ColorToolsAwt.getCachedColor(PathPrefs.colorSelectedObjectProperty().get());
else {
MeasurementMapper mapper = overlayOptions.getMeasurementMapper();
useMapper = mapper != null && mapper.isValid() && pathObject.isDetection();
if (useMapper) {
if (pathObject.hasMeasurements()) {
Integer rgb = mapper.getColorForObject(pathObject);
// If the mapper returns null, the object shouldn't be painted
if (rgb == null)
return false;
// , mapper.getColorMapper().hasAlpha());
color = ColorToolsAwt.getCachedColor(rgb);
} else
color = null;
// System.out.println(color + " - " + pathObject.getMeasurementList().getMeasurementValue(mapper.));
fillOpacity = 1.0;
// Outlines are not so helpful with the measurement mapper
if (doFill)
doOutline = doOutline && !pathObject.isTile();
} else {
Integer rgb = ColorToolsFX.getDisplayedColorARGB(pathObject);
color = ColorToolsAwt.getCachedColor(rgb);
}
// color = PathObjectHelpers.getDisplayedColor(pathObject);
}
// Check if we have only one or two pixels to draw - if so, we can be done quickly
if (isDetectedObject && downsample > 4 && roiBoundsWidth / downsample < 3 && roiBoundsHeight / downsample < 3) {
int x = (int) roiBoundsX;
int y = (int) roiBoundsY;
// Prefer rounding up, lest we lose a lot of regions unnecessarily
int w = (int) (roiBoundsWidth + .9);
int h = (int) (roiBoundsHeight + .9);
if (w > 0 && h > 0) {
g.setColor(color);
// g.setColor(DisplayHelpers.getMoreTranslucentColor(color));
// g.setStroke(getCachedStroke(overlayOptions.strokeThinThicknessProperty().get()));
g.fillRect(x, y, w, h);
}
painted = true;
} else {
Stroke stroke = null;
// Decide whether to fill or not
Color colorFill = doFill && (isDetectedObject || PathObjectTools.hasPointROI(pathObject)) ? color : null;
if (colorFill != null && fillOpacity != 1) {
if (pathObject instanceof ParallelTileObject)
colorFill = ColorToolsAwt.getMoreTranslucentColor(colorFill);
else if (pathObject instanceof PathCellObject && overlayOptions.getShowCellBoundaries() && overlayOptions.getShowCellNuclei()) {
// if (isSelected)
// colorFill = ColorToolsAwt.getTranslucentColor(colorFill);
// else
colorFill = ColorToolsAwt.getMoreTranslucentColor(colorFill);
} else if (pathObject.getParent() instanceof PathDetectionObject) {
colorFill = ColorToolsAwt.getTranslucentColor(colorFill);
} else if (pathObject instanceof PathTileObject && pathClass == null && color != null && color.getRGB() == PathPrefs.colorTileProperty().get()) {
// Don't fill in empty, unclassified tiles
// DisplayHelpers.getMoreTranslucentColor(colorFill);
colorFill = null;
}
}
// Color colorStroke = doOutline ? (colorFill == null ? color : (downsample > overlayOptions.strokeThinThicknessProperty().get() ? null : DisplayHelpers.darkenColor(color))) : null;
Color colorStroke = doOutline ? (colorFill == null ? color : ColorToolsAwt.darkenColor(color)) : null;
// For thick lines, antialiasing is very noticeable... less so for thin lines (of which there may be a huge number)
if (isDetectedObject) {
// Detections inside detections get half the line width
if (pathObject.getParent() instanceof PathDetectionObject)
stroke = getCachedStroke(PathPrefs.detectionStrokeThicknessProperty().get() / 2.0);
else
stroke = getCachedStroke(PathPrefs.detectionStrokeThicknessProperty().get());
} else {
double thicknessScale = downsample * (isSelected && !PathPrefs.useSelectedColorProperty().get() ? 1.6 : 1);
float thickness = (float) (PathPrefs.annotationStrokeThicknessProperty().get() * thicknessScale);
if (isSelected && pathObject.getParent() == null && PathPrefs.selectionModeProperty().get()) {
stroke = getCachedStrokeDashed(thickness);
} else {
stroke = getCachedStroke(thickness);
}
}
g.setStroke(stroke);
boolean paintSymbols = overlayOptions.getDetectionDisplayMode() == DetectionDisplayMode.CENTROIDS && pathObject.isDetection() && !pathObject.isTile();
if (paintSymbols) {
pathROI = PathObjectTools.getROI(pathObject, true);
double x = pathROI.getCentroidX();
double y = pathROI.getCentroidY();
double radius = PathPrefs.detectionStrokeThicknessProperty().get() * 2.0;
if (pathObject.getParent() instanceof PathDetectionObject)
radius /= 2.0;
Shape shape;
int nSubclasses = 0;
if (pathClass != null) {
nSubclasses = PathClassTools.splitNames(pathClass).size();
}
switch(nSubclasses) {
case 0:
var ellipse = localEllipse2D.get();
ellipse.setFrame(x - radius, y - radius, radius * 2, radius * 2);
shape = ellipse;
break;
case 1:
var rect = localRect2D.get();
rect.setFrame(x - radius, y - radius, radius * 2, radius * 2);
shape = rect;
break;
case 2:
var triangle = localPath2D.get();
double sqrt3 = Math.sqrt(3.0);
triangle.reset();
triangle.moveTo(x, y - radius * 2.0 / sqrt3);
triangle.lineTo(x - radius, y + radius / sqrt3);
triangle.lineTo(x + radius, y + radius / sqrt3);
triangle.closePath();
shape = triangle;
break;
case 3:
var plus = localPath2D.get();
plus.reset();
plus.moveTo(x, y - radius);
plus.lineTo(x, y + radius);
plus.moveTo(x - radius, y);
plus.lineTo(x + radius, y);
shape = plus;
break;
default:
var cross = localPath2D.get();
cross.reset();
radius /= Math.sqrt(2);
cross.moveTo(x - radius, y - radius);
cross.lineTo(x + radius, y + radius);
cross.moveTo(x + radius, y - radius);
cross.lineTo(x - radius, y + radius);
shape = cross;
break;
}
paintShape(shape, g, colorStroke, stroke, colorFill);
} else if (pathObject instanceof PathCellObject) {
PathCellObject cell = (PathCellObject) pathObject;
if (overlayOptions.getShowCellBoundaries())
paintROI(pathROI, g, colorStroke, stroke, colorFill, downsample);
if (overlayOptions.getShowCellNuclei())
paintROI(cell.getNucleusROI(), g, colorStroke, stroke, colorFill, downsample);
painted = true;
} else {
if ((overlayOptions.getFillAnnotations() && pathObject.isAnnotation() && pathObject.getPathClass() != PathClassFactory.getPathClass(StandardPathClasses.REGION) && (pathObject.getPathClass() != null || !pathObject.hasChildren())) || (pathObject.isTMACore() && overlayOptions.getShowTMACoreLabels()))
paintROI(pathROI, g, colorStroke, stroke, ColorToolsAwt.getMoreTranslucentColor(colorStroke), downsample);
else
paintROI(pathROI, g, colorStroke, stroke, colorFill, downsample);
painted = true;
}
}
}
}
}
// Paint the children, if necessary
if (paintChildren) {
for (PathObject childObject : pathObject.getChildObjectsAsArray()) {
// Only call the painting method if required
ROI childROI = childObject.getROI();
if ((childROI != null && boundsDisplayed.intersects(childROI.getBoundsX(), childROI.getBoundsY(), childROI.getBoundsWidth(), childROI.getBoundsHeight())) || childObject.hasChildren())
painted = paintObject(childObject, paintChildren, g, boundsDisplayed, overlayOptions, selectionModel, downsample) | painted;
}
}
return painted;
}
use of qupath.lib.objects.PathDetectionObject in project qupath by qupath.
the class IJExtension method extractOverlay.
/**
* Extract an ImageJ overlay for the specified region.
* @param hierarchy
* @param request
* @param options options to control which objects are being displayed
* @param filter optional additional filter used to determine which objects will be included (may be used in combination with options)
* @return
*/
public static Overlay extractOverlay(PathObjectHierarchy hierarchy, RegionRequest request, OverlayOptions options, Predicate<PathObject> filter) {
Overlay overlay = new Overlay();
double downsample = request.getDownsample();
double xOrigin = -request.getX() / downsample;
double yOrigin = -request.getY() / downsample;
// TODO: Permit filling/unfilling ROIs
for (PathObject child : hierarchy.getObjectsForRegion(PathObject.class, request, null)) {
if (filter != null && !filter.test(child))
continue;
if (child.hasROI()) {
// Check if this is displayed - skip it not
if (options != null && ((child instanceof PathDetectionObject && !options.getShowDetections()) || (child instanceof PathAnnotationObject && !options.getShowAnnotations()) || (child instanceof TMACoreObject && !options.getShowTMAGrid())))
continue;
boolean isCell = child instanceof PathCellObject;
Color color = ColorToolsAwt.getCachedColor(ColorToolsFX.getDisplayedColorARGB(child));
if (!(isCell && (options == null || !options.getShowCellBoundaries()))) {
Roi roi = IJTools.convertToIJRoi(child.getROI(), xOrigin, yOrigin, downsample);
roi.setStrokeColor(color);
roi.setName(child.getDisplayedName());
// roi.setStrokeWidth(2);
overlay.add(roi);
}
if (isCell && (options == null || options.getShowCellNuclei())) {
ROI nucleus = ((PathCellObject) child).getNucleusROI();
if (nucleus == null)
continue;
Roi roi = IJTools.convertToIJRoi(((PathCellObject) child).getNucleusROI(), xOrigin, yOrigin, downsample);
roi.setStrokeColor(color);
roi.setName(child.getDisplayedName() + " - nucleus");
overlay.add(roi);
}
}
}
return overlay;
}
use of qupath.lib.objects.PathDetectionObject in project qupath by qupath.
the class SmoothFeaturesPlugin method smoothMeasurements.
/**
* Using the centroids of the ROIs within PathObjects, 'smooth' measurements by summing up the corresponding measurements of
* nearby objects, weighted by centroid distance.
*
* @param pathObjects
* @param measurements
* @param fwhmPixels
* @param fwhmString
* @param withinClass
* @param useLegacyNames
*/
// public static Set<String> smoothMeasurements(List<PathObject> pathObjects, List<String> measurements, double fwhmPixels) {
public static void smoothMeasurements(List<PathObject> pathObjects, List<String> measurements, double fwhmPixels, String fwhmString, boolean withinClass, boolean useLegacyNames) {
if (measurements.isEmpty() || pathObjects.size() <= 1)
// Collections.emptySet();
return;
if (fwhmString == null)
fwhmString = String.format("%.2f px", fwhmPixels);
double fwhmPixels2 = fwhmPixels * fwhmPixels;
double sigmaPixels = fwhmPixels / Math.sqrt(8 * Math.log(2));
double sigma2 = 2 * sigmaPixels * sigmaPixels;
double maxDist = sigmaPixels * 3;
// Maximum separation
double maxDistSq = maxDist * maxDist;
int nObjects = pathObjects.size();
// int counter = 0;
// Sort by x-coordinate - this gives us a method of breaking early
Collections.sort(pathObjects, new Comparator<PathObject>() {
@Override
public int compare(PathObject o1, PathObject o2) {
double x1 = o1.getROI().getCentroidX();
double x2 = o2.getROI().getCentroidX();
// System.out.println(String.format("(%.2f, %.2f) vs (%.2f, %.2f)", o1.getROI().getCentroidX(), o1.getROI().getCentroidY(), o2.getROI().getCentroidX(), o2.getROI().getCentroidY())); }
return Double.compare(x1, x2);
// if (x1 > x2)
// return 1;
// if (x2 < x1)
// return -1;
// System.out.println(x1 + " vs. " + x2);
// System.out.println(String.format("(%.2f, %.2f) vs (%.2f, %.2f)", o1.getROI().getCentroidX(), o1.getROI().getCentroidY(), o2.getROI().getCentroidX(), o2.getROI().getCentroidY()));
// return 0;
// return (int)Math.signum(o1.getROI().getCentroidX() - o2.getROI().getCentroidX());
}
});
// Create a LUT for distances - calculating exp every time is expensive
double[] distanceWeights = new double[(int) (maxDist + .5) + 1];
for (int i = 0; i < distanceWeights.length; i++) {
distanceWeights[i] = Math.exp(-(i * i) / sigma2);
}
System.currentTimeMillis();
float[] xCentroids = new float[nObjects];
float[] yCentroids = new float[nObjects];
PathClass[] pathClasses = new PathClass[nObjects];
int[] nearbyDetectionCounts = new int[nObjects];
float[][] measurementsWeighted = new float[nObjects][measurements.size()];
float[][] measurementDenominators = new float[nObjects][measurements.size()];
float[][] measurementValues = new float[nObjects][measurements.size()];
for (int i = 0; i < nObjects; i++) {
PathObject pathObject = pathObjects.get(i);
if (withinClass)
pathClasses[i] = pathObject.getPathClass() == null ? null : pathObject.getPathClass().getBaseClass();
ROI roi = pathObject.getROI();
xCentroids[i] = (float) roi.getCentroidX();
yCentroids[i] = (float) roi.getCentroidY();
MeasurementList measurementList = pathObject.getMeasurementList();
int ind = 0;
for (String name : measurements) {
float value = (float) measurementList.getMeasurementValue(name);
// Used to cache values
measurementValues[i][ind] = value;
// Based on distances and measurements
measurementsWeighted[i][ind] = value;
// Based on distances along
measurementDenominators[i][ind] = 1;
ind++;
}
}
String prefix, postfix, denomName, countsName;
// Use previous syntax for naming smoothed measurements
if (useLegacyNames) {
prefix = "";
postfix = String.format(" - Smoothed (FWHM %s)", fwhmString);
denomName = String.format("Smoothed denominator (local density, FWHM %s)", fwhmString);
countsName = String.format("Nearby detection counts (radius %s)", fwhmString);
} else {
prefix = String.format("Smoothed: %s: ", fwhmString);
postfix = "";
// prefix + "Weighted density";
denomName = null;
countsName = prefix + "Nearby detection counts";
// denomName = prefix + "Denominator (local density)";
// countsName = prefix + "Nearby detection counts";
}
// Loop through objects, computing predominant class based on distance weighting
for (int i = 0; i < nObjects; i++) {
// Extract the current class index
PathObject pathObject = pathObjects.get(i);
PathClass pathClass = pathClasses[i];
MeasurementList measurementList = pathObject.getMeasurementList();
float[] mValues = measurementValues[i];
float[] mWeighted = measurementsWeighted[i];
float[] mDenominator = measurementDenominators[i];
// Compute centroid distances
double xi = xCentroids[i];
double yi = yCentroids[i];
for (int j = i + 1; j < nObjects; j++) {
double xj = xCentroids[j];
double yj = yCentroids[j];
// Break early if we are already too far away
if (Math.abs(xj - xi) > maxDist) {
break;
}
double distSq = (xj - xi) * (xj - xi) + (yj - yi) * (yj - yi);
// // Check if we are close enough to have an influence
if (distSq > maxDistSq || Double.isNaN(distSq))
continue;
// Check if the class is ok, if check needed
if (withinClass && pathClass != pathClasses[j])
continue;
// Update the counts, if close enough
if (distSq < fwhmPixels2) {
nearbyDetectionCounts[i]++;
nearbyDetectionCounts[j]++;
}
// Update the class weights for both objects currently being tested
// Compute weight based on centroid distances
// double weight = Math.exp(-distSq/sigma2);
// * pathObjects.get(j).getClassProbability();
double weight = distanceWeights[(int) (Math.sqrt(distSq) + .5)];
float[] temp = measurementValues[j];
float[] tempWeighted = measurementsWeighted[j];
float[] tempDenominator = measurementDenominators[j];
for (int ind = 0; ind < measurements.size(); ind++) {
float tempVal = temp[ind];
if (Float.isNaN(tempVal))
continue;
mWeighted[ind] += tempVal * weight;
mDenominator[ind] += weight;
float tempVal2 = mValues[ind];
if (Float.isNaN(tempVal2))
continue;
tempWeighted[ind] += tempVal2 * weight;
tempDenominator[ind] += weight;
}
}
// Store the measurements
int ind = 0;
float maxDenominator = Float.NEGATIVE_INFINITY;
for (String name : measurements) {
// if (name.contains(" - Smoothed (FWHM ") || name.startsWith("Smoothed denominator (local density, ") || name.startsWith("Nearby detection counts"))
// continue;
float denominator = mDenominator[ind];
if (denominator > maxDenominator)
maxDenominator = denominator;
String nameToAdd = prefix + name + postfix;
measurementList.putMeasurement(nameToAdd, mWeighted[ind] / denominator);
// measurementsAdded.add(nameToAdd);
// measurementList.putMeasurement(name + " - weighted sum", mWeighted[ind]); // TODO: Support optionally providing weighted sums
// measurementList.addMeasurement(name + " - smoothed", mWeighted[ind] / mDenominator[ind]);
ind++;
}
if (pathObject instanceof PathDetectionObject && denomName != null) {
measurementList.putMeasurement(denomName, maxDenominator);
// measurementsAdded.add(denomName);
}
if (pathObject instanceof PathDetectionObject && countsName != null) {
measurementList.putMeasurement(countsName, nearbyDetectionCounts[i]);
// measurementsAdded.add(countsName);
}
measurementList.close();
}
System.currentTimeMillis();
// return measurementsAdded;
}
use of qupath.lib.objects.PathDetectionObject in project qupath by qupath.
the class ObservableMeasurementTableData method updateMeasurementList.
/**
* Update the entire measurement list for the current objects.
* @see #setImageData(ImageData, Collection)
*/
public synchronized void updateMeasurementList() {
// PathPrefs.setAllredMinPercentagePositive(0);
builderMap.clear();
// Add the image name
if (!PathPrefs.maskImageNamesProperty().get())
builderMap.put("Image", new ImageNameMeasurementBuilder(imageData));
// Check if we have any annotations / TMA cores
boolean containsDetections = false;
boolean containsAnnotations = false;
// boolean containsParentAnnotations = false;
boolean containsTMACores = false;
boolean containsRoot = false;
List<PathObject> pathObjectListCopy = new ArrayList<>(list);
for (PathObject temp : pathObjectListCopy) {
if (temp instanceof PathAnnotationObject) {
// if (temp.hasChildren())
// containsParentAnnotations = true;
containsAnnotations = true;
} else if (temp instanceof TMACoreObject) {
containsTMACores = true;
} else if (temp instanceof PathDetectionObject) {
containsDetections = true;
} else if (temp.isRootObject())
containsRoot = true;
}
boolean detectionsAnywhere = imageData == null ? containsDetections : !imageData.getHierarchy().getDetectionObjects().isEmpty();
// Include the object displayed name
// if (containsDetections || containsAnnotations || containsTMACores)
builderMap.put("Name", new ObjectNameMeasurementBuilder());
// Include the class
if (containsAnnotations || containsDetections) {
builderMap.put("Class", new PathClassMeasurementBuilder());
// Get the name of the containing TMA core if we have anything other than cores
if (imageData != null && imageData.getHierarchy().getTMAGrid() != null) {
builderMap.put("TMA core", new TMACoreNameMeasurementBuilder());
}
// Get the name of the first parent object
builderMap.put("Parent", new ParentNameMeasurementBuilder());
}
// Include the TMA missing status, if appropriate
if (containsTMACores) {
builderMap.put("Missing", new MissingTMACoreMeasurementBuilder());
}
if (containsAnnotations || containsDetections) {
builderMap.put("ROI", new ROINameMeasurementBuilder());
}
// Add centroids
if (containsAnnotations || containsDetections || containsTMACores) {
// ROICentroidMeasurementBuilder builder = new ROICentroidMeasurementBuilder(imageData, CentroidType.X);
// builderMap.put("Centroid X", builder);
// builder = new ROICentroidMeasurementBuilder(imageData, CentroidType.Y);
// builderMap.put("Centroid Y", builder);
ROICentroidMeasurementBuilder builder = new ROICentroidMeasurementBuilder(imageData, CentroidType.X);
builderMap.put(builder.getName(), builder);
builder = new ROICentroidMeasurementBuilder(imageData, CentroidType.Y);
builderMap.put(builder.getName(), builder);
}
// If we have metadata, store it
Set<String> metadataNames = new LinkedHashSet<>();
metadataNames.addAll(builderMap.keySet());
for (PathObject pathObject : pathObjectListCopy) {
if (pathObject instanceof MetadataStore) {
metadataNames.addAll(((MetadataStore) pathObject).getMetadataKeys());
}
}
// Ensure we have suitable builders
for (String name : metadataNames) {
if (!builderMap.containsKey(name))
builderMap.put(name, new StringMetadataMeasurementBuilder(name));
}
// Get all the 'built-in' feature measurements, stored in the measurement list
Collection<String> features = PathClassifierTools.getAvailableFeatures(pathObjectListCopy);
// Add derived measurements if we don't have only detections
if (containsAnnotations || containsTMACores || containsRoot) {
if (detectionsAnywhere) {
var builder = new ObjectTypeCountMeasurementBuilder(PathDetectionObject.class);
builderMap.put(builder.getName(), builder);
features.add(builder.getName());
}
// Here, we allow TMA cores to act like annotations
manager = new DerivedMeasurementManager(getImageData(), containsAnnotations || containsTMACores);
for (MeasurementBuilder<?> builder2 : manager.getMeasurementBuilders()) {
builderMap.put(builder2.getName(), builder2);
features.add(builder2.getName());
}
}
// If we have an annotation, add shape features
if (containsAnnotations) {
boolean anyPoints = false;
boolean anyAreas = false;
boolean anyLines = false;
@SuppressWarnings("unused") boolean anyPolygons = false;
for (PathObject pathObject : pathObjectListCopy) {
if (!pathObject.isAnnotation())
continue;
ROI roi = pathObject.getROI();
if (roi == null)
continue;
if (roi.isPoint())
anyPoints = true;
if (roi.isArea())
anyAreas = true;
if (roi.isLine())
anyLines = true;
if (pathObject.getROI() instanceof PolygonROI)
anyPolygons = true;
}
// Add point count, if needed
if (anyPoints) {
MeasurementBuilder<?> builder = new NumPointsMeasurementBuilder();
builderMap.put(builder.getName(), builder);
features.add(builder.getName());
}
// Add spatial measurements, if needed
if (anyAreas) {
MeasurementBuilder<?> builder = new AreaMeasurementBuilder(imageData);
builderMap.put(builder.getName(), builder);
features.add(builder.getName());
builder = new PerimeterMeasurementBuilder(imageData);
builderMap.put(builder.getName(), builder);
features.add(builder.getName());
}
if (anyLines) {
MeasurementBuilder<?> builder = new LineLengthMeasurementBuilder(imageData);
builderMap.put(builder.getName(), builder);
features.add(builder.getName());
}
// if (anyPolygons) {
// MeasurementBuilder<?> builder = new MaxDiameterMeasurementBuilder(imageData);
// builderMap.put(builder.getName(), builder);
// features.add(builder.getName());
//
// builder = new MinDiameterMeasurementBuilder(imageData);
// builderMap.put(builder.getName(), builder);
// features.add(builder.getName());
// }
}
if (containsAnnotations || containsTMACores || containsRoot) {
var pixelClassifier = getPixelLayer(imageData);
if (pixelClassifier instanceof ImageServer<?>) {
ImageServer<BufferedImage> server = (ImageServer<BufferedImage>) pixelClassifier;
if (server.getMetadata().getChannelType() == ImageServerMetadata.ChannelType.CLASSIFICATION || server.getMetadata().getChannelType() == ImageServerMetadata.ChannelType.PROBABILITY) {
var pixelManager = new PixelClassificationMeasurementManager(server);
for (String name : pixelManager.getMeasurementNames()) {
// String nameLive = name + " (live)";
String nameLive = "(Live) " + name;
builderMap.put(nameLive, new PixelClassifierMeasurementBuilder(pixelManager, name));
features.add(nameLive);
}
}
}
}
// Update all the lists, if necessary
boolean changes = false;
if (metadataNames.size() != metadataList.size() || !metadataNames.containsAll(metadataList)) {
changes = metadataList.setAll(metadataNames);
}
if (features.size() != measurementList.size() || !features.containsAll(measurementList))
changes = measurementList.setAll(features);
if (changes) {
if (metadataList.isEmpty())
fullList.setAll(measurementList);
else {
fullList.setAll(metadataList);
fullList.addAll(measurementList);
}
}
}
use of qupath.lib.objects.PathDetectionObject in project qupath by qupath.
the class SmoothFeaturesPlugin method addRunnableTasks.
@Override
protected void addRunnableTasks(final ImageData<T> imageData, final PathObject parentObject, List<Runnable> tasks) {
double fwhm;
ImageServer<T> server = imageData.getServer();
String fwhmStringTemp;
PixelCalibration cal = server == null ? null : server.getPixelCalibration();
if (cal != null && cal.hasPixelSizeMicrons()) {
fwhm = getParameterList(imageData).getDoubleParameterValue("fwhmMicrons");
fwhmStringTemp = GeneralTools.createFormatter(2).format(fwhm) + " " + GeneralTools.micrometerSymbol();
fwhm /= cal.getAveragedPixelSizeMicrons();
// params.addDoubleParameter("fwhmPixels", "Radius (FWHM)", fwhm, "pixels"); // Set the FWHM in pixels too
} else {
fwhm = getParameterList(imageData).getDoubleParameterValue("fwhmPixels");
fwhmStringTemp = GeneralTools.createFormatter(2).format(fwhm) + " px";
}
// sigma = 50;
final String fwhmString = fwhmStringTemp;
final double fwhmPixels = fwhm;
final boolean withinClass = params.getBooleanParameterValue("smoothWithinClasses");
final boolean useLegacyNames = params.containsKey("useLegacyNames") && Boolean.TRUE.equals(params.getBooleanParameterValue("useLegacyNames"));
tasks.add(new Runnable() {
@Override
public void run() {
try {
if (!parentObject.hasChildren())
return;
// System.out.println("Smoothing with FWHM " +fwhmPixels);
// TODO: MAKE A MORE ELEGANT LIST!!!!
List<PathObject> pathObjects = PathObjectTools.getFlattenedObjectList(parentObject, null, false);
Iterator<PathObject> iterObjects = pathObjects.iterator();
while (iterObjects.hasNext()) {
PathObject temp = iterObjects.next();
if (!(temp instanceof PathDetectionObject || temp instanceof PathTileObject))
iterObjects.remove();
}
if (pathObjects.isEmpty())
return;
// TODO: ACCESS & USE THE CLASSIFIER DATA!!!!
List<String> measurements = new ArrayList<>(PathClassifierTools.getAvailableFeatures(pathObjects));
Iterator<String> iter = measurements.iterator();
while (iter.hasNext()) {
String name = iter.next().toLowerCase();
if (name.endsWith("smoothed") || name.startsWith("smoothed") || name.contains(" - smoothed (fwhm ") || name.startsWith("smoothed denominator (local density, ") || name.startsWith("nearby detection counts"))
iter.remove();
}
logger.debug(String.format("Smooth features: %s (FWHM: %.2f px)", parentObject.getDisplayedName(), fwhmPixels));
smoothMeasurements(pathObjects, measurements, fwhmPixels, fwhmString, withinClass, useLegacyNames);
// // REMOVE - the purpose was to test a 'difference of Gaussians' type of thing
// List<String> namesAdded1 = new ArrayList<>(smoothMeasurements(pathObjects, measurements, fwhmPixels));
// List<String> namesAdded2 = new ArrayList<>(smoothMeasurements(pathObjects, measurements, fwhmPixels * 2));
// for (PathObject pathObject : pathObjects) {
// MeasurementList ml = pathObject.getMeasurementList();
// ml.ensureListOpen();
// for (int i = 0; i < namesAdded1.size(); i++) {
// String name1 = namesAdded1.get(i);
// String name2 = namesAdded2.get(i);
// double m1 = ml.getMeasurementValue(name1);
// double m2 = ml.getMeasurementValue(name2);
// ml.addMeasurement(name1 + " - " + name2, m1 - m2);
// }
// ml.closeList();
// }
} catch (Exception e) {
e.printStackTrace();
throw (e);
}
}
});
}
Aggregations