Apply auto threshold to whole stack using python script

Hi guys,

does this code snippet makes in order to apply a threshold to a whole stack. For whatever reason this does not work as expected for me …

def apply_threshold_stack(imp, method='Otsu', corrf=1.0):
    
    th_cmd = method + ' dark stack'
    
    IJ.setAutoThreshold(imp, th_cmd)
    ip = imp.getProcessor()
    low = ip.getMinThreshold()
    high = ip.getMaxThreshold()
    # apply correction factor
    low_corr = int(round(low * corrf, 0))
    high_corr = int(round(high * corrf, 0))
    print('lower threshold : ' + str(low_corr))
    print('upper threshold : ' + str(high_corr))

    imp.getProcessor().threshold(high_corr)

    return imp

It returns for example 2 and 270 as lower or upper threshold. But even if I think I apply the upper value to the whole stack, it detects objects everywhere. What do I miss here?

I would think that function would produce an error: As written above: 1) background_threshold is undefined, 2) stack is missing a closing ’

Hi John,

This is just a snippet. It works inside the bigger script but does give not the results i expect. And i corrected the typos.

The crucial question is how to apply one threshold value to the whole stack or to every plane individually

To answer your last question, see Ellen Arena’s example

Do you want to apply the a threshold to every slice of the stack or do you want to apply the stack histogram threshold to all the slices?
I think those are two different things.
In the first case you compute the slice image by image. In the second case you compute the stack histogram, compute the threshold on that histogram and then apply the value to all slices.

Hi all,

I check various posted and took a deep dive into “what is the best way to apply a threshold” from a python script, that allows me to

  • either use the whole stack or the slice-by-slice histogram clacluation
  • correct the threshold value for both ways

And at least it seems to work now (as far as I can tell). Any feedback or improvements are really appreciated. So far the part of the script doing the threshold is this

class ThresholdTools:

    @staticmethod
    def apply_autothreshold(hist, method='Otsu'):
        
        if method == 'Otsu':
            lowthresh = Auto_Threshold.Otsu(hist)
        if method == 'Triangle':
            lowthresh = Auto_Threshold.Triangle(hist)
        if method == 'IJDefault':
            lowthresh = Auto_Threshold.IJDefault(hist)
        if method == 'Huang':
            lowthresh = Auto_Threshold.Huang(hist)
        if method == 'MaxEntropy':
            lowthresh = Auto_Threshold.MaxEntropy(hist)
        if method == 'Mean':
            lowthresh = Auto_Threshold.Mean(hist)
        if method == 'Shanbhag':
            lowthresh = Auto_Threshold.Shanbhag(hist)
        if method == 'Yen':
            lowthresh = Auto_Threshold.Yen(hist)
        if method == 'Li':
            lowthresh = Auto_Threshold.Li(hist)

        return lowthresh


    @staticmethod
    # helper function to appy threshold to whole stack
    # using one corrected value for the stack
    def apply_threshold_stack_corr(imp, lowth_corr, method='Otsu'):
        # get the stacks
        stack, nslices = ImageTools.getImageStack(imp)

        for index in range(1, nslices + 1):
            ip = stack.getProcessor(index)
            # get the histogramm
            hist = ip.getHistogram()
            lowth = ThresholdTools.apply_autothreshold(hist, method=method)
            ip.threshold(lowth_corr)

        return imp

    @staticmethod
    # apply threshold either to whole stack or slice-by-slice
    def apply_threshold(imp, method='Otsu',
                        background_threshold='dark',
                        stackopt=False,
                        corrf=1.0):

        # one threshold value for the whole stack with correction
        if stackopt:

            # create argument string for the IJ.setAutoThreshold
            thcmd = method + ' ' + background_threshold + ' stack'
            # set threshold and get the lower threshold value
            IJ.setAutoThreshold(imp, thcmd)
            ip = imp.getProcessor()
            hist = ip.getHistogram()
            lowth = ip.getMinThreshold()
            lowth_corr = int(round(lowth * corrf, 0))
            
            # process stack with corrected threshold value
            imp = ThresholdTools.apply_threshold_stack_corr(imp, lowth_corr,
                                                            method=method)

        # threshold slice-by-slice with correction
        if not stackopt:

            # get the stack
            stack, nslices = ImageTools.getImageStack(imp)
            print('Thresholding slice-by-slice')
            
            for index in range(1, nslices + 1):
                ip = stack.getProcessor(index)
                # get the histogramm
                hist = ip.getHistogram()
                # get the threshold value
                lowth = ThresholdTools.apply_autothreshold(hist, method=method)
                lowth_corr = int(round(lowth * corrf, 0))
                ip.threshold(lowth_corr)

        return imp

And I use it this way:

imp = ThresholdTools.apply_threshold(imp,
							         method=method,
							         background_threshold='dark',
							         stackopt=stackopt,
							         corrf=corrf)

imp.show()