InsightSoftwareConsortium / itkwidgets

An elegant Python interface for visualization on the web platform to interactively generate insights into multidimensional images, point sets, and geometry.
https://itkwidgets.readthedocs.io/
Apache License 2.0
576 stars 83 forks source link

Displaying vtkRenderingOpenGL2Python.vtkOpenGLActor possible? #223

Open quaeritis opened 4 years ago

quaeritis commented 4 years ago

Hi,

I try to display a vtkRenderingOpenGL2Python.vtkOpenGLActor is this possibel?

(Thank you for quickly providing a mayavi example in the past, even though I have now started to use vtk directly.)

I get the following error message:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~/miniconda3/envs/kwant/lib/python3.7/site-packages/itkwidgets/trait_types.py in validate(self, obj, value)
    472                 if not isinstance(point_set, dict) or not 'vtkClass' in point_set:
--> 473                     point_sets[index] = to_point_set(point_set)
    474             return point_sets

~/miniconda3/envs/kwant/lib/python3.7/site-packages/itkwidgets/_transform_types.py in to_point_set(point_set_like)
    219 
--> 220         points_data = vtk_to_numpy(point_set_like.GetPoints().GetData())
    221         points_data = points_data.astype(np.float32).ravel()

AttributeError: 'NoneType' object has no attribute 'GetData'

During handling of the above exception, another exception occurred:

TraitError                                Traceback (most recent call last)
<ipython-input-5-f1c3f3d40423> in <module>
      1 from itkwidgets import view
----> 2 view(actors=[actor[0]])

~/miniconda3/envs/kwant/lib/python3.7/site-packages/itkwidgets/widget_viewer.py in view(image, gradient_opacity, cmap, slicing_planes, select_roi, shadow, interpolation, point_sets, point_set_colors, point_set_opacities, point_set_representations, geometries, geometry_colors, geometry_opacities, ui_collapsed, rotate, annotations, mode, **kwargs)
    669             geometries=geometries, geometry_colors=geometry_colors, geometry_opacities=geometry_opacities,
    670             rotate=rotate, ui_collapsed=ui_collapsed, annotations=annotations, mode=mode,
--> 671              **kwargs)
    672     return viewer

~/miniconda3/envs/kwant/lib/python3.7/site-packages/itkwidgets/widget_viewer.py in __init__(self, **kwargs)
    197         self.observe(self._on_geometries_changed, ['geometries'])
    198 
--> 199         super(Viewer, self).__init__(**kwargs)
    200 
    201         if not self.image:

~/miniconda3/envs/kwant/lib/python3.7/site-packages/ipywidgets/widgets/widget.py in __init__(self, **kwargs)
    410         """Public constructor"""
    411         self._model_id = kwargs.pop('model_id', None)
--> 412         super(Widget, self).__init__(**kwargs)
    413 
    414         Widget._call_widget_constructed(self)

~/miniconda3/envs/kwant/lib/python3.7/site-packages/traitlets/traitlets.py in __init__(self, *args, **kwargs)
    995             for key, value in kwargs.items():
    996                 if self.has_trait(key):
--> 997                     setattr(self, key, value)
    998                 else:
    999                     # passthrough args that don't set traits to super

~/miniconda3/envs/kwant/lib/python3.7/site-packages/traitlets/traitlets.py in __set__(self, obj, value)
    583             raise TraitError('The "%s" trait is read-only.' % self.name)
    584         else:
--> 585             self.set(obj, value)
    586 
    587     def _validate(self, obj, value):

~/miniconda3/envs/kwant/lib/python3.7/site-packages/traitlets/traitlets.py in set(self, obj, value)
    557 
    558     def set(self, obj, value):
--> 559         new_value = self._validate(obj, value)
    560         try:
    561             old_value = obj._trait_values[self.name]

~/miniconda3/envs/kwant/lib/python3.7/site-packages/traitlets/traitlets.py in _validate(self, obj, value)
    589             return value
    590         if hasattr(self, 'validate'):
--> 591             value = self.validate(obj, value)
    592         if obj._cross_validation_lock is False:
    593             value = self._cross_validate(obj, value)

~/miniconda3/envs/kwant/lib/python3.7/site-packages/itkwidgets/trait_types.py in validate(self, obj, value)
    474             return point_sets
    475         except:
--> 476             self.error(obj, value)
    477 
    478 class Colormap(traitlets.Unicode):

~/miniconda3/envs/kwant/lib/python3.7/site-packages/traitlets/traitlets.py in error(self, obj, value)
    623             e = "The '%s' trait must be %s, but a value of %r was specified." \
    624                 % (self.name, self.info(), repr_type(value))
--> 625         raise TraitError(e)
    626 
    627     def get_metadata(self, key, default=None):

TraitError: The 'point_sets' trait of a Viewer instance must be Point set representation for rendering geometry in vtk.js., but a value of [(vtkCommonDataModelPython.vtkPolyData)0x7f4129ba67c0] <class 'list'> was specified.

Best, quaeritis

quaeritis commented 4 years ago

Basically I want to plot a point cloud (scalar field). I have already tried this with pyvista and vtkplotter and subsequent display with itkwidgets. Unfortunately I get problems with the colormap.

to produce the problem:

import numpy as np
import vtk
from vtk.util.numpy_support import numpy_to_vtk
colors = vtk.vtkNamedColors()

x = np.arange(0, 100, 10)
y = np.arange(0, 100, 10)
z = np.arange(0, 100, 10)
X, Y, Z = np.meshgrid(x, y, z)
points = np.stack((X.ravel(), Y.ravel(), Z.ravel()), axis=1)
scalar = np.random.rand(len(points))

def vtk_ugrid(np_points, scalars):
    points = vtk.vtkPoints()
    points.SetData(numpy_to_vtk(np_points))

    grid = vtk.vtkUnstructuredGrid()
    grid.SetPoints(points)

    vtk_array = numpy_to_vtk(scalars)
    vtk_array.SetName('scalars')
    grid.GetPointData().SetScalars(vtk_array)
    grid.GetPointData().SetActiveScalars('scalars')
    return grid

def vtk_actors(grid, scaling=False, r=4):
    # Create a custom lut. The lut is used both at the mapper and at the
    # scalar_bar
    lut = vtk.vtkLookupTable()
    lut.Build()

    scalar_range = grid.GetScalarRange()

    # Glyph the points
    sphere = vtk.vtkSphereSource()
    sphere.SetPhiResolution(21)
    sphere.SetThetaResolution(21)
    sphere.SetRadius(r)

    point_mapper = vtk.vtkGlyph3DMapper()
    point_mapper.SetInputData(grid)
    point_mapper.SetScalarRange(scalar_range)
    point_mapper.SetLookupTable(lut)
    point_mapper.SetSourceConnection(sphere.GetOutputPort())
    # point_mapper.ScalingOff()

    point_mapper.SetColorModeToMapScalars()
    point_mapper.ScalarVisibilityOn()
    if not scaling:
        point_mapper.ScalingOff()

    # create the scalar_bar
    scalar_bar = vtk.vtkScalarBarActor()
    scalar_bar.SetOrientationToHorizontal()
    scalar_bar.SetLookupTable(lut)

    actor = vtk.vtkActor()
    actor.SetMapper(point_mapper)

    return actor

grid = vtk_ugrid(points, scalar)
actor = vtk_actors(grid)

from itkwidgets import view
view(actors=[actor])
thewtex commented 4 years ago

@quaeritis does this example achieve what you are trying to do?

https://mybinder.org/v2/gh/InsightSoftwareConsortium/itkwidgets/master?filepath=examples/NumPyArrayPointSet.ipynb

quaeritis commented 4 years ago

if I use a vtkPolyData instead of an vtkUnstructuredGrid it works. (I also gave up to pass a vtkActor and now pass the vtkPolyData to point_set)

So yes, the example does what I want. However, my plan for the future was to add 1D cells to the vtkUnstructuredGrid and then use a vtkTubeFilter to represent connections between the points. That's why I first tried to use a vtkActor.

thewtex commented 4 years ago

We are working towards vtkLookupTable support in vtk.js, after which we will add it to itkwidgets. This will be useful for pyvista, vtk, etc.

@jourdain @floryst @banesullivan

thewtex commented 4 years ago

We are working towards vtkLookupTable support in vtk.js

It is worth noting that this also helps address #184 #182 #140