Пример #1
0
    def DoCreateResource(self):
        assert self.GetInstance() is None
        control_image = Image(parent=self.GetParentAsWindow(),
                              choices=ObservableList(),
                              id=self.GetID(),
                              pos=self.GetPosition(),
                              size=self.GetSize(),
                              style=self.GetStyle(),
                              name=self.GetName())
        self.SetupWindow(control_image)
        self.CreateChildren(control_image)

        return control_image
Пример #2
0
    def OnOpenClicked(self, event):
        """ Load the spectrum with either a reference spectrum or a computed histogram
        """

        # Create the image
        image = medipy.io.load(
            self._image_path)  #, 0, loader_class= nmr2D.Nmr2D)

        # Insert a reference spectrum into the image if one has been specified
        if self._reference_path is not None:
            spectrum = numpy.fromfile(self._reference_path, numpy.int32)
            image.metadata["header"]["proton_spectrum"] = spectrum

        # Load a list of annotations if an annotation file has been specified
        if self._annotations_path is not None:
            image.metadata["Data"] = image.data
            dom = md.parse(self._annotations_path)
            peaks = dom.getElementsByTagName("Peak2D")
            image.annotations = ObservableList()
            for peak in peaks:
                annotation = ImageAnnotation()
                ppm = (float(peak.getAttribute("F1")),
                       float(peak.getAttribute("F2")))
                point = rbnmr.ppm_to_point(ppm, image.metadata["Procs"],
                                           image.metadata["Proc2s"])
                annotation.position = [0, point[-2], point[-1]]
                annotation.label = peak.getAttribute("annotation")
                annotation.shape = ImageAnnotation.Shape.cross
                annotation.size = 10
                annotation.color = [0, 1., 0.]
                annotation.filled = False
                annotation.depth = 10
                image.annotations.append(annotation)

        self.GetParent().append_image([{"image": image}])

        # Close the window
        self.Destroy()
    def __call__(self):

        # Save the current value of the parameters
        #        self._save_parameters()

        # Build the expression namespace
        namespace = {"function": self._function}
        for parameter in self._parameters:
            if parameter.get("role", "") == "return":
                namespace[parameter["name"]] = None
            elif parameter.get("role", "") == "output":
                # Special case for images and 3D viewers
                if parameter["type"] == "Image" and self._controls[
                        parameter["name"]].output_checked:
                    dummy = Image(shape=(1, 1, 1), value=0, dtype=numpy.single)
                    namespace[parameter["name"]] = dummy
                elif parameter["type"] == "Object3D":
                    dummy = Object3D(name="New object")
                    namespace[parameter["name"]] = dummy
                else:
                    namespace[parameter["name"]] = self._controls[
                        parameter["name"]].value
            else:
                # Parameter will not be modified
                namespace[parameter["name"]] = self._controls[
                    parameter["name"]].value

        # Build the expression
        args = []
        return_values = []
        for parameter in self._parameters:
            if parameter.get("role", "") == "return":
                return_values.append(parameter["name"])
            else:
                args.append("{0} = {0}".format(parameter["name"]))
        expression = "function({0})".format(", ".join(args))
        if return_values:
            return_expression = ", ".join(return_values)
            expression = "{0} = {1}".format(return_expression, expression)

        # Execute it
        def f():
            exec expression in namespace

        periodic_progress_dialog = PeriodicProgressDialog(
            0.2, "Running %s ..." % self._function.func_name,
            "Running %s ..." % self._function.func_name)
        worker_thread = WorkerThread(periodic_progress_dialog, target=f)
        worker_thread.start()
        periodic_progress_dialog.start()
        worker_thread.join()
        periodic_progress_dialog.Destroy()

        if worker_thread.exception:
            wx.MessageBox(
                "Could not run function : {0}".format(worker_thread.exception),
                "Could not run function")
        else:
            # Update controls and application
            for parameter in self._parameters:
                if parameter.get("role", "") not in ["return", "output"]:
                    # Parameter has not been modified
                    continue
                else:
                    name = parameter["name"]
                    value = namespace[name]
                    control = self._controls[name]

                    # Special case for Image and Object3D
                    if parameter["type"] == "Image":
                        if control.output_checked:
                            self.notify_observers("new_image", image=value)
                        else:
                            self.notify_observers("replace_image",
                                                  old=control.value,
                                                  new=value)
                    elif parameter["type"] == "Object3D":
                        if control.output_checked:
                            viewer = Viewer3DFrame(parent=None,
                                                   objects_3d=ObservableList())
                            viewer.Show()
                            self.notify_observers("new_viewer_3d",
                                                  viewer=viewer)
                            control.value = self._viewer_3ds[-1]
                        viewer_3d = control.value
                        viewer_3d.objects_3d.append(value)
                        # If object has an associated image, set the GUI image
                        image = value.image
                        if image is not None:
                            self.notify_observers("set_image_to_object_3d",
                                                  image=image,
                                                  object_3d=value)
                        if len(viewer_3d.objects_3d) == 1:
                            viewer_3d.view_all()
                            viewer_3d.update_object_editor()

                    # In any case but Object3D (whose value is a Viewer3D),
                    # update the control
                    if parameter["type"] != "Object3D":
                        control.value = value
Пример #4
0
    def __init__(self, *args, **kwargs):

        wx.Panel.__init__(self, *args, **kwargs)
        Observable.__init__(self, ["active_object"])

        self._objects_3d = ObservableList()
        self._active_object = None

        # Currently pressed button, None if no button is pressed
        self._button = None

        # Bindings from mouse button to tool
        self.tools = {
            "LeftButton": viewer_3d_tools.Rotate(self),
            "MiddleButton": viewer_3d_tools.Pan(self),
            "RightButton": viewer_3d_tools.Dolly(self),
            "MouseWheelForward": viewer_3d_tools.WheelDolly(self, "forward"),
            "MouseWheelBackward": viewer_3d_tools.WheelDolly(self, "backward")
        }

        # Bindings from key to tool
        self.key_bindings = {
            # Nothing by default
        }

        # VTK objects for rendering
        self._renderer = vtkRenderer()
        self._renderer.SetBackground(82. / 255., 87. / 255., 110. / 255.)
        self._renderer.GetActiveCamera().ParallelProjectionOff()

        self._rwi = wxVTKRenderWindowInteractor(self, wx.ID_ANY)
        self._rwi.GetRenderWindow().AddRenderer(self._renderer)
        self._sizer = wx.BoxSizer()
        self._sizer.Add(self._rwi, 1, wx.EXPAND)
        self.SetSizer(self._sizer)

        # Interaction styles : customize events
        self._rwi.SetInteractorStyle(None)

        self._rwi.AddObserver("LeftButtonPressEvent", self._start_interaction)
        self._rwi.AddObserver("MiddleButtonPressEvent",
                              self._start_interaction)
        self._rwi.AddObserver("RightButtonPressEvent", self._start_interaction)

        self._rwi.AddObserver("LeftButtonReleaseEvent", self._stop_interaction)
        self._rwi.AddObserver("MiddleButtonReleaseEvent",
                              self._stop_interaction)
        self._rwi.AddObserver("RightButtonReleaseEvent",
                              self._stop_interaction)

        self._rwi.AddObserver("MouseWheelForwardEvent",
                              self._start_interaction)
        self._rwi.AddObserver("MouseWheelBackwardEvent",
                              self._start_interaction)

        self._rwi.AddObserver("MouseMoveEvent", self._dispatch_interaction)

        self._rwi.AddObserver("KeyPressEvent", self._keypress)
        self._rwi.AddObserver("KeyReleaseEvent", self._keyrelease)

        # Clean up when the window is closed
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        self.objects_3d.add_observer("any", self._on_objects_3d_modified)
Пример #5
0
class Viewer3D(wx.Panel, Observable):
    def __init__(self, *args, **kwargs):

        wx.Panel.__init__(self, *args, **kwargs)
        Observable.__init__(self, ["active_object"])

        self._objects_3d = ObservableList()
        self._active_object = None

        # Currently pressed button, None if no button is pressed
        self._button = None

        # Bindings from mouse button to tool
        self.tools = {
            "LeftButton": viewer_3d_tools.Rotate(self),
            "MiddleButton": viewer_3d_tools.Pan(self),
            "RightButton": viewer_3d_tools.Dolly(self),
            "MouseWheelForward": viewer_3d_tools.WheelDolly(self, "forward"),
            "MouseWheelBackward": viewer_3d_tools.WheelDolly(self, "backward")
        }

        # Bindings from key to tool
        self.key_bindings = {
            # Nothing by default
        }

        # VTK objects for rendering
        self._renderer = vtkRenderer()
        self._renderer.SetBackground(82. / 255., 87. / 255., 110. / 255.)
        self._renderer.GetActiveCamera().ParallelProjectionOff()

        self._rwi = wxVTKRenderWindowInteractor(self, wx.ID_ANY)
        self._rwi.GetRenderWindow().AddRenderer(self._renderer)
        self._sizer = wx.BoxSizer()
        self._sizer.Add(self._rwi, 1, wx.EXPAND)
        self.SetSizer(self._sizer)

        # Interaction styles : customize events
        self._rwi.SetInteractorStyle(None)

        self._rwi.AddObserver("LeftButtonPressEvent", self._start_interaction)
        self._rwi.AddObserver("MiddleButtonPressEvent",
                              self._start_interaction)
        self._rwi.AddObserver("RightButtonPressEvent", self._start_interaction)

        self._rwi.AddObserver("LeftButtonReleaseEvent", self._stop_interaction)
        self._rwi.AddObserver("MiddleButtonReleaseEvent",
                              self._stop_interaction)
        self._rwi.AddObserver("RightButtonReleaseEvent",
                              self._stop_interaction)

        self._rwi.AddObserver("MouseWheelForwardEvent",
                              self._start_interaction)
        self._rwi.AddObserver("MouseWheelBackwardEvent",
                              self._start_interaction)

        self._rwi.AddObserver("MouseMoveEvent", self._dispatch_interaction)

        self._rwi.AddObserver("KeyPressEvent", self._keypress)
        self._rwi.AddObserver("KeyReleaseEvent", self._keyrelease)

        # Clean up when the window is closed
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        self.objects_3d.add_observer("any", self._on_objects_3d_modified)

    def view_all(self):
        self.reset_camera()
        self._rwi.Render()

    def get_bounds(self):
        # get the bounding box of all of the objects of the current scene (visible or not)
        bounds = []

        for _object in self._objects_3d:
            actor = _object.actor
            bounds.append(actor.GetBounds())

        Xmin = 0
        Xmax = 0
        Ymin = 0
        Ymax = 0
        Zmin = 0
        Zmax = 0

        if len(bounds) > 0:
            Xmin = min(map(lambda x: x[0], bounds))
            Xmax = max(map(lambda x: x[1], bounds))
            Ymin = min(map(lambda x: x[2], bounds))
            Ymax = max(map(lambda x: x[3], bounds))
            Zmin = min(map(lambda x: x[4], bounds))
            Zmax = max(map(lambda x: x[5], bounds))

        return ((Xmin, Ymin, Zmin), (Xmax, Ymax, Zmax))

    def _get_distance(self):
        """get the greatest dimension of the enclosing bounding box of all the element in the scene\
           (visible or not)
        """

        Pmin, Pmax = self.get_bounds()

        w1 = Pmax[0] - Pmin[0]
        w2 = Pmax[1] - Pmin[1]
        w3 = Pmax[2] - Pmin[2]

        return max(w1, w2, w3)

    def reset_camera(self):
        # view angle
        view_angle = 53.13
        self._renderer.GetActiveCamera().SetViewAngle(view_angle)

        Pmin, Pmax = self.get_bounds()

        # target : focal point
        focal_point = numpy.add(Pmin, Pmax) / 2.

        distance = self._get_distance()

        position = self._renderer.GetActiveCamera().GetPosition()
        v = numpy.subtract(position, focal_point)

        norme = numpy.linalg.norm(v)

        remoteness = 1.5
        factor = (distance * remoteness) / norme
        new_pos = numpy.add(numpy.multiply(v, factor), focal_point)

        # update the camera ...
        self._renderer.GetActiveCamera().SetFocalPoint(*focal_point)

        self._renderer.GetActiveCamera().SetPosition(new_pos)

        self._renderer.ResetCameraClippingRange(Pmin[0], Pmax[0], Pmin[1],
                                                Pmax[1], Pmin[2], Pmax[2])

    ##############
    # Properties #
    ##############

    def _set_objects_3d(self, objects_3d):
        collection = self._renderer.GetViewProps()
        collection.InitTraversal()
        for i in range(collection.GetNumberOfItems()):
            actor = collection.GetNextProp()
            self._renderer.RemoveActor(actor)

        self._objects_3d = objects_3d
        for objet in self.objects_3d:
            self._renderer.AddActor(objet.actor)

        self._objects_3d.remove_observer("any", self._on_objects_3d_modified)
        self._objects_3d = objects_3d
        self.objects_3d.add_observer("any", self._on_objects_3d_modified)

    def _set_active_object(self, object):
        if object not in self.objects_3d:
            raise medipy.base.Exception("Object is not in viewer's objects")

        self._active_object = object
        self.notify_observers("active_object")

    objects_3d = property(lambda x: x._objects_3d, _set_objects_3d)
    active_object = property(lambda x: x._active_object, _set_active_object)
    renderer = property(lambda x: x._renderer)
    render_window_interactor = property(lambda x: x._rwi)

    ##################
    # Event handlers #
    ##################

    def OnClose(self, event):
        self._renderer.RemoveAllViewProps()
        del self._renderer
        self._rwi.GetRenderWindow().Finalize()
        self._rwi.SetRenderWindow(None)
        self._rwi.Close()
        del self._rwi
        self.Destroy()

    def _on_objects_3d_modified(self, event):
        if event.event == "append":
            for objet in self.objects_3d:
                if not self._renderer.HasViewProp(objet.actor):
                    self._renderer.AddActor(objet.actor)
        elif event.event == "delete_item":
            self._renderer.RemoveActor(event.old_value.actor)
        else:
            raise medipy.base.Exception("Unmanaged event : %s" % event.event)

        self._rwi.Render()

    def _start_interaction(self, object, event):
        if event.startswith("MouseWheel"):
            wheel_event = event[:-len("Event")]
            if wheel_event in self.tools:
                self.tools[wheel_event].start_interaction()
                self.tools[wheel_event].stop_interaction()
        elif event.endswith("PressEvent"):
            button = event[:-len("PressEvent")]

            event = button
            if object.GetControlKey():
                event += "Control"
            if object.GetShiftKey():
                event += "Shift"

            # In case no modifier is configured, fall back to the basic behavior
            if event not in self.tools:
                event = button

            if event in self.tools:
                self._button = button
                self.tools[event].start_interaction()

    def _stop_interaction(self, object, event):
        button = event[:-len("ReleaseEvent")]

        event = self._button
        if object.GetControlKey():
            event += "Control"
        if object.GetShiftKey():
            event += "Shift"

        for key, tool in self.tools.items():
            if key.startswith(button):
                tool.stop_interaction()

        self._button = None

    def _dispatch_interaction(self, object, event):
        event = self._button

        if event is None:
            return

        if object.GetControlKey():
            event += "Control"
        if object.GetShiftKey():
            event += "Shift"

        # In case no modifier is configured, fall back to the basic behavior
        if event not in self.tools:
            event = self._button
        if event in self.tools:
            self.tools[event].dispatch_interaction()

    def _keypress(self, object, event):
        ordinal = ord(object.GetKeyCode())
        if ordinal in self.key_bindings:
            self.key_bindings[ordinal].press()

    def _keyrelease(self, object, event):
        ordinal = ord(object.GetKeyCode())
        if ordinal in self.key_bindings:
            self.key_bindings[ordinal].release()

    #####################
    # Private interface #
    #####################

    def _update_actors(self):
        actors_from_renderer = []
        collection = self._renderer.GetViewProps()
        collection.InitTraversal()
        for i in range(collection.GetNumberOfItems()):
            actor = collection.GetNextProp()
            actors_from_renderer.append(actor)

        actors_from_objects = [object.actor for object in self.objects_3d]

        for actor in actors_from_renderer:
            if actor not in actors_from_objects:
                self._renderer.RemoveActor(actor)

        for object in self.objects_3d:
            if not self._renderer.HasViewProp(object.actor):
                self._renderer.AddActor(object.actor)
Пример #6
0
    def __init__(self,
                 parent,
                 slice_mode="multiplanar",
                 layers=None,
                 annotations=None,
                 interpolation=False,
                 display_coordinates="physical",
                 scalar_bar_visibility=False,
                 orientation_visibility=True,
                 corner_annotations_visibility=False,
                 convention="radiological",
                 crosshair="full",
                 *args,
                 **kwargs):

        if annotations is None:
            annotations = ObservableList()

        ##############
        # Properties #
        ##############

        self._slice_mode = None

        self._interpolation = None
        self._display_coordinates = None
        self._scalar_bar_visibility = None
        self._orientation_visibility = None
        self._convention = None
        self._crosshair = None
        self._corner_annotations_visibility = None

        self._annotations = None

        self._cursor_physical_position = None
        self._cursor_index_position = None

        self._center_physical_position = None
        self._center_index_position = None

        self._zoom = None
        self._mouse_tools = {}
        self._keyboard_tools = {}

        ###################
        # Private members #
        ###################
        self._rwi = None
        self._layers = ObservableList()
        self._slices = []
        self._slices_names = []
        self._informations_renderer = vtkRenderer()
        self._informations_renderer.SetViewport(
            *self._viewport["informations"])

        self._informations_corner_annotation = vtkCornerAnnotation()
        self._informations_corner_annotation.SetNonlinearFontScaleFactor(0.3)
        self._informations_renderer.AddActor(
            self._informations_corner_annotation)

        ##################
        # Initialization #
        ##################

        wx.Panel.__init__(self, parent, *args, **kwargs)
        # Explicitely pass size to RWI to propagate it correctly
        self._rwi = wxVTKRenderWindowInteractor(self,
                                                wx.ID_ANY,
                                                size=self.GetSize())
        self._rwi.Enable(1)
        sizer = wx.BoxSizer()
        sizer.Add(self._rwi, 1, wx.EXPAND | wx.ALL, 3)
        self.SetSizer(sizer)
        self._rwi.Bind(wx.EVT_LEFT_DOWN, self._on_button_down)
        self._rwi.Bind(wx.EVT_MIDDLE_DOWN, self._on_button_down)
        self._rwi.Bind(wx.EVT_RIGHT_DOWN, self._on_button_down)
        self._rwi.Bind(wx.EVT_MOUSEWHEEL, self._on_button_down)
        self.Bind(wx.EVT_CLOSE, self._on_close)

        PropertySynchronized.__init__(self, [
            "interpolation", "display_coordinates", "scalar_bar_visibility",
            "orientation_visibility", "zoom"
        ])
        self.add_allowed_event("cursor_position")
        self.add_allowed_event("image_position")
        self.add_allowed_event("center")
        self.add_allowed_event("layer_visibility")
        for event in [
                "data", "display_range", "cut_low", "cut_high",
                "zero_transparency"
        ]:
            self.add_allowed_event("colormap_{0}".format(event))
#        self.add_allowed_event("layer_modified")
#        self.add_allowed_event("activated")

        self._set_interpolation(interpolation)
        self._set_display_coordinates(display_coordinates)
        self._set_scalar_bar_visibility(scalar_bar_visibility)
        self._set_orientation_visibility(orientation_visibility)
        self._set_convention(convention)
        self._set_crosshair(crosshair)
        self._set_corner_annotations_visibility(corner_annotations_visibility)
        for layer in layers or []:
            self.append_layer(**layer)
        self._set_slice_mode(slice_mode)

        self._set_annotations(annotations)

        # Position image at middle of layer 0
        self.reset_view()
Пример #7
0
class Image(wx.Panel, PropertySynchronized):
    """ Synchronized representation of several slices with several layers.
        
        When passed to the constructor, layers are specified by a dictionary
        containing the following keys :
        
        * `"image"` : a :class:`medipy.base.Image`, mandatory
        * `"colormap"` : a :class:`medipy.gui.Colormap`, defaults to None
        * `"opacity"` : a scalar between `0.0` and `1.0`, defaults to `1.0`
        
        For example an image with two layers, the bottom one in grayscale and 
        the top one in rainbow can be created as follows : ::
        
            import medipy.io
            import medipy.gui
            import medipy.gui.image
            
            anatomical = medipy.io.load("/usr/share/data/fsl-mni152-templates/MNI152_T1_1mm.nii.gz")
            atlas = medipy.io.load("/usr/share/fsl/data/atlases/MNI/MNI-maxprob-thr0-1mm.nii.gz")
            
            colormap = medipy.gui.Colormap(medipy.gui.colormaps["rainbow"], 
                None, zero_transparency=True)
            
            layers = [
                {"image": anatomical},
                {"image": atlas, "colormap": colormap, "opacity": 0.5}
            ]
            
            image = medipy.gui.image.Image(parent, layers=layers)
    """

    _viewport = {
        "axial": (0.0, 0.0, 0.5, 0.5),
        "coronal": (0.0, 0.5, 0.5, 1.0),
        "sagittal": (0.5, 0.5, 1.0, 1.0),
        "informations": (0.5, 0.0, 1.0, 0.5)
    }

    # Index of location for vtkCornerAnnotation
    _corner_annotation_index = {
        "up_left": 2,
        "up_right": 3,
        "down_left": 0,
        "down_right": 1
    }

    def __init__(self,
                 parent,
                 slice_mode="multiplanar",
                 layers=None,
                 annotations=None,
                 interpolation=False,
                 display_coordinates="physical",
                 scalar_bar_visibility=False,
                 orientation_visibility=True,
                 corner_annotations_visibility=False,
                 convention="radiological",
                 crosshair="full",
                 *args,
                 **kwargs):

        if annotations is None:
            annotations = ObservableList()

        ##############
        # Properties #
        ##############

        self._slice_mode = None

        self._interpolation = None
        self._display_coordinates = None
        self._scalar_bar_visibility = None
        self._orientation_visibility = None
        self._convention = None
        self._crosshair = None
        self._corner_annotations_visibility = None

        self._annotations = None

        self._cursor_physical_position = None
        self._cursor_index_position = None

        self._center_physical_position = None
        self._center_index_position = None

        self._zoom = None
        self._mouse_tools = {}
        self._keyboard_tools = {}

        ###################
        # Private members #
        ###################
        self._rwi = None
        self._layers = ObservableList()
        self._slices = []
        self._slices_names = []
        self._informations_renderer = vtkRenderer()
        self._informations_renderer.SetViewport(
            *self._viewport["informations"])

        self._informations_corner_annotation = vtkCornerAnnotation()
        self._informations_corner_annotation.SetNonlinearFontScaleFactor(0.3)
        self._informations_renderer.AddActor(
            self._informations_corner_annotation)

        ##################
        # Initialization #
        ##################

        wx.Panel.__init__(self, parent, *args, **kwargs)
        # Explicitely pass size to RWI to propagate it correctly
        self._rwi = wxVTKRenderWindowInteractor(self,
                                                wx.ID_ANY,
                                                size=self.GetSize())
        self._rwi.Enable(1)
        sizer = wx.BoxSizer()
        sizer.Add(self._rwi, 1, wx.EXPAND | wx.ALL, 3)
        self.SetSizer(sizer)
        self._rwi.Bind(wx.EVT_LEFT_DOWN, self._on_button_down)
        self._rwi.Bind(wx.EVT_MIDDLE_DOWN, self._on_button_down)
        self._rwi.Bind(wx.EVT_RIGHT_DOWN, self._on_button_down)
        self._rwi.Bind(wx.EVT_MOUSEWHEEL, self._on_button_down)
        self.Bind(wx.EVT_CLOSE, self._on_close)

        PropertySynchronized.__init__(self, [
            "interpolation", "display_coordinates", "scalar_bar_visibility",
            "orientation_visibility", "zoom"
        ])
        self.add_allowed_event("cursor_position")
        self.add_allowed_event("image_position")
        self.add_allowed_event("center")
        self.add_allowed_event("layer_visibility")
        for event in [
                "data", "display_range", "cut_low", "cut_high",
                "zero_transparency"
        ]:
            self.add_allowed_event("colormap_{0}".format(event))
#        self.add_allowed_event("layer_modified")
#        self.add_allowed_event("activated")

        self._set_interpolation(interpolation)
        self._set_display_coordinates(display_coordinates)
        self._set_scalar_bar_visibility(scalar_bar_visibility)
        self._set_orientation_visibility(orientation_visibility)
        self._set_convention(convention)
        self._set_crosshair(crosshair)
        self._set_corner_annotations_visibility(corner_annotations_visibility)
        for layer in layers or []:
            self.append_layer(**layer)
        self._set_slice_mode(slice_mode)

        self._set_annotations(annotations)

        # Position image at middle of layer 0
        self.reset_view()

    def append_layer(self, *args, **kwargs):
        """ Append a new layer.
        """

        self.insert_layer(len(self._layers), *args, **kwargs)

    def insert_layer(self, index, image, colormap=None, opacity=1.0):
        """ Insert a new layer at specified position. The colormap defaults to
            a gray colormap. If the colormap's display range is None, it 
            defaults to the image range.
        """

        if colormap is None:
            colormap = Colormap(colormaps["gray"], None, False, False, False)

        if colormap.display_range is None:
            colormap.display_range = (image.data.min(), image.data.max())

        for slice in self._slices:
            slice.insert_layer(index, image, colormap, opacity)

        if len(self._slices) > 1:
            for event in [
                    "data", "display_range", "cut_low", "cut_high",
                    "zero_transparency"
            ]:
                self._slices[0].layers[index].colormap.add_observer(
                    event, self._on_colormap_event)
            for slice_index, slice in enumerate(self._slices):
                layer = slice.layers[index]
                next_slice_layer = self._slices[(slice_index + 1) % len(
                    self._slices)].layers[index]
                layer.colormap.append_child(next_slice_layer.colormap)

        # Update self._layers late to be sure that slices are correctly set-up
        # before observers get notified
        self._layers.insert(index, {
            "image": image,
            "colormap": colormap,
            "opacity": opacity
        })

    def delete_layer(self, index):
        """ Remove a layer from the list.
        """

        for event in [
                "data", "display_range", "cut_low", "cut_high",
                "zero_transparency"
        ]:
            self._slices[0].layers[index].colormap.remove_observer(
                event, self._on_colormap_event)

        del self._layers[index]
        for slice in self._slices:
            slice.delete_layer(index)

    def get_layer_visibility(self, index):
        return self._slices[0].get_layer_visibility(index)

    def set_layer_visibility(self, index, visibility):
        for slice in self._slices:
            slice.set_layer_visibility(index, visibility)

    def get_layer_image(self, index):
        return self._layers[index]["image"]

    def set_layer_image(self, index, image):
        self._layers[index]["image"] = image
        for slice in self._slices:
            slice.layers[index].image = image

    def get_layer_colormap(self, index):
        return self._layers[index]["colormap"]

    def set_layer_colormap(self, index, colormap):
        self._layers[index]["colormap"] = colormap
        for slice in self._slices:
            slice.layers[index].colormap = colormap

    def get_layer_opacity(self, index):
        return self._layers[index]["opacity"]

    def set_layer_opacity(self, index, opacity):
        self._layers[index]["opacity"] = opacity
        for slice in self._slices:
            slice.layers[index].opacity = opacity

    def get_layer_class(self, index):
        return self._slices[0].layers[index].__class__

    def get_layer_property(self, index, name):
        return getattr(self._slices[0].layers[index], name)

    def set_layer_property(self, index, name, value):
        for slice in self._slices:
            setattr(slice.layers[index], name, value)

    def render(self):
        self._rwi.Render()

    def reset_view(self):
        for slice in self._slices:
            slice.reset_view()

        self.notify_observers("center")

    def get_mouse_button_tool(self, button):
        """ Return a triplet (ToolClass, args, kwargs)
        """

        return self._mouse_tools[button]

    def set_mouse_button_tool(self, button, tool_class, *args, **kwargs):
        """ Set a tool associated with given button (Left, Middle, or Right),
            with an optional modifier (Shift or Control). Example : Right,
            ShiftLeft. Set tool to None to have no tool connected to the button.
        """

        self._mouse_tools[button] = (tool_class, args, kwargs)

        for slice in self._slices:
            if tool_class:
                tool = tool_class(*args, **kwargs)
            else:
                tool = None
            slice.set_mouse_button_tool(button, tool)

    def get_wheel_tool(self, direction):
        """ Return the tool associated with a mouse wheel direction (Forward or 
            Backward)
        """

        event_name = "MouseWheel%s" % direction
        return self._mouse_tools[event_name]

    def set_wheel_tool(self, direction, tool_class, *args, **kwargs):
        """ Set a tool associated with a mouse wheel direction (Forward or 
            Backward)
        """

        self._mouse_tools[direction] = (tool_class, args, kwargs)

        for slice in self._slices:
            if tool_class:
                tool = tool_class(*args, **kwargs)
            else:
                tool = None
            slice.set_wheel_tool(direction, tool)

    def get_keyboard_tool(self, key):
        return self._keyboard_tools[key]

    def set_keyboard_tool(self, key, tool_class, *args, **kwargs):
        self._keyboard_tools[key] = (tool_class, args, kwargs)

        for slice in self._slices:
            if tool_class:
                tool = tool_class(*args, **kwargs)
            else:
                tool = None
            slice.set_keyboard_tool(key, tool)

    def set_next_window_info(self, info):
        """ Remap the VTK (and underlying) windows. This has to be called when
            reparenting.
        """

        self._rwi.GetRenderWindow().SetNextWindowInfo(info)
        self._rwi.GetRenderWindow().WindowRemap()

    ##############
    # Properties #
    ##############

    def _get_layers(self):
        return self._layers

    def _get_slice_mode(self):
        return self._slice_mode

    def _set_slice_mode(self, slice_mode):
        if slice_mode not in ["axial", "coronal", "sagittal", "multiplanar"]:
            raise medipy.base.Exception("Unknown slice mode : %s" %
                                        (slice_mode, ))

        old_cursor_position = (numpy.copy(self.cursor_index_position)
                               if self.cursor_index_position is not None else
                               None)

        old_slice_mode = self._slice_mode
        self._slice_mode = slice_mode

        for slice in self._slices:
            self._rwi.GetRenderWindow().RemoveRenderer(slice.renderer)
            slice.unset_rwi(self._rwi)

        if old_slice_mode == "multiplanar" and slice_mode != "multiplanar":
            self._rwi.GetRenderWindow().RemoveRenderer(
                self._informations_renderer)

        self._slices = []
        self._slices_names = []

        names = (["axial", "coronal", "sagittal"]
                 if slice_mode == "multiplanar" else [slice_mode])

        # Build and configure the Slice objects
        for name in names:
            if self._display_coordinates == "index":
                # Do not use radiological or neurological slices
                convention = "index"
            else:
                convention = self._convention

            world_to_slice = medipy.base.coordinate_system.slices[convention][
                name]
            slice = Slice(world_to_slice, self._layers, self._annotations,
                          self._interpolation, self._display_coordinates,
                          self._scalar_bar_visibility,
                          self._orientation_visibility,
                          slice_mode != "multiplanar", self.crosshair)

            if slice_mode == "multiplanar":
                slice.renderer.SetViewport(*self._viewport[name])
            else:
                slice.renderer.SetViewport(0., 0., 1., 1.)
            self._rwi.GetRenderWindow().AddRenderer(slice.renderer)
            slice.setup_rwi(self._rwi)
            self._slices.append(slice)
            self._slices_names.append(name)

        for index, slice in enumerate(self._slices):
            for event in slice.allowed_events:
                if event in [
                        "any", "cursor_position", "image_position", "center",
                        "corner_annotations_visibility", "world_to_slice",
                        "layer_visibility"
                ]:
                    continue
                slice.add_observer(event, self._on_slice_event)

            slice.add_observer("cursor_position", self._on_cursor_position)
            slice.add_observer("center", self._on_center)
            slice.add_observer("layer_visibility", self._on_layer_visibility)

            # Synchronize the Slices' colormaps
            # Do not use PropertySynchronized API for better event handling
            # and thus better performances
            if len(self._slices) > 1:
                next_slice = self._slices[(index + 1) % len(self._slices)]

                for layer_index, layer in enumerate(slice.layers):
                    for event in [
                            "data", "display_range", "cut_low", "cut_high",
                            "zero_transparency"
                    ]:
                        layer.colormap.add_observer(event,
                                                    self._on_colormap_event)
                    next_slice_layer = next_slice.layers[layer_index]
                    layer.colormap.append_child(next_slice_layer.colormap)

        # Add a 4th renderer with image info in multiplanar mode, otherwise
        # display the corner annotations
        if slice_mode == "multiplanar":
            self._rwi.GetRenderWindow().AddRenderer(
                self._informations_renderer)
            self._informations_corner_annotation.SetVisibility(
                self.corner_annotations_visibility)
        else:
            self._slices[
                0].corner_annotations_visibility = self.corner_annotations_visibility

        for button, (class_, args, kwargs) in self._mouse_tools.items():
            self.set_mouse_button_tool(button, class_, *args, **kwargs)
        for key, (class_, args, kwargs) in self._keyboard_tools.items():
            self.set_keyboard_tool(key, class_, *args, **kwargs)

        # Keep the same pixel under the cursor and centered in the view
        self._locked = True
        if old_cursor_position is not None:
            self._set_cursor_index_position(old_cursor_position)
        self._locked = False

        self._update_informations()

    def _get_number_of_layers(self):
        return len(self._layers)

    def _get_interpolation(self):
        return self._interpolation

    def _set_interpolation(self, interpolation):
        self._set_slice_property("interpolation", interpolation)

    def _get_display_coordinates(self):
        return self._display_coordinates

    def _set_display_coordinates(self, display_coordinates):
        if display_coordinates not in [
                "physical", "nearest_axis_aligned", "index"
        ]:
            raise medipy.base.Exception("Unknown display coordinates : %s" %
                                        (display_coordinates, ))

        self._set_slice_property("display_coordinates", display_coordinates)

        # Update world_to_slice, necessary when moving to/from index
        if self._display_coordinates == "index":
            # Do not use radiological or neurological slices
            convention = "index"
        else:
            convention = self._convention

        for index, slice in enumerate(self._slices):
            name = self._slices_names[index]
            world_to_slice = medipy.base.coordinate_system.slices[convention][
                name]
            slice.world_to_slice = world_to_slice

    def _get_scalar_bar_visibility(self):
        return self._scalar_bar_visibility

    def _set_scalar_bar_visibility(self, scalar_bar_visibility):
        self._set_slice_property("scalar_bar_visibility",
                                 scalar_bar_visibility)

    def _get_orientation_visibility(self):
        return self._orientation_visibility

    def _set_orientation_visibility(self, orientation_visibility):
        self._set_slice_property("orientation_visibility",
                                 orientation_visibility)

    def _get_convention(self):
        """ Image viewing convention, can be either :
              * radiological (left-is-right)
              * neurological (left-is-left)
        """

        return self._convention

    def _set_convention(self, convention):
        if convention not in ["radiological", "neurological"]:
            raise medipy.base.Exception(
                "Unknown viewing convention : {0}".format(convention))
        self._convention = convention

        if self._display_coordinates == "index":
            # Do not use radiological or neurological slices
            convention = "index"

        for index, slice in enumerate(self._slices):
            name = self._slices_names[index]
            world_to_slice = medipy.base.coordinate_system.slices[convention][
                name]
            slice.world_to_slice = world_to_slice

    def _get_crosshair(self):
        """ Display mode of the crosshair.
        """

        return self._crosshair

    def _set_crosshair(self, value):
        if value not in ["full", "partial", "none"]:
            raise medipy.base.Exception(
                "Unknown crosshair mode: {0!r}".format(value))

        self._set_slice_property("crosshair", value)

    def _get_corner_annotations_visibility(self):
        """ Visibility of the corner annotations.
        """

        return self._corner_annotations_visibility

    def _set_corner_annotations_visibility(self, value):
        self._corner_annotations_visibility = value
        if self._slice_mode == "multiplanar":
            self._informations_corner_annotation.SetVisibility(value)
        else:
            self._set_slice_property("corner_annotations_visibility", value)

    def _get_annotations(self):
        return self._annotations

    def _set_annotations(self, annotations):

        if self._annotations is not None:
            self._annotations.remove_observer("any",
                                              self._on_annotations_changed)

        self._annotations = annotations
        self._annotations.add_observer("any", self._on_annotations_changed)

        for slice in self._slices:
            slice.annotations = annotations

    def _get_cursor_physical_position(self):
        return self._cursor_physical_position

    def _set_cursor_physical_position(self, cursor_physical_position):
        self._cursor_physical_position = cursor_physical_position
        if self._layers:
            image = self._layers[0]["image"]
            self._cursor_index_position = image.physical_to_index(
                cursor_physical_position)
        else:
            self._cursor_index_position = cursor_physical_position

        if self._slices:
            # All slices are synchronized
            self._slices[0].cursor_physical_position = cursor_physical_position

        self._rwi.Render()

        self.notify_observers("cursor_position")

    def _get_cursor_index_position(self):
        return self._cursor_index_position

    def _set_cursor_index_position(self, cursor_index_position):
        if self._layers:
            image = self._layers[0]["image"]
            cursor_physical_position = image.index_to_physical(
                cursor_index_position)
        else:
            cursor_physical_position = cursor_index_position

        self._set_cursor_physical_position(cursor_physical_position)

    def _get_center_physical_position(self):
        if self._center_physical_position is not None:
            return self._center_physical_position
        elif self._slices:
            self._center_physical_position = self._slices[
                0].image_physical_position
            return self._center_physical_position
        else:
            return None

    def _set_center_physical_position(self, position):
        self._center_physical_position = position
        if self._layers:
            image = self._layers[0]["image"]
            self._center_index_position = image.physical_to_index(position)
        else:
            self._center_index_position = cursor_physical_position

        if self._slices:
            # All slices are synchronized
            self._slices[0].center_on_physical_position(position)

        self._rwi.Render()

        self.notify_observers("cursor_position")

    def _get_center_index_position(self):
        if self._center_index_position is not None:
            return self._center_index_position
        elif self._slices:
            return self._slices[0].image_index_position
        else:
            return None

    def _set_center_index_position(self, position):
        if self._layers:
            image = self._layers[0]["image"]
            center_physical_position = image.index_to_physical(position)
        else:
            center_physical_position = position

        self._set_center_physical_position(center_physical_position)

    def _get_zoom(self):
        return self._zoom

    def _set_zoom(self, zoom):
        self._set_slice_property("zoom", zoom)

    layers = property(_get_layers)
    slice_mode = property(_get_slice_mode, _set_slice_mode)
    number_of_layers = property(_get_number_of_layers)
    interpolation = property(_get_interpolation, _set_interpolation)
    display_coordinates = property(_get_display_coordinates,
                                   _set_display_coordinates)
    scalar_bar_visibility = property(_get_scalar_bar_visibility,
                                     _set_scalar_bar_visibility)
    orientation_visibility = property(_get_orientation_visibility,
                                      _set_orientation_visibility)
    convention = property(_get_convention, _set_convention)
    crosshair = property(_get_crosshair, _set_crosshair)
    corner_annotations_visibility = property(
        _get_corner_annotations_visibility, _set_corner_annotations_visibility)
    annotations = property(_get_annotations, _set_annotations)
    cursor_physical_position = property(_get_cursor_physical_position,
                                        _set_cursor_physical_position)
    cursor_index_position = property(_get_cursor_index_position,
                                     _set_cursor_index_position)
    center_physical_position = property(_get_center_physical_position,
                                        _set_center_physical_position)
    center_index_position = property(_get_center_index_position,
                                     _set_center_index_position)
    zoom = property(_get_zoom, _set_zoom)

    #####################
    # Private interface #
    #####################

    def _set_slice_property(self, name, value):
        """ Set a property across all slices, and notify observers.
        """

        setattr(self, "_{0}".format(name), value)

        for slice in self._slices:
            slice.remove_observer(name, self._on_slice_event)
        for slice in self._slices:
            setattr(slice, name, value)
        for slice in self._slices:
            slice.add_observer(name, self._on_slice_event)

        self._rwi.Render()

        self.notify_observers(name)

    def _on_slice_event(self, event):

        # Don't use _set_xxx to avoid spurious events
        value = getattr(event.object, event.event)
        setattr(self, "_%s" % event.event, value)

        for slice in self._slices:
            slice.remove_observer(event.event, self._on_slice_event)

        for slice in self._slices:
            if slice != event.object:
                setattr(slice, event.event, value)
        self.notify_observers(event.event)

        for slice in self._slices:
            slice.add_observer(event.event, self._on_slice_event)

        if event.event == "zoom":
            self._update_informations()

    def _on_colormap_event(self, event):
        if self._slices:
            try:
                layers_colormaps = [x["colormap"] for x in self._layers]
                layer_index = layers_colormaps.index(event.object)
            except ValueError:
                logging.warning("No such colormap in layers {0}".format(self))
            else:
                self.notify_observers("colormap_{0}".format(event.event),
                                      layer_index=layer_index)

    def _on_cursor_position(self, event):
        self._cursor_index_position = event.object.cursor_index_position
        self._cursor_physical_position = event.object.cursor_physical_position

        for slice in self._slices:
            slice.remove_observer(event.event, self._on_cursor_position)

        for slice in self._slices:
            if slice != event.object:
                slice.cursor_physical_position = self._cursor_physical_position
        self.notify_observers(event.event)

        for slice in self._slices:
            slice.add_observer(event.event, self._on_cursor_position)

        self._update_informations()

    def _on_annotations_changed(self, event):
        self.render()

    def _on_center(self, event):

        self._center_index_position = event.object.image_index_position
        self._center_physical_position = event.object.image_physical_position

        for slice in self._slices:
            slice.remove_observer(event.event, self._on_center)

        for slice in self._slices:
            if slice != event.object:
                slice.center_on_physical_position(
                    self._cursor_physical_position)
        self.notify_observers(event.event)

        for slice in self._slices:
            slice.add_observer(event.event, self._on_center)

        self._update_informations()

    def _on_layer_visibility(self, event):
        self.notify_observers("layer_visibility", index=event.index)

    def _on_button_down(self, event):
        """ Propagate mouse clicks to parent.
        """

        event.Skip()
        new_event = event.Clone()
        new_event.SetEventObject(self)
        self.AddPendingEvent(new_event)

    def _on_close(self, event):
        for slice in self._slices:
            slice.close()
            slice.unset_rwi(self._rwi)
        self._rwi.Disable()
        self._rwi.Close()
        self.Destroy()

    def _update_informations(self):
        informations = get_informations(self)

        if self._slice_mode == "multiplanar":
            for where, label in informations.items():
                index = self._corner_annotation_index[where]
                self._informations_corner_annotation.SetText(index, label)
        else:
            for where, label in informations.items():
                self._slices[0].set_label(where, label)
Пример #8
0
    app = wx.App()

    frame = wx.Frame(None)
    app.SetTopWindow(frame)

    ok_button = wx.Button(frame, id=wx.ID_OK, label="OK")
    reset_button = wx.Button(frame, id=wx.ID_RESET, label="Reset")

    ok_button.Bind(
        wx.EVT_BUTTON,
        lambda event: sys.stdout.write("Value is %s (%s)\n" %
                                       (control.value, "valid"
                                        if control.validate() else "invalid")))
    reset_button.Bind(wx.EVT_BUTTON, lambda event: control.reset())

    scenes = ObservableList()
    for i in range(0):
        scenes.append(Viewer3DFrame(None, ObservableList()))

    control = Object3D(
        frame,
        scenes,
        #                    value="machin",
        output_checked=False)

    sizer = wx.BoxSizer(wx.VERTICAL)

    sizer.Add(control, flag=wx.EXPAND)

    button_sizer = wx.BoxSizer(wx.HORIZONTAL)
    button_sizer.Add(ok_button)
Пример #9
0
    def load_metadata(self, index=0):
        format = NiftiFormat(self._filename)

        metadata = {}

        # Get the number of dimensions in the image from the extent
        nb_dimensions = len(format.extent)
        for i in range(max(len(format.extent) - 3, 0)):
            if format.extent[-1] == 1:
                nb_dimensions -= 1
            else:
                break
        # Get spacing and origin for those dimensions. Reverse to keep the order
        # of the numpy array
        metadata["spacing"] = format.asDict()["pixdim"][1:nb_dimensions + 1]
        metadata["spacing"].reverse()
        metadata["spacing"] = numpy.asarray(metadata["spacing"])

        if format.asDict()["scl_slope"] != 0:
            metadata["slope"] = format.asDict()["scl_slope"]
            metadata["shift"] = format.asDict()["scl_inter"]

        metadata["header"] = format.asDict()
        metadata["annotations"] = ObservableList()

        #########################
        # Diffusion information #
        #########################

        # Load gradient direction file
        base_name = os.path.splitext(self._filename)[0]
        if base_name.endswith(".nii"):
            base_name = os.path.splitext(base_name)[0]
        gradient_candidates = [
            base_name + ".bvecs",  # /foo/bar/image.bvecs
            base_name + ".bvec",  # /foo/bar/image.bvec
            os.path.join(os.path.dirname(self._filename),
                         "bvecs"),  # /foo/bar/bvecs
            os.path.join(os.path.dirname(self._filename),
                         "bvec")  # /foo/bar/bvec
        ]
        gradient_file = None
        for candidate in gradient_candidates:
            if os.path.isfile(candidate):
                gradient_file = candidate
                break

        # Load b-values file
        bvalue_candidates = [
            base_name + ".bvals",  # /foo/bar/image.bvals
            base_name + ".bval",  # /foo/bar/image.bval
            os.path.join(os.path.dirname(self._filename),
                         "bval"),  # /foo/bar/bvals
            os.path.join(os.path.dirname(self._filename),
                         "bvals")  # /foo/bar/bval
        ]
        bvalue_file = None
        for candidate in bvalue_candidates:
            if os.path.isfile(candidate):
                bvalue_file = candidate
                break

        if None not in [gradient_file, bvalue_file]:
            gradients = numpy.loadtxt(gradient_file, dtype=numpy.single)
            bvalues = numpy.loadtxt(bvalue_file, dtype=numpy.single)

            gradients = gradients.T

            mr_diffusion_sequence = []
            for index, gradient in enumerate(gradients):
                dataset = medipy.io.dicom.DataSet()
                dataset.diffusion_directionality = "DIRECTIONAL"

                dataset.diffusion_bvalue = bvalues[index]

                gradient_dataset = medipy.io.dicom.DataSet()
                gradient_dataset.diffusion_gradient_orientation = gradient
                dataset.diffusion_gradient_direction_sequence = [
                    gradient_dataset
                ]

                mr_diffusion_sequence.append(dataset)

            metadata["mr_diffusion_sequence"] = mr_diffusion_sequence

        return metadata
Пример #10
0
    def __init__(self,
                 world_to_slice,
                 layers=None,
                 annotations=None,
                 interpolation=False,
                 display_coordinates="physical",
                 scalar_bar_visibility=False,
                 orientation_visibility=True,
                 corner_annotations_visibility=False,
                 crosshair="full"):

        layers = layers or []
        annotations = annotations or ObservableList()

        ############################
        # Property-related members #
        ############################

        self._interpolation = None
        self._display_coordinates = None
        self._scalar_bar_visibility = True
        self._orientation_visibility = None
        self._corner_annotations_visibility = None
        self._crosshair = None

        self._world_to_slice = None

        self._slice_to_world = None

        self._layers = []

        self._annotations = None
        self._gui_annotations = {}

        self._image_physical_position = None
        self._image_index_position = None

        self._cursor_physical_position = None
        self._cursor_index_position = None

        self._zoom = None

        self._mouse_tools = {}
        self._keyboard_tools = {}

        self._renderer = vtkRenderer()

        ###################
        # Private members #
        ###################

        # World-to-slice matrix, with rows and columns added or removed so that
        # it is 3x3.
        self._3d_world_to_slice = None
        self._3d_slice_to_world = None

        # Slice extent is the physical extent of all layers,
        # given as (x_min, x_max, y_min, y_max)
        self._slice_extent = (-100, 100, -100, 100)

        # VTK objects
        self._scalar_bar_actor = vtkScalarBarActor()
        self._corner_annotation = vtkCornerAnnotation()
        self._orientation_annotation = vtkOrientationAnnotation()
        self._crosshair = Crosshair()

        # Tools and interactions
        self._observer_tags = []
        self._active_source = None

        ##################
        # Initialization #
        ##################

        super(Slice, self).__init__([
            "world_to_slice", "interpolation", "display_coordinates",
            "scalar_bar_visibility", "orientation_visibility",
            "corner_annotations_visibility", "crosshair", "zoom"
        ])
        self.add_allowed_event("cursor_position")
        self.add_allowed_event("image_position")
        self.add_allowed_event("center")
        self.add_allowed_event("layer_visibility")

        # Configure camera
        camera = self._renderer.GetActiveCamera()
        camera.ParallelProjectionOn()
        camera.SetPosition(0, 0, self._actors_altitudes["camera"])
        camera.SetFocalPoint(0, 0, 0)

        # Create cursor
        self._crosshair.altitude = self._actors_altitudes["cursor"]
        self._crosshair.hole_size = 5
        self._renderer.AddActor(self._crosshair.actor)

        # Create scalar bar (from vtkInria3D)
        self._scalar_bar_actor.GetLabelTextProperty().SetColor(1.0, 1.0, 1.0)
        self._scalar_bar_actor.GetTitleTextProperty().SetColor(1.0, 1.0, 1.0)
        self._scalar_bar_actor.GetLabelTextProperty().BoldOff()
        self._scalar_bar_actor.GetLabelTextProperty().ShadowOff()
        self._scalar_bar_actor.GetLabelTextProperty().ItalicOff()
        self._scalar_bar_actor.SetNumberOfLabels(3)
        self._scalar_bar_actor.GetLabelTextProperty().SetFontSize(8)
        self._scalar_bar_actor.GetPositionCoordinate(
        ).SetCoordinateSystemToNormalizedViewport()
        self._scalar_bar_actor.SetWidth(0.1)
        self._scalar_bar_actor.SetHeight(0.5)
        self._scalar_bar_actor.SetPosition(0.8, 0.3)
        self._scalar_bar_actor.PickableOff()
        self._renderer.AddActor(self._scalar_bar_actor)

        # Setup text-annotation actors
        self._corner_annotation.SetNonlinearFontScaleFactor(0.3)
        self._renderer.AddActor(self._corner_annotation)
        self._orientation_annotation.SetNonlinearFontScaleFactor(0.25)
        self._renderer.AddActor(self._orientation_annotation)

        self._set_interpolation(interpolation)
        self._set_display_coordinates(display_coordinates)

        self._set_scalar_bar_visibility(scalar_bar_visibility)
        self._set_orientation_visibility(orientation_visibility)
        self._set_corner_annotations_visibility(corner_annotations_visibility)
        self._set_crosshair(crosshair)

        self._set_world_to_slice(world_to_slice)

        for layer in layers:
            self.append_layer(**layer)

        if annotations is not None:
            self._set_annotations(annotations)

        # Position slice at middle of layer 0
        self.reset_view()

        # Configure default tools
        self.set_mouse_button_tool("Left", mouse_tools.Select())
        self.set_mouse_button_tool("Middle", mouse_tools.Pan())
        self.set_mouse_button_tool("Right", mouse_tools.WindowLevel())
        self.set_wheel_tool("Forward", mouse_tools.Zoom(1.1))
        self.set_wheel_tool("Backward", mouse_tools.Zoom(1. / 1.1))
        self.set_keyboard_tool("Left", keyboard_tools.MoveCursor())
        self.set_keyboard_tool("Right", keyboard_tools.MoveCursor())
        self.set_keyboard_tool("Up", keyboard_tools.MoveCursor())
        self.set_keyboard_tool("Down", keyboard_tools.MoveCursor())
        self.set_keyboard_tool("Prior", keyboard_tools.MoveCursor())
        self.set_keyboard_tool("Next", keyboard_tools.MoveCursor())
        self.set_keyboard_tool("PageUp", keyboard_tools.MoveCursor())
        self.set_keyboard_tool("PageDown", keyboard_tools.MoveCursor())
        self.set_keyboard_tool("+", keyboard_tools.Zoom(1.1))
        self.set_keyboard_tool("-", keyboard_tools.Zoom(1. / 1.1))
        self.set_keyboard_tool("i", keyboard_tools.ToggleInterpolation())
        self.set_keyboard_tool("b", keyboard_tools.ToggleScalarBarVisibility())
        self.set_keyboard_tool(
            "c", keyboard_tools.ToggleCornerAnnotationsVisibility())
        self.set_keyboard_tool("o",
                               keyboard_tools.ToggleOrientationVisibility())