Commit 92f1f1a7a51b402386f88fa4d23a850a5d4ddece
1 parent
5926d404
Exists in
watershed_improvements_bkp
Added a gui to set some configs from watershed
Showing
3 changed files
with
139 additions
and
11 deletions
Show diff stats
invesalius/data/styles.py
@@ -32,6 +32,7 @@ import numpy as np | @@ -32,6 +32,7 @@ import numpy as np | ||
32 | 32 | ||
33 | from scipy import ndimage | 33 | from scipy import ndimage |
34 | from scipy.misc import imsave | 34 | from scipy.misc import imsave |
35 | +from scipy.ndimage import watershed_ift, generate_binary_structure | ||
35 | from skimage.morphology import watershed | 36 | from skimage.morphology import watershed |
36 | from skimage import filter | 37 | from skimage import filter |
37 | 38 | ||
@@ -740,20 +741,45 @@ class EditorInteractorStyle(DefaultInteractorStyle): | @@ -740,20 +741,45 @@ class EditorInteractorStyle(DefaultInteractorStyle): | ||
740 | class WatershedConfig(object): | 741 | class WatershedConfig(object): |
741 | __metaclass__= utils.Singleton | 742 | __metaclass__= utils.Singleton |
742 | def __init__(self): | 743 | def __init__(self): |
743 | - self.operation = BRUSH_FOREGROUND | 744 | + self.algorithm = "Watershed" |
745 | + self.con_2d = 4 | ||
746 | + self.con_3d = 18 | ||
747 | + self.mg_size = 3 | ||
744 | self.use_ww_wl = True | 748 | self.use_ww_wl = True |
749 | + self.operation = BRUSH_FOREGROUND | ||
745 | self.cursor_type = const.BRUSH_CIRCLE | 750 | self.cursor_type = const.BRUSH_CIRCLE |
746 | self.cursor_size = const.BRUSH_SIZE | 751 | self.cursor_size = const.BRUSH_SIZE |
747 | 752 | ||
748 | Publisher.subscribe(self.set_operation, 'Set watershed operation') | 753 | Publisher.subscribe(self.set_operation, 'Set watershed operation') |
749 | Publisher.subscribe(self.set_use_ww_wl, 'Set use ww wl') | 754 | Publisher.subscribe(self.set_use_ww_wl, 'Set use ww wl') |
750 | 755 | ||
756 | + Publisher.subscribe(self.set_algorithm, "Set watershed algorithm") | ||
757 | + Publisher.subscribe(self.set_2dcon, "Set watershed 2d con") | ||
758 | + Publisher.subscribe(self.set_3dcon, "Set watershed 3d con") | ||
759 | + Publisher.subscribe(self.set_gaussian_size, "Set watershed gaussian size") | ||
760 | + | ||
751 | def set_operation(self, pubsub_evt): | 761 | def set_operation(self, pubsub_evt): |
752 | self.operation = WATERSHED_OPERATIONS[pubsub_evt.data] | 762 | self.operation = WATERSHED_OPERATIONS[pubsub_evt.data] |
753 | 763 | ||
754 | def set_use_ww_wl(self, pubsub_evt): | 764 | def set_use_ww_wl(self, pubsub_evt): |
755 | self.use_ww_wl = pubsub_evt.data | 765 | self.use_ww_wl = pubsub_evt.data |
756 | 766 | ||
767 | + def set_algorithm(self, pubsub_evt): | ||
768 | + self.algorithm = pubsub_evt.data | ||
769 | + | ||
770 | + def set_2dcon(self, pubsub_evt): | ||
771 | + self.con_2d = pubsub_evt.data | ||
772 | + | ||
773 | + def set_3dcon(self, pubsub_evt): | ||
774 | + self.con_3d = pubsub_evt.data | ||
775 | + | ||
776 | + def set_gaussian_size(self, pubsub_evt): | ||
777 | + self.mg_size = pubsub_evt.data | ||
778 | + | ||
779 | +WALGORITHM = {"Watershed": watershed, | ||
780 | + "Watershed IFT": watershed_ift} | ||
781 | +CON2D = {4: 1, 8: 2} | ||
782 | +CON3D = {6: 1, 18: 2, 26: 3} | ||
757 | 783 | ||
758 | class WaterShedInteractorStyle(DefaultInteractorStyle): | 784 | class WaterShedInteractorStyle(DefaultInteractorStyle): |
759 | def __init__(self, viewer): | 785 | def __init__(self, viewer): |
@@ -765,7 +791,6 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): | @@ -765,7 +791,6 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): | ||
765 | 791 | ||
766 | self.operation = BRUSH_FOREGROUND | 792 | self.operation = BRUSH_FOREGROUND |
767 | 793 | ||
768 | - self.mg_size = 3 | ||
769 | self.config = WatershedConfig() | 794 | self.config = WatershedConfig() |
770 | 795 | ||
771 | self.picker = vtk.vtkWorldPointPicker() | 796 | self.picker = vtk.vtkWorldPointPicker() |
@@ -1047,12 +1072,17 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): | @@ -1047,12 +1072,17 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): | ||
1047 | wl = self.viewer.slice_.window_level | 1072 | wl = self.viewer.slice_.window_level |
1048 | 1073 | ||
1049 | if BRUSH_BACKGROUND in markers and BRUSH_FOREGROUND in markers: | 1074 | if BRUSH_BACKGROUND in markers and BRUSH_FOREGROUND in markers: |
1075 | + w_algorithm = WALGORITHM[self.config.algorithm] | ||
1076 | + bstruct = generate_binary_structure(2, CON2D[self.config.con_2d]) | ||
1050 | if self.config.use_ww_wl: | 1077 | if self.config.use_ww_wl: |
1051 | - tmp_image = ndimage.morphological_gradient(get_LUT_value(image, ww, wl).astype('uint16'), self.mg_size) | ||
1052 | - tmp_mask = watershed(tmp_image, markers) | 1078 | + tmp_image = ndimage.morphological_gradient( |
1079 | + get_LUT_value(image, ww, wl).astype('uint16'), | ||
1080 | + self.config.mg_size) | ||
1081 | + | ||
1082 | + tmp_mask = w_algorithm(tmp_image, markers.astype('int16'), bstruct) | ||
1053 | else: | 1083 | else: |
1054 | - tmp_image = ndimage.morphological_gradient(image, self.mg_size) | ||
1055 | - tmp_mask = watershed(tmp_image, markers) | 1084 | + tmp_image = ndimage.morphological_gradient(image, self.config.mg_size) |
1085 | + tmp_mask = w_algorithm(tmp_image, markers.astype('int16'), bstruct) | ||
1056 | 1086 | ||
1057 | if self.viewer.overwrite_mask: | 1087 | if self.viewer.overwrite_mask: |
1058 | mask[:] = 0 | 1088 | mask[:] = 0 |
@@ -1158,12 +1188,16 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): | @@ -1158,12 +1188,16 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): | ||
1158 | ww = self.viewer.slice_.window_width | 1188 | ww = self.viewer.slice_.window_width |
1159 | wl = self.viewer.slice_.window_level | 1189 | wl = self.viewer.slice_.window_level |
1160 | if BRUSH_BACKGROUND in markers and BRUSH_FOREGROUND in markers: | 1190 | if BRUSH_BACKGROUND in markers and BRUSH_FOREGROUND in markers: |
1191 | + w_algorithm = WALGORITHM[self.config.algorithm] | ||
1192 | + bstruct = generate_binary_structure(3, CON3D[self.config.con_3d]) | ||
1161 | if self.config.use_ww_wl: | 1193 | if self.config.use_ww_wl: |
1162 | - tmp_image = ndimage.morphological_gradient(get_LUT_value(image, ww, wl).astype('uint16'), self.mg_size) | ||
1163 | - tmp_mask = watershed(tmp_image, markers) | 1194 | + tmp_image = ndimage.morphological_gradient( |
1195 | + get_LUT_value(image, ww, wl).astype('uint16'), | ||
1196 | + self.config.mg_size) | ||
1197 | + tmp_mask = w_algorithm(tmp_image, markers.astype('int16'), bstruct) | ||
1164 | else: | 1198 | else: |
1165 | - tmp_image = ndimage.morphological_gradient(image, self.mg_size) | ||
1166 | - tmp_mask = watershed(tmp_image, markers) | 1199 | + tmp_image = ndimage.morphological_gradient(image, self.config.mg_size) |
1200 | + tmp_mask = w_algorithm(tmp_image, markers.astype('int16'), bstruct) | ||
1167 | 1201 | ||
1168 | if self.viewer.overwrite_mask: | 1202 | if self.viewer.overwrite_mask: |
1169 | mask[:] = 0 | 1203 | mask[:] = 0 |
invesalius/gui/dialogs.py
@@ -1384,3 +1384,88 @@ class ClutImagedataDialog(wx.Dialog): | @@ -1384,3 +1384,88 @@ class ClutImagedataDialog(wx.Dialog): | ||
1384 | super(wx.Dialog, self).Show(show) | 1384 | super(wx.Dialog, self).Show(show) |
1385 | if gen_evt: | 1385 | if gen_evt: |
1386 | self.clut_widget._generate_event() | 1386 | self.clut_widget._generate_event() |
1387 | + | ||
1388 | + | ||
1389 | +class WatershedOptions(wx.Panel): | ||
1390 | + def __init__(self, parent): | ||
1391 | + wx.Panel.__init__(self, parent) | ||
1392 | + | ||
1393 | + self.algorithms = ("Watershed", "Watershed IFT") | ||
1394 | + self.con2d_choices = (4, 8) | ||
1395 | + self.con3d_choices = (6, 18, 26) | ||
1396 | + | ||
1397 | + self._init_gui() | ||
1398 | + self._bind_events() | ||
1399 | + | ||
1400 | + def _init_gui(self): | ||
1401 | + self.choice_algorithm = wx.RadioBox(self, -1, "Algorithm", | ||
1402 | + choices=("Watershed", "Watershed IFT"), | ||
1403 | + style=wx.NO_BORDER | wx.HORIZONTAL) | ||
1404 | + | ||
1405 | + self.choice_2dcon = wx.RadioBox(self, -1, "2D", | ||
1406 | + choices=[str(i) for i in self.con2d_choices], | ||
1407 | + style=wx.NO_BORDER | wx.HORIZONTAL) | ||
1408 | + | ||
1409 | + self.choice_3dcon = wx.RadioBox(self, -1, "3D", | ||
1410 | + choices=[str(i) for i in self.con3d_choices], | ||
1411 | + style=wx.NO_BORDER | wx.HORIZONTAL) | ||
1412 | + | ||
1413 | + self.gaussian_size = wx.SpinCtrl(self, -1, "", min=1, max=10) | ||
1414 | + | ||
1415 | + box_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "Conectivity"), wx.VERTICAL) | ||
1416 | + box_sizer.Add(self.choice_2dcon, 0, wx.ALIGN_CENTER_VERTICAL,2) | ||
1417 | + box_sizer.Add(self.choice_3dcon, 0, wx.ALIGN_CENTER_VERTICAL,2) | ||
1418 | + | ||
1419 | + g_sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
1420 | + g_sizer.Add(wx.StaticText(self, -1, "Gaussian size"), 0, wx.ALIGN_RIGHT | wx.ALL, 5) | ||
1421 | + g_sizer.Add(self.gaussian_size, 0, wx.ALIGN_LEFT | wx.ALL, 5) | ||
1422 | + | ||
1423 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
1424 | + sizer.Add(self.choice_algorithm, 0, wx.ALIGN_CENTER_VERTICAL,2) | ||
1425 | + sizer.Add(box_sizer, 1, wx.EXPAND,2) | ||
1426 | + sizer.Add(g_sizer, 0, wx.ALIGN_LEFT, 2) | ||
1427 | + | ||
1428 | + self.SetSizer(sizer) | ||
1429 | + sizer.Fit(self) | ||
1430 | + self.Layout() | ||
1431 | + | ||
1432 | + def _bind_events(self): | ||
1433 | + self.choice_algorithm.Bind(wx.EVT_RADIOBOX, self.OnSetAlgorithm) | ||
1434 | + self.gaussian_size.Bind(wx.EVT_SPINCTRL, self.OnSetGaussianSize) | ||
1435 | + self.choice_2dcon.Bind(wx.EVT_RADIOBOX, self.OnSetCon2D) | ||
1436 | + self.choice_3dcon.Bind(wx.EVT_RADIOBOX, self.OnSetCon3D) | ||
1437 | + | ||
1438 | + def OnSetAlgorithm(self, evt): | ||
1439 | + v = self.algorithms[evt.GetInt()] | ||
1440 | + Publisher.sendMessage("Set watershed algorithm", v) | ||
1441 | + | ||
1442 | + def OnSetGaussianSize(self, evt): | ||
1443 | + v = self.gaussian_size.GetValue() | ||
1444 | + Publisher.sendMessage("Set watershed gaussian size", v) | ||
1445 | + | ||
1446 | + def OnSetCon2D(self, evt): | ||
1447 | + v = self.con2d_choices[evt.GetInt()] | ||
1448 | + Publisher.sendMessage("Set watershed 2d con", v) | ||
1449 | + | ||
1450 | + def OnSetCon3D(self, evt): | ||
1451 | + v = self.con3d_choices[evt.GetInt()] | ||
1452 | + Publisher.sendMessage("Set watershed 3d con", v) | ||
1453 | + | ||
1454 | + | ||
1455 | +class WatershedOptionsDialog(wx.Dialog): | ||
1456 | + def __init__(self): | ||
1457 | + pre = wx.PreDialog() | ||
1458 | + pre.Create(wx.GetApp().GetTopWindow(), -1, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) | ||
1459 | + self.PostCreate(pre) | ||
1460 | + | ||
1461 | + self._init_gui() | ||
1462 | + | ||
1463 | + def _init_gui(self): | ||
1464 | + wop = WatershedOptions(self) | ||
1465 | + | ||
1466 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
1467 | + sizer.Add(wop, 0, wx.EXPAND) | ||
1468 | + | ||
1469 | + self.SetSizer(sizer) | ||
1470 | + sizer.Fit(self) | ||
1471 | + self.Layout() |
invesalius/gui/task_slice.py
@@ -779,15 +779,20 @@ class WatershedTool(EditionTools): | @@ -779,15 +779,20 @@ class WatershedTool(EditionTools): | ||
779 | self.ww_wl_cbox = ww_wl_cbox | 779 | self.ww_wl_cbox = ww_wl_cbox |
780 | 780 | ||
781 | # Line 6 | 781 | # Line 6 |
782 | + self.btn_wconfig = wx.Button(self, -1, "C", size=(48, -1)) | ||
782 | self.btn_exp_watershed = wx.Button(self, -1, _('Expand watershed to 3D')) | 783 | self.btn_exp_watershed = wx.Button(self, -1, _('Expand watershed to 3D')) |
783 | 784 | ||
785 | + sizer_btns = wx.BoxSizer(wx.HORIZONTAL) | ||
786 | + sizer_btns.Add(self.btn_wconfig, 0, wx.ALIGN_LEFT | wx.LEFT | wx.TOP | wx.DOWN, 5) | ||
787 | + sizer_btns.Add(self.btn_exp_watershed, 0, wx.GROW|wx.EXPAND| wx.ALL, 5) | ||
788 | + | ||
784 | # Add lines into main sizer | 789 | # Add lines into main sizer |
785 | sizer = wx.BoxSizer(wx.VERTICAL) | 790 | sizer = wx.BoxSizer(wx.VERTICAL) |
786 | sizer.Add(text1, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) | 791 | sizer.Add(text1, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) |
787 | sizer.Add(line2, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) | 792 | sizer.Add(line2, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) |
788 | sizer.Add(check_box, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) | 793 | sizer.Add(check_box, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) |
789 | sizer.Add(ww_wl_cbox, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) | 794 | sizer.Add(ww_wl_cbox, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) |
790 | - sizer.Add(self.btn_exp_watershed, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) | 795 | + sizer.Add(sizer_btns, 0, wx.EXPAND) |
791 | sizer.Fit(self) | 796 | sizer.Fit(self) |
792 | 797 | ||
793 | self.SetSizer(sizer) | 798 | self.SetSizer(sizer) |
@@ -803,6 +808,7 @@ class WatershedTool(EditionTools): | @@ -803,6 +808,7 @@ class WatershedTool(EditionTools): | ||
803 | self.check_box.Bind(wx.EVT_CHECKBOX, self.OnCheckOverwriteMask) | 808 | self.check_box.Bind(wx.EVT_CHECKBOX, self.OnCheckOverwriteMask) |
804 | self.ww_wl_cbox.Bind(wx.EVT_CHECKBOX, self.OnCheckWWWL) | 809 | self.ww_wl_cbox.Bind(wx.EVT_CHECKBOX, self.OnCheckWWWL) |
805 | self.btn_exp_watershed.Bind(wx.EVT_BUTTON, self.OnExpandWatershed) | 810 | self.btn_exp_watershed.Bind(wx.EVT_BUTTON, self.OnExpandWatershed) |
811 | + self.btn_wconfig.Bind(wx.EVT_BUTTON, self.OnConfig) | ||
806 | 812 | ||
807 | def ChangeMaskColour(self, pubsub_evt): | 813 | def ChangeMaskColour(self, pubsub_evt): |
808 | colour = pubsub_evt.data | 814 | colour = pubsub_evt.data |
@@ -860,5 +866,8 @@ class WatershedTool(EditionTools): | @@ -860,5 +866,8 @@ class WatershedTool(EditionTools): | ||
860 | value = self.ww_wl_cbox.GetValue() | 866 | value = self.ww_wl_cbox.GetValue() |
861 | Publisher.sendMessage('Set use ww wl', value) | 867 | Publisher.sendMessage('Set use ww wl', value) |
862 | 868 | ||
869 | + def OnConfig(self, evt): | ||
870 | + dlg.WatershedOptionsDialog().Show() | ||
871 | + | ||
863 | def OnExpandWatershed(self, evt): | 872 | def OnExpandWatershed(self, evt): |
864 | Publisher.sendMessage('Expand watershed to 3D AXIAL') | 873 | Publisher.sendMessage('Expand watershed to 3D AXIAL') |