Commit 536f91e77b5dc857e3b9187ff61381031203f0c7

Authored by Thiago Franco de Moraes
2 parents dbecc12e 6b7a5567

Merge pull request #28 from tfmoraes/watershed_improvements

Improvements to watershed segmentation tool (algorithm, connectivity, gui to config).
icons/configuration.png 0 → 100644

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')
... ...