BIOP / qupath-extension-cellpose

an extension that wraps a Cellpose environment such that WSI can be analyzed using Cellpose through QuPath.
Apache License 2.0
63 stars 11 forks source link

Count number of individual masks #32

Closed saarah815 closed 1 year ago

saarah815 commented 1 year ago

Hi again,

Your Cellpose script templates have been super helpful, thank you so much! When running the detection script, I was hoping you could help me with something please. On QuPath I have my histology image, as shown below:

image

Using your detection script, I have made a few small changes to the tile size and Cellpose channels to fit my project:

def pathModel = 'C:/[...]/custom_model'
def cellpose = Cellpose2D.builder( pathModel )
        .pixelSize( 0.5 )            
        .tileSize(3072)                  // If your GPU can take it, make larger tiles to process fewer of them. Useful for Omnipose
        .cellposeChannels(0,0)           // Overwrites the logic of this plugin with these two values. These will be sent directly to --chan and --chan2
        .build()

// Run detection for the selected objects
def imageData = getCurrentImageData()

def pathObjects = getSelectedObjects() // To process only selected annotations, useful while testing

if (pathObjects.isEmpty()) {
    Dialogs.showErrorMessage("Cellpose", "Please select a parent object!")
    return
}

cellpose.detectObjects(imageData, pathObjects)

println 'Cellpose detection script done'

import qupath.ext.biop.cellpose.Cellpose2D

I have been able to output a tile containing my selected objects (the two yellow outlines in the above image). However, I was wondering if there was a way for the output tif tile to only show my objects, and essentially black out everything else.

Currently my output tile looks like this:

image

Comparing the two attached images, the script has successfully created a tile containing my two selected objects, but the tile also contains everything around my outlines, whereas I would like the tile to only contain what is within my object outlines.

If you could please assist me with this in any way, that would be greatly appreciated!

Thanks so much,

Saarah

saarah815 commented 1 year ago

And while I'm here - although the script is able to create the tiles, it does not seem to be segmenting the nuclei using Cellpose as I cannot see any masks or segmented images being outputted. I have not changed your original script in any other way besides the tile size and channels. Is there a way to fix this so that I can segment the nuclei and also print out the nuclei count?

For reference, this is how I was printing nuclei count on Python for images outside of QuPath:

model_path = 'path_to_model'
image_dir = 'path_to_images'

# Channel for segmentation (0 for grayscale)
chan = 0 
chan2 = 0

# Diameter for segmentation (0 to use model's diameter)
diameter = 0

flow_threshold = 0.4
cellprob_threshold = 0

# Get image files in the directory (ignoring files ending in '_masks')
files = io.get_image_files(dir, '_masks')
print(files)
images = [io.imread(f) for f in files]

# Load the model
model = models.CellposeModel(gpu=True, pretrained_model=model_path)

# Use model diameter if user diameter is 0
diameter = model.diam_labels if diameter==0 else diameter

# Run model on test images
masks, flows, styles = model.eval(images, channels=[chan, chan2], diameter=diameter, flow_threshold=flow_threshold, cellprob_threshold=cellprob_threshold)

# Count the number of nuclei in each image (number of masks)
for i, mask in enumerate(masks):
    num_nuclei = len(np.unique(mask)) - 1
    file_name = os.path.splitext(os.path.basename(files[i]))[0]
    print(f"Number of nuclei in {file_name}: {num_nuclei}")

print("Number of nuclei:", num_nuclei)

Thank you very much!

saarah815 commented 1 year ago

Hi @lacan,

Just a quick follow-up. Thanks once again for the awesome extension, it's been great.

I've solved my segmentation issue and it all works just fine! :)

image

Below is the code I used, which was based on your detection template:

import qupath.ext.biop.cellpose.Cellpose2D

def pathModel = 'path_to_model'
def cellpose = Cellpose2D.builder(pathModel)
        .tileSize(3072)                  // If your GPU can take it, make larger tiles to process fewer of them. Useful for Omnipose
        .cellposeChannels(0,0)           
        .cellprobThreshold(0.0)          // Threshold for the mask detection, defaults to 0.0
        .flowThreshold(0.4)              // Threshold for the flows, defaults to 0.4
        .diameter(0)                    // Median object diameter. Set to 0.0 for the `bact_omni` model or for automatic computation
        .measureShape()                  // Add shape measurements
        .measureIntensity()              // Add cell measurements (in all compartments)
//        .createAnnotations()             // Make annotations instead of detections. This ignores cellExpansion
        .simplify(0)                     // Simplification 1.6 by default, set to 0 to get the cellpose masks as precisely as possible
        .build()

// Run detection for the selected objects
def imageData = getCurrentImageData()

def pathObjects = getSelectedObjects() // To process only selected annotations, useful while testing
//def pathObjects = getAnnotationObjects().findAll{it.getPathClass() == getPathClass("HPF")}

// def pathObjects = getAnnotationObjects() // To process all annotations. For working in batch mode
if (pathObjects.isEmpty()) {
    Dialogs.showErrorMessage("Cellpose", "Please select a parent object!")
    returna
}

cellpose.detectObjects(imageData, pathObjects)

println 'Cellpose detection script done'

I am still stuck with one small thing, though - I would like to print the number of individual masks displayed so that I can keep track of the number of segmented nuclei. If you could please assist with this, that would be amazing!

Thanks,

Saarah

saarah815 commented 1 year ago

All good now, I figured it out! This was my solution:

int totalSegmentedNuclei = 0
for (annotation in pathObjects) {
    def list_of_nuclei = annotation.getChildObjects()
    def num_nuc = list_of_nuclei.size()
    totalSegmentedNuclei += num_nuc
}

println "Total count of segmented nuclei in all selected annotations: $totalSegmentedNuclei"