Created
June 20, 2020 09:01
-
-
Save petebankhead/8dd36f83e5d8cefe161287df8966bde9 to your computer and use it in GitHub Desktop.
Measure distances from detections to annotations, constrained to work within specific parent objects in QuPath v0.2.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Measure distances from detections to annotations, constrained to work within specific parent objects in QuPath v0.2.0. | |
* | |
* This is essentially like the 'Distance to annotations 2D' command, but it does not indiscriminately measure | |
* distances to *all* annotations, but rather only those contained within the specified parent objects. | |
* | |
* This makes it more suitable to TMA analysis. | |
* | |
* Note: see https://forum.image.sc/t/spatial-analysis-distance-to-annotation-2d-performance-in-tmas/39288 | |
* Script written hastily with limited testing - please report any bugs in the comments below. | |
* | |
* @author Pete Bankhead | |
*/ | |
import qupath.lib.analysis.DistanceTools | |
import static qupath.lib.gui.scripting.QPEx.* | |
// Get the current image | |
def imageData = getCurrentImageData() | |
def hierarchy = imageData.getHierarchy() | |
def server = imageData.getServer() | |
// If true, do all TMA cores. If false, do all selected annotations. | |
boolean doTMACores = !getTMACoreList().isEmpty() | |
// If true, skip detection objects that aren't cells | |
boolean doCellsOnly = false | |
// Get all available classifications | |
def pathClasses = getAnnotationObjects().findAll(p -> p.getPathClass() != null).collect(p -> p.getPathClass()) as Set | |
if (!pathClasses) { | |
println 'No classified annotations found!' | |
return | |
} | |
// Get all potential parent objects | |
def parentObjects = doTMACores ? getTMACoreList() : getSelectedObjects().findAll {it.isAnnotation()} | |
if (!parentObjects) { | |
println 'No selected annotations or TMA core objects found!' | |
} | |
// Parse pixel size info | |
def cal = server.getPixelCalibration() | |
String xUnit = cal.getPixelWidthUnit() | |
String yUnit = cal.getPixelHeightUnit() | |
double pixelWidth = cal.getPixelWidth().doubleValue() | |
double pixelHeight = cal.getPixelHeight().doubleValue() | |
if (!xUnit.equals(yUnit)) | |
throw new IllegalArgumentException("Pixel width & height units do not match! Width " + xUnit + ", height " + yUnit) | |
String unit = xUnit | |
// Process each parent object | |
for (parent in parentObjects) { | |
def pathObjects = hierarchy.getObjectsForROI(null, parent.getROI()) | |
// Get detections, limiting to cell objects if required | |
def detections = doCellsOnly ? pathObjects.findAll {it.isCell()} : pathObjects.findAll {it.isDetection()} | |
if (!detections) | |
continue | |
println 'Measuring distances for ' + parent | |
// Get annotations, grouped according to classification | |
def allAnnotations = pathObjects.findAll {it.isAnnotation()} | |
// Measure all relevant distances for the specific core | |
for (pathClass in pathClasses) { | |
def classifiedAnnotations = allAnnotations.findAll {it.getPathClass() == pathClass} | |
String name = "Distance to annotation " + pathClass + " " + unit | |
if (classifiedAnnotations) | |
DistanceTools.centroidToBoundsDistance2D(detections, classifiedAnnotations, pixelWidth, pixelHeight, name) | |
else | |
detections.forEach { | |
it.getMeasurementList().putMeasurement(name, Double.NaN) | |
it.getMeasurementList().close() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment