Skip to content

Commit e51df51

Browse files
authored
Merge pull request #449 from bnmajor/add-viewer-features
Add viewer features
2 parents f3462fe + c715de6 commit e51df51

File tree

7 files changed

+282
-36
lines changed

7 files changed

+282
-36
lines changed

examples/NumPyArrayPointSet.ipynb

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"id": "492c7f7a-b291-4f01-9c85-9fea9da52fcc",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"import sys\n",
11+
"!{sys.executable} -m pip install -q --pre itkwidgets"
12+
]
13+
},
14+
{
15+
"cell_type": "code",
16+
"execution_count": null,
17+
"id": "ee6c9cad-31f9-4adc-8740-6bb2103ade97",
18+
"metadata": {},
19+
"outputs": [],
20+
"source": [
21+
"import numpy as np\n",
22+
"from itkwidgets import view"
23+
]
24+
},
25+
{
26+
"cell_type": "code",
27+
"execution_count": null,
28+
"id": "684615f0-f389-460f-8de6-6e3fc986d022",
29+
"metadata": {},
30+
"outputs": [],
31+
"source": [
32+
"number_of_points = 3000\n",
33+
"gaussian_mean = [0.0, 0.0, 0.0]\n",
34+
"gaussian_cov = [[1.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 0.5]]\n",
35+
"point_set = np.random.multivariate_normal(gaussian_mean, gaussian_cov, number_of_points)"
36+
]
37+
},
38+
{
39+
"cell_type": "code",
40+
"execution_count": null,
41+
"id": "7571e8e2-5b3e-4204-9934-d30472e2f427",
42+
"metadata": {},
43+
"outputs": [],
44+
"source": [
45+
"view(point_sets=point_set)"
46+
]
47+
},
48+
{
49+
"cell_type": "code",
50+
"execution_count": null,
51+
"id": "4f8a0ef9-68fb-44f0-bd6d-7ad31615ee06",
52+
"metadata": {},
53+
"outputs": [],
54+
"source": []
55+
}
56+
],
57+
"metadata": {
58+
"kernelspec": {
59+
"display_name": "Python 3 (ipykernel)",
60+
"language": "python",
61+
"name": "python3"
62+
},
63+
"language_info": {
64+
"codemirror_mode": {
65+
"name": "ipython",
66+
"version": 3
67+
},
68+
"file_extension": ".py",
69+
"mimetype": "text/x-python",
70+
"name": "python",
71+
"nbconvert_exporter": "python",
72+
"pygments_lexer": "ipython3",
73+
"version": "3.8.10"
74+
}
75+
},
76+
"nbformat": 4,
77+
"nbformat_minor": 5
78+
}

itkwidgets/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""itkwidgets: Interactive widgets to visualize images, point sets, and 3D geometry on the web."""
22

3-
__version__ = "1.0a3"
3+
__version__ = "1.0a4"
44

55
from imjoy_rpc import register_default_codecs
66
register_default_codecs()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
def init_params_dict(itk_viewer):
2+
return {
3+
'annotations': itk_viewer.setAnnotationsEnabled,
4+
'axes': itk_viewer.setAxesEnabled,
5+
'bg_color': itk_viewer.setBackgroundColor,
6+
'blend_mode': itk_viewer.setImageBlendMode,
7+
'cmap': itk_viewer.setImageColorMap,
8+
'color_range': itk_viewer.setImageColorRange,
9+
'color_bounds': itk_viewer.setImageColorRangeBounds,
10+
'component_visible': itk_viewer.setImageComponentVisibility,
11+
'gradient_opacity': itk_viewer.setImageGradientOpacity,
12+
'gradient_opacity_scale': itk_viewer.setImageGradientOpacityScale,
13+
'interpolation': itk_viewer.setImageInterpolationEnabled,
14+
'gaussians': itk_viewer.setImagePiecewiseFunctionGaussians,
15+
'shadow_enabled': itk_viewer.setImageShadowEnabled,
16+
'sample_distance': itk_viewer.setImageVolumeSampleDistance,
17+
'label_blend': itk_viewer.setLabelImageBlend,
18+
'label_names': itk_viewer.setLabelImageLabelNames,
19+
'label_lut': itk_viewer.setLabelImageLookupTable,
20+
'label_weights': itk_viewer.setLabelImageWeights,
21+
'layer': itk_viewer.selectLayer,
22+
'layer_visible': itk_viewer.setLayerVisibility,
23+
'container_style': itk_viewer.setRenderingViewContainerStyle,
24+
'rotate': itk_viewer.setRotateEnabled,
25+
'ui_collapsed': itk_viewer.setUICollapsed,
26+
'units': itk_viewer.setUnits,
27+
'view_mode': itk_viewer.setViewMode,
28+
'x_slice': itk_viewer.setXSlice,
29+
'y_slice': itk_viewer.setYSlice,
30+
'z_slice': itk_viewer.setZSlice,
31+
}

itkwidgets/_type_aliases.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from .integrations.itk import HAVE_ITK
2+
import itkwasm
3+
import numpy as np
4+
from typing import Dict, List, Union
5+
import zarr
6+
7+
8+
Gaussian_Curve = Dict[str, float]
9+
Gaussians = Dict[str, List[Gaussian_Curve]]
10+
11+
Style = Dict[str, str]
12+
13+
Image = Union[np.ndarray, itkwasm.Image, zarr.Group]
14+
Point_Sets = Union[np.ndarray, itkwasm.PointSet, zarr.Group]
15+
if HAVE_ITK:
16+
import itk
17+
Image = Union[Image, itk.Image]
18+
Point_Sets = Union[Point_Sets, itk.GroupSpatialObject]

itkwidgets/integrations/__init__.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import itkwasm
22
import numpy as np
33
import zarr
4-
from .itk import HAVE_ITK, itk_image_to_wasm_image
4+
from .itk import HAVE_ITK, itk_image_to_wasm_image, itk_group_spatial_object_to_wasm_point_set
55
from ..render_types import RenderType
66

77
_image_count = 1
@@ -36,14 +36,38 @@ async def _set_viewer_image(itk_viewer, image, name=None):
3636
await itk_viewer.setImage(wasm_image, name)
3737

3838

39-
def _detect_render_type(data) -> RenderType:
39+
async def _set_viewer_point_sets(itk_viewer, point_sets):
40+
if isinstance(point_sets, itkwasm.PointSet):
41+
await itk_viewer.setPointSets(point_sets)
42+
elif isinstance(point_sets, np.ndarray):
43+
await itk_viewer.setPointSets(point_sets)
44+
elif isinstance(point_sets, zarr.Group):
45+
await itk_viewer.setPointSets(point_sets)
46+
elif HAVE_ITK:
47+
import itk
48+
if isinstance(point_sets, itk.GroupSpatialObject):
49+
wasm_point_sets = itk_group_spatial_object_to_wasm_point_set(point_sets)
50+
await itk_viewer.setPointSets(wasm_point_sets)
51+
52+
53+
def _detect_render_type(data, input_type) -> RenderType:
4054
if isinstance(data, itkwasm.Image):
4155
return RenderType.IMAGE
56+
elif isinstance(data, itkwasm.PointSet):
57+
return RenderType.POINT_SET
4258
elif isinstance(data, np.ndarray):
43-
return RenderType.IMAGE
59+
if input_type == 'point_sets':
60+
return RenderType.POINT_SET
61+
else:
62+
return RenderType.IMAGE
4463
elif isinstance(data, zarr.Group):
45-
return RenderType.IMAGE
64+
if input_type == 'point_sets':
65+
return RenderType.POINT_SET
66+
else:
67+
return RenderType.IMAGE
4668
elif HAVE_ITK:
4769
import itk
4870
if isinstance(data, itk.Image):
49-
return RenderType.IMAGE
71+
return RenderType.IMAGE
72+
elif isinstance(data, itk.GroupSpatialObject):
73+
return RenderType.POINT_SET

itkwidgets/integrations/itk.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ def itk_image_to_wasm_image(image):
1717
wasm_image = itkwasm.Image(**image_dict)
1818
return wasm_image
1919

20+
def itk_group_spatial_object_to_wasm_point_set(point_set):
21+
point_set_dict = itk.dict_from_pointset(point_set)
22+
wasm_point_set = itkwasm.PointSet(**point_set_dict)
23+
return wasm_point_set
24+
2025
else:
2126
def itk_image_to_wasm_image(image):
2227
raise RuntimeError('itk 5.3rc4 or newer is required. `pip install --upgrade --pre itk`')
28+
29+
def itk_group_spatial_object_to_wasm_point_set(point_set):
30+
raise RuntimeError('itk 5.3rc4 or newer is required. `pip install --upgrade --pre itk`')

itkwidgets/viewer.py

Lines changed: 117 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from imjoy import api
2+
from typing import List
23

3-
from typing import Optional
4-
5-
from .integrations import _detect_render_type, _set_viewer_image
4+
from ._type_aliases import Gaussians, Style, Image, Point_Sets
5+
from ._initialization_params import init_params_dict
6+
from .integrations import _detect_render_type, _set_viewer_image, _set_viewer_point_sets
67
from .render_types import RenderType
78

89
__all__ = [
@@ -20,40 +21,44 @@ def __init__(self, ui_collapsed=True, rotate=False, **add_data_kwargs):
2021
self._init_viewer_kwargs = dict(ui_collapsed=ui_collapsed, rotate=rotate)
2122
self._init_viewer_kwargs.update(**add_data_kwargs)
2223

24+
def _get_input_data(self):
25+
input_options = ['data', 'image', 'point_sets']
26+
for option in input_options:
27+
data = self._init_viewer_kwargs.get(option, None)
28+
if data is not None:
29+
break
30+
return data, option
31+
2332
async def setup(self):
2433
"""ImJoy plugin setup function."""
2534
global _viewer_count
26-
try:
27-
from google.colab import output
28-
running_in_colab = True
29-
except ModuleNotFoundError:
30-
running_in_colab = False
31-
if running_in_colab:
32-
itk_viewer = await api.showDialog(
33-
name =f'itkwidgets viewer {_viewer_count}',
34-
type='itk-vtk-viewer',
35-
src='https://kitware.github.io/itk-vtk-viewer/app',
36-
)
37-
else:
38-
itk_viewer = await api.createWindow(
39-
name =f'itkwidgets viewer {_viewer_count}',
40-
type='itk-vtk-viewer',
41-
# src='http://localhost:8082',
42-
src='https://kitware.github.io/itk-vtk-viewer/app',
43-
)
35+
itk_viewer = await api.createWindow(
36+
name =f'itkwidgets viewer {_viewer_count}',
37+
type='itk-vtk-viewer',
38+
src='https://kitware.github.io/itk-vtk-viewer/app',
39+
fullscreen=True,
40+
)
4441
_viewer_count += 1
4542

46-
data = self._init_viewer_kwargs.get('data', None)
43+
data, input_type = self._get_input_data()
4744
if data is not None:
48-
render_type = _detect_render_type(data)
45+
render_type = _detect_render_type(data, input_type)
4946
if render_type is RenderType.IMAGE:
5047
await _set_viewer_image(itk_viewer, data)
48+
elif render_type is RenderType.POINT_SET:
49+
await _set_viewer_point_sets(itk_viewer, data)
5150

52-
itk_viewer.setUICollapsed(self._init_viewer_kwargs['ui_collapsed'])
53-
itk_viewer.setRotateEnabled(self._init_viewer_kwargs['rotate'])
51+
self.set_default_ui_values(itk_viewer)
5452

5553
self.itk_viewer = itk_viewer
5654

55+
def set_default_ui_values(self, itk_viewer):
56+
settings = init_params_dict(itk_viewer)
57+
for key, value in self._init_viewer_kwargs.items():
58+
if key in settings.keys():
59+
settings[key](value)
60+
61+
5762
class Viewer:
5863
"""Pythonic Viewer class."""
5964

@@ -62,18 +67,100 @@ def __init__(self, ui_collapsed=True, rotate=False, **add_data_kwargs):
6267
self.viewer_rpc = ViewerRPC(ui_collapsed=ui_collapsed, rotate=rotate, **add_data_kwargs)
6368
api.export(self.viewer_rpc)
6469

70+
def set_annotations_enabled(self, enabled: bool):
71+
self.viewer_rpc.itk_viewer.setAnnotationsEnabled(enabled)
72+
73+
def set_axes_enabled(self, enabled: bool):
74+
self.viewer_rpc.itk_viewer.setAxesEnabled(enabled)
75+
76+
def set_background_color(self, bgColor: List[float]):
77+
self.viewer_rpc.itk_viewer.setBackgroundColor(bgColor)
78+
79+
def set_image(self, image: Image):
80+
self.viewer_rpc.itk_viewer.setImage(image)
81+
82+
def set_image_blend_mode(self, mode: str):
83+
self.viewer_rpc.itk_viewer.setImageBlendMode(mode)
84+
85+
def set_image_color_map(self, colorMap: str):
86+
self.viewer_rpc.itk_viewer.setImageColorMap(colorMap)
87+
88+
def set_image_color_range(self, range: List[float]):
89+
self.viewer_rpc.itk_viewer.setImageColorRange(range)
90+
91+
def set_image_color_range_bounds(self, range: List[float]):
92+
self.viewer_rpc.itk_viewer.setImageColorRangeBounds(range)
93+
94+
def set_image_component_visibility(self, visibility: bool):
95+
self.viewer_rpc.itk_viewer.setImageComponentVisibility(visibility)
96+
97+
def set_image_gradient_opacity(self, opacity: float):
98+
self.viewer_rpc.itk_viewer.setImageGradientOpacity(opacity)
99+
100+
def set_image_gradient_opacity_scale(self, min: float):
101+
self.viewer_rpc.itk_viewer.setImageGradientOpacityScale(min)
102+
103+
def set_image_interpolation_enabled(self, enabled: bool):
104+
self.viewer_rpc.itk_viewer.setImageInterpolationEnabled(enabled)
105+
106+
def set_image_piecewise_function_gaussians(self, gaussians: Gaussians):
107+
self.viewer_rpc.itk_viewer.setImagePiecewiseFunctionGaussians(gaussians)
108+
109+
def set_image_shadow_enabled(self, enabled: bool):
110+
self.viewer_rpc.itk_viewer.setImageShadowEnabled(enabled)
111+
112+
def set_image_volume_sample_distance(self, distance: float):
113+
self.viewer_rpc.itk_viewer.setImageVolumeSampleDistance(distance)
114+
115+
def set_label_image_blend(self, blend: float):
116+
self.viewer_rpc.itk_viewer.setLabelImageBlend(blend)
117+
118+
def set_label_image_label_names(self, names: List[str]):
119+
self.viewer_rpc.itk_viewer.setLabelImageLabelNames(names)
120+
121+
def set_label_image_lookup_table(self, lookupTable: str):
122+
self.viewer_rpc.itk_viewer.setLabelImageLookupTable(lookupTable)
123+
124+
def set_label_image_weights(self, weights: float):
125+
self.viewer_rpc.itk_viewer.setLabelImageWeights(weights)
126+
127+
def select_layer(self, name: str):
128+
self.viewer_rpc.itk_viewer.selectLayer(name)
129+
130+
def set_layer_visibility(self, visible: bool):
131+
self.viewer_rpc.itk_viewer.setLayerVisibility(visible)
132+
133+
def set_point_sets(self, pointSets: Point_Sets):
134+
self.viewer_rpc.itk_viewer.setPointSets(pointSets)
135+
136+
def set_rendering_view_container_style(self, containerStyle: Style):
137+
self.viewer_rpc.itk_viewer.setRenderingViewContainerStyle(
138+
containerStyle
139+
)
140+
141+
def set_rotate(self, enabled: bool):
142+
self.viewer_rpc.itk_viewer.setRotateEnabled(enabled)
143+
65144
def set_ui_collapsed(self, collapsed: bool):
66145
self.viewer_rpc.itk_viewer.setUICollapsed(collapsed)
67146

68-
def set_rotate(self, rotate: bool):
69-
self.viewer_rpc.itk_viewer.setRotateEnabled(rotate)
147+
def set_units(self, units: str):
148+
self.viewer_rpc.itk_viewer.setUnits(units)
70149

71-
def set_image_gradient_opacity(self, opacity:float):
72-
self.viewer_rpc.itk_viewer.setImageGradientOpacity(opacity)
150+
def set_view_mode(self, mode: str):
151+
self.viewer_rpc.itk_viewer.setViewMode(mode)
152+
153+
def set_x_slice(self, position: float):
154+
self.viewer_rpc.itk_viewer.setXSlice(position)
155+
156+
def set_y_slice(self, position: float):
157+
self.viewer_rpc.itk_viewer.setYSlice(position)
73158

159+
def set_z_slice(self, position: float):
160+
self.viewer_rpc.itk_viewer.setZSlice(position)
74161

75162
def view(data=None, **kwargs):
76163
"""View the data provided and return the resulting Viewer object."""
77164
viewer = Viewer(data=data, **kwargs)
78165

79-
return viewer
166+
return viewer

0 commit comments

Comments
 (0)