diff filter_skimage.py @ 0:0cb07fefbe70 draft

planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/ridge_filter/ commit 85b0f6afacb8933db19e03682559cc4d71031cf1
author imgteam
date Fri, 12 Dec 2025 22:21:46 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/filter_skimage.py	Fri Dec 12 22:21:46 2025 +0000
@@ -0,0 +1,111 @@
+import argparse
+import json
+from typing import (
+    Any,
+    Callable,
+)
+
+import giatools
+import numpy as np
+import skimage.filters
+
+
+filters = {
+    'frangi': lambda img, **kwargs: (
+        apply_nd_filter(skimage.filters.frangi, img, **kwargs)
+    ),
+    'hessian': lambda img, **kwargs: (
+        apply_nd_filter(skimage.filters.hessian, img, **kwargs)
+    ),
+    'laplace': lambda img, **kwargs: (
+        apply_nd_filter(skimage.filters.laplace, img, **kwargs)
+    ),
+    'meijering': lambda img, **kwargs: (
+        apply_nd_filter(skimage.filters.meijering, img, **kwargs)
+    ),
+    'sato': lambda img, **kwargs: (
+        apply_nd_filter(skimage.filters.sato, img, **kwargs)
+    ),
+}
+
+
+def apply_nd_filter(
+    filter_impl: Callable[[np.ndarray, Any, ...], np.ndarray],
+    img: giatools.Image,
+    dtype: str,
+    **kwargs: Any,
+) -> giatools.Image:
+    """
+    Apply the filter to the 2-D/3-D, potentially multi-frame and multi-channel image.
+    """
+    result_data = np.empty(img.data.shape, dtype=dtype)
+    for qtc in np.ndindex(
+        img.data.shape[ 0],  # Q axis
+        img.data.shape[ 1],  # T axis
+        img.data.shape[-1],  # C axis
+    ):
+        sl = np.s_[*qtc[:2], ..., qtc[2]]  # noqa: E999
+        arr = img.data[sl]
+        assert arr.ndim == 3  # sanity check, should always be True
+
+        # Perform 2-D or 3-D filtering
+        if arr.shape[0] == 1:
+            info = 'Performing 2-D filtering'
+            result_data[sl][0] = filter_impl(arr[0], **kwargs).astype(dtype)
+        else:
+            info = 'Performing 3-D filtering'
+            result_data[sl] = filter_impl(arr, **kwargs).astype(dtype)
+
+    # Print status info
+    print(info)
+
+    # Return results as 16bit, 32bit, or 64bit floating point
+    return giatools.Image(result_data.astype(dtype), img.axes)
+
+
+def apply_filter(
+    input_filepath: str,
+    output_filepath: str,
+    filter_type: str,
+    **kwargs: Any,
+):
+    # Validate and transform input parameters
+    params = dict(kwargs)
+    if (sigma_min := params.pop('sigma_min', None)) is not None and (sigma_max := params.pop('sigma_max', None)) is not None:
+        num_sigma = params.pop('num_sigma')
+        if sigma_min < sigma_max:
+            params['sigmas'] = np.linspace(sigma_min, sigma_max, num_sigma)
+        elif sigma_min == sigma_max:
+            params['sigmas'] = [sigma_min]
+        else:
+            raise ValueError(f'Minimum sigma ({sigma_min:g}) must not be greater than Maximum sigma ({sigma_max:g})')
+
+    # Read the input image
+    img = giatools.Image.read(input_filepath)
+
+    # Perform filtering
+    print(f'Applying filter: "{filter_type}"')
+    filter_impl = filters[filter_type]
+    res = filter_impl(img, **params).normalize_axes_like(img.original_axes)
+
+    # Adopt metadata and write the result
+    res.metadata = img.metadata
+    res.write(output_filepath, backend='tifffile')
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument('input', type=str)
+    parser.add_argument('output', type=str)
+    parser.add_argument('params', type=str)
+    args = parser.parse_args()
+
+    # Read the config file
+    with open(args.params) as cfgf:
+        cfg = json.load(cfgf)
+
+    apply_filter(
+        args.input,
+        args.output,
+        **cfg,
+    )