Clip channels for intensity features calculation

Dear colleagues,

I am trying to calculate staining intensities of cells that have a non-uniformly stained cytoplasm. So as my target measurement is the staining mean intensity I would like to perform these calculations based on ImageData that has my negative values clipped to 0. I wrote the following script, which however does not result in any changes of my detected intensities (my target staining still often has a negative mean intensity…). Does anyone see what I got wrong? Thank you!

import static qupath.lib.gui.scripting.QPEx.*
import qupath.opencv.ops.*

def imageDataOriginal = getCurrentImageData()
def hierarchy = imageDataOriginal.getHierarchy()
def serverOriginal = imageDataOriginal.getServer()

def calibration = serverOriginal.getPixelCalibration();
def stains = imageDataOriginal.getColorDeconvolutionStains();

def op = ImageOps.Normalize.minMax(0, 10)

ImageDataOp idop = new ImageOps.DefaultImageDataOp(op);

ImageDataServer serverClipped = ImageOps.buildServer(imageDataOriginal, idop, calibration);
ImageData imageDataClipped = serverClipped.getImageData();

selectCells();
runPlugin('qupath.lib.algorithms.IntensityFeaturesPlugin', imageDataClipped,'{"pixelSizeMicrons": 0.5,  "region": "ROI",  "tileSizeMicrons": 25.0,  "colorOD": false,  "colorStain1": true,  "colorStain2": true,  "colorStain3": true,  "colorRed": false,  "colorGreen": false,  "colorBlue": false,  "colorHue": false,  "colorSaturation": false,  "colorBrightness": false,  "doMean": true,  "doStdDev": false,  "doMinMax": false,  "doMedian": false,  "doHaralick": false,  "haralickDistance": 1,  "haralickBins": 32}');

(I edited your post to format the code as code rather than a quote to make it more readable)

Your script may do something because of Groovy’s lax attitude towards controlling access to variables/methods, but new ImageOps.DefaultImageDataOp(op) shouldn’t really work because the constructor isn’t public.

Also, serverClipped.getImageData() will return imageDataOriginal; if you want an entirely new ImageData with serverClipped you’d need

def imageDataClipped = new ImageData(serverClipped)

but then that will have an entirely different object hierarchy.

Finally, Normalize.minMax(0, 10) also won’t really do clipping.

I’m afraid all this is a bit beyond anything QuPath tries to support right now… ops are used internally and I’ve posted some tricks they can help with, but I can’t think of a straightforward way they can currently be used to achieve this goal.

If possible, I’d suggest trying to reformulate your plan to avoid requiring anything op-related that is too acrobatic for now. One simple option may be to refine the stain vectors, which would hopefully remove/reduce the occurrence of negative values.

Thanks for your feedback, seems like I messed up quite a bunch of things… I might then try to calculate my needed intensities looping through the pixels within the cell one-by-one.

Thank you anyway!

1 Like

PS: For anyone struggling with a similar issue, I found that working with subcellular detections of the required channel with a threshold of 0.01 gives the possibility to calculate mean cell intensity with non-negative constraint by using subcellular spot mean intensity and area.

2 Likes

Yep, been distracted, but that was what I was going to recommend!

1 Like