Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save petebankhead/ee07786cbc012f67d2c4e0d88d718908 to your computer and use it in GitHub Desktop.
Save petebankhead/ee07786cbc012f67d2c4e0d88d718908 to your computer and use it in GitHub Desktop.
One way to threshold a fluorescence multi-channel image in QuPath v0.2 to generate a new annotation
/**
* Slightly involved script to threshold a fluorescence multi-channel image
* in QuPath v0.2 to generate a new region of interest annotation.
*
* Might be used whenever there is too much variation for a threshold classifier
* to work.
*
* @author Pete Bankhead
*/
import ij.plugin.filter.ThresholdToSelection
import ij.process.Blitter
import ij.process.ByteProcessor
import ij.process.ImageProcessor
import qupath.imagej.processing.RoiLabeling
import qupath.imagej.processing.SimpleThresholding
import qupath.imagej.tools.IJTools
import qupath.lib.objects.PathObjects
import qupath.lib.regions.RegionRequest
import qupath.lib.roi.interfaces.ROI
import static qupath.lib.gui.scripting.QPEx.*
// Adjust downsample according to the detail you need
double downsample = 10.0
// Adjust the smoothing for details as well
double sigma = 2.0
// Define channels you want to use
int[] channels = [1, 2, 3, 4]
// Set to false to retain all ROI fragments
boolean keepLargestOnly = true
// Set threshold as mean + k * standard deviation
double k = 0.0
// Minimum size of the smallest ROI fragment to keep (in downsampled pixels)
double minPixels = 1000
// Get the current image
def server = getCurrentServer()
def request = RegionRequest.createInstance(server, downsample)
def pathImage = IJTools.convertToImagePlus(server, request)
def imp = pathImage.getImage()
// Create a binary image for the output
def bp = new ByteProcessor(imp.getWidth(), imp.getHeight())
// Threshold each channel and add results
for (int c : channels) {
def fp = imp.getStack().getProcessor(c).convertToFloatProcessor()
fp.blurGaussian(sigma)
def stats = fp.getStatistics()
double threshold = stats.mean + k * stats.stdDev
def bpChannel = SimpleThresholding.thresholdAbove(fp, threshold as float)
bp.copyBits(bpChannel, 0, 0, Blitter.MAX)
}
// Create an annotation
RoiLabeling.removeSmallAreas(bp, minPixels, true)
bp.setThreshold(127, Double.POSITIVE_INFINITY, ImageProcessor.NO_LUT_UPDATE)
ROI roi
if (keepLargestOnly) {
def ipLabels = RoiLabeling.labelImage(bp, 0.5f, false)
def roisIJ = RoiLabeling.labelsToConnectedROIs(ipLabels, ipLabels.getStatistics().max as int)
def rois = roisIJ.collect {r -> IJTools.convertToROI(r, pathImage)}
rois.sort(Comparator.comparingDouble(r -> r.getArea()))
roi = rois[-1]
} else {
def roiIJ = new ThresholdToSelection().convert(bp)
roi = IJTools.convertToROI(roiIJ, pathImage)
}
def annotation = PathObjects.createAnnotationObject(roi)
addObject(annotation)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment