WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit a6ccb54

Browse files
authored
Merge branch 'release/25.12' into feature/nvimagecodec-infrastructure-only
2 parents 0c0a9f5 + 1687a56 commit a6ccb54

File tree

4 files changed

+65
-9
lines changed

4 files changed

+65
-9
lines changed

python/cucim/src/cucim/skimage/morphology/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
ball,
2020
diamond,
2121
disk,
22+
ellipse,
2223
footprint_from_sequence,
2324
footprint_rectangle,
2425
octagon,
@@ -63,6 +64,7 @@
6364
"footprint_rectangle",
6465
"diamond",
6566
"disk",
67+
"ellipse",
6668
"octahedron",
6769
"ball",
6870
"octagon",

python/cucim/src/cucim/skimage/morphology/grayreconstruct.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@ def reconstruction(seed, mask, method="dilation", footprint=None, offset=None):
7070
Returns
7171
-------
7272
reconstructed : ndarray
73-
The result of morphological reconstruction.
73+
The result of morphological reconstruction. Note that scikit-image always
74+
returns a floating-point image. cuCIM returns the same dtype as the input
75+
except in the case of erosion, where it will have promoted any unsigned
76+
integer dtype to a signed type (using the dtype returned by
77+
``cp.promote_types(seed.dtype, cp.int8)``).
7478
7579
Examples
7680
--------
@@ -197,6 +201,12 @@ def reconstruction(seed, mask, method="dilation", footprint=None, offset=None):
197201
# CuPy Backend: modified to allow images_dtype based on input dtype
198202
# instead of float64
199203
images_dtype = np.promote_types(seed.dtype, mask.dtype)
204+
# For erosion, we need to negate the array, so ensure we use a signed type
205+
# that can represent negative values without wraparound
206+
if method == "erosion" and cp.issubdtype(images_dtype, cp.unsignedinteger):
207+
# Promote unsigned types to signed types with sufficient range
208+
images_dtype = np.promote_types(images_dtype, cp.int8)
209+
200210
images = cp.full(dims, pad_value, dtype=images_dtype)
201211
images[(0, *inside_slices)] = seed
202212
images[(1, *inside_slices)] = mask
@@ -259,5 +269,5 @@ def reconstruction(seed, mask, method="dilation", footprint=None, offset=None):
259269
value_rank = cp.asarray(value_rank[:image_stride])
260270

261271
rec_img = value_map[value_rank]
262-
rec_img.shape = dims[1:]
272+
rec_img = rec_img.reshape(dims[1:])
263273
return rec_img[inside_slices]

python/cucim/src/cucim/skimage/morphology/tests/test_footprints.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,19 @@
1515
from cupy.testing import assert_array_equal
1616

1717
from cucim.skimage._shared.testing import assert_stacklevel, fetch
18+
19+
# also test that import from top-level morphology namespace work
1820
from cucim.skimage.morphology import (
21+
ball,
22+
diamond,
23+
disk,
24+
ellipse,
1925
footprint_from_sequence,
2026
footprint_rectangle,
2127
footprints,
28+
octagon,
29+
octahedron,
30+
star,
2231
)
2332

2433

@@ -146,14 +155,14 @@ def test_footprint_star(self):
146155
@pytest.mark.parametrize(
147156
"function, args, supports_sequence_decomposition",
148157
[
149-
(footprints.disk, (3,), True),
150-
(footprints.ball, (3,), True),
151-
(footprints.diamond, (3,), True),
152-
(footprints.octahedron, (3,), True),
158+
(disk, (3,), True),
159+
(ball, (3,), True),
160+
(diamond, (3,), True),
161+
(octahedron, (3,), True),
153162
(footprint_rectangle, ((3, 5),), True),
154-
(footprints.ellipse, (3, 4), False),
155-
(footprints.octagon, (3, 4), True),
156-
(footprints.star, (3,), False),
163+
(ellipse, (3, 4), False),
164+
(octagon, (3, 4), True),
165+
(star, (3,), False),
157166
],
158167
)
159168
@pytest.mark.parametrize("dtype", [np.uint8, np.float64])

python/cucim/src/cucim/skimage/morphology/tests/test_reconstruction.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
import numpy as np
1919
import pytest
2020
from cupy.testing import assert_array_almost_equal
21+
from skimage import data
2122
from skimage.morphology import reconstruction as reconstruction_cpu
2223

24+
from cucim.skimage.exposure import rescale_intensity
2325
from cucim.skimage.morphology import reconstruction
2426

2527

@@ -102,6 +104,39 @@ def test_invalid_seed():
102104
reconstruction(seed * 0.5, mask, method="erosion")
103105

104106

107+
@pytest.mark.parametrize(
108+
"unsigned_dtype", [cp.uint8, cp.uint16, cp.uint32, cp.uint64]
109+
)
110+
def test_erosion_uint8_input(unsigned_dtype):
111+
image = cp.asarray(data.moon())
112+
# Rescale image intensity so that we can see dim features.
113+
image = rescale_intensity(image, in_range=(50, 200))
114+
115+
image2 = cp.copy(image)
116+
image2[1:-1, 1:-1] = image.max()
117+
mask = image
118+
119+
image2 = image2.astype(unsigned_dtype)
120+
image2_erosion = reconstruction(image2, mask, method="erosion")
121+
122+
expected_out_type = cp.promote_types(image2.dtype, cp.int8)
123+
assert image2_erosion.dtype == expected_out_type
124+
# promoted to signed dtype (or float in the case of cp.uint64)
125+
assert (
126+
image2_erosion.dtype.kind == "i" if unsigned_dtype != cp.uint64 else "f"
127+
)
128+
assert image2_erosion.dtype.itemsize >= image2.dtype.itemsize
129+
130+
# compare to scikit-image CPU result
131+
image2_erosion_cpu = reconstruction_cpu(
132+
cp.asnumpy(image2), cp.asnumpy(mask), method="erosion"
133+
)
134+
# filled_cpu will be np.float64, so convert to the type returned by cuCIM
135+
cp.testing.assert_allclose(
136+
image2_erosion, image2_erosion_cpu.astype(image2_erosion.dtype)
137+
)
138+
139+
105140
def test_invalid_footprint():
106141
seed = cp.ones((5, 5))
107142
mask = cp.ones((5, 5))

0 commit comments

Comments
 (0)