changeset 0:f10112b317a1 draft

planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/morphological_operations commit c6d2a4fb19b54c804029ae3d52eb9fb2619e4265
author imgteam
date Fri, 08 Mar 2024 11:00:41 +0000
parents
children ad795d260ba8
files morphological_operations.py morphological_operations.xml test-data/input1.tiff test-data/input2.png test-data/input3.png test-data/output1_closing.tiff test-data/output1_dilation.tiff test-data/output1_erosion.tiff test-data/output1_opening.tiff test-data/output2_fill_holes.tiff test-data/output3_closing.tiff test-data/output3_dilation.tiff test-data/output3_erosion.tiff test-data/output3_opening.tiff
diffstat 14 files changed, 226 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/morphological_operations.py	Fri Mar 08 11:00:41 2024 +0000
@@ -0,0 +1,93 @@
+import argparse
+
+import numpy as np
+import scipy.ndimage as ndi
+import skimage.io
+import skimage.morphology as morph
+
+
+def create_selem(args):
+    """
+    Creates structuring element based on commandline arguments.
+    """
+    assert args.selem_shape in (
+        'square',
+        'disk',
+    )
+
+    if args.selem_shape == 'square':
+        return np.ones((args.selem_size, args.selem_size))
+
+    elif args.selem_shape == 'disk':
+        return morph.disk(args.selem_size)
+
+
+def apply_operation(args, im):
+    """
+    Applies morphological operation to a 2-D single-channel image.
+    """
+    assert im.ndim == 2
+    selem = create_selem(args)
+    values_count = len(np.unique(im))
+    if values_count <= 2:
+        im_proxy = np.zeros(im.shape, bool)
+        im_proxy[im == im.max()] = True
+        result_proxy = apply_binary_operation(args, im_proxy, selem)
+        result = np.full(im.shape, im.min(), im.dtype)
+        result[result_proxy] = im.max()
+        return result
+    else:
+        return apply_intensity_based_operation(args, im, selem)
+
+
+def apply_intensity_based_operation(args, im, selem):
+    operations = {
+        'erosion': ndi.grey_erosion,
+        'dilation': ndi.grey_dilation,
+        'opening': ndi.grey_opening,
+        'closing': ndi.grey_closing,
+    }
+    if args.operation in operations:
+        operation = operations[args.operation]
+        return operation(input=im, structure=selem)
+    else:
+        raise ValueError(f'Operation "{args.operation}" not supported for this image type ({im.dtype}).')
+
+
+def apply_binary_operation(args, im, selem):
+    operations = {
+        'erosion': ndi.binary_erosion,
+        'dilation': ndi.binary_dilation,
+        'opening': ndi.binary_opening,
+        'closing': ndi.binary_closing,
+        'fill_holes': ndi.binary_fill_holes,
+    }
+    operation = operations[args.operation]
+    return operation(input=im, structure=selem)
+
+
+if __name__ == '__main__':
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--operation', type=str)
+    parser.add_argument('--selem-shape', type=str)
+    parser.add_argument('--selem-size', type=int)
+    parser.add_argument('input', type=str)
+    parser.add_argument('output', type=str)
+    args = parser.parse_args()
+
+    im = skimage.io.imread(args.input)
+    assert im.ndim in (2, 3), 'Input image must be two-dimensional and either single-channel or multi-channel.'
+
+    if im.ndim == 2:
+        im_result = apply_operation(args, im)
+
+    else:
+        ch_result_list = []
+        for ch_idx in range(im.shape[2]):
+            ch = im[:, :, ch_idx]
+            ch_result = apply_operation(args, ch)
+            ch_result_list.append(ch_result)
+        im_result = np.dstack(ch_result_list)
+
+    skimage.io.imsave(args.output, im_result)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/morphological_operations.xml	Fri Mar 08 11:00:41 2024 +0000
@@ -0,0 +1,133 @@
+<tool id="morphological_operations" name="Apply a morphological operation" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="23.0">
+    <description>with SciPy</description>
+    <macros>
+        <token name="@TOOL_VERSION@">1.12.0</token>
+        <token name="@VERSION_SUFFIX@">0</token>
+    </macros>
+    <edam_operations>
+        <edam_operation>operation_3443</edam_operation>
+    </edam_operations>
+    <xrefs>
+        <xref type="biii">scipy</xref>
+    </xrefs>
+    <requirements>
+        <requirement type="package" version="1.12.0">scipy</requirement>
+        <requirement type="package" version="0.22.0">scikit-image</requirement>
+    </requirements>
+    <command><![CDATA[
+    
+    ## Inputs
+    
+    python '$__tool_directory__/morphological_operations.py' '$image'
+    --operation $operation
+    --selem-shape $selem_shape
+    --selem-size $selem_size
+    
+    ## Outputs
+    
+    ./result.tiff
+    
+    ]]>
+    </command>
+    <inputs>
+        <param name="image" type="data" format="png,tiff" label="Input image" />
+        <param label="Morphological operation" argument="--operation" type="select">
+            <option value="erosion" selected="True">Erosion</option>
+            <option value="dilation">Dilation</option>
+            <option value="opening">Opening</option>
+            <option value="closing">Closing</option>
+            <option value="fill_holes">Fill holes</option>
+        </param>
+        <param label="Structuring element" argument="--selem-shape" type="select">
+            <option value="disk" selected="True">Disk</option>
+            <option value="square">Square</option>
+        </param>
+        <param label="Structuring element size" argument="--selem-size" type="integer" min="1" max="256" value="1" help="The radius of the disk, or the width of the square, in pixels." />
+    </inputs>
+    <outputs>
+       <data format="tiff" name="result" from_work_dir="result.tiff" />
+    </outputs>
+    <tests>
+        <!-- Test binary operations (input images with two distinct values) -->
+        <test>
+            <param name="image" value="input1.tiff" />
+            <param name="operation" value="erosion" />
+            <param name="selem_shape" value="disk" />
+            <param name="selem_size" value="1" />
+            <output name="result" value="output1_erosion.tiff" ftype="tiff" compare="sim_size" delta="10" />
+        </test>
+        <test>
+            <param name="image" value="input1.tiff" />
+            <param name="operation" value="dilation" />
+            <param name="selem_shape" value="disk" />
+            <param name="selem_size" value="2" />
+            <output name="result" value="output1_dilation.tiff" ftype="tiff" compare="sim_size" delta="10" />
+        </test>
+        <test>
+            <param name="image" value="input1.tiff" />
+            <param name="operation" value="opening" />
+            <param name="selem_shape" value="square" />
+            <param name="selem_size" value="1" />
+            <output name="result" value="output1_opening.tiff" ftype="tiff" compare="sim_size" delta="10" />
+        </test>
+        <test>
+            <param name="image" value="input1.tiff" />
+            <param name="operation" value="closing" />
+            <param name="selem_shape" value="square" />
+            <param name="selem_size" value="2" />
+            <output name="result" value="output1_closing.tiff" ftype="tiff" compare="sim_size" delta="10" />
+        </test>
+        <!-- Test intensity-based operations (more than two distinct values) -->
+        <test>
+            <param name="image" value="input3.png" />
+            <param name="operation" value="erosion" />
+            <param name="selem_shape" value="disk" />
+            <param name="selem_size" value="1" />
+            <output name="result" value="output3_erosion.tiff" ftype="tiff" compare="sim_size" delta="10" />
+        </test>
+        <test>
+            <param name="image" value="input3.png" />
+            <param name="operation" value="dilation" />
+            <param name="selem_shape" value="disk" />
+            <param name="selem_size" value="2" />
+            <output name="result" value="output3_dilation.tiff" ftype="tiff" compare="sim_size" delta="10" />
+        </test>
+        <test>
+            <param name="image" value="input3.png" />
+            <param name="operation" value="opening" />
+            <param name="selem_shape" value="square" />
+            <param name="selem_size" value="1" />
+            <output name="result" value="output3_opening.tiff" ftype="tiff" compare="sim_size" delta="10" />
+        </test>
+        <test>
+            <param name="image" value="input3.png" />
+            <param name="operation" value="closing" />
+            <param name="selem_shape" value="square" />
+            <param name="selem_size" value="2" />
+            <output name="result" value="output3_closing.tiff" ftype="tiff" compare="sim_size" delta="10" />
+        </test>
+        <!-- Test multi-channel image (with two distinct values per channel) -->
+        <test>
+            <param name="image" value="input2.png" />
+            <param name="operation" value="fill_holes" />
+            <param name="selem_shape" value="disk" />
+            <param name="selem_size" value="1" />
+            <output name="result" value="output2_fill_holes.tiff" ftype="tiff" compare="sim_size" delta="10" />
+        </test>
+    </tests>
+    <help>
+        Applies a morphological operation to a 2-D image.
+        For multi-channel images, the operation is applied to each channel separately.
+
+        The following operations are supported:
+
+        - **Erosion:** Shrink bright areas.
+        - **Dilation:** Grow bright areas.
+        - **Opening:** Erosion followed by Dilation. Erases tiny bright spots.
+        - **Closing:** Dilation followed by Erosion. Erases tiny dark holes.
+        - **Fill holes:** Fills the holes (dark areas) in binary images.
+    </help>
+    <citations>
+        <citation type="doi">10.1038/s41592-019-0686-2</citation>
+    </citations>
+</tool>
\ No newline at end of file
Binary file test-data/input1.tiff has changed
Binary file test-data/input2.png has changed
Binary file test-data/input3.png has changed
Binary file test-data/output1_closing.tiff has changed
Binary file test-data/output1_dilation.tiff has changed
Binary file test-data/output1_erosion.tiff has changed
Binary file test-data/output1_opening.tiff has changed
Binary file test-data/output2_fill_holes.tiff has changed
Binary file test-data/output3_closing.tiff has changed
Binary file test-data/output3_dilation.tiff has changed
Binary file test-data/output3_erosion.tiff has changed
Binary file test-data/output3_opening.tiff has changed