Skip to content

Commit

Permalink
Update "Threshold image" tool (#109)
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit bc594cf
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 15:55:40 2024 +0000

    Update tool version

commit 92657e4
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 15:54:38 2024 +0000

    Update help

commit f8e732a
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 15:51:41 2024 +0000

    Add help

commit 6124766
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 15:33:11 2024 +0000

    Replace `dark_bg` by `invert_output`

commit a41c52f
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 12:42:16 2024 +0000

    Update UI

commit 044709d
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 12:00:05 2024 +0000

    Add help

commit e5630a8
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 12:59:42 2024 +0100

    Fix test output

commit 108ab02
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 11:50:17 2024 +0000

    Add test

commit 6406061
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 11:46:15 2024 +0000

    Add `manual` thresholding method

commit 0a89784
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 11:36:42 2024 +0000

    Fix issues

commit 912977e
Author: Leonid Kostrykin <[email protected]>
Date:   Mon Mar 11 11:27:51 2024 +0000

    Refactor auto_threshold.xml
  • Loading branch information
kostrykin authored Mar 11, 2024
1 parent 026a568 commit 8b9f24c
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 40 deletions.
40 changes: 21 additions & 19 deletions tools/2d_auto_threshold/auto_threshold.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
"""
Copyright 2017-2022 Biomedical Computer Vision Group, Heidelberg University.
Copyright 2017-2024 Biomedical Computer Vision Group, Heidelberg University.
Distributed under the MIT license.
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
"""

import argparse

import numpy as np
import skimage.filters
import skimage.io
import skimage.util
import tifffile

thOptions = {
'otsu': lambda img_raw, bz: skimage.filters.threshold_otsu(img_raw),
'li': lambda img_raw, bz: skimage.filters.threshold_li(img_raw),
'yen': lambda img_raw, bz: skimage.filters.threshold_yen(img_raw),
'isodata': lambda img_raw, bz: skimage.filters.threshold_isodata(img_raw),
th_methods = {
'manual': lambda thres, **kwargs: thres,

'otsu': lambda img_raw, **kwargs: skimage.filters.threshold_otsu(img_raw),
'li': lambda img_raw, **kwargs: skimage.filters.threshold_li(img_raw),
'yen': lambda img_raw, **kwargs: skimage.filters.threshold_yen(img_raw),
'isodata': lambda img_raw, **kwargs: skimage.filters.threshold_isodata(img_raw),

'loc_gaussian': lambda img_raw, bz: skimage.filters.threshold_local(img_raw, bz, method='gaussian'),
'loc_median': lambda img_raw, bz: skimage.filters.threshold_local(img_raw, bz, method='median'),
'loc_mean': lambda img_raw, bz: skimage.filters.threshold_local(img_raw, bz, method='mean')
'loc_gaussian': lambda img_raw, bz, **kwargs: skimage.filters.threshold_local(img_raw, bz, method='gaussian'),
'loc_median': lambda img_raw, bz, **kwargs: skimage.filters.threshold_local(img_raw, bz, method='median'),
'loc_mean': lambda img_raw, bz, **kwargs: skimage.filters.threshold_local(img_raw, bz, method='mean')
}


def auto_thresholding(in_fn, out_fn, th_method, block_size=5, dark_bg=True):
def do_thresholding(in_fn, out_fn, th_method, block_size=5, threshold=0, invert_output=False):
img = skimage.io.imread(in_fn)
th = thOptions[th_method](img, block_size)
if dark_bg:
res = img > th
else:
res = img <= th
th = th_methods[th_method](img_raw=img, bz=block_size, thres=threshold)
res = img > th
if invert_output:
res = np.logical_not(res)
tifffile.imwrite(out_fn, skimage.util.img_as_ubyte(res))


if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Automatic Image Thresholding')
parser.add_argument('im_in', help='Path to the input image')
parser.add_argument('im_out', help='Path to the output image (TIFF)')
parser.add_argument('th_method', choices=thOptions.keys(), help='Thresholding method')
parser.add_argument('th_method', choices=th_methods.keys(), help='Thresholding method')
parser.add_argument('block_size', type=int, default=5, help='Odd size of pixel neighborhood for calculating the threshold')
parser.add_argument('dark_bg', default=True, type=bool, help='True if background is dark')
parser.add_argument('threshold', type=float, default=0, help='Manual thresholding value')
parser.add_argument('invert_output', default=False, type=bool, help='Values below/above the threshold are labeled with 0/255 if False, and with 255/0 otherwise')
args = parser.parse_args()

auto_thresholding(args.im_in, args.im_out, args.th_method, args.block_size, args.dark_bg)
do_thresholding(args.im_in, args.im_out, args.th_method, args.block_size, args.threshold, args.invert_output)
94 changes: 73 additions & 21 deletions tools/2d_auto_threshold/auto_threshold.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<tool id="ip_threshold" name="Threshold image" version="0.0.5-2" profile="20.05">
<tool id="ip_threshold" name="Threshold image" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="20.05">
<description>with scikit-image</description>
<macros>
<token name="@TOOL_VERSION@">0.18.1</token>
<token name="@VERSION_SUFFIX@">0</token>
</macros>
<edam_operations>
<edam_operation>operation_3443</edam_operation>
</edam_operations>
Expand All @@ -16,46 +20,94 @@
python '$__tool_directory__/auto_threshold.py'
'$input'
./out.tif
'$th_method'
'$block_size'
'$dark_bg'
'$th_method.method_id'
'$th_method.block_size'
'$th_method.threshold'
'$invert_output'
]]>
</command>
<inputs>
<param name="input" type="data" format="tiff,png" label="Input image" />
<param name="th_method" type="select" label="Thresholding method">
<option value="otsu" selected="True">Otsu</option>
<option value="li">Li's Minimum Cross Entropy</option>
<option value="isodata">Isodata</option>
<option value="yen">Yen</option>
<option value="loc_gaussian">Adaptive (Gaussian)</option>
<option value="loc_median">Adaptive (Median)</option>
<option value="loc_mean">Adaptive (Mean)</option>
</param>
<param name="block_size" type="integer" value="5" label="Odd size of pixel neighborhood for determining the threshold (only valid for adaptive thresholding methods)" />
<param name="dark_bg" type="boolean" checked="true" truevalue="True" falsevalue="False" label="Dark Background" />
<conditional name="th_method">
<param name="method_id" type="select" label="Thresholding method">
<option value="manual">Manual</option>
<option value="otsu" selected="True">Globally adaptive / Otsu</option>
<option value="li">Globally adaptive / Li's Minimum Cross Entropy</option>
<option value="isodata">Globally adaptive / Isodata</option>
<option value="yen">Globally adaptive / Yen</option>
<option value="loc_gaussian">Locally adaptive / Gaussian</option>
<option value="loc_median">Locally adaptive / Median</option>
<option value="loc_mean">Locally adaptive / Mean</option>
</param>
<when value="manual">
<param name="threshold" type="float" value="0" label="Threshold value" />
<param name="block_size" type="hidden" value="0" />
</when>
<when value="otsu">
<param name="threshold" type="hidden" value="0" />
<param name="block_size" type="hidden" value="0" />
</when>
<when value="li">
<param name="threshold" type="hidden" value="0" />
<param name="block_size" type="hidden" value="0" />
</when>
<when value="isodata">
<param name="threshold" type="hidden" value="0" />
<param name="block_size" type="hidden" value="0" />
</when>
<when value="yen">
<param name="threshold" type="hidden" value="0" />
<param name="block_size" type="hidden" value="0" />
</when>
<when value="loc_gaussian">
<param name="threshold" type="hidden" value="0" />
<param name="block_size" type="integer" value="5" label="Odd size of pixel neighborhood for determining the threshold" />
</when>
<when value="loc_median">
<param name="threshold" type="hidden" value="0" />
<param name="block_size" type="integer" value="5" label="Odd size of pixel neighborhood for determining the threshold" />
</when>
<when value="loc_mean">
<param name="threshold" type="hidden" value="0" />
<param name="block_size" type="integer" value="5" label="Odd size of pixel neighborhood for determining the threshold" />
</when>
</conditional>
<param name="invert_output" type="boolean" checked="false" truevalue="True" falsevalue="False" label="Invert output labels" help="Pixels are usually assigned the label 0 if the pixel value is below (or equal to) the threshold, and 255 if it is above the threshold. If this option is activated, pixels are assigned the label 255 if the pixel value is below (or equal to) the threshold, and 0 if it is above the threshold." />
</inputs>
<outputs>
<data format="tiff" name="output" from_work_dir="out.tif" />
</outputs>
<tests>
<test>
<param name="input" value="sample.tif"/>
<output name="output" value="out.tif" ftype="tiff" compare="sim_size"/>
<param name="th_method" value="loc_gaussian"/>
<output name="output" value="out.tif" ftype="tiff" compare="sim_size" delta="10"/>
<param name="method_id" value="loc_gaussian"/>
<param name="block_size" value="3"/>
<param name="dark_bg" value="True"/>
<param name="invert_output" value="False"/>
</test>
<test>
<param name="input" value="sample.tif"/>
<output name="output" value="out2.tif" ftype="tiff" compare="sim_size"/>
<param name="th_method" value="otsu"/>
<output name="output" value="out2.tif" ftype="tiff" compare="sim_size" delta="10"/>
<param name="method_id" value="otsu"/>
<param name="block_size" value="5"/>
<param name="dark_bg" value="True"/>
<param name="invert_output" value="False"/>
</test>
<test>
<param name="input" value="sample.tif"/>
<output name="output" value="out3.tif" ftype="tiff" compare="sim_size" delta="10"/>
<param name="method_id" value="manual"/>
<param name="threshold" value="64"/>
<param name="invert_output" value="True"/>
</test>
</tests>
<help>
Applies a standard thresholding algorithm to an image.

The thresholding algorithm automatically determines a threshold value (unless manual thresholding is used).
The input image is then thresholded, by assigning white (pixel value 255) to image regions above the determined threshold,
and black (pixel value 0) to image regions below or equal to the determined threshold.

The assignment of black and white to image regions below and above the threshold is inverted, if the corresponding option is set.
</help>
<citations>
<citation type="doi">10.1016/j.jbiotec.2017.07.019</citation>
Expand Down
Binary file added tools/2d_auto_threshold/test-data/out3.tif
Binary file not shown.

0 comments on commit 8b9f24c

Please sign in to comment.