Commit 536f91e77b5dc857e3b9187ff61381031203f0c7
Exists in
master
and in
42 other branches
Merge pull request #28 from tfmoraes/watershed_improvements
Improvements to watershed segmentation tool (algorithm, connectivity, gui to config).
Showing
4 changed files
with
341 additions
and
17 deletions
Show diff stats
614 Bytes
invesalius/data/styles.py
... | ... | @@ -18,7 +18,9 @@ |
18 | 18 | #-------------------------------------------------------------------------- |
19 | 19 | |
20 | 20 | import os |
21 | +import multiprocessing | |
21 | 22 | import tempfile |
23 | +import time | |
22 | 24 | |
23 | 25 | import vtk |
24 | 26 | import wx |
... | ... | @@ -27,13 +29,17 @@ from wx.lib.pubsub import pub as Publisher |
27 | 29 | |
28 | 30 | import constants as const |
29 | 31 | import converters |
32 | +import cursor_actors as ca | |
30 | 33 | import numpy as np |
31 | 34 | |
32 | 35 | from scipy import ndimage |
33 | 36 | from scipy.misc import imsave |
37 | +from scipy.ndimage import watershed_ift, generate_binary_structure | |
34 | 38 | from skimage.morphology import watershed |
35 | 39 | from skimage import filter |
36 | 40 | |
41 | +import utils | |
42 | + | |
37 | 43 | ORIENTATIONS = { |
38 | 44 | "AXIAL": const.AXIAL, |
39 | 45 | "CORONAL": const.CORONAL, |
... | ... | @@ -734,6 +740,86 @@ class EditorInteractorStyle(DefaultInteractorStyle): |
734 | 740 | return x, y, z |
735 | 741 | |
736 | 742 | |
743 | +class WatershedProgressWindow(wx.Frame): | |
744 | + def __init__(self, process, parent=None): | |
745 | + wx.Frame.__init__(self, parent, -1) | |
746 | + self.process = process | |
747 | + self._build_gui() | |
748 | + self._bind_wx_events() | |
749 | + self.timer = wx.Timer(self) | |
750 | + self.timer.Start(1000) | |
751 | + | |
752 | + def _build_gui(self): | |
753 | + self.gauge = wx.Gauge(self, -1, 100) | |
754 | + self.btn_cancel = wx.Button(self, wx.ID_CANCEL) | |
755 | + | |
756 | + sizer = wx.BoxSizer(wx.VERTICAL) | |
757 | + sizer.Add(wx.StaticText(self, -1, _("Applying watershed"))) | |
758 | + sizer.Add(self.gauge, 0, wx.EXPAND) | |
759 | + sizer.Add(self.btn_cancel, 0, wx.ALIGN_LEFT) | |
760 | + | |
761 | + self.SetSizer(sizer) | |
762 | + sizer.Fit(self) | |
763 | + self.Layout() | |
764 | + | |
765 | + def __del__(self): | |
766 | + self.timer.Stop() | |
767 | + | |
768 | + def _bind_wx_events(self): | |
769 | + self.Bind(wx.EVT_TIMER, self.TimeHandler) | |
770 | + self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_cancel) | |
771 | + | |
772 | + def on_cancel(self, evt): | |
773 | + self.timer.Stop() | |
774 | + self.process.terminate() | |
775 | + | |
776 | + def TimeHandler(self, evt): | |
777 | + self.gauge.Pulse() | |
778 | + | |
779 | + | |
780 | +class WatershedConfig(object): | |
781 | + __metaclass__= utils.Singleton | |
782 | + def __init__(self): | |
783 | + self.algorithm = "Watershed" | |
784 | + self.con_2d = 4 | |
785 | + self.con_3d = 6 | |
786 | + self.mg_size = 3 | |
787 | + self.use_ww_wl = True | |
788 | + self.operation = BRUSH_FOREGROUND | |
789 | + self.cursor_type = const.BRUSH_CIRCLE | |
790 | + self.cursor_size = const.BRUSH_SIZE | |
791 | + | |
792 | + Publisher.subscribe(self.set_operation, 'Set watershed operation') | |
793 | + Publisher.subscribe(self.set_use_ww_wl, 'Set use ww wl') | |
794 | + | |
795 | + Publisher.subscribe(self.set_algorithm, "Set watershed algorithm") | |
796 | + Publisher.subscribe(self.set_2dcon, "Set watershed 2d con") | |
797 | + Publisher.subscribe(self.set_3dcon, "Set watershed 3d con") | |
798 | + Publisher.subscribe(self.set_gaussian_size, "Set watershed gaussian size") | |
799 | + | |
800 | + def set_operation(self, pubsub_evt): | |
801 | + self.operation = WATERSHED_OPERATIONS[pubsub_evt.data] | |
802 | + | |
803 | + def set_use_ww_wl(self, pubsub_evt): | |
804 | + self.use_ww_wl = pubsub_evt.data | |
805 | + | |
806 | + def set_algorithm(self, pubsub_evt): | |
807 | + self.algorithm = pubsub_evt.data | |
808 | + | |
809 | + def set_2dcon(self, pubsub_evt): | |
810 | + self.con_2d = pubsub_evt.data | |
811 | + | |
812 | + def set_3dcon(self, pubsub_evt): | |
813 | + self.con_3d = pubsub_evt.data | |
814 | + | |
815 | + def set_gaussian_size(self, pubsub_evt): | |
816 | + self.mg_size = pubsub_evt.data | |
817 | + | |
818 | +WALGORITHM = {"Watershed": watershed, | |
819 | + "Watershed IFT": watershed_ift} | |
820 | +CON2D = {4: 1, 8: 2} | |
821 | +CON3D = {6: 1, 18: 2, 26: 3} | |
822 | + | |
737 | 823 | class WaterShedInteractorStyle(DefaultInteractorStyle): |
738 | 824 | def __init__(self, viewer): |
739 | 825 | DefaultInteractorStyle.__init__(self, viewer) |
... | ... | @@ -742,9 +828,7 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): |
742 | 828 | self.orientation = self.viewer.orientation |
743 | 829 | self.matrix = None |
744 | 830 | |
745 | - self.operation = BRUSH_FOREGROUND | |
746 | - | |
747 | - self.mg_size = 3 | |
831 | + self.config = WatershedConfig() | |
748 | 832 | |
749 | 833 | self.picker = vtk.vtkWorldPointPicker() |
750 | 834 | |
... | ... | @@ -761,7 +845,10 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): |
761 | 845 | self.AddObserver("MouseMoveEvent", self.OnBrushMove) |
762 | 846 | |
763 | 847 | Publisher.subscribe(self.expand_watershed, 'Expand watershed to 3D ' + self.orientation) |
764 | - Publisher.subscribe(self.set_operation, 'Set watershed operation') | |
848 | + Publisher.subscribe(self.set_bsize, 'Set watershed brush size') | |
849 | + Publisher.subscribe(self.set_bformat, 'Set watershed brush format') | |
850 | + | |
851 | + self._set_cursor() | |
765 | 852 | |
766 | 853 | def SetUp(self): |
767 | 854 | mask = self.viewer.slice_.current_mask.matrix |
... | ... | @@ -772,7 +859,8 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): |
772 | 859 | def CleanUp(self): |
773 | 860 | #self._remove_mask() |
774 | 861 | Publisher.unsubscribe(self.expand_watershed, 'Expand watershed to 3D ' + self.orientation) |
775 | - Publisher.unsubscribe(self.set_operation, 'Set watershed operation') | |
862 | + Publisher.unsubscribe(self.set_bformat, 'Set watershed brush format') | |
863 | + Publisher.unsubscribe(self.set_bsize, 'Set watershed brush size') | |
776 | 864 | self.RemoveAllObservers() |
777 | 865 | self.viewer.slice_.to_show_aux = '' |
778 | 866 | self.viewer.OnScrollBar() |
... | ... | @@ -791,8 +879,33 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): |
791 | 879 | os.remove(self.temp_file) |
792 | 880 | print "deleting", self.temp_file |
793 | 881 | |
794 | - def set_operation(self, pubsub_evt): | |
795 | - self.operation = WATERSHED_OPERATIONS[pubsub_evt.data] | |
882 | + def _set_cursor(self): | |
883 | + if self.config.cursor_type == const.BRUSH_SQUARE: | |
884 | + cursor = ca.CursorRectangle() | |
885 | + elif self.config.cursor_type == const.BRUSH_CIRCLE: | |
886 | + cursor = ca.CursorCircle() | |
887 | + | |
888 | + cursor.SetOrientation(self.orientation) | |
889 | + n = self.viewer.slice_data.number | |
890 | + coordinates = {"SAGITAL": [n, 0, 0], | |
891 | + "CORONAL": [0, n, 0], | |
892 | + "AXIAL": [0, 0, n]} | |
893 | + cursor.SetPosition(coordinates[self.orientation]) | |
894 | + spacing = self.viewer.slice_.spacing | |
895 | + cursor.SetSpacing(spacing) | |
896 | + cursor.SetColour(self.viewer._brush_cursor_colour) | |
897 | + cursor.SetSize(self.config.cursor_size) | |
898 | + self.viewer.slice_data.SetCursor(cursor) | |
899 | + self.viewer.interactor.Render() | |
900 | + | |
901 | + def set_bsize(self, pubsub_evt): | |
902 | + size = pubsub_evt.data | |
903 | + self.config.cursor_size = size | |
904 | + self.viewer.slice_data.cursor.SetSize(size) | |
905 | + | |
906 | + def set_bformat(self, pubsub_evt): | |
907 | + self.config.cursor_type = pubsub_evt.data | |
908 | + self._set_cursor() | |
796 | 909 | |
797 | 910 | def OnEnterInteractor(self, obj, evt): |
798 | 911 | if (self.viewer.slice_.buffer_slices[self.orientation].mask is None): |
... | ... | @@ -869,7 +982,7 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): |
869 | 982 | if position < 0: |
870 | 983 | position = viewer.calculate_matrix_position(coord) |
871 | 984 | |
872 | - operation = self.operation | |
985 | + operation = self.config.operation | |
873 | 986 | |
874 | 987 | if operation == BRUSH_FOREGROUND: |
875 | 988 | if iren.GetControlKey(): |
... | ... | @@ -937,7 +1050,7 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): |
937 | 1050 | if position < 0: |
938 | 1051 | position = viewer.calculate_matrix_position(coord) |
939 | 1052 | |
940 | - operation = self.operation | |
1053 | + operation = self.config.operation | |
941 | 1054 | |
942 | 1055 | if operation == BRUSH_FOREGROUND: |
943 | 1056 | if iren.GetControlKey(): |
... | ... | @@ -991,8 +1104,35 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): |
991 | 1104 | wl = self.viewer.slice_.window_level |
992 | 1105 | |
993 | 1106 | if BRUSH_BACKGROUND in markers and BRUSH_FOREGROUND in markers: |
994 | - tmp_image = ndimage.morphological_gradient(get_LUT_value(image, ww, wl).astype('uint16'), self.mg_size) | |
995 | - tmp_mask = watershed(tmp_image, markers) | |
1107 | + #w_algorithm = WALGORITHM[self.config.algorithm] | |
1108 | + bstruct = generate_binary_structure(2, CON2D[self.config.con_2d]) | |
1109 | + if self.config.use_ww_wl: | |
1110 | + if self.config.algorithm == 'Watershed': | |
1111 | + tmp_image = ndimage.morphological_gradient( | |
1112 | + get_LUT_value(image, ww, wl).astype('uint16'), | |
1113 | + self.config.mg_size) | |
1114 | + tmp_mask = watershed(tmp_image, markers.astype('int16'), bstruct) | |
1115 | + else: | |
1116 | + #tmp_image = ndimage.gaussian_filter(get_LUT_value(image, ww, wl).astype('uint16'), self.config.mg_size) | |
1117 | + #tmp_image = ndimage.morphological_gradient( | |
1118 | + #get_LUT_value(image, ww, wl).astype('uint16'), | |
1119 | + #self.config.mg_size) | |
1120 | + tmp_image = get_LUT_value(image, ww, wl).astype('uint16') | |
1121 | + #markers[markers == 2] = -1 | |
1122 | + tmp_mask = watershed_ift(tmp_image, markers.astype('int16'), bstruct) | |
1123 | + #markers[markers == -1] = 2 | |
1124 | + #tmp_mask[tmp_mask == -1] = 2 | |
1125 | + | |
1126 | + else: | |
1127 | + if self.config.algorithm == 'Watershed': | |
1128 | + tmp_image = ndimage.morphological_gradient((image - image.min()).astype('uint16'), self.config.mg_size) | |
1129 | + tmp_mask = watershed(tmp_image, markers.astype('int16'), bstruct) | |
1130 | + else: | |
1131 | + #tmp_image = (image - image.min()).astype('uint16') | |
1132 | + #tmp_image = ndimage.gaussian_filter(tmp_image, self.config.mg_size) | |
1133 | + #tmp_image = ndimage.morphological_gradient((image - image.min()).astype('uint16'), self.config.mg_size) | |
1134 | + tmp_image = image - image.min().astype('uint16') | |
1135 | + tmp_mask = watershed_ift(tmp_image, markers.astype('int16'), bstruct) | |
996 | 1136 | |
997 | 1137 | if self.viewer.overwrite_mask: |
998 | 1138 | mask[:] = 0 |
... | ... | @@ -1098,8 +1238,59 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): |
1098 | 1238 | ww = self.viewer.slice_.window_width |
1099 | 1239 | wl = self.viewer.slice_.window_level |
1100 | 1240 | if BRUSH_BACKGROUND in markers and BRUSH_FOREGROUND in markers: |
1101 | - tmp_image = ndimage.morphological_gradient(get_LUT_value(image, ww, wl).astype('uint16'), self.mg_size) | |
1102 | - tmp_mask = watershed(tmp_image, markers) | |
1241 | + #w_algorithm = WALGORITHM[self.config.algorithm] | |
1242 | + bstruct = generate_binary_structure(3, CON3D[self.config.con_3d]) | |
1243 | + tfile = tempfile.mktemp() | |
1244 | + tmp_mask = np.memmap(tfile, shape=mask.shape, dtype=mask.dtype, | |
1245 | + mode='w+') | |
1246 | + q = multiprocessing.Queue() | |
1247 | + p = multiprocessing.Process(target=do_watershed, args=(image, | |
1248 | + markers, tmp_mask, bstruct, | |
1249 | + self.config.algorithm, | |
1250 | + self.config.mg_size, | |
1251 | + self.config.use_ww_wl, wl, ww, q)) | |
1252 | + | |
1253 | + wp = WatershedProgressWindow(p) | |
1254 | + wp.Center(wx.BOTH) | |
1255 | + wp.Show() | |
1256 | + wp.MakeModal() | |
1257 | + | |
1258 | + p.start() | |
1259 | + | |
1260 | + while q.empty() and p.is_alive(): | |
1261 | + time.sleep(0.5) | |
1262 | + wx.Yield() | |
1263 | + | |
1264 | + wp.MakeModal(False) | |
1265 | + wp.Destroy() | |
1266 | + del wp | |
1267 | + | |
1268 | + if q.empty(): | |
1269 | + return | |
1270 | + #do_watershed(image, markers, tmp_mask, bstruct, self.config.algorithm, | |
1271 | + #self.config.mg_size, self.config.use_ww_wl, wl, ww) | |
1272 | + #if self.config.use_ww_wl: | |
1273 | + #if self.config.algorithm == 'Watershed': | |
1274 | + #tmp_image = ndimage.morphological_gradient( | |
1275 | + #get_LUT_value(image, ww, wl).astype('uint16'), | |
1276 | + #self.config.mg_size) | |
1277 | + #tmp_mask = watershed(tmp_image, markers.astype('int16'), bstruct) | |
1278 | + #else: | |
1279 | + #tmp_image = get_LUT_value(image, ww, wl).astype('uint16') | |
1280 | + ##tmp_image = ndimage.gaussian_filter(tmp_image, self.config.mg_size) | |
1281 | + ##tmp_image = ndimage.morphological_gradient( | |
1282 | + ##get_LUT_value(image, ww, wl).astype('uint16'), | |
1283 | + ##self.config.mg_size) | |
1284 | + #tmp_mask = watershed_ift(tmp_image, markers.astype('int16'), bstruct) | |
1285 | + #else: | |
1286 | + #if self.config.algorithm == 'Watershed': | |
1287 | + #tmp_image = ndimage.morphological_gradient((image - image.min()).astype('uint16'), self.config.mg_size) | |
1288 | + #tmp_mask = watershed(tmp_image, markers.astype('int16'), bstruct) | |
1289 | + #else: | |
1290 | + #tmp_image = (image - image.min()).astype('uint16') | |
1291 | + ##tmp_image = ndimage.gaussian_filter(tmp_image, self.config.mg_size) | |
1292 | + ##tmp_image = ndimage.morphological_gradient((image - image.min()).astype('uint16'), self.config.mg_size) | |
1293 | + #tmp_mask = watershed_ift(tmp_image, markers.astype('int8'), bstruct) | |
1103 | 1294 | |
1104 | 1295 | if self.viewer.overwrite_mask: |
1105 | 1296 | mask[:] = 0 |
... | ... | @@ -1118,6 +1309,34 @@ class WaterShedInteractorStyle(DefaultInteractorStyle): |
1118 | 1309 | Publisher.sendMessage('Reload actual slice') |
1119 | 1310 | |
1120 | 1311 | |
1312 | +def do_watershed(image, markers, mask, bstruct, algorithm, mg_size, use_ww_wl, wl, ww, q): | |
1313 | + if use_ww_wl: | |
1314 | + if algorithm == 'Watershed': | |
1315 | + tmp_image = ndimage.morphological_gradient( | |
1316 | + get_LUT_value(image, ww, wl).astype('uint16'), | |
1317 | + mg_size) | |
1318 | + tmp_mask = watershed(tmp_image, markers.astype('int16'), bstruct) | |
1319 | + else: | |
1320 | + tmp_image = get_LUT_value(image, ww, wl).astype('uint16') | |
1321 | + #tmp_image = ndimage.gaussian_filter(tmp_image, self.config.mg_size) | |
1322 | + #tmp_image = ndimage.morphological_gradient( | |
1323 | + #get_LUT_value(image, ww, wl).astype('uint16'), | |
1324 | + #self.config.mg_size) | |
1325 | + tmp_mask = watershed_ift(tmp_image, markers.astype('int16'), bstruct) | |
1326 | + else: | |
1327 | + if algorithm == 'Watershed': | |
1328 | + tmp_image = ndimage.morphological_gradient((image - image.min()).astype('uint16'), mg_size) | |
1329 | + tmp_mask = watershed(tmp_image, markers.astype('int16'), bstruct) | |
1330 | + else: | |
1331 | + tmp_image = (image - image.min()).astype('uint16') | |
1332 | + #tmp_image = ndimage.gaussian_filter(tmp_image, self.config.mg_size) | |
1333 | + #tmp_image = ndimage.morphological_gradient((image - image.min()).astype('uint16'), self.config.mg_size) | |
1334 | + tmp_mask = watershed_ift(tmp_image, markers.astype('int8'), bstruct) | |
1335 | + mask[:] = tmp_mask | |
1336 | + q.put(1) | |
1337 | + | |
1338 | + | |
1339 | + | |
1121 | 1340 | def get_style(style): |
1122 | 1341 | STYLES = { |
1123 | 1342 | const.STATE_DEFAULT: DefaultInteractorStyle, | ... | ... |
invesalius/gui/dialogs.py
... | ... | @@ -1384,3 +1384,88 @@ class ClutImagedataDialog(wx.Dialog): |
1384 | 1384 | super(wx.Dialog, self).Show(show) |
1385 | 1385 | if gen_evt: |
1386 | 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
... | ... | @@ -720,7 +720,7 @@ class WatershedTool(EditionTools): |
720 | 720 | self.SetBackgroundColour(default_colour) |
721 | 721 | |
722 | 722 | ## LINE 1 |
723 | - text1 = wx.StaticText(self, -1, _("Choose brush type and size:")) | |
723 | + text1 = wx.StaticText(self, -1, _("Choose brush type, size or operation:")) | |
724 | 724 | |
725 | 725 | ## LINE 2 |
726 | 726 | menu = wx.Menu() |
... | ... | @@ -773,17 +773,28 @@ class WatershedTool(EditionTools): |
773 | 773 | |
774 | 774 | # LINE 5 |
775 | 775 | check_box = wx.CheckBox(self, -1, _("Overwrite mask")) |
776 | + ww_wl_cbox = wx.CheckBox(self, -1, _("Use WW&WL")) | |
777 | + ww_wl_cbox.SetValue(True) | |
776 | 778 | self.check_box = check_box |
779 | + self.ww_wl_cbox = ww_wl_cbox | |
777 | 780 | |
778 | 781 | # Line 6 |
782 | + bmp = wx.Bitmap("../icons/configuration.png", wx.BITMAP_TYPE_PNG) | |
783 | + self.btn_wconfig = wx.BitmapButton(self, -1, bitmap=bmp, | |
784 | + size=(bmp.GetWidth()+10, bmp.GetHeight()+10)) | |
779 | 785 | self.btn_exp_watershed = wx.Button(self, -1, _('Expand watershed to 3D')) |
780 | 786 | |
787 | + sizer_btns = wx.BoxSizer(wx.HORIZONTAL) | |
788 | + sizer_btns.Add(self.btn_wconfig, 0, wx.ALIGN_LEFT | wx.LEFT | wx.TOP | wx.DOWN, 5) | |
789 | + sizer_btns.Add(self.btn_exp_watershed, 0, wx.GROW|wx.EXPAND| wx.ALL, 5) | |
790 | + | |
781 | 791 | # Add lines into main sizer |
782 | 792 | sizer = wx.BoxSizer(wx.VERTICAL) |
783 | 793 | sizer.Add(text1, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) |
784 | 794 | sizer.Add(line2, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) |
785 | 795 | sizer.Add(check_box, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) |
786 | - sizer.Add(self.btn_exp_watershed, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) | |
796 | + sizer.Add(ww_wl_cbox, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) | |
797 | + sizer.Add(sizer_btns, 0, wx.EXPAND) | |
787 | 798 | sizer.Fit(self) |
788 | 799 | |
789 | 800 | self.SetSizer(sizer) |
... | ... | @@ -797,7 +808,9 @@ class WatershedTool(EditionTools): |
797 | 808 | self.Bind(wx.EVT_MENU, self.OnMenu) |
798 | 809 | self.combo_brush_op.Bind(wx.EVT_COMBOBOX, self.OnComboBrushOp) |
799 | 810 | self.check_box.Bind(wx.EVT_CHECKBOX, self.OnCheckOverwriteMask) |
811 | + self.ww_wl_cbox.Bind(wx.EVT_CHECKBOX, self.OnCheckWWWL) | |
800 | 812 | self.btn_exp_watershed.Bind(wx.EVT_BUTTON, self.OnExpandWatershed) |
813 | + self.btn_wconfig.Bind(wx.EVT_BUTTON, self.OnConfig) | |
801 | 814 | |
802 | 815 | def ChangeMaskColour(self, pubsub_evt): |
803 | 816 | colour = pubsub_evt.data |
... | ... | @@ -834,14 +847,14 @@ class WatershedTool(EditionTools): |
834 | 847 | |
835 | 848 | self.btn_brush_format.SetBitmap(bitmap[evt.GetId()]) |
836 | 849 | |
837 | - Publisher.sendMessage('Set brush format', brush[evt.GetId()]) | |
850 | + Publisher.sendMessage('Set watershed brush format', brush[evt.GetId()]) | |
838 | 851 | |
839 | 852 | def OnBrushSize(self, evt): |
840 | 853 | """ """ |
841 | 854 | # FIXME: Using wx.EVT_SPINCTRL in MacOS it doesnt capture changes only |
842 | 855 | # in the text ctrl - so we are capturing only changes on text |
843 | 856 | # Strangelly this is being called twice |
844 | - Publisher.sendMessage('Set edition brush size',self.spin.GetValue()) | |
857 | + Publisher.sendMessage('Set watershed brush size',self.spin.GetValue()) | |
845 | 858 | |
846 | 859 | def OnComboBrushOp(self, evt): |
847 | 860 | brush_op = self.combo_brush_op.GetValue() |
... | ... | @@ -851,5 +864,12 @@ class WatershedTool(EditionTools): |
851 | 864 | value = self.check_box.GetValue() |
852 | 865 | Publisher.sendMessage('Set overwrite mask', value) |
853 | 866 | |
867 | + def OnCheckWWWL(self, evt): | |
868 | + value = self.ww_wl_cbox.GetValue() | |
869 | + Publisher.sendMessage('Set use ww wl', value) | |
870 | + | |
871 | + def OnConfig(self, evt): | |
872 | + dlg.WatershedOptionsDialog().Show() | |
873 | + | |
854 | 874 | def OnExpandWatershed(self, evt): |
855 | 875 | Publisher.sendMessage('Expand watershed to 3D AXIAL') | ... | ... |