From 3749eea2ab80ccbef0693c465311bd973c4551e6 Mon Sep 17 00:00:00 2001 From: Thiago Franco de Moraes Date: Tue, 23 Aug 2016 15:02:14 -0300 Subject: [PATCH] Starting to select part to new mask --- invesalius/constants.py | 4 ++++ invesalius/data/slice_.py | 16 ++++++++++++++++ invesalius/data/styles.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ invesalius/gui/data_notebook.py | 10 +++++----- invesalius/gui/frame.py | 22 ++++++++++++++-------- 5 files changed, 149 insertions(+), 13 deletions(-) diff --git a/invesalius/constants.py b/invesalius/constants.py index c9fe27e..bc92fbd 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -483,6 +483,7 @@ ID_CLEAN_MASK = wx.NewId() ID_REORIENT_IMG = wx.NewId() ID_FLOODFILL_MASK = wx.NewId() ID_REMOVE_MASK_PART = wx.NewId() +ID_SELECT_MASK_PART = wx.NewId() #--------------------------------------------------------- STATE_DEFAULT = 1000 @@ -502,6 +503,7 @@ SLICE_STATE_WATERSHED = 3009 SLICE_STATE_REORIENT = 3010 SLICE_STATE_MASK_FFILL = 3011 SLICE_STATE_REMOVE_MASK_PARTS = 3012 +SLICE_STATE_SELECT_MASK_PARTS = 3013 VOLUME_STATE_SEED = 2001 # STATE_LINEAR_MEASURE = 3001 @@ -521,6 +523,7 @@ SLICE_STYLES.append(SLICE_STATE_EDITOR) SLICE_STYLES.append(SLICE_STATE_WATERSHED) SLICE_STYLES.append(SLICE_STATE_MASK_FFILL) SLICE_STYLES.append(SLICE_STATE_REMOVE_MASK_PARTS) +SLICE_STYLES.append(SLICE_STATE_SELECT_MASK_PARTS) VOLUME_STYLES = TOOL_STATES + [VOLUME_STATE_SEED, STATE_MEASURE_DISTANCE, STATE_MEASURE_ANGLE] @@ -531,6 +534,7 @@ STYLE_LEVEL = {SLICE_STATE_EDITOR: 1, SLICE_STATE_WATERSHED: 1, SLICE_STATE_MASK_FFILL: 2, SLICE_STATE_REMOVE_MASK_PARTS: 2, + SLICE_STATE_SELECT_MASK_PARTS: 2, SLICE_STATE_CROSS: 2, SLICE_STATE_SCROLL: 2, SLICE_STATE_REORIENT: 2, diff --git a/invesalius/data/slice_.py b/invesalius/data/slice_.py index 1adead7..1903129 100644 --- a/invesalius/data/slice_.py +++ b/invesalius/data/slice_.py @@ -1282,6 +1282,22 @@ class Slice(object): op, m1, m2 = pubsub_evt.data self.do_boolean_op(op, m1, m2) + def create_new_mask(self, show=True): + last_name = self.current_mask.name + + proj = Project() + mask_dict = proj.mask_dict + names_list = [mask_dict[i].name for i in mask_dict.keys()] + new_name = utils.next_copy_name(last_name, names_list) + + future_mask = Mask() + future_mask.create_mask(self.matrix.shape) + future_mask.name = new_name + + self._add_mask_into_proj(future_mask, show=show) + + return future_mask + def do_boolean_op(self, op, m1, m2): name_ops = {const.BOOLEAN_UNION: _(u"Union"), const.BOOLEAN_DIFF: _(u"Diff"), diff --git a/invesalius/data/styles.py b/invesalius/data/styles.py index d22295b..a41531b 100644 --- a/invesalius/data/styles.py +++ b/invesalius/data/styles.py @@ -1928,6 +1928,115 @@ class RemoveMaskPartsInteractorStyle(FloodFillMaskInteractorStyle): self._progr_msg = _(u"Removing part ...") +class SelectPartConfig(object): + __metaclass__= utils.Singleton + def __init__(self): + self.matrix = None + self.con_3d = 6 + + +class SelectMaskPartsInteractorStyle(DefaultInteractorStyle): + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + self.orientation = self.viewer.orientation + + self.picker = vtk.vtkWorldPointPicker() + self.slice_actor = viewer.slice_data.actor + self.slice_data = viewer.slice_data + + self.config = SelectPartConfig() + self.dlg_ffill = None + + self.t0 = 254 + self.t1 = 255 + self.fill_value = 254 + + self.AddObserver("LeftButtonPressEvent", self.OnSelect) + + def CleanUp(self): + self.config.matrix = None + + def OnSelect(self, obj, evt): + if (self.viewer.slice_.buffer_slices[self.orientation].mask is None): + return + + viewer = self.viewer + iren = viewer.interactor + + mouse_x, mouse_y = iren.GetEventPosition() + render = iren.FindPokedRenderer(mouse_x, mouse_y) + slice_data = viewer.get_slice_data(render) + + self.picker.Pick(mouse_x, mouse_y, 0, render) + + coord = self.get_coordinate_cursor() + position = slice_data.actor.GetInput().FindPoint(coord) + + if position != -1: + coord = slice_data.actor.GetInput().GetPoint(position) + + if position < 0: + position = viewer.calculate_matrix_position(coord) + + mask = self.viewer.slice_.current_mask.matrix[1:, 1:, 1:] + x, y, z = self.calcultate_scroll_position(position) + + bstruct = np.array(generate_binary_structure(3, CON3D[self.config.con_3d]), dtype='uint8') + self.viewer.slice_.do_threshold_to_all_slices() + + if self.config.matrix is None: + self._create_new_mask() + + floodfill.floodfill_threshold(mask, [[x, y, z]], self.t0, self.t1, self.fill_value, bstruct, self.config.matrix[1:, 1:, 1:]) + + def _create_new_mask(self): + mask = self.viewer.slice_.create_new_mask(show=False) + mask.was_edited = True + mask.matrix[0, :, :] = 1 + mask.matrix[:, 0, :] = 1 + mask.matrix[:, :, 0] = 1 + + self.config.matrix = mask.matrix + + def get_coordinate_cursor(self): + # Find position + x, y, z = self.picker.GetPickPosition() + bounds = self.viewer.slice_data.actor.GetBounds() + if bounds[0] == bounds[1]: + x = bounds[0] + elif bounds[2] == bounds[3]: + y = bounds[2] + elif bounds[4] == bounds[5]: + z = bounds[4] + return x, y, z + + def calcultate_scroll_position(self, position): + # Based in the given coord (x, y, z), returns a list with the scroll positions for each + # orientation, being the first position the sagital, second the coronal + # and the last, axial. + + if self.orientation == 'AXIAL': + image_width = self.slice_actor.GetInput().GetDimensions()[0] + axial = self.slice_data.number + coronal = position / image_width + sagital = position % image_width + + elif self.orientation == 'CORONAL': + image_width = self.slice_actor.GetInput().GetDimensions()[0] + axial = position / image_width + coronal = self.slice_data.number + sagital = position % image_width + + elif self.orientation == 'SAGITAL': + image_width = self.slice_actor.GetInput().GetDimensions()[1] + axial = position / image_width + coronal = position % image_width + sagital = self.slice_data.number + + return sagital, coronal, axial + def get_style(style): STYLES = { const.STATE_DEFAULT: DefaultInteractorStyle, @@ -1945,6 +2054,7 @@ def get_style(style): const.SLICE_STATE_REORIENT: ReorientImageInteractorStyle, const.SLICE_STATE_MASK_FFILL: FloodFillMaskInteractorStyle, const.SLICE_STATE_REMOVE_MASK_PARTS: RemoveMaskPartsInteractorStyle, + const.SLICE_STATE_SELECT_MASK_PARTS: SelectMaskPartsInteractorStyle, } return STYLES[style] diff --git a/invesalius/gui/data_notebook.py b/invesalius/gui/data_notebook.py index a63ee53..59f3d62 100644 --- a/invesalius/gui/data_notebook.py +++ b/invesalius/gui/data_notebook.py @@ -512,11 +512,11 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): self.SetStringItem(index, 1, label, imageId=self.mask_list_index[index]) self.SetStringItem(index, 2, threshold) - self.SetItemImage(index, 1) - for key in self.mask_list_index.keys(): - if key != index: - self.SetItemImage(key, 0) - self.current_index = index + # self.SetItemImage(index, 1) + # for key in self.mask_list_index.keys(): + # if key != index: + # self.SetItemImage(key, 0) + # self.current_index = index def AddMask(self, pubsub_evt): index, mask_name, threshold_range, colour = pubsub_evt.data diff --git a/invesalius/gui/frame.py b/invesalius/gui/frame.py index 4d54aed..3089316 100644 --- a/invesalius/gui/frame.py +++ b/invesalius/gui/frame.py @@ -451,20 +451,16 @@ class Frame(wx.Frame): elif id == const.ID_REMOVE_MASK_PART: self.OnRemoveMaskParts() + elif id == const.ID_SELECT_MASK_PART: + self.OnSelectMaskParts() + elif id == const.ID_VIEW_INTERPOLATED: - st = self.actived_interpolated_slices.IsChecked(const.ID_VIEW_INTERPOLATED) - if st: self.OnInterpolatedSlices(True) else: self.OnInterpolatedSlices(False) - def OnInterpolatedSlices(self, status): - - Publisher.sendMessage('Set interpolated slices', status) - - def OnSize(self, evt): """ Refresh GUI when frame is resized. @@ -594,6 +590,12 @@ class Frame(wx.Frame): def OnRemoveMaskParts(self): Publisher.sendMessage('Enable style', const.SLICE_STATE_REMOVE_MASK_PARTS) + def OnSelectMaskParts(self): + Publisher.sendMessage('Enable style', const.SLICE_STATE_SELECT_MASK_PARTS) + + def OnInterpolatedSlices(self, status): + Publisher.sendMessage('Set interpolated slices', status) + # ------------------------------------------------------------------ # ------------------------------------------------------------------ # ------------------------------------------------------------------ @@ -615,7 +617,8 @@ class MenuBar(wx.MenuBar): const.ID_PROJECT_CLOSE, const.ID_REORIENT_IMG, const.ID_FLOODFILL_MASK, - const.ID_REMOVE_MASK_PART,] + const.ID_REMOVE_MASK_PART, + const.ID_SELECT_MASK_PART,] self.__init_items() self.__bind_events() @@ -733,6 +736,9 @@ class MenuBar(wx.MenuBar): self.remove_mask_part_menu = mask_menu.Append(const.ID_REMOVE_MASK_PART, _(u"Remove parts")) self.remove_mask_part_menu.Enable(False) + self.select_mask_part_menu = mask_menu.Append(const.ID_SELECT_MASK_PART, _(u"Select parts")) + self.select_mask_part_menu.Enable(False) + tools_menu.AppendMenu(-1, _(u"Mask"), mask_menu) # Image menu -- libgit2 0.21.2