From bf2722383d09caf040fd9becd1bab29662a6ab97 Mon Sep 17 00:00:00 2001 From: Thiago Franco de Moraes Date: Tue, 10 Jun 2014 11:08:21 -0300 Subject: [PATCH] Applying threshold before starting watershed mode --- invesalius/data/slice_.py | 20 ++++++++++++++++++-- invesalius/data/styles.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- invesalius/data/viewer_slice.py | 9 ++++++++- invesalius/gui/task_slice.py | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 225 insertions(+), 10 deletions(-) diff --git a/invesalius/data/slice_.py b/invesalius/data/slice_.py index ee15f62..eb9ae4b 100644 --- a/invesalius/data/slice_.py +++ b/invesalius/data/slice_.py @@ -107,6 +107,7 @@ class Slice(object): self.from_ = OTHER self.__bind_events() + self.opacity = 0.8 self.qblend = {'AXIAL': {}, 'CORONAL': {}, 'SAGITAL': {}} @@ -1181,6 +1182,19 @@ class Slice(object): m[mask == 254] = 254 return m.astype('uint8') + def do_threshold_to_all_slices(self): + mask = self.current_mask + + # This is very important. Do not use masks' imagedata. It would mess up + # surface quality event when using contour + #self.SetMaskThreshold(mask.index, threshold) + for n in xrange(1, mask.matrix.shape[0]): + if mask.matrix[n, 0, 0] == 0: + m = mask.matrix[n, 1:, 1:] + mask.matrix[n, 1:, 1:] = self.do_threshold_to_a_slice(self.matrix[n-1], m) + + mask.matrix.flush() + def do_colour_image(self, imagedata): if self.from_ in (PLIST, WIDGET): return imagedata @@ -1216,8 +1230,10 @@ class Slice(object): lut_mask.SetNumberOfTableValues(256) lut_mask.SetTableValue(0, 0, 0, 0, 0.0) lut_mask.SetTableValue(1, 0, 0, 0, 0.0) - lut_mask.SetTableValue(254, r, g, b, 1.0) - lut_mask.SetTableValue(255, r, g, b, 1.0) + lut_mask.SetTableValue(2, 0, 0, 0, 0.0) + lut_mask.SetTableValue(253, r, g, b, self.opacity) + lut_mask.SetTableValue(254, r, g, b, self.opacity) + lut_mask.SetTableValue(255, r, g, b, self.opacity) lut_mask.SetRampToLinear() lut_mask.Build() # self.lut_mask = lut_mask diff --git a/invesalius/data/styles.py b/invesalius/data/styles.py index 3444828..8d42f8e 100644 --- a/invesalius/data/styles.py +++ b/invesalius/data/styles.py @@ -674,11 +674,17 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): self.AddObserver("EnterEvent", self.OnEnterInteractor) self.AddObserver("LeaveEvent", self.OnLeaveInteractor) + self.RemoveObservers("MouseWheelForwardEvent") + self.RemoveObservers("MouseWheelBackwardEvent") + self.AddObserver("MouseWheelForwardEvent",self.WOnScrollForward) + self.AddObserver("MouseWheelBackwardEvent", self.WOnScrollBackward) + self.AddObserver("LeftButtonPressEvent", self.OnBrushClick) self.AddObserver("LeftButtonReleaseEvent", self.OnBrushRelease) self.AddObserver("MouseMoveEvent", self.OnBrushMove) def SetUp(self): + self.viewer.slice_.do_threshold_to_all_slices() mask = self.viewer.slice_.current_mask.matrix mask[0] = 1 mask[:, 0, :] = 1 @@ -687,6 +693,7 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): def CleanUp(self): self._remove_mask() + self.viewer.slice_.qblend[self.orientation] = {} def _create_mask(self): if self.matrix is None: @@ -711,6 +718,35 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) self.viewer.interactor.Render() + def WOnScrollBackward(self, obj, evt): + viewer = self.viewer + iren = viewer.interactor + if iren.GetControlKey(): + if viewer.slice_.opacity > 0: + viewer.slice_.opacity -= 0.1 + self.viewer.slice_.buffer_slices['AXIAL'].discard_vtk_mask() + self.viewer.slice_.buffer_slices['CORONAL'].discard_vtk_mask() + self.viewer.slice_.buffer_slices['SAGITAL'].discard_vtk_mask() + viewer.OnScrollBar() + else: + self.OnScrollBackward(obj, evt) + + + def WOnScrollForward(self, obj, evt): + viewer = self.viewer + iren = viewer.interactor + print "AUIQ" + if iren.GetControlKey(): + if viewer.slice_.opacity < 1: + viewer.slice_.opacity += 0.1 + self.viewer.slice_.buffer_slices['AXIAL'].discard_vtk_mask() + self.viewer.slice_.buffer_slices['CORONAL'].discard_vtk_mask() + self.viewer.slice_.buffer_slices['SAGITAL'].discard_vtk_mask() + viewer.OnScrollBar() + else: + self.OnScrollForward(obj, evt) + + def OnBrushClick(self, obj, evt): if (self.viewer.slice_.buffer_slices[self.orientation].mask is None): return @@ -873,11 +909,17 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): wl = self.viewer.slice_.window_level #tmp_image = get_LUT_value(image, ww, wl).astype('uint16') - tmp_image = ndimage.morphological_gradient((image - image.min()).astype('uint16'), 5) + tmp_image = ndimage.morphological_gradient(get_LUT_value(image, ww, wl).astype('uint16'), 5) print tmp_image.dtype, tmp_image.min(), tmp_image.max() tmp_mask = watershed(tmp_image, markers) - mask[:] = 0 - mask[tmp_mask == 1] = 255 + + if self.viewer.overwrite_mask: + mask[:] = 0 + mask[tmp_mask == 1] = 253 + else: + mask[(tmp_mask==2) & ((mask == 0) | (mask == 2) | (mask == 253))] = 2 + mask[(tmp_mask==1) & ((mask == 0) | (mask == 2) | (mask == 253))] = 253 + self.viewer._flush_buffer = True self.viewer.OnScrollBar(update3D=False) @@ -942,14 +984,14 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): index = index[abs(yi):,:] yi = 0 if yf > mask.shape[0]: - index = index[:index.shape[0]-(yf-image.shape[0]), :] + index = index[:index.shape[0]-(yf-mask.shape[0]), :] yf = mask.shape[0] if xi < 0: index = index[:,abs(xi):] xi = 0 if xf > mask.shape[1]: - index = index[:,:index.shape[1]-(xf-image.shape[1])] + index = index[:,:index.shape[1]-(xf-mask.shape[1])] xf = mask.shape[1] # Verifying if the points is over the image array. diff --git a/invesalius/data/viewer_slice.py b/invesalius/data/viewer_slice.py index dff54a4..951406f 100755 --- a/invesalius/data/viewer_slice.py +++ b/invesalius/data/viewer_slice.py @@ -166,6 +166,8 @@ class Viewer(wx.Panel): self.last_position_mouse_move = () self.state = const.STATE_DEFAULT + self.overwrite_mask = False + # All renderers and image actors in this viewer self.slice_data_list = [] self.slice_data = None @@ -751,6 +753,8 @@ class Viewer(wx.Panel): Publisher.subscribe(self.OnSetMIPInvert, 'Set MIP Invert %s' % self.orientation) Publisher.subscribe(self.OnShowMIPInterface, 'Show MIP interface') + Publisher.subscribe(self.OnSetOverwriteMask, "Set overwrite mask") + def SetDefaultCursor(self, pusub_evt): self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) @@ -1247,7 +1251,10 @@ class Viewer(wx.Panel): self.mip_ctrls.Hide() self.GetSizer().Remove(self.mip_ctrls) self.Layout() - + + def OnSetOverwriteMask(self, pubsub_evt): + value = pubsub_evt.data + self.overwrite_mask = value def set_slice_number(self, index): inverted = self.mip_ctrls.inverted.GetValue() diff --git a/invesalius/gui/task_slice.py b/invesalius/gui/task_slice.py index c46056c..f2e7628 100644 --- a/invesalius/gui/task_slice.py +++ b/invesalius/gui/task_slice.py @@ -228,7 +228,7 @@ class InnerFoldPanel(wx.Panel): # parent panel. Perhaps we need to insert the item into the sizer also... # Study this. fold_panel = fpb.FoldPanelBar(self, -1, wx.DefaultPosition, - (10, 190), 0,fpb.FPB_SINGLE_FOLD) + (10, 220), 0,fpb.FPB_SINGLE_FOLD) # Fold panel style style = fpb.CaptionBarStyle() @@ -706,6 +706,156 @@ class EditionTools(wx.Panel): class WatershedTool(EditionTools): - pass + def __init__(self, parent): + wx.Panel.__init__(self, parent, size=(50,240)) + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + self.SetBackgroundColour(default_colour) + + ## LINE 1 + text1 = wx.StaticText(self, -1, _("Choose brush type, size or operation:")) + + ## LINE 2 + menu = wx.Menu() + + CIRCLE_BMP = wx.Bitmap("../icons/brush_circle.jpg", wx.BITMAP_TYPE_JPEG) + item = wx.MenuItem(menu, MENU_BRUSH_CIRCLE, _("Circle")) + item.SetBitmap(CIRCLE_BMP) + + SQUARE_BMP = wx.Bitmap("../icons/brush_square.jpg", wx.BITMAP_TYPE_JPEG) + item2 = wx.MenuItem(menu, MENU_BRUSH_SQUARE, _("Square")) + item2.SetBitmap(SQUARE_BMP) + + menu.AppendItem(item) + menu.AppendItem(item2) + + bmp_brush_format = {const.BRUSH_CIRCLE: CIRCLE_BMP, + const.BRUSH_SQUARE: SQUARE_BMP} + selected_bmp = bmp_brush_format[const.DEFAULT_BRUSH_FORMAT] + + btn_brush_format = pbtn.PlateButton(self, wx.ID_ANY,"", selected_bmp, + style=pbtn.PB_STYLE_SQUARE) + btn_brush_format.SetMenu(menu) + self.btn_brush_format = btn_brush_format + + spin_brush_size = wx.SpinCtrl(self, -1, "", (20, 50)) + spin_brush_size.SetRange(1,100) + spin_brush_size.SetValue(const.BRUSH_SIZE) + spin_brush_size.Bind(wx.EVT_TEXT, self.OnBrushSize) + self.spin = spin_brush_size + + combo_brush_op = wx.ComboBox(self, -1, "", size=(15,-1), + choices = const.BRUSH_OP_NAME, + style = wx.CB_DROPDOWN|wx.CB_READONLY) + combo_brush_op.SetSelection(const.DEFAULT_BRUSH_OP) + if sys.platform != 'win32': + combo_brush_op.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) + self.combo_brush_op = combo_brush_op + + # Sizer which represents the second line + line2 = wx.BoxSizer(wx.HORIZONTAL) + line2.Add(btn_brush_format, 0, wx.EXPAND|wx.GROW|wx.TOP|wx.RIGHT, 0) + line2.Add(spin_brush_size, 0, wx.RIGHT, 5) + line2.Add(combo_brush_op, 1, wx.EXPAND|wx.TOP|wx.RIGHT|wx.LEFT, 5) + + ## LINE 3 + text_thresh = wx.StaticText(self, -1, _("Brush threshold range:")) + + ## LINE 4 + gradient_thresh = grad.GradientCtrl(self, -1, 0, 5000, 0, 5000, + (0, 0, 255, 100)) + self.gradient_thresh = gradient_thresh + self.bind_evt_gradient = True + + # LINE 5 + check_box = wx.CheckBox(self, -1, _("Overwrite mask")) + self.check_box = check_box + + # Add lines into main sizer + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(text1, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) + sizer.Add(line2, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) + sizer.Add(text_thresh, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) + sizer.Add(gradient_thresh, 0, wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT| + wx.BOTTOM, 6) + sizer.Add(check_box, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) + sizer.Fit(self) + + self.SetSizer(sizer) + self.Update() + self.SetAutoLayout(1) + + self.__bind_events() + self.__bind_events_wx() + + + def __bind_events_wx(self): + self.Bind(wx.EVT_MENU, self.OnMenu) + self.Bind(grad.EVT_THRESHOLD_CHANGED, self.OnGradientChanged, + self.gradient_thresh) + self.combo_brush_op.Bind(wx.EVT_COMBOBOX, self.OnComboBrushOp) + self.check_box.Bind(wx.EVT_CHECKBOX, self.OnCheckOverwriteMask) + + def __bind_events(self): + Publisher.subscribe(self.SetThresholdBounds, + 'Update threshold limits') + Publisher.subscribe(self.ChangeMaskColour, 'Change mask colour') + Publisher.subscribe(self.SetGradientColour, 'Add mask') + + def ChangeMaskColour(self, pubsub_evt): + colour = pubsub_evt.data + self.gradient_thresh.SetColour(colour) + + def SetGradientColour(self, pubsub_evt): + vtk_colour = pubsub_evt.data[3] + wx_colour = [c*255 for c in vtk_colour] + self.gradient_thresh.SetColour(wx_colour) + + def SetThresholdValues(self, pubsub_evt): + thresh_min, thresh_max = pubsub_evt.data + self.bind_evt_gradient = False + self.gradient_thresh.SetMinValue(thresh_min) + self.gradient_thresh.SetMaxValue(thresh_max) + self.bind_evt_gradient = True + + def SetThresholdBounds(self, pubsub_evt): + thresh_min = pubsub_evt.data[0] + thresh_max = pubsub_evt.data[1] + self.gradient_thresh.SetMinRange(thresh_min) + self.gradient_thresh.SetMaxRange(thresh_max) + self.gradient_thresh.SetMinValue(thresh_min) + self.gradient_thresh.SetMaxValue(thresh_max) + + def OnGradientChanged(self, evt): + thresh_min = self.gradient_thresh.GetMinValue() + thresh_max = self.gradient_thresh.GetMaxValue() + if self.bind_evt_gradient: + Publisher.sendMessage('Set edition threshold values', + (thresh_min, thresh_max)) + + def OnMenu(self, evt): + SQUARE_BMP = wx.Bitmap("../icons/brush_square.jpg", wx.BITMAP_TYPE_JPEG) + CIRCLE_BMP = wx.Bitmap("../icons/brush_circle.jpg", wx.BITMAP_TYPE_JPEG) + + brush = {MENU_BRUSH_CIRCLE: const.BRUSH_CIRCLE, + MENU_BRUSH_SQUARE: const.BRUSH_SQUARE} + bitmap = {MENU_BRUSH_CIRCLE: CIRCLE_BMP, + MENU_BRUSH_SQUARE: SQUARE_BMP} + + self.btn_brush_format.SetBitmap(bitmap[evt.GetId()]) + + Publisher.sendMessage('Set brush format', brush[evt.GetId()]) + + def OnBrushSize(self, evt): + """ """ + # FIXME: Using wx.EVT_SPINCTRL in MacOS it doesnt capture changes only + # in the text ctrl - so we are capturing only changes on text + # Strangelly this is being called twice + Publisher.sendMessage('Set edition brush size',self.spin.GetValue()) + def OnComboBrushOp(self, evt): + brush_op_id = evt.GetSelection() + Publisher.sendMessage('Set edition operation', brush_op_id) + def OnCheckOverwriteMask(self, evt): + value = self.check_box.GetValue() + Publisher.sendMessage('Set overwrite mask', value) -- libgit2 0.21.2