From cd774259f8e55350e93672322aca91c743d6bcb8 Mon Sep 17 00:00:00 2001 From: Kimberly Meechan Date: Fri, 16 Feb 2024 16:45:12 +0000 Subject: [PATCH 1/7] add grayscale and binary erosion, dilation, opening and closing --- .../__init__.py | 153 +++++++++++++++++- .../_tests/test_function.py | 20 ++- 2 files changed, 166 insertions(+), 7 deletions(-) diff --git a/napari_segment_blobs_and_things_with_membranes/__init__.py b/napari_segment_blobs_and_things_with_membranes/__init__.py index c008d55..3d9195a 100644 --- a/napari_segment_blobs_and_things_with_membranes/__init__.py +++ b/napari_segment_blobs_and_things_with_membranes/__init__.py @@ -8,9 +8,12 @@ from skimage.filters import threshold_otsu as sk_threshold_otsu, gaussian, sobel from skimage.segmentation import watershed from skimage.feature import peak_local_max -from skimage.morphology import binary_opening from skimage.measure import label -from skimage.morphology import local_maxima, local_minima +from skimage.morphology import local_maxima, local_minima, opening, closing, erosion, dilation +from skimage.morphology import binary_opening as sk_binary_opening +from skimage.morphology import binary_closing as sk_binary_closing +from skimage.morphology import binary_erosion as sk_binary_erosion +from skimage.morphology import binary_dilation as sk_binary_dilation from skimage.restoration import rolling_ball from napari_tools_menu import register_function from skimage.measure import regionprops @@ -61,7 +64,15 @@ def napari_experimental_provide_function(): Manually_split_labels, rescale, resize, - extract_slice + extract_slice, + grayscale_erosion, + binary_erosion, + grayscale_dilation, + binary_dilation, + grayscale_closing, + binary_closing, + grayscale_opening, + binary_opening ] @@ -88,6 +99,10 @@ def _sobel_3d(image): return ndi.convolve(image, kernel) +def _generate_footprint(radius, ndim): + return np.ones((int(radius * 2 + 1),) * ndim) + + @register_function(menu="Segmentation post-processing > Split touching objects (nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer @@ -125,7 +140,7 @@ def split_touching_objects(binary:"napari.types.LabelsData", sigma: float = 3.5) edges2 = _sobel_3d(binary) almost = np.logical_not(np.logical_xor(edges != 0, edges2 != 0)) * binary - return binary_opening(almost) + return sk_binary_opening(almost) @register_function(menu="Segmentation / binarization > Threshold (Otsu et al 1979, scikit-image, nsbatwm)") @@ -344,7 +359,7 @@ def mode_filter(labels: "napari.types.LabelsData", radius: int = 2)-> "napari.ty raise ValueError("The mode filter only works on label images with less than 256 labels.") from skimage.filters.rank import majority - footprint = np.ones((int(radius * 2 + 1),) * labels.ndim) + footprint = _generate_footprint(radius, labels.ndim) return majority(labels.astype(np.uint8), footprint=footprint).astype(labels.dtype) @register_function(menu="Filtering / noise removal > Percentile (scipy, nsbatwm)") @@ -944,3 +959,131 @@ def sub_sample(image:"napari.types.ImageData", sample_x: int = 1, sample_y: int @time_slicer def squeeze(image:"napari.types.ImageData") -> "napari.types.ImageData": return np.squeeze(image) + + +@register_function(menu="Segmentation post-processing > Grayscale erosion (scikit-image, nsbatwm)") +@jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') +@time_slicer +def grayscale_erosion(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": + """ + Applies grayscale erosion to an image, using a footprint with the given radius. + + See also + -------- + ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.erosion + """ + + footprint = _generate_footprint(radius, labels.ndim) + return erosion(labels, footprint=footprint) + + +@register_function(menu="Segmentation post-processing > Binary erosion (scikit-image, nsbatwm)") +@jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') +@time_slicer +def binary_erosion(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": + """ + Applies binary erosion to an image, using a footprint with the given radius. + + See also + -------- + ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_erosion + """ + + footprint = _generate_footprint(radius, labels.ndim) + return sk_binary_erosion(labels, footprint=footprint) + + +@register_function(menu="Segmentation post-processing > Grayscale dilation (scikit-image, nsbatwm)") +@jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') +@time_slicer +def grayscale_dilation(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": + """ + Applies grayscale dilation to an image, using a footprint with the given radius. + + See also + -------- + ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.dilation + """ + + footprint = _generate_footprint(radius, labels.ndim) + return dilation(labels, footprint=footprint) + + +@register_function(menu="Segmentation post-processing > Binary dilation (scikit-image, nsbatwm)") +@jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') +@time_slicer +def binary_dilation(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": + """ + Applies binary dilation to an image, using a footprint with the given radius. + + See also + -------- + ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_dilation + """ + + footprint = _generate_footprint(radius, labels.ndim) + return sk_binary_dilation(labels, footprint=footprint) + + +@register_function(menu="Segmentation post-processing > Grayscale opening (scikit-image, nsbatwm)") +@jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') +@time_slicer +def grayscale_opening(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": + """ + Applies grayscale opening to an image, using a footprint with the given radius. + + See also + -------- + ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.opening + """ + + footprint = _generate_footprint(radius, labels.ndim) + return opening(labels, footprint=footprint) + + +@register_function(menu="Segmentation post-processing > Binary opening (scikit-image, nsbatwm)") +@jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') +@time_slicer +def binary_opening(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": + """ + Applies binary opening to an image, using a footprint with the given radius. + + See also + -------- + ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_opening + """ + + footprint = _generate_footprint(radius, labels.ndim) + return sk_binary_opening(labels, footprint=footprint) + + +@register_function(menu="Segmentation post-processing > Grayscale closing (scikit-image, nsbatwm)") +@jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') +@time_slicer +def grayscale_closing(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": + """ + Applies grayscale closing to an image, using a footprint with the given radius. + + See also + -------- + ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.closing + """ + + footprint = _generate_footprint(radius, labels.ndim) + return closing(labels, footprint=footprint) + + +@register_function(menu="Segmentation post-processing > Binary closing (scikit-image, nsbatwm)") +@jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') +@time_slicer +def binary_closing(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": + """ + Applies binary opening to an image, using a footprint with the given radius. + + See also + -------- + ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_closing + """ + + footprint = _generate_footprint(radius, labels.ndim) + return sk_binary_closing(labels, footprint=footprint) diff --git a/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py b/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py index 20c6aff..4f4e619 100644 --- a/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py +++ b/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py @@ -42,7 +42,15 @@ def test_something(): butterworth, \ extract_slice, \ sub_sample, \ - squeeze + squeeze, \ + grayscale_erosion, \ + binary_erosion, \ + grayscale_dilation, \ + binary_dilation, \ + grayscale_opening, \ + binary_opening, \ + grayscale_closing, \ + binary_closing import numpy as np @@ -80,7 +88,15 @@ def test_something(): butterworth, extract_slice, sub_sample, - squeeze]: + squeeze, + grayscale_erosion, + binary_erosion, + grayscale_dilation, + binary_dilation, + grayscale_opening, + binary_opening, + grayscale_closing, + binary_closing]: print(operation) From 6c4e7ba13331d871548bf15959b2754733d6cfb0 Mon Sep 17 00:00:00 2001 From: Kimberly Meechan Date: Mon, 19 Feb 2024 11:58:36 +0000 Subject: [PATCH 2/7] add square or disk footprints --- .../__init__.py | 89 +++++++++++++------ .../_tests/test_function.py | 28 +++--- 2 files changed, 81 insertions(+), 36 deletions(-) diff --git a/napari_segment_blobs_and_things_with_membranes/__init__.py b/napari_segment_blobs_and_things_with_membranes/__init__.py index 3d9195a..0ef1d7e 100644 --- a/napari_segment_blobs_and_things_with_membranes/__init__.py +++ b/napari_segment_blobs_and_things_with_membranes/__init__.py @@ -2,6 +2,7 @@ __version__ = "0.3.7" __common_alias__ = "nsbatwm" +import skimage.morphology from napari_plugin_engine import napari_hook_implementation import numpy as np from scipy import ndimage as ndi @@ -25,6 +26,7 @@ from scipy import ndimage from napari_time_slicer import time_slicer from stackview import jupyter_displayable_output +from enum import Enum @napari_hook_implementation def napari_experimental_provide_function(): @@ -99,10 +101,37 @@ def _sobel_3d(image): return ndi.convolve(image, kernel) -def _generate_footprint(radius, ndim): +class FootprintShape(Enum): + square = 'square' + disk = 'disk' + + +def _generate_square_footprint(radius, ndim): + """Generate a square footprint (for any number of dimensions - ndim) with the + given radius""" return np.ones((int(radius * 2 + 1),) * ndim) +def _generate_disk_footprint(radius, ndim): + """Generate a disk footprint (for 2D or 3D) with the given radius.""" + if ndim == 2: + return skimage.morphology.disk(radius) + elif ndim == 3: + return skimage.morphology.ball(radius) + else: + raise ValueError("Disk footprints are only implemented for 2D or 3D images - " + "please switch to a square footprint") + + +def _generate_footprint(footprint_shape, radius, ndim): + """Generate a footprint with given shape, radius and number of dimensions (ndim)""" + if footprint_shape == FootprintShape.square: + return _generate_square_footprint(radius, ndim) + + elif footprint_shape == FootprintShape.disk: + return _generate_disk_footprint(radius, ndim) + + @register_function(menu="Segmentation post-processing > Split touching objects (nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer @@ -359,7 +388,7 @@ def mode_filter(labels: "napari.types.LabelsData", radius: int = 2)-> "napari.ty raise ValueError("The mode filter only works on label images with less than 256 labels.") from skimage.filters.rank import majority - footprint = _generate_footprint(radius, labels.ndim) + footprint = _generate_square_footprint(radius, labels.ndim) return majority(labels.astype(np.uint8), footprint=footprint).astype(labels.dtype) @register_function(menu="Filtering / noise removal > Percentile (scipy, nsbatwm)") @@ -964,126 +993,134 @@ def squeeze(image:"napari.types.ImageData") -> "napari.types.ImageData": @register_function(menu="Segmentation post-processing > Grayscale erosion (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def grayscale_erosion(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def grayscale_erosion(labels: "napari.types.LabelsData", radius: int = 1, + footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": """ - Applies grayscale erosion to an image, using a footprint with the given radius. + Applies grayscale erosion to an image, using a footprint with the given radius and shape. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.erosion """ - footprint = _generate_footprint(radius, labels.ndim) + footprint = _generate_footprint(footprint_shape, radius, labels.ndim) return erosion(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Binary erosion (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_erosion(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def binary_erosion(labels: "napari.types.LabelsData", radius: int = 1, + footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": """ - Applies binary erosion to an image, using a footprint with the given radius. + Applies binary erosion to an image, using a footprint with the given radius and shape. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_erosion """ - footprint = _generate_footprint(radius, labels.ndim) + footprint = _generate_footprint(footprint_shape, radius, labels.ndim) return sk_binary_erosion(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Grayscale dilation (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def grayscale_dilation(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def grayscale_dilation(labels: "napari.types.LabelsData", radius: int = 1, + footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": """ - Applies grayscale dilation to an image, using a footprint with the given radius. + Applies grayscale dilation to an image, using a footprint with the given radius and shape. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.dilation """ - footprint = _generate_footprint(radius, labels.ndim) + footprint = _generate_footprint(footprint_shape, radius, labels.ndim) return dilation(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Binary dilation (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_dilation(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def binary_dilation(labels: "napari.types.LabelsData", radius: int = 1, + footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": """ - Applies binary dilation to an image, using a footprint with the given radius. + Applies binary dilation to an image, using a footprint with the given radius and shape. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_dilation """ - footprint = _generate_footprint(radius, labels.ndim) + footprint = _generate_footprint(footprint_shape, radius, labels.ndim) return sk_binary_dilation(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Grayscale opening (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def grayscale_opening(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def grayscale_opening(labels: "napari.types.LabelsData", radius: int = 1, + footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": """ - Applies grayscale opening to an image, using a footprint with the given radius. + Applies grayscale opening to an image, using a footprint with the given radius and shape. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.opening """ - footprint = _generate_footprint(radius, labels.ndim) + footprint = _generate_footprint(footprint_shape, radius, labels.ndim) return opening(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Binary opening (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_opening(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def binary_opening(labels: "napari.types.LabelsData", radius: int = 1, + footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": """ - Applies binary opening to an image, using a footprint with the given radius. + Applies binary opening to an image, using a footprint with the given radius and shape. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_opening """ - footprint = _generate_footprint(radius, labels.ndim) + footprint = _generate_footprint(footprint_shape, radius, labels.ndim) return sk_binary_opening(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Grayscale closing (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def grayscale_closing(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def grayscale_closing(labels: "napari.types.LabelsData", radius: int = 1, + footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": """ - Applies grayscale closing to an image, using a footprint with the given radius. + Applies grayscale closing to an image, using a footprint with the given radius and shape. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.closing """ - footprint = _generate_footprint(radius, labels.ndim) + footprint = _generate_footprint(footprint_shape, radius, labels.ndim) return closing(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Binary closing (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_closing(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def binary_closing(labels: "napari.types.LabelsData", radius: int = 1, + footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": """ - Applies binary opening to an image, using a footprint with the given radius. + Applies binary opening to an image, using a footprint with the given radius and shape. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_closing """ - footprint = _generate_footprint(radius, labels.ndim) + footprint = _generate_footprint(footprint_shape, radius, labels.ndim) return sk_binary_closing(labels, footprint=footprint) diff --git a/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py b/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py index 4f4e619..9134c89 100644 --- a/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py +++ b/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py @@ -50,7 +50,8 @@ def test_something(): grayscale_opening, \ binary_opening, \ grayscale_closing, \ - binary_closing + binary_closing, \ + FootprintShape import numpy as np @@ -88,15 +89,7 @@ def test_something(): butterworth, extract_slice, sub_sample, - squeeze, - grayscale_erosion, - binary_erosion, - grayscale_dilation, - binary_dilation, - grayscale_opening, - binary_opening, - grayscale_closing, - binary_closing]: + squeeze]: print(operation) @@ -112,6 +105,21 @@ def test_something(): operation(image, image) + for operation in [ + grayscale_erosion, + binary_erosion, + grayscale_dilation, + binary_dilation, + grayscale_opening, + binary_opening, + grayscale_closing, + binary_closing + ]: + labels = image > 0 + for footprint_shape in FootprintShape: + print(f"{operation} with {footprint_shape}") + operation(labels, footprint_shape=footprint_shape) + skeletonize(image > 0) seeded_watershed_with_mask(image, image, image) From 074ff81268b2d361c7dfbe0f3bfe033ace9c147a Mon Sep 17 00:00:00 2001 From: Kimberly Meechan Date: Mon, 19 Feb 2024 12:07:27 +0000 Subject: [PATCH 3/7] fix imports --- napari_segment_blobs_and_things_with_membranes/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/napari_segment_blobs_and_things_with_membranes/__init__.py b/napari_segment_blobs_and_things_with_membranes/__init__.py index 0ef1d7e..4b42ed8 100644 --- a/napari_segment_blobs_and_things_with_membranes/__init__.py +++ b/napari_segment_blobs_and_things_with_membranes/__init__.py @@ -2,7 +2,6 @@ __version__ = "0.3.7" __common_alias__ = "nsbatwm" -import skimage.morphology from napari_plugin_engine import napari_hook_implementation import numpy as np from scipy import ndimage as ndi @@ -10,7 +9,7 @@ from skimage.segmentation import watershed from skimage.feature import peak_local_max from skimage.measure import label -from skimage.morphology import local_maxima, local_minima, opening, closing, erosion, dilation +from skimage.morphology import local_maxima, local_minima, opening, closing, erosion, dilation, disk, ball from skimage.morphology import binary_opening as sk_binary_opening from skimage.morphology import binary_closing as sk_binary_closing from skimage.morphology import binary_erosion as sk_binary_erosion @@ -115,9 +114,9 @@ def _generate_square_footprint(radius, ndim): def _generate_disk_footprint(radius, ndim): """Generate a disk footprint (for 2D or 3D) with the given radius.""" if ndim == 2: - return skimage.morphology.disk(radius) + return disk(radius) elif ndim == 3: - return skimage.morphology.ball(radius) + return ball(radius) else: raise ValueError("Disk footprints are only implemented for 2D or 3D images - " "please switch to a square footprint") From f324f60b2c211a2b312c021d3558596266fe756f Mon Sep 17 00:00:00 2001 From: Kimberly Meechan Date: Fri, 23 Feb 2024 11:50:02 +0000 Subject: [PATCH 4/7] Remove FootprintShape and add 3d image to test cases --- .../__init__.py | 83 ++++++------------- .../_tests/test_function.py | 31 +++---- 2 files changed, 43 insertions(+), 71 deletions(-) diff --git a/napari_segment_blobs_and_things_with_membranes/__init__.py b/napari_segment_blobs_and_things_with_membranes/__init__.py index 4b42ed8..32f4038 100644 --- a/napari_segment_blobs_and_things_with_membranes/__init__.py +++ b/napari_segment_blobs_and_things_with_membranes/__init__.py @@ -100,35 +100,14 @@ def _sobel_3d(image): return ndi.convolve(image, kernel) -class FootprintShape(Enum): - square = 'square' - disk = 'disk' - - -def _generate_square_footprint(radius, ndim): - """Generate a square footprint (for any number of dimensions - ndim) with the - given radius""" - return np.ones((int(radius * 2 + 1),) * ndim) - - def _generate_disk_footprint(radius, ndim): - """Generate a disk footprint (for 2D or 3D) with the given radius.""" + """Generate a disk/sphere footprint (for 2D or 3D) with the given radius.""" if ndim == 2: return disk(radius) elif ndim == 3: return ball(radius) else: - raise ValueError("Disk footprints are only implemented for 2D or 3D images - " - "please switch to a square footprint") - - -def _generate_footprint(footprint_shape, radius, ndim): - """Generate a footprint with given shape, radius and number of dimensions (ndim)""" - if footprint_shape == FootprintShape.square: - return _generate_square_footprint(radius, ndim) - - elif footprint_shape == FootprintShape.disk: - return _generate_disk_footprint(radius, ndim) + raise ValueError("Disk footprints are only implemented for 2D or 3D images") @register_function(menu="Segmentation post-processing > Split touching objects (nsbatwm)") @@ -387,7 +366,7 @@ def mode_filter(labels: "napari.types.LabelsData", radius: int = 2)-> "napari.ty raise ValueError("The mode filter only works on label images with less than 256 labels.") from skimage.filters.rank import majority - footprint = _generate_square_footprint(radius, labels.ndim) + footprint = np.ones((int(radius * 2 + 1),) * labels.ndim) return majority(labels.astype(np.uint8), footprint=footprint).astype(labels.dtype) @register_function(menu="Filtering / noise removal > Percentile (scipy, nsbatwm)") @@ -992,134 +971,126 @@ def squeeze(image:"napari.types.ImageData") -> "napari.types.ImageData": @register_function(menu="Segmentation post-processing > Grayscale erosion (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def grayscale_erosion(labels: "napari.types.LabelsData", radius: int = 1, - footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": +def grayscale_erosion(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies grayscale erosion to an image, using a footprint with the given radius and shape. + Applies grayscale erosion to an image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.erosion """ - footprint = _generate_footprint(footprint_shape, radius, labels.ndim) + footprint = _generate_disk_footprint(radius, labels.ndim) return erosion(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Binary erosion (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_erosion(labels: "napari.types.LabelsData", radius: int = 1, - footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": +def binary_erosion(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies binary erosion to an image, using a footprint with the given radius and shape. + Applies binary erosion to an image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_erosion """ - footprint = _generate_footprint(footprint_shape, radius, labels.ndim) + footprint = _generate_disk_footprint(radius, labels.ndim) return sk_binary_erosion(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Grayscale dilation (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def grayscale_dilation(labels: "napari.types.LabelsData", radius: int = 1, - footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": +def grayscale_dilation(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies grayscale dilation to an image, using a footprint with the given radius and shape. + Applies grayscale dilation to an image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.dilation """ - footprint = _generate_footprint(footprint_shape, radius, labels.ndim) + footprint = _generate_disk_footprint(radius, labels.ndim) return dilation(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Binary dilation (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_dilation(labels: "napari.types.LabelsData", radius: int = 1, - footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": +def binary_dilation(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies binary dilation to an image, using a footprint with the given radius and shape. + Applies binary dilation to an image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_dilation """ - footprint = _generate_footprint(footprint_shape, radius, labels.ndim) + footprint = _generate_disk_footprint(radius, labels.ndim) return sk_binary_dilation(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Grayscale opening (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def grayscale_opening(labels: "napari.types.LabelsData", radius: int = 1, - footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": +def grayscale_opening(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies grayscale opening to an image, using a footprint with the given radius and shape. + Applies grayscale opening to an image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.opening """ - footprint = _generate_footprint(footprint_shape, radius, labels.ndim) + footprint = _generate_disk_footprint(radius, labels.ndim) return opening(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Binary opening (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_opening(labels: "napari.types.LabelsData", radius: int = 1, - footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": +def binary_opening(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies binary opening to an image, using a footprint with the given radius and shape. + Applies binary opening to an image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_opening """ - footprint = _generate_footprint(footprint_shape, radius, labels.ndim) + footprint = _generate_disk_footprint(radius, labels.ndim) return sk_binary_opening(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Grayscale closing (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def grayscale_closing(labels: "napari.types.LabelsData", radius: int = 1, - footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": +def grayscale_closing(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies grayscale closing to an image, using a footprint with the given radius and shape. + Applies grayscale closing to an image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.closing """ - footprint = _generate_footprint(footprint_shape, radius, labels.ndim) + footprint = _generate_disk_footprint(radius, labels.ndim) return closing(labels, footprint=footprint) @register_function(menu="Segmentation post-processing > Binary closing (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_closing(labels: "napari.types.LabelsData", radius: int = 1, - footprint_shape: FootprintShape = FootprintShape.square) -> "napari.types.LabelsData": +def binary_closing(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies binary opening to an image, using a footprint with the given radius and shape. + Applies binary opening to an image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_closing """ - footprint = _generate_footprint(footprint_shape, radius, labels.ndim) + footprint = _generate_disk_footprint(radius, labels.ndim) return sk_binary_closing(labels, footprint=footprint) diff --git a/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py b/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py index 9134c89..0ada5a6 100644 --- a/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py +++ b/napari_segment_blobs_and_things_with_membranes/_tests/test_function.py @@ -50,15 +50,16 @@ def test_something(): grayscale_opening, \ binary_opening, \ grayscale_closing, \ - binary_closing, \ - FootprintShape + binary_closing import numpy as np - image = np.asarray([[0, 1, 2, 3], - [2, 0, 1, 3], - [2, 253, 1, 3], - [255, 253, 1, 3]]) + image_2d = np.asarray([[0, 1, 2, 3], + [2, 0, 1, 3], + [2, 253, 1, 3], + [255, 253, 1, 3]]) + + image_3d = np.ones((4, 4, 4)) for operation in [gaussian_blur, subtract_background, @@ -93,7 +94,7 @@ def test_something(): print(operation) - operation(image) + operation(image_2d) for operation in [ seeded_watershed, @@ -103,7 +104,7 @@ def test_something(): print(operation) - operation(image, image) + operation(image_2d, image_2d) for operation in [ grayscale_erosion, @@ -115,16 +116,16 @@ def test_something(): grayscale_closing, binary_closing ]: - labels = image > 0 - for footprint_shape in FootprintShape: - print(f"{operation} with {footprint_shape}") - operation(labels, footprint_shape=footprint_shape) + for image in (image_2d, image_3d): + labels = image > 0 + print(f"{operation} with {labels.ndim}d image") + operation(labels) - skeletonize(image > 0) + skeletonize(image_2d > 0) - seeded_watershed_with_mask(image, image, image) + seeded_watershed_with_mask(image_2d, image_2d, image_2d) - mode_filter(image.astype(int)) + mode_filter(image_2d.astype(int)) def test_remove_labels_on_edges_sequential_labeling(): image = np.asarray([ From f7c69c87b8ac4c1708aff307d2b15868604d41e4 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Tue, 26 Mar 2024 08:36:36 +0100 Subject: [PATCH 5/7] move greyscale operations to Filtering / background removal menu --- .../__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/napari_segment_blobs_and_things_with_membranes/__init__.py b/napari_segment_blobs_and_things_with_membranes/__init__.py index 32f4038..815d650 100644 --- a/napari_segment_blobs_and_things_with_membranes/__init__.py +++ b/napari_segment_blobs_and_things_with_membranes/__init__.py @@ -968,7 +968,7 @@ def squeeze(image:"napari.types.ImageData") -> "napari.types.ImageData": return np.squeeze(image) -@register_function(menu="Segmentation post-processing > Grayscale erosion (scikit-image, nsbatwm)") +@register_function(menu="Filtering / background removal > Grayscale erosion (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer def grayscale_erosion(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": @@ -1000,7 +1000,7 @@ def binary_erosion(labels: "napari.types.LabelsData", radius: int = 1) -> "napar return sk_binary_erosion(labels, footprint=footprint) -@register_function(menu="Segmentation post-processing > Grayscale dilation (scikit-image, nsbatwm)") +@register_function(menu="Filtering / background removal > Grayscale dilation (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer def grayscale_dilation(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": @@ -1032,7 +1032,7 @@ def binary_dilation(labels: "napari.types.LabelsData", radius: int = 1) -> "napa return sk_binary_dilation(labels, footprint=footprint) -@register_function(menu="Segmentation post-processing > Grayscale opening (scikit-image, nsbatwm)") +@register_function(menu="Filtering / background removal > Grayscale opening (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer def grayscale_opening(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": @@ -1064,7 +1064,7 @@ def binary_opening(labels: "napari.types.LabelsData", radius: int = 1) -> "napar return sk_binary_opening(labels, footprint=footprint) -@register_function(menu="Segmentation post-processing > Grayscale closing (scikit-image, nsbatwm)") +@register_function(menu="Filtering / background removal > Grayscale closing (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer def grayscale_closing(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": From 06d086ffd7baee0c844bf0dd03e13ab8a52b610a Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Tue, 26 Mar 2024 08:38:06 +0100 Subject: [PATCH 6/7] renamed labels parameter to binary_image of binary_ functions --- .../__init__.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/napari_segment_blobs_and_things_with_membranes/__init__.py b/napari_segment_blobs_and_things_with_membranes/__init__.py index 815d650..7f78b4b 100644 --- a/napari_segment_blobs_and_things_with_membranes/__init__.py +++ b/napari_segment_blobs_and_things_with_membranes/__init__.py @@ -987,17 +987,17 @@ def grayscale_erosion(labels: "napari.types.LabelsData", radius: int = 1) -> "na @register_function(menu="Segmentation post-processing > Binary erosion (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_erosion(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def binary_erosion(binary_image: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies binary erosion to an image, using a disk/sphere footprint with the given radius. + Applies binary erosion to a binary image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_erosion """ - footprint = _generate_disk_footprint(radius, labels.ndim) - return sk_binary_erosion(labels, footprint=footprint) + footprint = _generate_disk_footprint(radius, binary_image.ndim) + return sk_binary_erosion(binary_image, footprint=footprint) @register_function(menu="Filtering / background removal > Grayscale dilation (scikit-image, nsbatwm)") @@ -1019,17 +1019,17 @@ def grayscale_dilation(labels: "napari.types.LabelsData", radius: int = 1) -> "n @register_function(menu="Segmentation post-processing > Binary dilation (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_dilation(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def binary_dilation(binary_image: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies binary dilation to an image, using a disk/sphere footprint with the given radius. + Applies binary dilation to a binary image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_dilation """ - footprint = _generate_disk_footprint(radius, labels.ndim) - return sk_binary_dilation(labels, footprint=footprint) + footprint = _generate_disk_footprint(radius, binary_image.ndim) + return sk_binary_dilation(binary_image, footprint=footprint) @register_function(menu="Filtering / background removal > Grayscale opening (scikit-image, nsbatwm)") @@ -1051,17 +1051,17 @@ def grayscale_opening(labels: "napari.types.LabelsData", radius: int = 1) -> "na @register_function(menu="Segmentation post-processing > Binary opening (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_opening(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def binary_opening(binary_image: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies binary opening to an image, using a disk/sphere footprint with the given radius. + Applies binary opening to a binary image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_opening """ - footprint = _generate_disk_footprint(radius, labels.ndim) - return sk_binary_opening(labels, footprint=footprint) + footprint = _generate_disk_footprint(radius, binary_image.ndim) + return sk_binary_opening(binary_image, footprint=footprint) @register_function(menu="Filtering / background removal > Grayscale closing (scikit-image, nsbatwm)") @@ -1083,14 +1083,14 @@ def grayscale_closing(labels: "napari.types.LabelsData", radius: int = 1) -> "na @register_function(menu="Segmentation post-processing > Binary closing (scikit-image, nsbatwm)") @jupyter_displayable_output(library_name='nsbatwm', help_url='https://www.napari-hub.org/plugins/napari-segment-blobs-and-things-with-membranes') @time_slicer -def binary_closing(labels: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": +def binary_closing(binary_image: "napari.types.LabelsData", radius: int = 1) -> "napari.types.LabelsData": """ - Applies binary opening to an image, using a disk/sphere footprint with the given radius. + Applies binary opening to a binary image, using a disk/sphere footprint with the given radius. See also -------- ..[0] https://scikit-image.org/docs/stable/api/skimage.morphology.html#skimage.morphology.binary_closing """ - footprint = _generate_disk_footprint(radius, labels.ndim) - return sk_binary_closing(labels, footprint=footprint) + footprint = _generate_disk_footprint(radius, binary_image.ndim) + return sk_binary_closing(binary_image, footprint=footprint) From e7ae92edba91db516ddaa26283c6ebaf1e0d8ee6 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Tue, 26 Mar 2024 08:39:37 +0100 Subject: [PATCH 7/7] removed unnecessary import --- napari_segment_blobs_and_things_with_membranes/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/napari_segment_blobs_and_things_with_membranes/__init__.py b/napari_segment_blobs_and_things_with_membranes/__init__.py index 7f78b4b..0518c1c 100644 --- a/napari_segment_blobs_and_things_with_membranes/__init__.py +++ b/napari_segment_blobs_and_things_with_membranes/__init__.py @@ -25,7 +25,6 @@ from scipy import ndimage from napari_time_slicer import time_slicer from stackview import jupyter_displayable_output -from enum import Enum @napari_hook_implementation def napari_experimental_provide_function():