def test_get_bounds(): size = (15, 15) test_bounds = [0.0, 15, 0.0, 15, 0.0, 0.0] points = vtk.vtkPoints() points.InsertNextPoint(0, 0, 0) points.InsertNextPoint(size[0], 0, 0) points.InsertNextPoint(size[0], size[1], 0) points.InsertNextPoint(0, size[1], 0) # Create the polygon polygon = vtk.vtkPolygon() polygon.GetPointIds().SetNumberOfIds(4) # make a quad polygon.GetPointIds().SetId(0, 0) polygon.GetPointIds().SetId(1, 1) polygon.GetPointIds().SetId(2, 2) polygon.GetPointIds().SetId(3, 3) # Add the polygon to a list of polygons polygons = vtk.vtkCellArray() polygons.InsertNextCell(polygon) # Create a PolyData polygonPolyData = vtk.vtkPolyData() polygonPolyData.SetPoints(points) polygonPolyData.SetPolys(polygons) # Create a mapper and actor mapper = vtk.vtkPolyDataMapper2D() mapper = set_input(mapper, polygonPolyData) actor = vtk.vtkActor2D() actor.SetMapper(mapper) compute_bounds(actor) npt.assert_equal(get_bounds(actor), test_bounds)
def set_icon(self, icon): """Modify the icon used by the vtkTexturedActor2D. Parameters ---------- icon : imageData """ self.texture = set_input(self.texture, icon)
def set_img(self, img): """Modify the image used by the vtkTexturedActor2D. Parameters ---------- img : imageData """ self.texture = set_input(self.texture, img)
def save_polydata(polydata, file_name, binary=False, color_array_name=None, is_mni_obj=False): """Save a vtk polydata to a supported format file. Save formats can be VTK, FIB, PLY, STL and XML. Parameters ---------- polydata : vtkPolyData file_name : string binary : bool color_array_name: ndarray is_mni_obj : bool """ # get file extension (type) file_extension = file_name.split(".")[-1].lower() if file_extension == "vtk": writer = vtk.vtkPolyDataWriter() elif file_extension == "fib": writer = vtk.vtkPolyDataWriter() elif file_extension == "ply": writer = vtk.vtkPLYWriter() elif file_extension == "stl": writer = vtk.vtkSTLWriter() elif file_extension == "xml": writer = vtk.vtkXMLPolyDataWriter() elif file_extension == "obj": if is_mni_obj: writer = vtk.vtkMNIObjectWriter() else: # vtkObjWriter not available on python # vtk.vtkOBJWriter() raise IOError("OBJ Writer not available. MNI obj is the only" " available writer so set mni_tag option to True") else: raise IOError("Unknown extension ({})".format(file_extension)) writer.SetFileName(file_name) writer = set_input(writer, polydata) if color_array_name is not None and file_extension == "ply": writer.SetArrayName(color_array_name) if binary: writer.SetFileTypeToBinary() writer.Update() writer.Write()
def _setup(self): """Setup this UI Component. Return an image as a 2D actor with a specific position. Returns ------- :class:`vtkTexturedActor2D` """ self.texture_polydata = PolyData() self.texture_points = Points() self.texture_points.SetNumberOfPoints(4) polys = CellArray() polys.InsertNextCell(4) polys.InsertCellPoint(0) polys.InsertCellPoint(1) polys.InsertCellPoint(2) polys.InsertCellPoint(3) self.texture_polydata.SetPolys(polys) tc = FloatArray() tc.SetNumberOfComponents(2) tc.SetNumberOfTuples(4) tc.InsertComponent(0, 0, 0.0) tc.InsertComponent(0, 1, 0.0) tc.InsertComponent(1, 0, 1.0) tc.InsertComponent(1, 1, 0.0) tc.InsertComponent(2, 0, 1.0) tc.InsertComponent(2, 1, 1.0) tc.InsertComponent(3, 0, 0.0) tc.InsertComponent(3, 1, 1.0) self.texture_polydata.GetPointData().SetTCoords(tc) texture_mapper = PolyDataMapper2D() texture_mapper = set_input(texture_mapper, self.texture_polydata) image = TexturedActor2D() image.SetMapper(texture_mapper) self.texture = Texture() image.SetTexture(self.texture) image_property = Property2D() image_property.SetOpacity(1.0) image.SetProperty(image_property) self.actor = image # Add default events listener to the VTK actor. self.handle_events(self.actor)
def _setup(self): """Set up this UI component. Creating the button actor used internally. """ # This is highly inspired by # https://github.com/Kitware/VTK/blob/c3ec2495b183e3327820e927af7f8f90d34c3474/Interaction/Widgets/vtkBalloonRepresentation.cxx#L47 self.texture_polydata = PolyData() self.texture_points = Points() self.texture_points.SetNumberOfPoints(4) polys = CellArray() polys.InsertNextCell(4) polys.InsertCellPoint(0) polys.InsertCellPoint(1) polys.InsertCellPoint(2) polys.InsertCellPoint(3) self.texture_polydata.SetPolys(polys) tc = FloatArray() tc.SetNumberOfComponents(2) tc.SetNumberOfTuples(4) tc.InsertComponent(0, 0, 0.0) tc.InsertComponent(0, 1, 0.0) tc.InsertComponent(1, 0, 1.0) tc.InsertComponent(1, 1, 0.0) tc.InsertComponent(2, 0, 1.0) tc.InsertComponent(2, 1, 1.0) tc.InsertComponent(3, 0, 0.0) tc.InsertComponent(3, 1, 1.0) self.texture_polydata.GetPointData().SetTCoords(tc) texture_mapper = PolyDataMapper2D() texture_mapper = set_input(texture_mapper, self.texture_polydata) button = TexturedActor2D() button.SetMapper(texture_mapper) self.texture = Texture() button.SetTexture(self.texture) button_property = Property2D() button_property.SetOpacity(1.0) button.SetProperty(button_property) self.actor = button # Add default events listener to the VTK actor. self.handle_events(self.actor)
def save_polydata(polydata, file_name, binary=False, color_array_name=None): """Save a vtk polydata to a supported format file. Save formats can be VTK, FIB, PLY, STL and XML. Parameters ---------- polydata : vtkPolyData file_name : string binary : bool color_array_name: ndarray """ # get file extension (type) file_extension = file_name.split(".")[-1].lower() poly_writer = { "vtk": PolyDataWriter, "vtp": XMLPolyDataWriter, "fib": PolyDataWriter, "ply": PLYWriter, "stl": STLWriter, "xml": XMLPolyDataWriter } if file_extension in poly_writer.keys(): writer = poly_writer.get(file_extension)() elif file_extension == "obj": # Special case, since there is two obj format find_keyword = file_name.lower().split(".") if "mni" in find_keyword or "mnc" in find_keyword: writer = MNIObjectWriter() else: raise IOError("Wavefront obj requires a scene \n" " for MNI obj, use '.mni.obj' extension") else: raise IOError("." + file_extension + " is not supported by FURY") writer.SetFileName(file_name) writer = set_input(writer, polydata) if color_array_name is not None and file_extension == "ply": writer.SetArrayName(color_array_name) if binary: writer.SetFileTypeToBinary() writer.Update() writer.Write()
def resize(self, size): """Set the button size. Parameters ---------- size : (float, float) Button size (width, height) in pixels. """ self._points.SetPoint(0, 0, 0, 0.0) self._points.SetPoint(1, size[0], 0, 0.0) self._points.SetPoint(2, size[0], size[1], 0.0) self._points.SetPoint(3, 0, size[1], 0.0) self._polygonPolyData.SetPoints(self._points) mapper = PolyDataMapper2D() mapper = set_input(mapper, self._polygonPolyData) self.actor.SetMapper(mapper)
def test_update_actor(): size = (15, 15) test_bounds = [0.0, 15, 0.0, 15, 0.0, 0.0] points = Points() points.InsertNextPoint(0, 0, 0) points.InsertNextPoint(size[0], 0, 0) points.InsertNextPoint(size[0], size[1], 0) points.InsertNextPoint(0, size[1], 0) # Create the polygon polygon = Polygon() polygon.GetPointIds().SetNumberOfIds(4) # make a quad polygon.GetPointIds().SetId(0, 0) polygon.GetPointIds().SetId(1, 1) polygon.GetPointIds().SetId(2, 2) polygon.GetPointIds().SetId(3, 3) # Add the polygon to a list of polygons polygons = CellArray() polygons.InsertNextCell(polygon) # Create a PolyData polygonPolyData = PolyData() polygonPolyData.SetPoints(points) polygonPolyData.SetPolys(polygons) # Create a mapper and actor mapper = PolyDataMapper2D() mapper = set_input(mapper, polygonPolyData) actor = Actor2D() actor.SetMapper(mapper) compute_bounds(actor) npt.assert_equal(actor.GetMapper().GetInput().GetBounds(), test_bounds) updated_size = (35, 35) points.SetPoint(0, 0, 0, 0.0) points.SetPoint(1, updated_size[0], 0, 0.0) points.SetPoint(2, updated_size[0], updated_size[1], 0.0) points.SetPoint(3, 0, updated_size[1], 0.0) polygonPolyData.SetPoints(points) test_bounds = [0.0, 35.0, 0.0, 35.0, 0.0, 0.0] compute_bounds(actor) npt.assert_equal(None, update_actor(actor)) npt.assert_equal(test_bounds, actor.GetMapper().GetInput().GetBounds())
def _setup(self): """Setup this UI component. Creating the disk actor used internally. """ # Setting up disk actor. self._disk = DiskSource() self._disk.SetRadialResolution(10) self._disk.SetCircumferentialResolution(50) self._disk.Update() # Mapper mapper = PolyDataMapper2D() mapper = set_input(mapper, self._disk.GetOutputPort()) # Actor self.actor = Actor2D() self.actor.SetMapper(mapper) # Add default events listener to the VTK actor. self.handle_events(self.actor)
def _setup(self): """Set up this UI component. Creating the polygon actor used internally. """ # Setup four points size = (1, 1) self._points = Points() self._points.InsertNextPoint(0, 0, 0) self._points.InsertNextPoint(size[0], 0, 0) self._points.InsertNextPoint(size[0], size[1], 0) self._points.InsertNextPoint(0, size[1], 0) # Create the polygon polygon = Polygon() polygon.GetPointIds().SetNumberOfIds(4) # make a quad polygon.GetPointIds().SetId(0, 0) polygon.GetPointIds().SetId(1, 1) polygon.GetPointIds().SetId(2, 2) polygon.GetPointIds().SetId(3, 3) # Add the polygon to a list of polygons polygons = CellArray() polygons.InsertNextCell(polygon) # Create a PolyData self._polygonPolyData = PolyData() self._polygonPolyData.SetPoints(self._points) self._polygonPolyData.SetPolys(polygons) # Create a mapper and actor mapper = PolyDataMapper2D() mapper = set_input(mapper, self._polygonPolyData) self.actor = Actor2D() self.actor.SetMapper(mapper) # Add default events listener to the VTK actor. self.handle_events(self.actor)
def test_custom_interactor_style_events(recording=False): print("Using VTK {}".format(vtk.vtkVersion.GetVTKVersion())) filename = "test_custom_interactor_style_events.log.gz" recording_filename = pjoin(DATA_DIR, filename) scene = window.Scene() # the show manager allows to break the rendering process # in steps so that the widgets can be added properly interactor_style = interactor.CustomInteractorStyle() show_manager = window.ShowManager(scene, size=(800, 800), reset_camera=False, interactor_style=interactor_style) # Create a cursor, a circle that will follow the mouse. polygon_source = vtk.vtkRegularPolygonSource() polygon_source.GeneratePolygonOff() # Only the outline of the circle. polygon_source.SetNumberOfSides(50) polygon_source.SetRadius(10) # polygon_source.SetRadius polygon_source.SetCenter(0, 0, 0) mapper = vtk.vtkPolyDataMapper2D() vtk_utils.set_input(mapper, polygon_source.GetOutputPort()) cursor = vtk.vtkActor2D() cursor.SetMapper(mapper) cursor.GetProperty().SetColor(1, 0.5, 0) scene.add(cursor) def follow_mouse(iren, obj): obj.SetPosition(*iren.event.position) iren.force_render() interactor_style.add_active_prop(cursor) interactor_style.add_callback(cursor, "MouseMoveEvent", follow_mouse) # create some minimalistic streamlines lines = [ np.array([[-1, 0, 0.], [1, 0, 0.]]), np.array([[-1, 1, 0.], [1, 1, 0.]]) ] colors = np.array([[1., 0., 0.], [0.3, 0.7, 0.]]) tube1 = actor.streamtube([lines[0]], colors[0]) tube2 = actor.streamtube([lines[1]], colors[1]) scene.add(tube1) scene.add(tube2) # Define some counter callback. states = defaultdict(lambda: 0) def counter(iren, _obj): states[iren.event.name] += 1 # Assign the counter callback to every possible event. for event in [ "CharEvent", "MouseMoveEvent", "KeyPressEvent", "KeyReleaseEvent", "LeftButtonPressEvent", "LeftButtonReleaseEvent", "RightButtonPressEvent", "RightButtonReleaseEvent", "MiddleButtonPressEvent", "MiddleButtonReleaseEvent" ]: interactor_style.add_callback(tube1, event, counter) # Add callback to scale up/down tube1. def scale_up_obj(iren, obj): counter(iren, obj) scale = np.asarray(obj.GetScale()) + 0.1 obj.SetScale(*scale) iren.force_render() iren.event.abort() # Stop propagating the event. def scale_down_obj(iren, obj): counter(iren, obj) scale = np.array(obj.GetScale()) - 0.1 obj.SetScale(*scale) iren.force_render() iren.event.abort() # Stop propagating the event. interactor_style.add_callback(tube2, "MouseWheelForwardEvent", scale_up_obj) interactor_style.add_callback(tube2, "MouseWheelBackwardEvent", scale_down_obj) # Add callback to hide/show tube1. def toggle_visibility(iren, obj): key = iren.event.key if key.lower() == "v": obj.SetVisibility(not obj.GetVisibility()) iren.force_render() interactor_style.add_active_prop(tube1) interactor_style.add_active_prop(tube2) interactor_style.remove_active_prop(tube2) interactor_style.add_callback(tube1, "CharEvent", toggle_visibility) if recording: show_manager.record_events_to_file(recording_filename) print(list(states.items())) else: show_manager.play_events_from_file(recording_filename) msg = ("Wrong count for '{}'.") expected = [('CharEvent', 6), ('KeyPressEvent', 6), ('KeyReleaseEvent', 6), ('MouseMoveEvent', 1652), ('LeftButtonPressEvent', 1), ('RightButtonPressEvent', 1), ('MiddleButtonPressEvent', 2), ('LeftButtonReleaseEvent', 1), ('MouseWheelForwardEvent', 3), ('MouseWheelBackwardEvent', 1), ('MiddleButtonReleaseEvent', 2), ('RightButtonReleaseEvent', 1)] # Useful loop for debugging. for event, count in expected: if states[event] != count: print("{}: {} vs. {} (expected)".format( event, states[event], count)) for event, count in expected: npt.assert_equal(states[event], count, err_msg=msg.format(event))
# Build an edge list of 3d lines df = pd.read_csv(graph_properties) node_cols = [s for s in list(df.columns) if isinstance(s, int) or any(c.isdigit() for c in s)] conn_matrix = np.load(conn_matrix_path) conn_matrix = normalize(conn_matrix) G = nx.from_numpy_array(conn_matrix) # Add adj. mat fig = plot_conn_mat(conn_matrix, label_names) fig.figure.set_size_inches(0.17,0.17) fig.figure.set_dpi(500) data = figure_to_image(fig.figure) plt_actor = set_input(vtk.vtkImageActor(), data) scene.add(plt_actor) # Get communities gamma = 1 [node_comm_aff_mat, q] = modularity_louvain_und_sign(conn_matrix, gamma=float(gamma * 0.0000001)) coord_node_mappings = {} node_coord_mappings = {} for i in G.nodes(): node_props = [k for k in node_cols if str(i) == k.split('_')[0]] coord = tuple([round(j, 0) for j in coords_vox[i]]) coord_node = namedtuple("coord", ["x", "y", "z"]) coord_node_mappings[coord_node(x=coord[0], y=coord[1], z=coord[2])] = i node_coord_mappings[i] = coord nx.set_node_attributes(G, {i: coords_vox[i]}, label_names[i])
def line(lines, colors=None, opacity=1, linewidth=1, spline_subdiv=None, lod=True, lod_points=10**4, lod_points_size=3, lookup_colormap=None): """ Create an actor for one or more lines. Parameters ------------ lines : list of arrays colors : array (N, 3), list of arrays, tuple (3,), array (K,), None If None then a standard orientation colormap is used for every line. If one tuple of color is used. Then all streamlines will have the same colour. If an array (N, 3) is given, where N is equal to the number of lines. Then every line is coloured with a different RGB color. If a list of RGB arrays is given then every point of every line takes a different color. If an array (K, ) is given, where K is the number of points of all lines then these are considered as the values to be used by the colormap. If an array (L, ) is given, where L is the number of streamlines then these are considered as the values to be used by the colormap per streamline. If an array (X, Y, Z) or (X, Y, Z, 3) is given then the values for the colormap are interpolated automatically using trilinear interpolation. opacity : float, optional Takes values from 0 (fully transparent) to 1 (opaque). Default is 1. linewidth : float, optional Line thickness. Default is 1. spline_subdiv : int, optional Number of splines subdivision to smooth streamtubes. Default is None which means no subdivision. lod : bool Use vtkLODActor(level of detail) rather than vtkActor. Default is True. Level of detail actors do not render the full geometry when the frame rate is low. lod_points : int Number of points to be used when LOD is in effect. Default is 10000. lod_points_size : int Size of points when lod is in effect. Default is 3. lookup_colormap : bool, optional Add a default lookup table to the colormap. Default is None which calls :func:`fury.actor.colormap_lookup_table`. Returns ---------- v : vtkActor or vtkLODActor object Line. Examples ---------- >>> from fury import actor, window >>> scene = window.Scene() >>> lines = [np.random.rand(10, 3), np.random.rand(20, 3)] >>> colors = np.random.rand(2, 3) >>> c = actor.line(lines, colors) >>> scene.add(c) >>> #window.show(scene) """ # Poly data with lines and colors poly_data, is_colormap = lines_to_vtk_polydata(lines, colors) next_input = poly_data # use spline interpolation if (spline_subdiv is not None) and (spline_subdiv > 0): spline_filter = set_input(vtk.vtkSplineFilter(), next_input) spline_filter.SetSubdivideToSpecified() spline_filter.SetNumberOfSubdivisions(spline_subdiv) spline_filter.Update() next_input = spline_filter.GetOutputPort() poly_mapper = set_input(vtk.vtkPolyDataMapper(), next_input) poly_mapper.ScalarVisibilityOn() poly_mapper.SetScalarModeToUsePointFieldData() poly_mapper.SelectColorArray("Colors") poly_mapper.Update() # Color Scale with a lookup table if is_colormap: if lookup_colormap is None: lookup_colormap = colormap_lookup_table() poly_mapper.SetLookupTable(lookup_colormap) poly_mapper.UseLookupTableScalarRangeOn() poly_mapper.Update() # Set Actor if lod: actor = vtk.vtkLODActor() actor.SetNumberOfCloudPoints(lod_points) actor.GetProperty().SetPointSize(lod_points_size) else: actor = vtk.vtkActor() # actor = vtk.vtkActor() actor.SetMapper(poly_mapper) actor.GetProperty().SetLineWidth(linewidth) actor.GetProperty().SetOpacity(opacity) return actor
def streamtube(lines, colors=None, opacity=1, linewidth=0.1, tube_sides=9, lod=True, lod_points=10**4, lod_points_size=3, spline_subdiv=None, lookup_colormap=None): """Use streamtubes to visualize polylines Parameters ---------- lines : list list of N curves represented as 2D ndarrays colors : array (N, 3), list of arrays, tuple (3,), array (K,), None If None then a standard orientation colormap is used for every line. If one tuple of color is used. Then all streamlines will have the same colour. If an array (N, 3) is given, where N is equal to the number of lines. Then every line is coloured with a different RGB color. If a list of RGB arrays is given then every point of every line takes a different color. If an array (K, ) is given, where K is the number of points of all lines then these are considered as the values to be used by the colormap. If an array (L, ) is given, where L is the number of streamlines then these are considered as the values to be used by the colormap per streamline. If an array (X, Y, Z) or (X, Y, Z, 3) is given then the values for the colormap are interpolated automatically using trilinear interpolation. opacity : float Takes values from 0 (fully transparent) to 1 (opaque). Default is 1. linewidth : float Default is 0.01. tube_sides : int Default is 9. lod : bool Use vtkLODActor(level of detail) rather than vtkActor. Default is True. Level of detail actors do not render the full geometry when the frame rate is low. lod_points : int Number of points to be used when LOD is in effect. Default is 10000. lod_points_size : int Size of points when lod is in effect. Default is 3. spline_subdiv : int Number of splines subdivision to smooth streamtubes. Default is None. lookup_colormap : vtkLookupTable Add a default lookup table to the colormap. Default is None which calls :func:`fury.actor.colormap_lookup_table`. Examples -------- >>> import numpy as np >>> from fury import actor, window >>> scene = window.Scene() >>> lines = [np.random.rand(10, 3), np.random.rand(20, 3)] >>> colors = np.random.rand(2, 3) >>> c = actor.streamtube(lines, colors) >>> scene.add(c) >>> #window.show(scene) Notes ----- Streamtubes can be heavy on GPU when loading many streamlines and therefore, you may experience slow rendering time depending on system GPU. A solution to this problem is to reduce the number of points in each streamline. In Dipy we provide an algorithm that will reduce the number of points on the straighter parts of the streamline but keep more points on the curvier parts. This can be used in the following way:: from dipy.tracking.distances import approx_polygon_track lines = [approx_polygon_track(line, 0.2) for line in lines] Alternatively we suggest using the ``line`` actor which is much more efficient. See Also -------- :func:`fury.actor.line` """ # Poly data with lines and colors poly_data, is_colormap = lines_to_vtk_polydata(lines, colors) next_input = poly_data # Set Normals poly_normals = set_input(vtk.vtkPolyDataNormals(), next_input) poly_normals.ComputeCellNormalsOn() poly_normals.ComputePointNormalsOn() poly_normals.ConsistencyOn() poly_normals.AutoOrientNormalsOn() poly_normals.Update() next_input = poly_normals.GetOutputPort() # Spline interpolation if (spline_subdiv is not None) and (spline_subdiv > 0): spline_filter = set_input(vtk.vtkSplineFilter(), next_input) spline_filter.SetSubdivideToSpecified() spline_filter.SetNumberOfSubdivisions(spline_subdiv) spline_filter.Update() next_input = spline_filter.GetOutputPort() # Add thickness to the resulting lines tube_filter = set_input(vtk.vtkTubeFilter(), next_input) tube_filter.SetNumberOfSides(tube_sides) tube_filter.SetRadius(linewidth) # TODO using the line above we will be able to visualize # streamtubes of varying radius # tube_filter.SetVaryRadiusToVaryRadiusByScalar() tube_filter.CappingOn() tube_filter.Update() next_input = tube_filter.GetOutputPort() # Poly mapper poly_mapper = set_input(vtk.vtkPolyDataMapper(), next_input) poly_mapper.ScalarVisibilityOn() poly_mapper.SetScalarModeToUsePointFieldData() poly_mapper.SelectColorArray("Colors") poly_mapper.Update() # Color Scale with a lookup table if is_colormap: if lookup_colormap is None: lookup_colormap = colormap_lookup_table() poly_mapper.SetLookupTable(lookup_colormap) poly_mapper.UseLookupTableScalarRangeOn() poly_mapper.Update() # Set Actor if lod: actor = vtk.vtkLODActor() actor.SetNumberOfCloudPoints(lod_points) actor.GetProperty().SetPointSize(lod_points_size) else: actor = vtk.vtkActor() actor.SetMapper(poly_mapper) actor.GetProperty().SetInterpolationToPhong() actor.GetProperty().BackfaceCullingOn() actor.GetProperty().SetOpacity(opacity) return actor
def contour_from_roi(data, affine=None, color=np.array([1, 0, 0]), opacity=1): """Generate surface actor from a binary ROI. The color and opacity of the surface can be customized. Parameters ---------- data : array, shape (X, Y, Z) An ROI file that will be binarized and displayed. affine : array, shape (4, 4) Grid to space (usually RAS 1mm) transformation matrix. Default is None. If None then the identity matrix is used. color : (1, 3) ndarray RGB values in [0,1]. opacity : float Opacity of surface between 0 and 1. Returns ------- contour_assembly : vtkAssembly ROI surface object displayed in space coordinates as calculated by the affine parameter. """ if data.ndim != 3: raise ValueError('Only 3D arrays are currently supported.') else: nb_components = 1 data = (data > 0) * 1 vol = np.interp(data, xp=[data.min(), data.max()], fp=[0, 255]) vol = vol.astype('uint8') im = vtk.vtkImageData() di, dj, dk = vol.shape[:3] im.SetDimensions(di, dj, dk) voxsz = (1., 1., 1.) # im.SetOrigin(0,0,0) im.SetSpacing(voxsz[2], voxsz[0], voxsz[1]) im.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, nb_components) # copy data vol = np.swapaxes(vol, 0, 2) vol = np.ascontiguousarray(vol) if nb_components == 1: vol = vol.ravel() else: vol = np.reshape(vol, [np.prod(vol.shape[:3]), vol.shape[3]]) uchar_array = numpy_support.numpy_to_vtk(vol, deep=0) im.GetPointData().SetScalars(uchar_array) if affine is None: affine = np.eye(4) # Set the transform (identity if none given) transform = vtk.vtkTransform() transform_matrix = vtk.vtkMatrix4x4() transform_matrix.DeepCopy( (affine[0][0], affine[0][1], affine[0][2], affine[0][3], affine[1][0], affine[1][1], affine[1][2], affine[1][3], affine[2][0], affine[2][1], affine[2][2], affine[2][3], affine[3][0], affine[3][1], affine[3][2], affine[3][3])) transform.SetMatrix(transform_matrix) transform.Inverse() # Set the reslicing image_resliced = vtk.vtkImageReslice() set_input(image_resliced, im) image_resliced.SetResliceTransform(transform) image_resliced.AutoCropOutputOn() # Adding this will allow to support anisotropic voxels # and also gives the opportunity to slice per voxel coordinates rzs = affine[:3, :3] zooms = np.sqrt(np.sum(rzs * rzs, axis=0)) image_resliced.SetOutputSpacing(*zooms) image_resliced.SetInterpolationModeToLinear() image_resliced.Update() skin_extractor = vtk.vtkContourFilter() skin_extractor.SetInputData(image_resliced.GetOutput()) skin_extractor.SetValue(0, 1) skin_normals = vtk.vtkPolyDataNormals() skin_normals.SetInputConnection(skin_extractor.GetOutputPort()) skin_normals.SetFeatureAngle(60.0) skin_mapper = vtk.vtkPolyDataMapper() skin_mapper.SetInputConnection(skin_normals.GetOutputPort()) skin_mapper.ScalarVisibilityOff() skin_actor = vtk.vtkActor() skin_actor.SetMapper(skin_mapper) skin_actor.GetProperty().SetOpacity(opacity) skin_actor.GetProperty().SetColor(color[0], color[1], color[2]) return skin_actor
def slicer(data, affine=None, value_range=None, opacity=1., lookup_colormap=None, interpolation='linear', picking_tol=0.025): """Cut 3D scalar or rgb volumes into 2D images. Parameters ---------- data : array, shape (X, Y, Z) or (X, Y, Z, 3) A grayscale or rgb 4D volume as a numpy array. affine : array, shape (4, 4) Grid to space (usually RAS 1mm) transformation matrix. Default is None. If None then the identity matrix is used. value_range : None or tuple (2,) If None then the values will be interpolated from (data.min(), data.max()) to (0, 255). Otherwise from (value_range[0], value_range[1]) to (0, 255). opacity : float, optional Opacity of 0 means completely transparent and 1 completely visible. lookup_colormap : vtkLookupTable If None (default) then a grayscale map is created. interpolation : string If 'linear' (default) then linear interpolation is used on the final texture mapping. If 'nearest' then nearest neighbor interpolation is used on the final texture mapping. picking_tol : float The tolerance for the vtkCellPicker, specified as a fraction of rendering window size. Returns ------- image_actor : ImageActor An object that is capable of displaying different parts of the volume as slices. The key method of this object is ``display_extent`` where one can input grid coordinates and display the slice in space (or grid) coordinates as calculated by the affine parameter. """ if data.ndim != 3: if data.ndim == 4: if data.shape[3] != 3: raise ValueError('Only RGB 3D arrays are currently supported.') else: nb_components = 3 else: raise ValueError('Only 3D arrays are currently supported.') else: nb_components = 1 if value_range is None: vol = np.interp(data, xp=[data.min(), data.max()], fp=[0, 255]) else: vol = np.interp(data, xp=[value_range[0], value_range[1]], fp=[0, 255]) vol = vol.astype('uint8') im = vtk.vtkImageData() I, J, K = vol.shape[:3] im.SetDimensions(I, J, K) voxsz = (1., 1., 1.) # im.SetOrigin(0,0,0) im.SetSpacing(voxsz[2], voxsz[0], voxsz[1]) im.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, nb_components) # copy data # what I do below is the same as what is commented here but much faster # for index in ndindex(vol.shape): # i, j, k = index # im.SetScalarComponentFromFloat(i, j, k, 0, vol[i, j, k]) vol = np.swapaxes(vol, 0, 2) vol = np.ascontiguousarray(vol) if nb_components == 1: vol = vol.ravel() else: vol = np.reshape(vol, [np.prod(vol.shape[:3]), vol.shape[3]]) uchar_array = numpy_support.numpy_to_vtk(vol, deep=0) im.GetPointData().SetScalars(uchar_array) if affine is None: affine = np.eye(4) # Set the transform (identity if none given) transform = vtk.vtkTransform() transform_matrix = vtk.vtkMatrix4x4() transform_matrix.DeepCopy( (affine[0][0], affine[0][1], affine[0][2], affine[0][3], affine[1][0], affine[1][1], affine[1][2], affine[1][3], affine[2][0], affine[2][1], affine[2][2], affine[2][3], affine[3][0], affine[3][1], affine[3][2], affine[3][3])) transform.SetMatrix(transform_matrix) transform.Inverse() # Set the reslicing image_resliced = vtk.vtkImageReslice() set_input(image_resliced, im) image_resliced.SetResliceTransform(transform) image_resliced.AutoCropOutputOn() # Adding this will allow to support anisotropic voxels # and also gives the opportunity to slice per voxel coordinates RZS = affine[:3, :3] zooms = np.sqrt(np.sum(RZS * RZS, axis=0)) image_resliced.SetOutputSpacing(*zooms) image_resliced.SetInterpolationModeToLinear() image_resliced.Update() ex1, ex2, ey1, ey2, ez1, ez2 = image_resliced.GetOutput().GetExtent() class ImageActor(vtk.vtkImageActor): def __init__(self): self.picker = vtk.vtkCellPicker() def input_connection(self, output): self.GetMapper().SetInputConnection(output.GetOutputPort()) self.output = output self.shape = (ex2 + 1, ey2 + 1, ez2 + 1) def display_extent(self, x1, x2, y1, y2, z1, z2): self.SetDisplayExtent(x1, x2, y1, y2, z1, z2) self.Update() def display(self, x=None, y=None, z=None): if x is None and y is None and z is None: self.display_extent(ex1, ex2, ey1, ey2, ez2 // 2, ez2 // 2) if x is not None: self.display_extent(x, x, ey1, ey2, ez1, ez2) if y is not None: self.display_extent(ex1, ex2, y, y, ez1, ez2) if z is not None: self.display_extent(ex1, ex2, ey1, ey2, z, z) def opacity(self, value): self.GetProperty().SetOpacity(value) def tolerance(self, value): self.picker.SetTolerance(value) def copy(self): im_actor = ImageActor() im_actor.input_connection(self.output) im_actor.SetDisplayExtent(*self.GetDisplayExtent()) im_actor.opacity(self.GetOpacity()) im_actor.tolerance(self.picker.GetTolerance()) if interpolation == 'nearest': im_actor.SetInterpolate(False) else: im_actor.SetInterpolate(True) im_actor.GetMapper().BorderOn() return im_actor image_actor = ImageActor() if nb_components == 1: lut = lookup_colormap if lookup_colormap is None: # Create a black/white lookup table. lut = colormap_lookup_table((0, 255), (0, 0), (0, 0), (0, 1)) plane_colors = vtk.vtkImageMapToColors() plane_colors.SetLookupTable(lut) plane_colors.SetInputConnection(image_resliced.GetOutputPort()) plane_colors.Update() image_actor.input_connection(plane_colors) else: image_actor.input_connection(image_resliced) image_actor.display() image_actor.opacity(opacity) image_actor.tolerance(picking_tol) if interpolation == 'nearest': image_actor.SetInterpolate(False) else: image_actor.SetInterpolate(True) image_actor.GetMapper().BorderOn() return image_actor
interactor_style = interactor.CustomInteractorStyle() show_manager = window.ShowManager(scene, size=(800, 800), reset_camera=False, interactor_style=interactor_style) # Create a cursor, a circle that will follow the mouse. polygon_source = vtk.vtkRegularPolygonSource() polygon_source.GeneratePolygonOff() # Only the outline of the circle. polygon_source.SetNumberOfSides(50) polygon_source.SetRadius(10) # polygon_source.SetRadius polygon_source.SetCenter(0, 0, 0) mapper = vtk.vtkPolyDataMapper2D() vtk_utils.set_input(mapper, polygon_source.GetOutputPort()) cursor = vtk.vtkActor2D() cursor.SetMapper(mapper) cursor.GetProperty().SetColor(1, 0.5, 0) scene.add(cursor) def follow_mouse(iren, obj): obj.SetPosition(*iren.event.position) iren.force_render() interactor_style.add_active_prop(cursor) interactor_style.add_callback(cursor, "MouseMoveEvent", follow_mouse) show_manager.start()