Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petebankhead/8dd36f83e5d8cefe161287df8966bde9 to your computer and use it in GitHub Desktop.
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
/**
* 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