Assigning cells to correct Parent

Hi,
I’m struggling to resolve the hierarchy after cell detection such that cells are classified with tumour/stroma as Parents and not the initial thresholded “Tissue”. I run cell detection on “tissue” first, which allocates all objects to “tissue” parents. I then run tumour/stroma createAnnotationsFromPixelClassifier which generates >10,000 annotations. If I perform this first and then run cell detection to correctly allocate detections, the PC crashes as it is trying to do cell detection within those 10,000 annotations. I hoped selecting these tumour/stroma annotations after cell deteciton, then running object classifier would allocate these detections within these parents but they remain allocated to the initial “tissue” parent.
Any suggestions? Many thanks!

setImageType('FLUORESCENCE');
createAnnotationsFromPixelClassifier("Tissue", 0.0, 0.0);
selectObjectsByClassification("Tissue");
runPlugin('qupath.imagej.detect.cells.WatershedCellDetection', '{"detectionImage": "DAPI (DAPI)",  "requestedPixelSizeMicrons": 0.4,  "backgroundRadiusMicrons": 8.0,  "medianRadiusMicrons": 0.0,  "sigmaMicrons": 1.4,  "minAreaMicrons": 10.0,  "maxAreaMicrons": 400.0,  "threshold": 5.0,  "watershedPostProcess": true,  "cellExpansionMicrons": 5.0,  "includeNuclei": true,  "smoothBoundaries": true,  "makeMeasurements": true}');

createAnnotationsFromPixelClassifier("Tumour_Stroma", 0.0, 0.0, "SPLIT")
selectObjectsByClassification("Tumor", "Stroma");
runObjectClassifier("watershed_CD8", "watershed_PDL1", "watershed_CK", "watershed_PD1", "watershed_CD68", "watershed_FOXP3");
detectionToAnnotationDistances(true)

Do you need the annotations or could you skip that to simply classify the cells as tumor or stroma using the pixel classifier that created the annotations?
IE, “classify” rather than “createAnnotations” from the pixel classifier.

Alternatively you might try to resolveHierarchy() to set the parent child status.

Sorry, I’m not sure I follow?
I would run cell detection, then object classification on the cells, then classifyobjectsbypixelclassifier?
Do you have an example of resolveHierarchy()?

resolveHierarchy applies across all objects in the same way that the context menu for a single annotation has a resolve hierarchy option. It collects what is under it and makes them child objects… roughly. I think there is more to it than that since annotations and detections are handled somewhat differently in terms of borders and centroids.

Essentially, run it whenever you want to resolve where your cells are. It is standalone.

As far as order, that’s a bit different; those were two different solutions to what I thought your question was about.

From your text it sounded like you wanted to classify the cells as tumor or stroma, but looking at your code you want to classify your cells with another object classifier, so you should use something like:

selectCells()
runObjectClassifier("watershed_CD8", "watershed_PDL1", "watershed_CK", "watershed_PD1", "watershed_CD68", "watershed_FOXP3");

Equivalent of Objects->Select->Select Detections->Select cells

If you want to select your cells by class, you can still run the tumor/stroma pixel classifier in object classification mode, then selectobjectsbyclassification on tumor and stroma, but that seems like extra steps if all you want is to run a second classifier on the cells.

1 Like

Thanks @Research_Associate,
resolveHierarchy() worked to allocate the classified cells into the correct annotations. In this way, distance measurements between cells in stroma to a tumour annotation can be made, and cells can be filtered by their compartment for downstream analysis. I realise this is a very straightforward workflow to detect cells, classify them, then allocate to a tumour/stroma compartment. Hopefully this helps someone else!

1 Like

Just to clarify, you don’t need the cells to be parents or children of anything for the Distance to annotations command (though it might help for the selection). Which is pretty nice, since it allows you to do things like use Point objects or lines for your annotations that you get a distance to.

Also, if you ever want to ignore the hierarchy and get all of the objects below any other object, there is an option for that:
https://gist.github.com/Svidro/8f9c06e2c8bcae214cdd7aa9afe57c50#file-get-objects-within-another-object-groovy
I think it is actually faster on large numbers of objects than accessing the hierarchy.

1 Like

Glad it’s working, here are the relevant docs:

In this particular workflow, I also think resolving the hierarchy shouldn’t be needed (although if it works it shouldn’t do any harm either).

2 Likes

Sorry Guys,
I am still struggling with this as resolveHierarchy() with thousands of annotations isn’t feasible.
Could you please advise on the correct workflow to get cells with both a tumour/stroma class(or parent) as well as for cells to be classified by marker within these compartments. I am sure this is a very simple workflow for multiplex analysis, but I have tried every permutation of the following, so appreciate advise- I apologise for the bother.
Overall I wish to:

  1. create tissue annotation to detect the tissue regions on the slide
  2. run cell detection inside this tissue annotation
  3. allocate these cells to tumour/stroma parents (to enable filtering in data analysis)
  4. classify markers within these compartments (CD8 etc)
  5. create tumour/stroma annotation to enable distance to tumour measurements (if I run this last i get the error: Cannot add non-detection objects to detections! Objects will be skipped...
  6. measure distances for cells in stroma to tumour
//create annotation around regions of slide containing tissue, thresholded on DAPI with high sigma smoothing
createAnnotationsFromPixelClassifier("Tissue", 0.0, 0.0);

//select "Tissue" annotation for watershed cell detection
selectObjectsByClassification("Tissue");

//run watershed cell detection
runPlugin('qupath.imagej.detect.cells.WatershedCellDetection', '{"detectionImage": "DAPI (DAPI)",  "requestedPixelSizeMicrons": 0.4,  "backgroundRadiusMicrons": 8.0,  "medianRadiusMicrons": 0.0,  "sigmaMicrons": 1.4,  "minAreaMicrons": 10.0,  "maxAreaMicrons": 400.0,  "threshold": 5.0,  "watershedPostProcess": true,  "cellExpansionMicrons": 5.0,  "includeNuclei": true,  "smoothBoundaries": true,  "makeMeasurements": true}');

//create tumour annotations for distance calculation
createAnnotationsFromPixelClassifier("Tumour_Stroma", 0.0, 0.0, "SPLIT")

//classify cells as tumour/stroma - works but is overwritten by object classifier 
classifyDetectionsByCentroid("Tumour_Stroma")

//classify cells based on marker - removes tumour/stroma classifications 
selectCells()
runObjectClassifier("watershed_CD8", "watershed_PDL1", "watershed_CK", "watershed_PD1", "watershed_CD68", "watershed_FOXP3");

detectionToAnnotationDistances(true)

I would use the tumor/stroma classifier first and create a measurement called Tumor that is either 1 for tumor or 0 for stroma (depending on the class of the cell, so that you are “storing” the first class). Then apply your classifier for cell type, then do your distance to annotations.

It is a little bit weird that you have thousands of tumor annotations, though. Maybe set a minimum size threshold?
rough sample from memory


getCellObjects().each{
if(it.getPathClass() == getPathClass("Tumor")){
it.getMeasurementList().putMeasurement("TumorStroma",1);
} else{
it.getMeasurementList().putMeasurement("TumorStroma",0);
}
}

That is assuming 100% of your cells are either tumor or stroma.

1 Like