QuPath + Stardist?

Hi,

Is there a way to use StarDist for cell detection in QuPath instead of intensity based nuclei segmentation ?

Thanks
Ofra

1 Like

Good question, and something I discussed with @mweigert at NeuBIAS :slight_smile:

In the short term, the easy answer is answer is ‘no’, or at least ‘not easily’… since it is quite possible to get contours generated from StarDist into QuPath as detection objects via scripting + Fiji/Python (and a lot of effort).

But the aim is to have a much nicer way to call StarDist from within QuPath. This remains a work in progress, and needs some patience. There is nothing really needs changed on the StarDist side, except perhaps training alternative histology/brightfield-friendly models. However, a lot can/should be done within QuPath to make it useful and maintainable (e.g. estimating cell boundaries, measuring the different cell compartments etc.). Most of this work is not StarDist specific, but needed to help make lots more algorithms QuPath-friendly.

5 Likes

Your Stardist script at Neubias webinar was impressive. Any repository ?
Thanks

Not yet, currently working through the 200 questions from the webinar - will post the answers here in the next few days, including for the StarDist ones :slight_smile:

6 Likes

Thanks for the nice tutorial for using stardist with qupath (https://qupath.readthedocs.io/en/latest/docs/advanced/stardist.html).

It works great but I couldn’t train an object classifier after Stardist unlike with the build in Qupath cell detection. What am I doing wrong ?

Thanks for the help.

2 Likes

@Perfidalbion did you use the settings in the script that creates measurements? These are needed for an object classifier.

Hi there,

The script you used with Stardist is very impressive!
I only don’t get how to use the script you’ve made cause you need several plug-ins.
Is there a tutorial how to download Stardist/Python library/Fiji plugin. I don’t have much experience with scripting.

Thank you! :slight_smile:

@K.massy The tutorial is https://qupath.readthedocs.io/en/latest/docs/advanced/stardist.html (thanks @Perfidalbion for reviving this thread and updating adding the link :slight_smile: )

There is no need to install any extensions / Python library / Fiji plugin, but you do need to build QuPath from the source code using Gradle, as described in the link. I’m afraid I don’t have time to make it any more user-friendly in the near future.

4 Likes

Oh thanks I’m silly this must be the issue I used the first script you gave (in Detecting nuclei) and not the one in Cell expansion & measurements.

Thanks

2 Likes

Good to know! I’ll check more closely here, thanks for responding. :grin:

1 Like

@petebankhead Thank you for that detail write up about the StarDist in QuPath. Also, the “build from source” instructions were very easy to follow. It was very helpful in getting started and trying it out !

How can one configure QuPath-StarDist with GPU, I was able to compile with GPU option but then It didn’t seem like GPU was actually being utilized. I saw usage of a single core and no GPU.

I have installed CUDA V10.0 respective cuDNN V7.6.4, the combination seems to work for FIJI StarDist plugin at the moment. StarDist is yet to be part of an official release, I am sure we will see explanations about how to configure for GPU in Docs in the near future.

Keep up the good work!

1 Like

@Ajay_Zalavadia On Windows, all I had to do was build with the appropriate -Ptensorflow-gpu=true flag (you should see from the name of the tensorflow jar file if the correct one is used) and then have CUDA & cuDNN installed. From memory, I think the versions you mention should work - but I remember I had ‘wrong’ ones installed previously, and then nothing would work.

As I mention in the notes, a substantial part of the processing time is spent doing all the post-processing (depending upon your chosen settings). So depending upon the size of the region you choose, the difference in speed might not be huge.

If you choose a larger region, the processing will be tiled and parallelized (including the CPU part).

I noticed this two jars:

tensorflow-1.15.2-1.5.3.jar
tensorflow-1.15.2-1.5.3-windows-x86_64-gpu.jar

I will do more testing next week to see if I can get it to work. Is there a way to see CUDA version, cuDNN version and GPU info in logs or script output?

On further testing, it seems like it is working fine with GPU. I think I was having problem the first time because my pixel calibration values were wrong.

Here are my numbers using Luca-7 color component image using the script example you posted in docs (which detect cell nucleus using DAPI, expand and add measurements to cell objects).

CPU: Script run time: 15.42 seconds
GPU: Script run time: 5.31 seconds

That is pretty good improvement, next week I want to see if it scales well with larger image :smile:

2 Likes

Wanted to add that I was also able to get this all up and running over the weekend. Only snags I hit were not remembering to upgrade my version of Gradle (had 5.5, I think), and a couple of small changes related to StarDist (Keras version) that were covered in another post.

This should only be necessary if you’ve got some custom setup – following the instructions in the QuPath docs Gradle wrapper should take care of this itself.

1 Like

Could be. I did find that I had a PATH variable pointing to 5.5 from… somewhen. Previous versions of things have often been the bane of my attempts to do things like this.

Hi!

The Stardist nucleus segmentation is such a great addition to Qupath - thank you! There is however an issue with the selection not being registered if you e.g. do a simple tissue detection command before the Stardist script: selectAnnotations(); or other similar command does not work with the script, and the error message “ERROR: StarDist: Please select a parent object!” always occurs if the annotation is not selected manually with a mouse doble click. SelectAnnotations(); does selects the simple tissue detection object, but not in the list of annotations like when you double click it (seems to be the only difference preventing the script from proceeding). This prevents it from being part of a batch script for a project. Any suggestions?

runPlugin(‘qupath.imagej.detect.tissue.SimpleTissueDetection2’, ‘{“threshold”: 205, “requestedPixelSizeMicrons”: 5.0, “minAreaMicrons”: 500000.0, “maxHoleAreaMicrons”: 1000000.0, “darkBackground”: false, “smoothImage”: true, “medianCleanup”: true, “dilateBoundaries”: true, “smoothCoordinates”: true, “excludeOnBoundary”: false, “singleAnnotation”: true}’);

selectAnnotations();

import qupath.tensorflow.stardist.StarDist2D

// Specify the model directory (you will need to change this!)
def pathModel = ‘/QuPath/he_heavy_augment’

def stardist = StarDist2D.builder(pathModel)
.threshold(0.01) // Prediction threshold
.normalizePercentiles(1, 99) // Percentile normalization
.pixelSize(0.5) // Resolution for detection
.cellExpansion(5.0) // Approximate cells based upon nucleus expansion
.cellConstrainScale(1.5) // Constrain cell expansion using nucleus size
.measureShape() // Add shape measurements
.measureIntensity() // Add cell measurements (in all compartments)
.includeProbability(true) // Include prediction probability as measurement
.build()

// Run detection for the selected objects
def imageData = getCurrentImageData()
def pathObject = getSelectedObject()
if (pathObject == null) {
Dialogs.showErrorMessage(“StarDist”, “Please select a parent object!”)
return
}
stardist.detectObjects(imageData, pathObject, true)
println ‘Done!’

Hi @HenrikSP, you could try switching

def pathObject = getSelectedObject()
if (pathObject == null) {
   Dialogs.showErrorMessage(“StarDist”, “Please select a parent object!”)
   return
}
stardist.detectObjects(imageData, pathObject, true)

for

def pathObjects = getSelectedObjects()
if (!pathObjects) {
   Dialogs.showErrorMessage(“StarDist”, “Please select a parent object!”)
   return
}
stardist.detectObjects(imageData, pathObjects)

The trouble is that QuPath makes a (usually ignore-able) distinction between the collection of all selected objects, and one ‘main’ selected object which might be among them. The ‘main’ one is the one you can edit in the viewer (assuming it’s an unlocked annotation).

You can set the main selected object programmatically or by double-clicking in the viewer, but it seems that selectAnnotations() doesn’t do this (perhaps it should).

In any case, QuPath’s stardist class supports passing multiple selected objects… so you can just request all of them with getSelectedObjects() and not have to worry about whether a main one has been set or not.

1 Like

Thanks for the quick response! I still get an error message though:

INFO: 
qupath.imagej.detect.tissue.SimpleTissueDetection2  {"threshold": 215,  "requestedPixelSizeMicrons": 5.0,  "minAreaMicrons": 60000.0,  "maxHoleAreaMicrons": 20000.0,  "darkBackground": false,  "smoothImage": true,  "medianCleanup": true,  "dilateBoundaries": false,  "smoothCoordinates": true,  "excludeOnBoundary": false,  "singleAnnotation": true}
ERROR: I cannot find 'pathObjects'!

ERROR: MissingPropertyException at line 32: No such property: pathObjects for class: Script84

ERROR: Script error (MissingPropertyException)
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:65)
    at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:51)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:341)
    at Script84.run(Script84.groovy:33)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155)
    at qupath.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:895)
    at qupath.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:829)
    at qupath.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:754)
    at qupath.lib.gui.scripting.DefaultScriptEditor$2.run(DefaultScriptEditor.java:1225)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.base/java.lang.Thread.run(Unknown Source)

Script looked like this:

setImageType(‘BRIGHTFIELD_H_DAB’);
Thread.sleep(100)
setColorDeconvolutionStains(’{“Name” : “H-DAB 1”, “Stain 1” : “Hematoxylin”, “Values 1” : "0.6532 0.6699 0.35292 ", “Stain 2” : “DAB”, “Values 2” : "0.23313 0.58012 0.78046 ", “Background” : " 218 218 218 "}’);
Thread.sleep(100)
clearAllObjects();
Thread.sleep(100)

runPlugin(‘qupath.imagej.detect.tissue.SimpleTissueDetection2’, ‘{“threshold”: 215, “requestedPixelSizeMicrons”: 5.0, “minAreaMicrons”: 60000.0, “maxHoleAreaMicrons”: 20000.0, “darkBackground”: false, “smoothImage”: true, “medianCleanup”: true, “dilateBoundaries”: false, “smoothCoordinates”: true, “excludeOnBoundary”: false, “singleAnnotation”: true}’);
Thread.sleep(100)
selectAnnotations();

import qupath.tensorflow.stardist.StarDist2D

// Specify the model directory (you will need to change this!)
def pathModel = ‘/QuPath/he_heavy_augment’

def stardist = StarDist2D.builder(pathModel)
.threshold(0.01) // Prediction threshold
.normalizePercentiles(1, 99) // Percentile normalization
.pixelSize(0.5) // Resolution for detection
.cellExpansion(5.0) // Approximate cells based upon nucleus expansion
.cellConstrainScale(1.5) // Constrain cell expansion using nucleus size
.measureShape() // Add shape measurements
.measureIntensity() // Add cell measurements (in all compartments)
.includeProbability(true) // Include prediction probability as measurement
.build()

// Run detection for the selected objects
def imageData = getCurrentImageData()
def pathObject = getSelectedObject()
if (!pathObjects) {
Dialogs.showErrorMessage(“StarDist”, “Please select a parent object!”)
return
}
stardist.detectObjects(imageData, pathObjects)
println ‘Done!’