Skip to content

Commit

Permalink
additional updates from cupy morphology axes branch
Browse files Browse the repository at this point in the history
  • Loading branch information
grlee77 committed Jan 18, 2025
1 parent 7f693d1 commit d6b63b3
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 90 deletions.
80 changes: 46 additions & 34 deletions python/cucim/src/cucim/skimage/_vendored/_ndimage_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,35 +843,37 @@ def generic_laplace(
"""
if extra_keywords is None:
extra_keywords = {}
ndim = input.ndim
modes = _util._fix_sequence_arg(mode, ndim, "mode", _util._check_mode)
axes = _util._check_axes(axes, input.ndim)
num_axes = len(axes)
output = _util._get_output(output, input)
axes = _util._check_axes(axes, ndim)
if ndim == 0:
output[:] = input
return output
derivative2(
input,
axes[0],
output,
modes[0],
cval,
*extra_arguments,
**extra_keywords,
)
if ndim > 1:
tmp = _util._get_output(output.dtype, input)
for i in range(1, ndim):
derivative2(
input,
axes[i],
tmp,
modes[i],
cval,
*extra_arguments,
**extra_keywords,
)
output += tmp
if num_axes > 0:
modes = _util._fix_sequence_arg(
mode, num_axes, "mode", _util._check_mode
)
derivative2(
input,
axes[0],
output,
modes[0],
cval,
*extra_arguments,
**extra_keywords,
)
if num_axes > 1:
tmp = _util._get_output(output.dtype, input)
for i in range(1, num_axes):
derivative2(
input,
axes[i],
tmp,
modes[i],
cval,
*extra_arguments,
**extra_keywords,
)
output += tmp
else:
_core.elementwise_copy(input, output)
return output


Expand Down Expand Up @@ -956,7 +958,7 @@ def gaussian_laplace(
from SciPy due to floating-point rounding of intermediate results.
"""

def derivative2(input, axis, output, mode, cval):
def derivative2(input, axis, output, mode, cval, sigma, **kwargs):
order = [0] * input.ndim
order[axis] = 2
return gaussian_filter(
Expand All @@ -972,7 +974,7 @@ def derivative2(input, axis, output, mode, cval):

axes = _util._check_axes(axes, input.ndim)
num_axes = len(axes)
sigma = _util._fix_sequence_arg(sigma, num_axes, "sigma", int)
sigma = _util._fix_sequence_arg(sigma, num_axes, "sigma", float)
if num_axes < input.ndim:
# set sigma = 0 for any axes not being filtered
sigma_temp = [
Expand All @@ -982,7 +984,16 @@ def derivative2(input, axis, output, mode, cval):
sigma_temp[ax] = s
sigma = sigma_temp

return generic_laplace(input, derivative2, output, mode, cval, axes=axes)
return generic_laplace(
input,
derivative2,
output,
mode,
cval,
extra_arguments=(sigma,),
extra_keywords=kwargs,
axes=axes,
)


def generic_gradient_magnitude(
Expand Down Expand Up @@ -1312,6 +1323,7 @@ def _min_or_max_filter(
# expand origins ,footprint and structure if num_axes < input.ndim
ftprnt = _util._expand_footprint(input.ndim, axes, ftprnt)
origins = _util._expand_origin(input.ndim, axes, origin)
modes = tuple(_util._expand_mode(input.ndim, axes, modes))

if structure is not None:
structure = _util._expand_footprint(
Expand All @@ -1320,7 +1332,7 @@ def _min_or_max_filter(

offsets = _filters_core._origins_to_offsets(origins, ftprnt.shape)
kernel = _get_min_or_max_kernel(
mode,
modes,
ftprnt.shape,
func,
offsets,
Expand Down Expand Up @@ -1424,7 +1436,7 @@ def _min_or_max_1d(

@cupy._util.memoize(for_each_device=True)
def _get_min_or_max_kernel(
mode,
modes,
w_shape,
func,
offsets,
Expand Down Expand Up @@ -1461,7 +1473,7 @@ def _get_min_or_max_kernel(
pre.format(ctype),
found.format(func=func, value=value),
"y = cast<Y>(value);",
mode,
modes,
w_shape,
int_type,
offsets,
Expand Down
108 changes: 52 additions & 56 deletions python/cucim/src/cucim/skimage/_vendored/_ndimage_morphology.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def _get_binary_erosion_kernel(
true_val = 0
false_val = 1
else:
border_value = int(border_value)
true_val = 1
false_val = 0

Expand Down Expand Up @@ -59,14 +60,14 @@ def _get_binary_erosion_kernel(
# {{{{ required because format is called again within _generate_nd_kernel
found = f"""
if ({{cond}}) {{{{
if (!{int(border_value)}) {{{{
y = cast<Y>({int(false_val)});
if (!{border_value}) {{{{
y = cast<Y>({false_val});
return;
}}}}
}}}} else {{{{
bool nn = {{value}} ? {int(true_val)} : {int(false_val)};
bool nn = {{value}} ? {true_val} : {false_val};
if (!nn) {{{{
y = cast<Y>({int(false_val)});
y = cast<Y>({false_val});
return;
}}}}
}}}}"""
Expand All @@ -76,12 +77,13 @@ def _get_binary_erosion_kernel(
name += "_invert"
has_weights = not all_weights_nonzero

modes = ("constant",) * len(w_shape)
return _filters_core._generate_nd_kernel(
name,
pre,
found,
"",
"constant",
modes,
w_shape,
int_type,
offsets,
Expand Down Expand Up @@ -190,7 +192,6 @@ def _binary_erosion(
ndim = input.ndim
axes = _util._check_axes(axes, ndim)
num_axes = len(axes)
default_structure = False
if structure is None:
structure = generate_binary_structure(num_axes, 1)
all_weights_nonzero = input.ndim == 1
Expand Down Expand Up @@ -254,19 +255,18 @@ def _binary_erosion(
origin = tuple(origin)
int_type = _util._get_inttype(input)
offsets = _filters_core._origins_to_offsets(origin, structure_shape)
if not default_structure:
if isinstance(structure, tuple):
nnz = math.prod(structure_shape)
all_weights_nonzero = True
if isinstance(structure, tuple):
nnz = math.prod(structure_shape)
all_weights_nonzero = True
center_is_true = True
else:
# synchronize required to determine if all weights are non-zero
nnz = int(cupy.count_nonzero(structure))
all_weights_nonzero = nnz == structure.size
if all_weights_nonzero:
center_is_true = True
else:
# synchronize required to determine if all weights are non-zero
nnz = int(cupy.count_nonzero(structure))
all_weights_nonzero = nnz == structure.size
if all_weights_nonzero:
center_is_true = True
else:
center_is_true = _center_is_true(structure, origin)
center_is_true = _center_is_true(structure, origin)

erode_kernel = _get_binary_erosion_kernel(
structure_shape,
Expand Down Expand Up @@ -372,12 +372,12 @@ def binary_erosion(
Non-zero (True) elements form the subset to be eroded.
structure(cupy.ndarray or tuple or int, optional): The structuring
element used for the erosion. Non-zero elements are considered
True. If no structuring element is provided an element is generated
with a square connectivity equal to one. (Default value = None). If
a tuple of integers is provided, a structuring element of the
specified shape is used (all elements True). If an integer is
provided, the structuring element will have the same size along all
axes.
true. If no structuring element is provided an element is
generated with a square connectivity equal to one. If a tuple of
integers is provided, a structuring element of the specified shape
is used (all elements true). If an integer is provided, the
structuring element will have the same size along all axes.
(Default value = None).
iterations(int, optional): The erosion is repeated ``iterations`` times
(one, by default). If iterations is less than 1, the erosion is
repeated until the result does not change anymore. Only an integer
Expand Down Expand Up @@ -442,13 +442,13 @@ def binary_dilation(
input(cupy.ndarray): The input binary array_like to be dilated.
Non-zero (True) elements form the subset to be dilated.
structure(cupy.ndarray or tuple or int, optional): The structuring
element used for the erosion. Non-zero elements are considered
True. If no structuring element is provided an element is generated
with a square connectivity equal to one. (Default value = None). If
a tuple of integers is provided, a structuring element of the
specified shape is used (all elements True). If an integer is
provided, the structuring element will have the same size along all
axes.
element used for the dilation. Non-zero elements are considered
true. If no structuring element is provided an element is
generated with a square connectivity equal to one. If a tuple of
integers is provided, a structuring element of the specified shape
is used (all elements true). If an integer is provided, the
structuring element will have the same size along all axes.
(Default value = None).
iterations(int, optional): The dilation is repeated ``iterations``
times (one, by default). If iterations is less than 1, the dilation
is repeated until the result does not change anymore. Only an
Expand Down Expand Up @@ -485,6 +485,7 @@ def binary_dilation(
)
axes = _util._check_axes(axes, input.ndim)
origin = _util._fix_sequence_arg(origin, len(axes), "origin", int)
# no point in flipping if already symmetric
if not symmetric:
structure = structure[tuple([slice(None, None, -1)] * structure.ndim)]
for ii in range(len(origin)):
Expand Down Expand Up @@ -527,13 +528,13 @@ def binary_opening(
input(cupy.ndarray): The input binary array to be opened.
Non-zero (True) elements form the subset to be opened.
structure(cupy.ndarray or tuple or int, optional): The structuring
element used for the erosion. Non-zero elements are considered
True. If no structuring element is provided an element is generated
with a square connectivity equal to one. (Default value = None). If
a tuple of integers is provided, a structuring element of the
specified shape is used (all elements True). If an integer is
provided, the structuring element will have the same size along all
axes.
element used for the opening. Non-zero elements are considered
true. If no structuring element is provided an element is
generated with a square connectivity equal to one. If a tuple of
integers is provided, a structuring element of the specified shape
is used (all elements true). If an integer is provided, the
structuring element will have the same size along all axes.
(Default value = None).
iterations(int, optional): The opening is repeated ``iterations`` times
(one, by default). If iterations is less than 1, the opening is
repeated until the result does not change anymore. Only an integer
Expand Down Expand Up @@ -612,13 +613,13 @@ def binary_closing(
input(cupy.ndarray): The input binary array to be closed.
Non-zero (True) elements form the subset to be closed.
structure(cupy.ndarray or tuple or int, optional): The structuring
element used for the erosion. Non-zero elements are considered
True. If no structuring element is provided an element is generated
with a square connectivity equal to one. (Default value = None). If
a tuple of integers is provided, a structuring element of the
specified shape is used (all elements True). If an integer is
provided, the structuring element will have the same size along all
axes.
element used for the closing. Non-zero elements are considered
true. If no structuring element is provided an element is
generated with a square connectivity equal to one. If a tuple of
integers is provided, a structuring element of the specified shape
is used (all elements true). If an integer is provided, the
structuring element will have the same size along all axes.
(Default value = None).
iterations(int, optional): The closing is repeated ``iterations`` times
(one, by default). If iterations is less than 1, the closing is
repeated until the result does not change anymore. Only an integer
Expand Down Expand Up @@ -912,6 +913,7 @@ def grey_erosion(
.. seealso:: :func:`scipy.ndimage.grey_erosion`
"""

if size is None and footprint is None and structure is None:
raise ValueError("size, footprint or structure must be specified")

Expand Down Expand Up @@ -1062,12 +1064,9 @@ def grey_closing(
warnings.warn(
"ignoring size because footprint is set", UserWarning, stacklevel=2
)
tmp = grey_dilation(
input, size, footprint, structure, None, mode, cval, origin, axes=axes
)
return grey_erosion(
tmp, size, footprint, structure, output, mode, cval, origin, axes=axes
)
kwargs = dict(mode=mode, cval=cval, origin=origin, axes=axes)
tmp = grey_dilation(input, size, footprint, structure, None, **kwargs)
return grey_erosion(tmp, size, footprint, structure, output, **kwargs)


def grey_opening(
Expand Down Expand Up @@ -1119,12 +1118,9 @@ def grey_opening(
warnings.warn(
"ignoring size because footprint is set", UserWarning, stacklevel=2
)
tmp = grey_erosion(
input, size, footprint, structure, None, mode, cval, origin, axes=axes
)
return grey_dilation(
tmp, size, footprint, structure, output, mode, cval, origin, axes=axes
)
kwargs = dict(mode=mode, cval=cval, origin=origin, axes=axes)
tmp = grey_erosion(input, size, footprint, structure, None, **kwargs)
return grey_dilation(tmp, size, footprint, structure, output, **kwargs)


def morphological_gradient(
Expand Down

0 comments on commit d6b63b3

Please sign in to comment.