Skip to content

Commit

Permalink
Merge pull request #449 from bnmajor/add-viewer-features
Browse files Browse the repository at this point in the history
Add viewer features
  • Loading branch information
bnmajor authored Jun 16, 2022
2 parents f3462fe + c715de6 commit e51df51
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 36 deletions.
78 changes: 78 additions & 0 deletions examples/NumPyArrayPointSet.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "492c7f7a-b291-4f01-9c85-9fea9da52fcc",
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"!{sys.executable} -m pip install -q --pre itkwidgets"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ee6c9cad-31f9-4adc-8740-6bb2103ade97",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from itkwidgets import view"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "684615f0-f389-460f-8de6-6e3fc986d022",
"metadata": {},
"outputs": [],
"source": [
"number_of_points = 3000\n",
"gaussian_mean = [0.0, 0.0, 0.0]\n",
"gaussian_cov = [[1.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 0.5]]\n",
"point_set = np.random.multivariate_normal(gaussian_mean, gaussian_cov, number_of_points)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7571e8e2-5b3e-4204-9934-d30472e2f427",
"metadata": {},
"outputs": [],
"source": [
"view(point_sets=point_set)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4f8a0ef9-68fb-44f0-bd6d-7ad31615ee06",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
2 changes: 1 addition & 1 deletion itkwidgets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""itkwidgets: Interactive widgets to visualize images, point sets, and 3D geometry on the web."""

__version__ = "1.0a3"
__version__ = "1.0a4"

from imjoy_rpc import register_default_codecs
register_default_codecs()
Expand Down
31 changes: 31 additions & 0 deletions itkwidgets/_initialization_params.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
def init_params_dict(itk_viewer):
return {
'annotations': itk_viewer.setAnnotationsEnabled,
'axes': itk_viewer.setAxesEnabled,
'bg_color': itk_viewer.setBackgroundColor,
'blend_mode': itk_viewer.setImageBlendMode,
'cmap': itk_viewer.setImageColorMap,
'color_range': itk_viewer.setImageColorRange,
'color_bounds': itk_viewer.setImageColorRangeBounds,
'component_visible': itk_viewer.setImageComponentVisibility,
'gradient_opacity': itk_viewer.setImageGradientOpacity,
'gradient_opacity_scale': itk_viewer.setImageGradientOpacityScale,
'interpolation': itk_viewer.setImageInterpolationEnabled,
'gaussians': itk_viewer.setImagePiecewiseFunctionGaussians,
'shadow_enabled': itk_viewer.setImageShadowEnabled,
'sample_distance': itk_viewer.setImageVolumeSampleDistance,
'label_blend': itk_viewer.setLabelImageBlend,
'label_names': itk_viewer.setLabelImageLabelNames,
'label_lut': itk_viewer.setLabelImageLookupTable,
'label_weights': itk_viewer.setLabelImageWeights,
'layer': itk_viewer.selectLayer,
'layer_visible': itk_viewer.setLayerVisibility,
'container_style': itk_viewer.setRenderingViewContainerStyle,
'rotate': itk_viewer.setRotateEnabled,
'ui_collapsed': itk_viewer.setUICollapsed,
'units': itk_viewer.setUnits,
'view_mode': itk_viewer.setViewMode,
'x_slice': itk_viewer.setXSlice,
'y_slice': itk_viewer.setYSlice,
'z_slice': itk_viewer.setZSlice,
}
18 changes: 18 additions & 0 deletions itkwidgets/_type_aliases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from .integrations.itk import HAVE_ITK
import itkwasm
import numpy as np
from typing import Dict, List, Union
import zarr


Gaussian_Curve = Dict[str, float]
Gaussians = Dict[str, List[Gaussian_Curve]]

Style = Dict[str, str]

Image = Union[np.ndarray, itkwasm.Image, zarr.Group]
Point_Sets = Union[np.ndarray, itkwasm.PointSet, zarr.Group]
if HAVE_ITK:
import itk
Image = Union[Image, itk.Image]
Point_Sets = Union[Point_Sets, itk.GroupSpatialObject]
34 changes: 29 additions & 5 deletions itkwidgets/integrations/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import itkwasm
import numpy as np
import zarr
from .itk import HAVE_ITK, itk_image_to_wasm_image
from .itk import HAVE_ITK, itk_image_to_wasm_image, itk_group_spatial_object_to_wasm_point_set
from ..render_types import RenderType

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


def _detect_render_type(data) -> RenderType:
async def _set_viewer_point_sets(itk_viewer, point_sets):
if isinstance(point_sets, itkwasm.PointSet):
await itk_viewer.setPointSets(point_sets)
elif isinstance(point_sets, np.ndarray):
await itk_viewer.setPointSets(point_sets)
elif isinstance(point_sets, zarr.Group):
await itk_viewer.setPointSets(point_sets)
elif HAVE_ITK:
import itk
if isinstance(point_sets, itk.GroupSpatialObject):
wasm_point_sets = itk_group_spatial_object_to_wasm_point_set(point_sets)
await itk_viewer.setPointSets(wasm_point_sets)


def _detect_render_type(data, input_type) -> RenderType:
if isinstance(data, itkwasm.Image):
return RenderType.IMAGE
elif isinstance(data, itkwasm.PointSet):
return RenderType.POINT_SET
elif isinstance(data, np.ndarray):
return RenderType.IMAGE
if input_type == 'point_sets':
return RenderType.POINT_SET
else:
return RenderType.IMAGE
elif isinstance(data, zarr.Group):
return RenderType.IMAGE
if input_type == 'point_sets':
return RenderType.POINT_SET
else:
return RenderType.IMAGE
elif HAVE_ITK:
import itk
if isinstance(data, itk.Image):
return RenderType.IMAGE
return RenderType.IMAGE
elif isinstance(data, itk.GroupSpatialObject):
return RenderType.POINT_SET
8 changes: 8 additions & 0 deletions itkwidgets/integrations/itk.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ def itk_image_to_wasm_image(image):
wasm_image = itkwasm.Image(**image_dict)
return wasm_image

def itk_group_spatial_object_to_wasm_point_set(point_set):
point_set_dict = itk.dict_from_pointset(point_set)
wasm_point_set = itkwasm.PointSet(**point_set_dict)
return wasm_point_set

else:
def itk_image_to_wasm_image(image):
raise RuntimeError('itk 5.3rc4 or newer is required. `pip install --upgrade --pre itk`')

def itk_group_spatial_object_to_wasm_point_set(point_set):
raise RuntimeError('itk 5.3rc4 or newer is required. `pip install --upgrade --pre itk`')
147 changes: 117 additions & 30 deletions itkwidgets/viewer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from imjoy import api
from typing import List

from typing import Optional

from .integrations import _detect_render_type, _set_viewer_image
from ._type_aliases import Gaussians, Style, Image, Point_Sets
from ._initialization_params import init_params_dict
from .integrations import _detect_render_type, _set_viewer_image, _set_viewer_point_sets
from .render_types import RenderType

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

def _get_input_data(self):
input_options = ['data', 'image', 'point_sets']
for option in input_options:
data = self._init_viewer_kwargs.get(option, None)
if data is not None:
break
return data, option

async def setup(self):
"""ImJoy plugin setup function."""
global _viewer_count
try:
from google.colab import output
running_in_colab = True
except ModuleNotFoundError:
running_in_colab = False
if running_in_colab:
itk_viewer = await api.showDialog(
name =f'itkwidgets viewer {_viewer_count}',
type='itk-vtk-viewer',
src='https://kitware.github.io/itk-vtk-viewer/app',
)
else:
itk_viewer = await api.createWindow(
name =f'itkwidgets viewer {_viewer_count}',
type='itk-vtk-viewer',
# src='http://localhost:8082',
src='https://kitware.github.io/itk-vtk-viewer/app',
)
itk_viewer = await api.createWindow(
name =f'itkwidgets viewer {_viewer_count}',
type='itk-vtk-viewer',
src='https://kitware.github.io/itk-vtk-viewer/app',
fullscreen=True,
)
_viewer_count += 1

data = self._init_viewer_kwargs.get('data', None)
data, input_type = self._get_input_data()
if data is not None:
render_type = _detect_render_type(data)
render_type = _detect_render_type(data, input_type)
if render_type is RenderType.IMAGE:
await _set_viewer_image(itk_viewer, data)
elif render_type is RenderType.POINT_SET:
await _set_viewer_point_sets(itk_viewer, data)

itk_viewer.setUICollapsed(self._init_viewer_kwargs['ui_collapsed'])
itk_viewer.setRotateEnabled(self._init_viewer_kwargs['rotate'])
self.set_default_ui_values(itk_viewer)

self.itk_viewer = itk_viewer

def set_default_ui_values(self, itk_viewer):
settings = init_params_dict(itk_viewer)
for key, value in self._init_viewer_kwargs.items():
if key in settings.keys():
settings[key](value)


class Viewer:
"""Pythonic Viewer class."""

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

def set_annotations_enabled(self, enabled: bool):
self.viewer_rpc.itk_viewer.setAnnotationsEnabled(enabled)

def set_axes_enabled(self, enabled: bool):
self.viewer_rpc.itk_viewer.setAxesEnabled(enabled)

def set_background_color(self, bgColor: List[float]):
self.viewer_rpc.itk_viewer.setBackgroundColor(bgColor)

def set_image(self, image: Image):
self.viewer_rpc.itk_viewer.setImage(image)

def set_image_blend_mode(self, mode: str):
self.viewer_rpc.itk_viewer.setImageBlendMode(mode)

def set_image_color_map(self, colorMap: str):
self.viewer_rpc.itk_viewer.setImageColorMap(colorMap)

def set_image_color_range(self, range: List[float]):
self.viewer_rpc.itk_viewer.setImageColorRange(range)

def set_image_color_range_bounds(self, range: List[float]):
self.viewer_rpc.itk_viewer.setImageColorRangeBounds(range)

def set_image_component_visibility(self, visibility: bool):
self.viewer_rpc.itk_viewer.setImageComponentVisibility(visibility)

def set_image_gradient_opacity(self, opacity: float):
self.viewer_rpc.itk_viewer.setImageGradientOpacity(opacity)

def set_image_gradient_opacity_scale(self, min: float):
self.viewer_rpc.itk_viewer.setImageGradientOpacityScale(min)

def set_image_interpolation_enabled(self, enabled: bool):
self.viewer_rpc.itk_viewer.setImageInterpolationEnabled(enabled)

def set_image_piecewise_function_gaussians(self, gaussians: Gaussians):
self.viewer_rpc.itk_viewer.setImagePiecewiseFunctionGaussians(gaussians)

def set_image_shadow_enabled(self, enabled: bool):
self.viewer_rpc.itk_viewer.setImageShadowEnabled(enabled)

def set_image_volume_sample_distance(self, distance: float):
self.viewer_rpc.itk_viewer.setImageVolumeSampleDistance(distance)

def set_label_image_blend(self, blend: float):
self.viewer_rpc.itk_viewer.setLabelImageBlend(blend)

def set_label_image_label_names(self, names: List[str]):
self.viewer_rpc.itk_viewer.setLabelImageLabelNames(names)

def set_label_image_lookup_table(self, lookupTable: str):
self.viewer_rpc.itk_viewer.setLabelImageLookupTable(lookupTable)

def set_label_image_weights(self, weights: float):
self.viewer_rpc.itk_viewer.setLabelImageWeights(weights)

def select_layer(self, name: str):
self.viewer_rpc.itk_viewer.selectLayer(name)

def set_layer_visibility(self, visible: bool):
self.viewer_rpc.itk_viewer.setLayerVisibility(visible)

def set_point_sets(self, pointSets: Point_Sets):
self.viewer_rpc.itk_viewer.setPointSets(pointSets)

def set_rendering_view_container_style(self, containerStyle: Style):
self.viewer_rpc.itk_viewer.setRenderingViewContainerStyle(
containerStyle
)

def set_rotate(self, enabled: bool):
self.viewer_rpc.itk_viewer.setRotateEnabled(enabled)

def set_ui_collapsed(self, collapsed: bool):
self.viewer_rpc.itk_viewer.setUICollapsed(collapsed)

def set_rotate(self, rotate: bool):
self.viewer_rpc.itk_viewer.setRotateEnabled(rotate)
def set_units(self, units: str):
self.viewer_rpc.itk_viewer.setUnits(units)

def set_image_gradient_opacity(self, opacity:float):
self.viewer_rpc.itk_viewer.setImageGradientOpacity(opacity)
def set_view_mode(self, mode: str):
self.viewer_rpc.itk_viewer.setViewMode(mode)

def set_x_slice(self, position: float):
self.viewer_rpc.itk_viewer.setXSlice(position)

def set_y_slice(self, position: float):
self.viewer_rpc.itk_viewer.setYSlice(position)

def set_z_slice(self, position: float):
self.viewer_rpc.itk_viewer.setZSlice(position)

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

return viewer
return viewer

0 comments on commit e51df51

Please sign in to comment.