Skip to content

Commit

Permalink
Merge pull request #401 from rapidsai/branch-22.08
Browse files Browse the repository at this point in the history
[RELEASE] cucim v22.08.01
  • Loading branch information
raydouglass authored Sep 1, 2022
2 parents 8abf103 + 0b4f061 commit 5d78970
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 40 deletions.
60 changes: 52 additions & 8 deletions python/cucim/src/cucim/core/operations/morphology/_pba_2d.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,40 @@
import functools
import math
import numbers
import os


import cupy

try:
# math.lcm was introduced in Python 3.9
from math import lcm
except ImportError:

"""Fallback implementation of least common multiple (lcm)
TODO: remove once minimum Python requirement is >= 3.9
"""

def _lcm(a, b):
return abs(b * (a // math.gcd(a, b)))

@functools.lru_cache()
def lcm(*args):
nargs = len(args)
if not all(isinstance(a, numbers.Integral) for a in args):
raise TypeError("all arguments must be integers")
if nargs == 0:
return 1
res = int(args[0])
if nargs == 1:
return abs(res)
for i in range(1, nargs):
x = int(args[i])
res = _lcm(res, x)
return res


pba2d_defines_template = """
// MARKER is used to mark blank pixels in the texture.
Expand Down Expand Up @@ -119,27 +151,39 @@ def _pba_2d(arr, sampling=None, return_distances=True, return_indices=False,
# if len(sampling) != 2:
# raise ValueError("sampling must be a sequence of two values.")

padded_size = math.ceil(max(arr.shape) / block_size) * block_size
if block_params is None:
# should be <= size / 64. sy must be a multiple of m1
m1 = max(1, min(padded_size // block_size, 32))
padded_size = math.ceil(max(arr.shape) / block_size) * block_size

# should be <= size / block_size. sy must be a multiple of m1
m1 = padded_size // block_size
# size must be a multiple of m2
m2 = max(1, min(padded_size // block_size, 32))
m2 = max(1, min(padded_size // block_size, block_size))
# m2 must also be a power of two
m2 = 2**math.floor(math.log2(m2))
if padded_size % m2 != 0:
raise RuntimeError("error in setting default m2")

# should be <= 64. texture size must be a multiple of m3
m3 = min(min(m1, m2), 2)
else:
if any(p < 1 for p in block_params):
raise ValueError("(m1, m2, m3) in blockparams must be >= 1")
m1, m2, m3 = block_params
if math.log2(m2) % 1 > 1e-5:
raise ValueError("m2 must be a power of 2")
multiple = lcm(block_size, m1, m2, m3)
padded_size = math.ceil(max(arr.shape) / multiple) * multiple

if m1 > padded_size // block_size:
raise ValueError("m1 too large. must be <= arr.shape[0] // 32")
raise ValueError(
f"m1 too large. must be <= padded arr.shape[0] // {block_size}"
)
if m2 > padded_size // block_size:
raise ValueError("m2 too large. must be <= arr.shape[1] // 32")
raise ValueError(
f"m2 too large. must be <= padded arr.shape[1] // {block_size}"
)
if m3 > padded_size // block_size:
raise ValueError(
f"m3 too large. must be <= padded arr.shape[1] // {block_size}"
)
for m in (m1, m2, m3):
if padded_size % m != 0:
raise ValueError(
Expand Down
32 changes: 1 addition & 31 deletions python/cucim/src/cucim/core/operations/morphology/_pba_3d.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,10 @@
import functools
import math
import numbers
import os

import cupy
import numpy as np

from ._pba_2d import _get_block_size

try:
# math.lcm was introduced in Python 3.9
from math import lcm
except ImportError:

"""Fallback implementation of least common multiple (lcm)
TODO: remove once minimum Python requirement is >= 3.9
"""

def _lcm(a, b):
return abs(b * (a // math.gcd(a, b)))

@functools.lru_cache()
def lcm(*args):
nargs = len(args)
if not all(isinstance(a, numbers.Integral) for a in args):
raise TypeError("all arguments must be integers")
if nargs == 0:
return 1
res = int(args[0])
if nargs == 1:
return abs(res)
for i in range(1, nargs):
x = int(args[i])
res = _lcm(res, x)
return res
from ._pba_2d import _get_block_size, lcm


pba3d_defines_template = """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def assert_percentile_equal(arr1, arr2, pct=95):
((384, 256), (1.5, 1.5)),
((14, 32, 50), None),
((50, 32, 24), (2, 2, 2)),
]
],
)
@pytest.mark.parametrize('density', [5, 50, 95])
@pytest.mark.parametrize('block_params', [None, (1, 1, 1)])
Expand Down Expand Up @@ -65,6 +65,60 @@ def test_distance_transform_edt(
assert_percentile_equal(out, expected, pct=95)


@pytest.mark.parametrize(
'shape',
(
[(s,) * 2 for s in range(512, 512 + 32)]
+ [(s,) * 2 for s in range(1024, 1024 + 16)]
+ [(s,) * 2 for s in range(2050, 2050)]
+ [(s,) * 2 for s in range(4100, 4100)]
),
)
@pytest.mark.parametrize('density', [2, 98])
def test_distance_transform_edt_additional_shapes(shape, density):

kwargs_scipy = dict(return_distances=True, return_indices=False)
kwargs_cucim = copy(kwargs_scipy)
img = binary_image(shape, pct_true=density)
distances = distance_transform_edt(img, **kwargs_cucim)
expected = ndi_cpu.distance_transform_edt(cp.asnumpy(img), **kwargs_scipy)
cp.testing.assert_allclose(distances, expected)


@pytest.mark.parametrize(
'shape',
[(s,) * 2 for s in range(1024, 1024 + 4)],
)
@pytest.mark.parametrize(
'block_params',
[(1, 1, 1), (5, 4, 2), (3, 8, 4), (7, 16, 1), (11, 32, 3), (1, 1, 16)]
)
def test_distance_transform_edt_block_params(shape, block_params):

kwargs_scipy = dict(return_distances=True, return_indices=False)
kwargs_cucim = copy(kwargs_scipy)
kwargs_cucim['block_params'] = block_params
img = binary_image(shape, pct_true=4)
distances = distance_transform_edt(img, **kwargs_cucim)
expected = ndi_cpu.distance_transform_edt(cp.asnumpy(img), **kwargs_scipy)
cp.testing.assert_allclose(distances, expected)


@pytest.mark.parametrize(
'block_params', [
(0, 1, 1), (1, 0, 1), (1, 1, 0), # no elements can be < 1
(1, 3, 1), (1, 5, 1), (1, 7, 1), # 2nd element must be a power of 2
(128, 1, 1), # m1 too large for the array size
(1, 128, 1), # m2 too large for the array size
(1, 1, 128), # m3 too large for the array size
]
)
def test_distance_transform_edt_block_params_invalid(block_params):
img = binary_image((512, 512), pct_true=4)
with pytest.raises(ValueError):
distance_transform_edt(img, block_params=block_params)


@pytest.mark.parametrize('return_indices', [False, True])
@pytest.mark.parametrize('return_distances', [False, True])
@pytest.mark.parametrize(
Expand Down

0 comments on commit 5d78970

Please sign in to comment.