Cell detection on a Z-stack

Hello, I am new to Qupath.
I am working with Z stack- Fluorescent images of GCaMP labelled neurons in the cortex.
I need to detect cells throughout the entire Z stack ( more or less 100 planes).
I found a pretty good way to detect them on a single plane.
But now, I am not able to propagate this analysis to the entire stack.
Any idea how I could do that in QuPath.
Thanks a lot for your support

Hi Stephane,
Unfortunately, QuPath doesn’t really support that type of 3D analysis. One way to do it would be to export the Z stack as a series of 2D images, load those into QuPath, and then run the cell detection parameters for the whole batch. That would give you how many cells are in each plane, though it wouldn’t relate the cells in slice 1 to those in slice 2, etc. Depending on the size of your images and what you plan to do with the data, I would recommend ImageJ or CellProfiler for 3D segmentation.

Seconding this. Also seconding splitting the image as that will give you more information (likely through the image name) of what Z slice any particular thing is going on that.

However, if you still want to run cell detection per slice on a whole Z stack, you can do that. Just realize that you will have a lot of different annotations with different cell counts and percentages, per slice, not one combined “whole” measurement.
https://gist.github.com/Svidro/ffb6951e70187de5eb007290f61aea4a#file-zstack-or-time-series-full-image-annotations-groovy
If you use the 0.2.3 version of the script above:

//0.2.3 version
//should work for Zstacks OR time series
//Creates a full image annotation in each frame, which can then be used to generate detections.
import qupath.lib.roi.RectangleROI
import qupath.lib.objects.PathAnnotationObject
hierarchy = getCurrentHierarchy()
def imageData = getCurrentImageData()
def server = imageData.getServer()
def xdist = server.getWidth()
def ydist = server.getHeight()
clearAllObjects()
if (server.nZSlices() >0){
    0.upto(server.nZSlices()-1){
        frame = PathObjects.createAnnotationObject(ROIs.createRectangleROI(0,0,xdist,ydist,ImagePlane.getPlane(it,0)));
        addObject(frame);
    }
}
if (server.nTimepoints() >0){
    0.upto(server.nTimepoints()-1){
        frame = PathObjects.createAnnotationObject(ROIs.createRectangleROI(0,0,xdist,ydist,ImagePlane.getPlane(0,it)));
        addObject(frame);
    }
}

selectAnnotations()
//cell detection here

You can use the whole image annotations to “do something inside of.” So, I could take those annotations as created and run cell detection on all slices using the select annotations command. Alternatively, I could perform thresholding per annotation to find tissue borders, then run cell detections inside of those using selectObjectsByClassification, and then running a cell detection.
Zstack
You should get something like the above (if running a thresholder), with output that looks like:


I did not add in any measurements to indicate which Z slice is which, but that could possibly be done.

Just to re-emphasize, all this is doing is creating a lot of 2D objects spread out across the Z stack.

1 Like

And to set the annotation name to the Z slice you could add:

getAnnotationObjects().each{
    it.setName(it.getROI().getZ().toString())
}

Or as a measurement so the name doesn’t float in the middle.

getAnnotationObjects().each{
    //it.setName(it.getROI().getZ().toString())
    it.getMeasurementList().putMeasurement("Z", it.getROI().getZ())
}
1 Like