Created
May 11, 2021 17:35
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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