Skip to content

Commit d37d2de

Browse files
authored
Improvements to create_overlay_data (#39)
* add option to reset overlay data, plus some fixes * add tests for create_overlay_data * Update reamde
1 parent f1a86fb commit d37d2de

File tree

3 files changed

+56
-10
lines changed

3 files changed

+56
-10
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,10 @@ up the slicer (and which must be present in the layout) are:
106106

107107
**method `VolumeSlicer.create_overlay_data(mask, color=None)`**
108108

109-
Given a 3D mask array and an index, create an object that
110-
can be used as output for `slicer.overlay_data`. The color
111-
can be a hex color or an rgb/rgba tuple. Alternatively, color
112-
can be a list of such colors, defining a colormap.
109+
Given a 3D mask array, create an object that can be used as
110+
output for `slicer.overlay_data`. Set mask to `None` to clear the mask.
111+
The color can be a hex color or an rgb/rgba tuple. Alternatively,
112+
color can be a list of such colors, defining a colormap.
113113

114114
**property `VolumeSlicer.axis`** (`int`): The axis to slice.
115115

dash_slicer/slicer.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -289,15 +289,19 @@ def overlay_data(self):
289289
return self._overlay_data
290290

291291
def create_overlay_data(self, mask, color=None):
292-
"""Given a 3D mask array and an index, create an object that
293-
can be used as output for `slicer.overlay_data`. The color
294-
can be a hex color or an rgb/rgba tuple. Alternatively, color
295-
can be a list of such colors, defining a colormap.
292+
"""Given a 3D mask array, create an object that can be used as
293+
output for `slicer.overlay_data`. Set mask to `None` to clear the mask.
294+
The color can be a hex color or an rgb/rgba tuple. Alternatively,
295+
color can be a list of such colors, defining a colormap.
296296
"""
297297
# Check the mask
298-
if mask.dtype not in (np.bool, np.uint8):
298+
if mask is None:
299+
return [None for index in range(self.nslices)] # A reset
300+
elif not isinstance(mask, np.ndarray):
301+
raise TypeError("Mask must be an ndarray or None.")
302+
elif mask.dtype not in (np.bool, np.uint8):
299303
raise ValueError(f"Mask must have bool or uint8 dtype, not {mask.dtype}.")
300-
if mask.shape != self._volume.shape:
304+
elif mask.shape != self._volume.shape:
301305
raise ValueError(
302306
f"Overlay must has shape {mask.shape}, but expected {self._volume.shape}"
303307
)
@@ -306,6 +310,8 @@ def create_overlay_data(self, mask, color=None):
306310
# Create a colormap (list) from the given color(s)
307311
if color is None:
308312
colormap = discrete_colors[3:]
313+
elif isinstance(color, str):
314+
colormap = [color]
309315
elif isinstance(color, (tuple, list)) and all(
310316
isinstance(x, (int, float)) for x in color
311317
):

tests/test_slicer.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,43 @@ def test_scene_id_and_context_id():
6363

6464
# Context id's must be unique
6565
assert s1._context_id != s2._context_id and s1._context_id != s3._context_id
66+
67+
68+
def test_create_overlay_data():
69+
70+
app = dash.Dash()
71+
vol = np.random.uniform(0, 255, (100, 100, 100)).astype(np.uint8)
72+
s = VolumeSlicer(app, vol)
73+
74+
# Bool overlay
75+
overlay = s.create_overlay_data(vol > 10)
76+
assert isinstance(overlay, list) and len(overlay) == s.nslices
77+
assert all(isinstance(x, str) for x in overlay)
78+
79+
# Bool overlay - with color
80+
overlay = s.create_overlay_data(vol > 10, "#ff0000")
81+
assert isinstance(overlay, list) and len(overlay) == s.nslices
82+
assert all(isinstance(x, str) for x in overlay)
83+
84+
# Uint8 overlay - with colormap
85+
overlay = s.create_overlay_data(vol.astype(np.uint8), ["#ff0000", "#00ff00"])
86+
assert isinstance(overlay, list) and len(overlay) == s.nslices
87+
assert all(isinstance(x, str) for x in overlay)
88+
89+
# Reset
90+
overlay = s.create_overlay_data(None)
91+
assert isinstance(overlay, list) and len(overlay) == s.nslices
92+
assert all(x is None for x in overlay)
93+
94+
# Reset by zero mask
95+
overlay = s.create_overlay_data(vol > 300)
96+
assert isinstance(overlay, list) and len(overlay) == s.nslices
97+
assert all(x is None for x in overlay)
98+
99+
# Wrong
100+
with raises(TypeError):
101+
s.create_overlay_data("not a valid mask")
102+
with raises(ValueError):
103+
s.create_overlay_data(vol.astype(np.float32)) # wrong dtype
104+
with raises(ValueError):
105+
s.create_overlay_data(vol[:-1]) # wrong shape

0 commit comments

Comments
 (0)