Skip to content

Commit

Permalink
add isotropic binary morphology functions (#421)
Browse files Browse the repository at this point in the history
related to #419

These are based on the distance transform and are a faster way of computing binary morphological operations for large diameter disk or ball footprints. Unlike the sequence footprint decomposition methods, the footprint is exactly circular (spherical).

One test case `test_isotropic_erosion_spacing` will require #407 to be merged first. 

#406 will also improve the performance of this implementation

Authors:
  - Gregory Lee (https://github.com/grlee77)

Approvers:
  - Gigon Bae (https://github.com/gigony)

URL: #421
  • Loading branch information
grlee77 authored Nov 29, 2022
1 parent f355506 commit f5408d0
Show file tree
Hide file tree
Showing 5 changed files with 402 additions and 16 deletions.
56 changes: 44 additions & 12 deletions benchmarks/skimage/cucim_morphology_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,15 @@ def __init__(
)

def set_args(self, dtype):
imaged = cp.random.standard_normal(self.shape).astype(dtype) > 0
imaged = (cp.random.standard_normal(self.shape) > 0).astype(dtype)
image = cp.asnumpy(imaged)
self.args_cpu = (image,)
self.args_gpu = (imaged,)


class IsotropicMorphologyBench(ImageBench):
def set_args(self, dtype):
imaged = (cp.random.standard_normal(self.shape) > 0).astype(dtype)
image = cp.asnumpy(imaged)
self.args_cpu = (image,)
self.args_gpu = (imaged,)
Expand Down Expand Up @@ -127,6 +135,10 @@ def main(args):
("binary_dilation", dict(), dict(), False, True),
("binary_opening", dict(), dict(), False, True),
("binary_closing", dict(), dict(), False, True),
("isotropic_erosion", dict(), dict(radius=[5, 10, 20]), False, True),
("isotropic_dilation", dict(), dict(radius=[5, 10, 20]), False, True),
("isotropic_opening", dict(), dict(radius=[5, 10, 20]), False, True),
("isotropic_closing", dict(), dict(radius=[5, 10, 20]), False, True),
# misc.py
("remove_small_objects", dict(), dict(), False, True),
("remove_small_holes", dict(), dict(), False, True),
Expand All @@ -149,6 +161,10 @@ def main(args):
if function_name != args.func_name:
continue

if not allow_color:
if len(shape) > 2 and shape[-1] == 3:
continue

ndim = len(shape)
if function_name in ['thin', 'medial_axis']:
if ndim != 2:
Expand All @@ -168,9 +184,7 @@ def main(args):
run_cpu=run_cpu,
)

results = B.run_benchmark(duration=args.duration)
all_results = pd.concat([all_results, results["full"]])
elif function_name.startswith('binary_'):
if function_name.startswith('binary'):

if not allow_nd and ndim > 2:
continue
Expand All @@ -191,9 +205,22 @@ def main(args):
module_gpu=cucim.skimage.morphology,
run_cpu=run_cpu,
)
results = B.run_benchmark(duration=args.duration)
all_results = pd.concat([all_results, results["full"]])

elif function_name.startswith('isotropic'):

if not allow_nd and ndim > 2:
continue

B = IsotropicMorphologyBench(
function_name=function_name,
shape=shape,
dtypes=[bool],
fixed_kwargs=fixed_kwargs,
var_kwargs=var_kwargs,
module_cpu=skimage.morphology,
module_gpu=cucim.skimage.morphology,
run_cpu=run_cpu,
)

elif function_name in ['remove_small_holes', 'remove_small_objects']:
if not allow_nd and ndim > 2:
Expand All @@ -216,9 +243,6 @@ def main(args):
module_gpu=cucim.skimage.morphology,
run_cpu=run_cpu,
)
results = B.run_benchmark(duration=args.duration)
all_results = pd.concat([all_results, results["full"]])

else:

if not allow_nd:
Expand Down Expand Up @@ -247,8 +271,8 @@ def main(args):
module_gpu=cucim.skimage.morphology,
run_cpu=run_cpu,
)
results = B.run_benchmark(duration=args.duration)
all_results = pd.concat([all_results, results["full"]])
results = B.run_benchmark(duration=args.duration)
all_results = pd.concat([all_results, results["full"]])

fbase = os.path.splitext(pfile)[0]
all_results.to_csv(fbase + ".csv")
Expand All @@ -264,7 +288,15 @@ def main(args):

if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Benchmarking cuCIM morphology functions')
func_name_choices = ['binary_erosion', 'binary_dilation', 'binary_opening', 'binary_closing', 'remove_small_objects', 'remove_small_holes', 'erosion', 'dilation', 'opening', 'closing', 'white_tophat', 'black_tophat', 'thin', 'medial_axis', 'reconstruction']
# fmt: off
func_name_choices = [
'binary_erosion', 'binary_dilation', 'binary_opening',
'binary_closing', 'isotropic_erosion', 'isotropic_dilation',
'isotropic_opening', 'isotropic_closing','remove_small_objects',
'remove_small_holes', 'erosion', 'dilation', 'opening', 'closing',
'white_tophat', 'black_tophat', 'thin', 'medial_axis', 'reconstruction'
]
# fmt: on
dtype_choices = ['float16', 'float32', 'float64', 'int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64']
parser.add_argument('-i','--img_size', type=str, help='Size of input image (omit color channel, it will be appended as needed)', required=True)
parser.add_argument('-d','--dtype', type=str, help='Dtype of input image', choices = dtype_choices, required=True)
Expand Down
7 changes: 3 additions & 4 deletions benchmarks/skimage/run-nv-bench-morphology.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#!/bin/bash
param_shape=(512,512 3840,2160 3840,2160,3 192,192,192)
param_filt=(binary_erosion binary_dilation binary_opening binary_closing remove_small_objects remove_small_holes erosion dilation opening closing white_tophat black_tophat medial_axis thin reconstruction)

param_filt=(binary_erosion binary_dilation binary_opening binary_closing isotropic_erosion isotropic_dilation isotropic_opening isotropic_closing remove_small_objects remove_small_holes erosion dilation opening closing white_tophat black_tophat medial_axis thin reconstruction)
param_dt=(uint8)
for shape in "${param_shape[@]}"; do
for filt in "${param_filt[@]}"; do
for dt in "${param_dt[@]}"; do
python cucim_morphology_bench.py -f $filt -i $shape -d $dt -t 10
python cucim_morphology_bench.py -f $filt -i $shape -d $dt -t 4
done
done
done
Expand All @@ -18,7 +17,7 @@ param_dt=(float32)
for shape in "${param_shape[@]}"; do
for filt in "${param_filt[@]}"; do
for dt in "${param_dt[@]}"; do
python cucim_morphology_bench.py -f $filt -i $shape -d $dt -t 10
python cucim_morphology_bench.py -f $filt -i $shape -d $dt -t 4
done
done
done
6 changes: 6 additions & 0 deletions python/cucim/src/cucim/skimage/morphology/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@
from .gray import (black_tophat, closing, dilation, erosion, opening,
white_tophat)
from .grayreconstruct import reconstruction
from .isotropic import (isotropic_dilation, isotropic_erosion,
isotropic_opening, isotropic_closing)
from .misc import remove_small_holes, remove_small_objects

__all__ = [
"binary_erosion",
"binary_dilation",
"binary_opening",
"binary_closing",
"isotropic_dilation",
"isotropic_erosion",
"isotropic_opening",
"isotropic_closing",
"erosion",
"dilation",
"opening",
Expand Down
Loading

0 comments on commit f5408d0

Please sign in to comment.