Commit 7c2fdb4bbe8bb56d5fbc8e15f04f26f0168f8712
1 parent
4191a837
Exists in
master
and in
67 other branches
Merge branch 'surface_creation'
Showing
8 changed files
with
380 additions
and
97 deletions
Show diff stats
invesalius/data/slice_.py
@@ -596,10 +596,11 @@ class Slice(object): | @@ -596,10 +596,11 @@ class Slice(object): | ||
596 | #--------------------------------------------------------------------------- | 596 | #--------------------------------------------------------------------------- |
597 | 597 | ||
598 | def CreateSurfaceFromIndex(self, pubsub_evt): | 598 | def CreateSurfaceFromIndex(self, pubsub_evt): |
599 | - mask_index, overwrite_surface, algorithm, options = pubsub_evt.data | 599 | + print pubsub_evt.data |
600 | + surface_parameters = pubsub_evt.data | ||
600 | 601 | ||
601 | proj = Project() | 602 | proj = Project() |
602 | - mask = proj.mask_dict[mask_index] | 603 | + mask = proj.mask_dict[surface_parameters['options']['index']] |
603 | 604 | ||
604 | # This is very important. Do not use masks' imagedata. It would mess up | 605 | # This is very important. Do not use masks' imagedata. It would mess up |
605 | # surface quality event when using contour | 606 | # surface quality event when using contour |
@@ -611,12 +612,8 @@ class Slice(object): | @@ -611,12 +612,8 @@ class Slice(object): | ||
611 | 612 | ||
612 | mask.matrix.flush() | 613 | mask.matrix.flush() |
613 | 614 | ||
614 | - Publisher.sendMessage('Create surface', (algorithm, options, | ||
615 | - self.matrix, | ||
616 | - self.matrix_filename, | ||
617 | - mask, self.spacing, | ||
618 | - overwrite_surface)) | ||
619 | - | 615 | + Publisher.sendMessage('Create surface', (self, mask, |
616 | + surface_parameters)) | ||
620 | def GetOutput(self): | 617 | def GetOutput(self): |
621 | return self.blend_filter.GetOutput() | 618 | return self.blend_filter.GetOutput() |
622 | 619 |
invesalius/data/surface.py
@@ -369,31 +369,29 @@ class SurfaceManager(): | @@ -369,31 +369,29 @@ class SurfaceManager(): | ||
369 | """ | 369 | """ |
370 | Create surface actor, save into project and send it to viewer. | 370 | Create surface actor, save into project and send it to viewer. |
371 | """ | 371 | """ |
372 | - algorithm, options, matrix, filename_img, mask, spacing, overwrite = pubsub_evt.data | ||
373 | - min_value, max_value = mask.threshold_range | ||
374 | - fill_holes = False | ||
375 | - | ||
376 | - mask.matrix.flush() | ||
377 | - | ||
378 | - #if len(surface_data) == 5: | ||
379 | - #imagedata, colour, [min_value, max_value], \ | ||
380 | - #edited_points, overwrite = pubsub_evt.data | ||
381 | - #quality=_('Optimal *') | ||
382 | - #surface_name = "" | ||
383 | - #fill_holes = True | ||
384 | - #keep_largest = False | ||
385 | - #else: | ||
386 | - #imagedata, colour, [min_value, max_value],\ | ||
387 | - #edited_points, overwrite, surface_name,\ | ||
388 | - #quality, fill_holes, keep_largest =\ | ||
389 | - #pubsub_evt.data | 372 | + slice_, mask, surface_parameters = pubsub_evt.data |
373 | + matrix = slice_.matrix | ||
374 | + filename_img = slice_.matrix_filename | ||
375 | + spacing = slice_.spacing | ||
376 | + | ||
377 | + algorithm = surface_parameters['method']['algorithm'] | ||
378 | + options = surface_parameters['method']['options'] | ||
379 | + | ||
380 | + surface_name = surface_parameters['options']['name'] | ||
381 | + quality = surface_parameters['options']['quality'] | ||
382 | + fill_holes = surface_parameters['options']['fill'] | ||
383 | + keep_largest = surface_parameters['options']['keep_largest'] | ||
390 | 384 | ||
391 | mode = 'CONTOUR' # 'GRAYSCALE' | 385 | mode = 'CONTOUR' # 'GRAYSCALE' |
392 | - quality=_('Optimal *') | ||
393 | - keep_largest = False | ||
394 | - surface_name = "" | 386 | + min_value, max_value = mask.threshold_range |
395 | colour = mask.colour | 387 | colour = mask.colour |
396 | 388 | ||
389 | + try: | ||
390 | + overwrite = surface_parameters['options']['overwrite'] | ||
391 | + except KeyError: | ||
392 | + overwrite = False | ||
393 | + mask.matrix.flush() | ||
394 | + | ||
397 | if quality in const.SURFACE_QUALITY.keys(): | 395 | if quality in const.SURFACE_QUALITY.keys(): |
398 | imagedata_resolution = const.SURFACE_QUALITY[quality][0] | 396 | imagedata_resolution = const.SURFACE_QUALITY[quality][0] |
399 | smooth_iterations = const.SURFACE_QUALITY[quality][1] | 397 | smooth_iterations = const.SURFACE_QUALITY[quality][1] |
@@ -448,8 +446,9 @@ class SurfaceManager(): | @@ -448,8 +446,9 @@ class SurfaceManager(): | ||
448 | smooth_relaxation_factor, | 446 | smooth_relaxation_factor, |
449 | smooth_iterations, language, | 447 | smooth_iterations, language, |
450 | flip_image, q_in, q_out, | 448 | flip_image, q_in, q_out, |
451 | - mask.was_edited and algorithm != u'InVesalius 3.b2', | ||
452 | - algorithm) | 449 | + algorithm != 'Default', |
450 | + algorithm, | ||
451 | + imagedata_resolution) | ||
453 | p.append(sp) | 452 | p.append(sp) |
454 | sp.start() | 453 | sp.start() |
455 | 454 | ||
@@ -501,7 +500,7 @@ class SurfaceManager(): | @@ -501,7 +500,7 @@ class SurfaceManager(): | ||
501 | polydata.SetSource(None) | 500 | polydata.SetSource(None) |
502 | del polydata_append | 501 | del polydata_append |
503 | 502 | ||
504 | - if algorithm == u'Context aware smoothing': | 503 | + if algorithm == 'ca_smoothing': |
505 | normals = vtk.vtkPolyDataNormals() | 504 | normals = vtk.vtkPolyDataNormals() |
506 | normals_ref = weakref.ref(normals) | 505 | normals_ref = weakref.ref(normals) |
507 | normals_ref().AddObserver("ProgressEvent", lambda obj,evt: | 506 | normals_ref().AddObserver("ProgressEvent", lambda obj,evt: |
@@ -542,21 +541,23 @@ class SurfaceManager(): | @@ -542,21 +541,23 @@ class SurfaceManager(): | ||
542 | polydata.DebugOn() | 541 | polydata.DebugOn() |
543 | 542 | ||
544 | else: | 543 | else: |
545 | - smoother = vtk.vtkWindowedSincPolyDataFilter() | 544 | + #smoother = vtk.vtkWindowedSincPolyDataFilter() |
545 | + smoother = vtk.vtkSmoothPolyDataFilter() | ||
546 | smoother_ref = weakref.ref(smoother) | 546 | smoother_ref = weakref.ref(smoother) |
547 | smoother_ref().AddObserver("ProgressEvent", lambda obj,evt: | 547 | smoother_ref().AddObserver("ProgressEvent", lambda obj,evt: |
548 | UpdateProgress(smoother_ref(), _("Generating 3D surface..."))) | 548 | UpdateProgress(smoother_ref(), _("Generating 3D surface..."))) |
549 | smoother.SetInput(polydata) | 549 | smoother.SetInput(polydata) |
550 | smoother.SetNumberOfIterations(smooth_iterations) | 550 | smoother.SetNumberOfIterations(smooth_iterations) |
551 | - smoother.SetFeatureAngle(120) | ||
552 | - smoother.SetEdgeAngle(90.0) | 551 | + smoother.SetRelaxationFactor(smooth_relaxation_factor) |
552 | + smoother.SetFeatureAngle(80) | ||
553 | + #smoother.SetEdgeAngle(90.0) | ||
554 | + #smoother.SetPassBand(0.1) | ||
553 | smoother.BoundarySmoothingOn() | 555 | smoother.BoundarySmoothingOn() |
554 | - smoother.SetPassBand(0.1) | 556 | + smoother.FeatureEdgeSmoothingOn() |
557 | + #smoother.NormalizeCoordinatesOn() | ||
558 | + #smoother.NonManifoldSmoothingOn() | ||
555 | smoother.ReleaseDataFlagOn() | 559 | smoother.ReleaseDataFlagOn() |
556 | smoother.GetOutput().ReleaseDataFlagOn() | 560 | smoother.GetOutput().ReleaseDataFlagOn() |
557 | - #smoother.FeatureEdgeSmoothingOn() | ||
558 | - #smoother.NonManifoldSmoothingOn() | ||
559 | - #smoother.NormalizeCoordinatesOn() | ||
560 | smoother.Update() | 561 | smoother.Update() |
561 | del polydata | 562 | del polydata |
562 | polydata = smoother.GetOutput() | 563 | polydata = smoother.GetOutput() |
invesalius/data/surface_process.py
@@ -7,6 +7,8 @@ import vtk | @@ -7,6 +7,8 @@ import vtk | ||
7 | 7 | ||
8 | import i18n | 8 | import i18n |
9 | import converters | 9 | import converters |
10 | +import imagedata_utils as iu | ||
11 | + | ||
10 | from scipy import ndimage | 12 | from scipy import ndimage |
11 | 13 | ||
12 | class SurfaceProcess(multiprocessing.Process): | 14 | class SurfaceProcess(multiprocessing.Process): |
@@ -15,7 +17,7 @@ class SurfaceProcess(multiprocessing.Process): | @@ -15,7 +17,7 @@ class SurfaceProcess(multiprocessing.Process): | ||
15 | mask_shape, mask_dtype, spacing, mode, min_value, max_value, | 17 | mask_shape, mask_dtype, spacing, mode, min_value, max_value, |
16 | decimate_reduction, smooth_relaxation_factor, | 18 | decimate_reduction, smooth_relaxation_factor, |
17 | smooth_iterations, language, flip_image, q_in, q_out, | 19 | smooth_iterations, language, flip_image, q_in, q_out, |
18 | - from_binary, algorithm): | 20 | + from_binary, algorithm, imagedata_resolution): |
19 | 21 | ||
20 | multiprocessing.Process.__init__(self) | 22 | multiprocessing.Process.__init__(self) |
21 | self.pipe = pipe | 23 | self.pipe = pipe |
@@ -35,6 +37,7 @@ class SurfaceProcess(multiprocessing.Process): | @@ -35,6 +37,7 @@ class SurfaceProcess(multiprocessing.Process): | ||
35 | self.shape = shape | 37 | self.shape = shape |
36 | self.from_binary = from_binary | 38 | self.from_binary = from_binary |
37 | self.algorithm = algorithm | 39 | self.algorithm = algorithm |
40 | + self.imagedata_resolution = imagedata_resolution | ||
38 | 41 | ||
39 | self.mask_filename = mask_filename | 42 | self.mask_filename = mask_filename |
40 | self.mask_shape = mask_shape | 43 | self.mask_shape = mask_shape |
@@ -96,6 +99,9 @@ class SurfaceProcess(multiprocessing.Process): | @@ -96,6 +99,9 @@ class SurfaceProcess(multiprocessing.Process): | ||
96 | "AXIAL") | 99 | "AXIAL") |
97 | del a_image | 100 | del a_image |
98 | 101 | ||
102 | + if self.imagedata_resolution: | ||
103 | + image = iu.ResampleImage3D(image, self.imagedata_resolution) | ||
104 | + | ||
99 | flip = vtk.vtkImageFlip() | 105 | flip = vtk.vtkImageFlip() |
100 | flip.SetInput(image) | 106 | flip.SetInput(image) |
101 | flip.SetFilteredAxis(1) | 107 | flip.SetFilteredAxis(1) |
invesalius/gui/data_notebook.py
@@ -29,6 +29,7 @@ import wx.lib.platebtn as pbtn | @@ -29,6 +29,7 @@ import wx.lib.platebtn as pbtn | ||
29 | from wx.lib.pubsub import pub as Publisher | 29 | from wx.lib.pubsub import pub as Publisher |
30 | 30 | ||
31 | import constants as const | 31 | import constants as const |
32 | +import data.slice_ as slice_ | ||
32 | import gui.dialogs as dlg | 33 | import gui.dialogs as dlg |
33 | import gui.widgets.listctrl as listmix | 34 | import gui.widgets.listctrl as listmix |
34 | import utils as ul | 35 | import utils as ul |
@@ -615,9 +616,10 @@ class SurfaceButtonControlPanel(wx.Panel): | @@ -615,9 +616,10 @@ class SurfaceButtonControlPanel(wx.Panel): | ||
615 | self.OnDuplicate() | 616 | self.OnDuplicate() |
616 | 617 | ||
617 | def OnNew(self): | 618 | def OnNew(self): |
618 | - import project as prj | ||
619 | - | ||
620 | - dialog = dlg.NewSurfaceDialog() | 619 | + sl = slice_.Slice() |
620 | + dialog = dlg.SurfaceCreationDialog(self, -1, | ||
621 | + _('InVesalius 3 - New surface'), | ||
622 | + mask_edited=sl.current_mask.was_edited) | ||
621 | try: | 623 | try: |
622 | if dialog.ShowModal() == wx.ID_OK: | 624 | if dialog.ShowModal() == wx.ID_OK: |
623 | ok = 1 | 625 | ok = 1 |
@@ -627,25 +629,10 @@ class SurfaceButtonControlPanel(wx.Panel): | @@ -627,25 +629,10 @@ class SurfaceButtonControlPanel(wx.Panel): | ||
627 | ok = 1 | 629 | ok = 1 |
628 | 630 | ||
629 | if ok: | 631 | if ok: |
630 | - (mask_index, surface_name, surface_quality, fill_holes,\ | ||
631 | - keep_largest) = dialog.GetValue() | ||
632 | - | ||
633 | - # Retrieve information from mask | ||
634 | - proj = prj.Project() | ||
635 | - mask = proj.mask_dict[mask_index] | ||
636 | - | ||
637 | - # Send all information so surface can be created | ||
638 | - surface_data = [proj.imagedata, | ||
639 | - mask.colour, | ||
640 | - mask.threshold_range, | ||
641 | - mask.edited_points, | ||
642 | - False, # overwrite | ||
643 | - surface_name, | ||
644 | - surface_quality, | ||
645 | - fill_holes, | ||
646 | - keep_largest] | ||
647 | - | ||
648 | - Publisher.sendMessage('Create surface', surface_data) | 632 | + surface_options = dialog.GetValue() |
633 | + | ||
634 | + Publisher.sendMessage('Create surface from index', surface_options) | ||
635 | + dialog.Destroy() | ||
649 | 636 | ||
650 | def OnRemove(self): | 637 | def OnRemove(self): |
651 | self.parent.listctrl.RemoveSurfaces() | 638 | self.parent.listctrl.RemoveSurfaces() |
invesalius/gui/default_tasks.py
@@ -260,7 +260,7 @@ class UpperTaskPanel(wx.Panel): | @@ -260,7 +260,7 @@ class UpperTaskPanel(wx.Panel): | ||
260 | Publisher.subscribe(self.OnFoldExport, 'Fold export task') | 260 | Publisher.subscribe(self.OnFoldExport, 'Fold export task') |
261 | 261 | ||
262 | def OnOverwrite(self, pubsub_evt): | 262 | def OnOverwrite(self, pubsub_evt): |
263 | - self.overwrite = pubsub_evt.data[1] | 263 | + self.overwrite = pubsub_evt.data['options']['overwrite'] |
264 | 264 | ||
265 | def OnFoldSurface(self, pubsub_evt): | 265 | def OnFoldSurface(self, pubsub_evt): |
266 | if not self.overwrite: | 266 | if not self.overwrite: |
invesalius/gui/dialogs.py
@@ -34,6 +34,14 @@ import project as proj | @@ -34,6 +34,14 @@ import project as proj | ||
34 | import session as ses | 34 | import session as ses |
35 | import utils | 35 | import utils |
36 | 36 | ||
37 | +class MaskEvent(wx.PyCommandEvent): | ||
38 | + def __init__(self , evtType, id, mask_index): | ||
39 | + wx.PyCommandEvent.__init__(self, evtType, id,) | ||
40 | + self.mask_index = mask_index | ||
41 | + | ||
42 | +myEVT_MASK_SET = wx.NewEventType() | ||
43 | +EVT_MASK_SET = wx.PyEventBinder(myEVT_MASK_SET, 1) | ||
44 | + | ||
37 | class NumberDialog(wx.Dialog): | 45 | class NumberDialog(wx.Dialog): |
38 | def __init__(self, message, value=0): | 46 | def __init__(self, message, value=0): |
39 | pre = wx.PreDialog() | 47 | pre = wx.PreDialog() |
@@ -964,7 +972,6 @@ class SurfaceDialog(wx.Dialog): | @@ -964,7 +972,6 @@ class SurfaceDialog(wx.Dialog): | ||
964 | def __init__(self): | 972 | def __init__(self): |
965 | wx.Dialog.__init__(self, None, -1, u'Surface generation options') | 973 | wx.Dialog.__init__(self, None, -1, u'Surface generation options') |
966 | self._build_widgets() | 974 | self._build_widgets() |
967 | - self._bind_wx() | ||
968 | self.CenterOnScreen() | 975 | self.CenterOnScreen() |
969 | 976 | ||
970 | def _build_widgets(self): | 977 | def _build_widgets(self): |
@@ -975,12 +982,266 @@ class SurfaceDialog(wx.Dialog): | @@ -975,12 +982,266 @@ class SurfaceDialog(wx.Dialog): | ||
975 | btn_sizer.AddButton(btn_cancel) | 982 | btn_sizer.AddButton(btn_cancel) |
976 | btn_sizer.Realize() | 983 | btn_sizer.Realize() |
977 | 984 | ||
978 | - self.alg_types = {0: u'Context aware smoothing', 1: u'Binary'}#,2: u'InVesalius 3.b2'} | ||
979 | - self.cb_types = wx.ComboBox(self, -1, self.alg_types[0], | ||
980 | - choices=[self.alg_types[i] for i in sorted(self.alg_types)], | ||
981 | - style=wx.CB_READONLY) | 985 | + self.ca = SurfaceMethodPanel(self, -1, True) |
986 | + | ||
987 | + self.main_sizer = wx.BoxSizer(wx.VERTICAL) | ||
988 | + self.main_sizer.Add(self.ca, 0, wx.EXPAND|wx.ALL, 5) | ||
989 | + self.main_sizer.Add(btn_sizer, 0, wx.EXPAND | wx.ALL, 5) | ||
990 | + | ||
991 | + self.SetSizer(self.main_sizer) | ||
992 | + self.Fit() | ||
993 | + | ||
994 | + def GetOptions(self): | ||
995 | + return self.ca.GetOptions() | ||
996 | + | ||
997 | + def GetAlgorithmSelected(self): | ||
998 | + return self.ca.GetAlgorithmSelected() | ||
999 | + | ||
1000 | + | ||
1001 | +####################### New surface creation dialog ########################### | ||
1002 | +class SurfaceCreationDialog(wx.Dialog): | ||
1003 | + def __init__(self, parent=None, ID=-1, title=_(u"Surface creation"), | ||
1004 | + size=wx.DefaultSize, pos=wx.DefaultPosition, | ||
1005 | + style=wx.DEFAULT_DIALOG_STYLE, useMetal=False, | ||
1006 | + mask_edited=False): | ||
1007 | + | ||
1008 | + # Instead of calling wx.Dialog.__init__ we precreate the dialog | ||
1009 | + # so we can set an extra style that must be set before | ||
1010 | + # creation, and then we create the GUI object using the Create | ||
1011 | + # method. | ||
1012 | + pre = wx.PreDialog() | ||
1013 | + pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) | ||
1014 | + pre.Create(parent, ID, title, pos, (50,30), style) | ||
1015 | + | ||
1016 | + # This extra style can be set after the UI object has been created. | ||
1017 | + if 'wxMac' in wx.PlatformInfo and useMetal: | ||
1018 | + self.SetExtraStyle(wx.DIALOG_EX_METAL) | ||
1019 | + | ||
1020 | + # This next step is the most important, it turns this Python | ||
1021 | + # object into the real wrapper of the dialog (instead of pre) | ||
1022 | + # as far as the wxPython extension is concerned. | ||
1023 | + self.PostCreate(pre) | ||
1024 | + | ||
1025 | + self.CenterOnScreen() | ||
1026 | + | ||
1027 | + self.nsd = SurfaceCreationOptionsPanel(self, -1) | ||
1028 | + self.nsd.Bind(EVT_MASK_SET, self.OnSetMask) | ||
1029 | + surface_options_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, | ||
1030 | + _('Surface creation options')), wx.VERTICAL) | ||
1031 | + surface_options_sizer.Add(self.nsd, 0, wx.EXPAND|wx.ALL, 5) | ||
1032 | + | ||
1033 | + self.ca = SurfaceMethodPanel(self, -1, mask_edited) | ||
1034 | + surface_method_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, | ||
1035 | + _('Surface creation method')), wx.VERTICAL) | ||
1036 | + surface_method_sizer.Add(self.ca, 0, wx.EXPAND|wx.ALL, 5) | ||
1037 | + | ||
1038 | + btn_ok = wx.Button(self, wx.ID_OK) | ||
1039 | + btn_ok.SetDefault() | ||
1040 | + btn_cancel = wx.Button(self, wx.ID_CANCEL) | ||
1041 | + | ||
1042 | + btnsizer = wx.StdDialogButtonSizer() | ||
1043 | + btnsizer.AddButton(btn_ok) | ||
1044 | + btnsizer.AddButton(btn_cancel) | ||
1045 | + btnsizer.Realize() | ||
1046 | + | ||
1047 | + sizer_panels = wx.BoxSizer(wx.HORIZONTAL) | ||
1048 | + sizer_panels.Add(surface_method_sizer, 0, wx.EXPAND|wx.ALL, 5) | ||
1049 | + sizer_panels.Add(surface_options_sizer, 0, wx.EXPAND|wx.ALL, 5) | ||
1050 | + | ||
1051 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
1052 | + sizer.Add(sizer_panels, 0, wx.ALIGN_RIGHT|wx.ALL, 5) | ||
1053 | + sizer.Add(btnsizer, 0, wx.ALIGN_RIGHT|wx.ALL, 5) | ||
1054 | + | ||
1055 | + self.SetSizer(sizer) | ||
1056 | + sizer.Fit(self) | ||
1057 | + | ||
1058 | + self.Show() | ||
1059 | + | ||
1060 | + def OnSetMask(self, evt): | ||
1061 | + mask = proj.Project().mask_dict[evt.mask_index] | ||
1062 | + self.ca.mask_edited = mask.was_edited | ||
1063 | + self.ca.ReloadMethodsOptions() | ||
1064 | + | ||
1065 | + | ||
1066 | + | ||
1067 | + def GetValue(self): | ||
1068 | + return {"method": self.ca.GetValue(), | ||
1069 | + "options": self.nsd.GetValue()} | ||
1070 | + | ||
1071 | + | ||
1072 | +class SurfaceCreationOptionsPanel(wx.Panel): | ||
1073 | + def __init__(self, parent, ID=-1): | ||
1074 | + import constants as const | ||
1075 | + import data.surface as surface | ||
1076 | + import project as prj | ||
1077 | + | ||
1078 | + wx.Panel.__init__(self, parent, ID) | ||
1079 | + | ||
1080 | + # LINE 1: Surface name | ||
1081 | + label_surface = wx.StaticText(self, -1, _("New surface name:")) | ||
1082 | + | ||
1083 | + default_name = const.SURFACE_NAME_PATTERN %(surface.Surface.general_index+2) | ||
1084 | + text = wx.TextCtrl(self, -1, "", size=(80,-1)) | ||
1085 | + text.SetHelpText(_("Name the surface to be created")) | ||
1086 | + text.SetValue(default_name) | ||
1087 | + self.text = text | ||
1088 | + | ||
1089 | + # LINE 2: Mask of reference | ||
1090 | + | ||
1091 | + # Informative label | ||
1092 | + label_mask = wx.StaticText(self, -1, _("Mask of reference:")) | ||
1093 | + | ||
1094 | + #Retrieve existing masks | ||
1095 | + project = prj.Project() | ||
1096 | + index_list = project.mask_dict.keys() | ||
1097 | + index_list.sort() | ||
1098 | + self.mask_list = [project.mask_dict[index].name for index in index_list] | ||
1099 | + | ||
1100 | + # Mask selection combo | ||
1101 | + combo_mask = wx.ComboBox(self, -1, "", choices= self.mask_list, | ||
1102 | + style=wx.CB_DROPDOWN|wx.CB_READONLY) | ||
1103 | + combo_mask.SetSelection(len(self.mask_list)-1) | ||
1104 | + combo_mask.Bind(wx.EVT_COMBOBOX, self.OnSetMask) | ||
1105 | + if sys.platform != 'win32': | ||
1106 | + combo_mask.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) | ||
1107 | + self.combo_mask = combo_mask | ||
1108 | + | ||
1109 | + # LINE 3: Surface quality | ||
1110 | + label_quality = wx.StaticText(self, -1, _("Surface quality:")) | ||
1111 | + | ||
1112 | + choices = const.SURFACE_QUALITY_LIST | ||
1113 | + style = wx.CB_DROPDOWN|wx.CB_READONLY | ||
1114 | + combo_quality = wx.ComboBox(self, -1, "", | ||
1115 | + choices= choices, | ||
1116 | + style=style) | ||
1117 | + combo_quality.SetSelection(3) | ||
1118 | + if sys.platform != 'win32': | ||
1119 | + combo_quality.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) | ||
1120 | + self.combo_quality = combo_quality | ||
1121 | + | ||
1122 | + # OVERVIEW | ||
1123 | + # Sizer that joins content above | ||
1124 | + flag_link = wx.EXPAND|wx.GROW|wx.ALL | ||
1125 | + flag_button = wx.ALL | wx.EXPAND| wx.GROW | ||
1126 | + | ||
1127 | + fixed_sizer = wx.FlexGridSizer(rows=2, cols=2, hgap=10, vgap=5) | ||
1128 | + fixed_sizer.AddGrowableCol(0, 1) | ||
1129 | + fixed_sizer.AddMany([ (label_surface, 1, flag_link, 0), | ||
1130 | + (text, 1, flag_button, 0), | ||
1131 | + (label_mask, 1, flag_link, 0), | ||
1132 | + (combo_mask, 0, flag_button, 0), | ||
1133 | + (label_quality, 1, flag_link, 0), | ||
1134 | + (combo_quality, 0, flag_button, 0)]) | ||
1135 | + | ||
1136 | + | ||
1137 | + # LINES 4 and 5: Checkboxes | ||
1138 | + check_box_holes = wx.CheckBox(self, -1, _("Fill holes")) | ||
1139 | + check_box_holes.SetValue(True) | ||
1140 | + self.check_box_holes = check_box_holes | ||
1141 | + check_box_largest = wx.CheckBox(self, -1, _("Keep largest region")) | ||
1142 | + self.check_box_largest = check_box_largest | ||
1143 | + | ||
1144 | + # OVERVIEW | ||
1145 | + # Merge all sizers and checkboxes | ||
1146 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
1147 | + sizer.Add(fixed_sizer, 0, wx.TOP|wx.RIGHT|wx.LEFT|wx.GROW|wx.EXPAND, 5) | ||
1148 | + sizer.Add(check_box_holes, 0, wx.RIGHT|wx.LEFT, 5) | ||
1149 | + sizer.Add(check_box_largest, 0, wx.RIGHT|wx.LEFT, 5) | ||
1150 | + | ||
1151 | + self.SetSizer(sizer) | ||
1152 | + sizer.Fit(self) | ||
1153 | + | ||
1154 | + def OnSetMask(self, evt): | ||
1155 | + new_evt = MaskEvent(myEVT_MASK_SET, -1, self.combo_mask.GetSelection()) | ||
1156 | + self.GetEventHandler().ProcessEvent(new_evt) | ||
1157 | + | ||
1158 | + def GetValue(self): | ||
1159 | + mask_index = self.combo_mask.GetSelection() | ||
1160 | + surface_name = self.text.GetValue() | ||
1161 | + quality = const.SURFACE_QUALITY_LIST[self.combo_quality.GetSelection()] | ||
1162 | + fill_holes = self.check_box_holes.GetValue() | ||
1163 | + keep_largest = self.check_box_largest.GetValue() | ||
1164 | + return {"index": mask_index, | ||
1165 | + "name": surface_name, | ||
1166 | + "quality": quality, | ||
1167 | + "fill": fill_holes, | ||
1168 | + "keep_largest": keep_largest, | ||
1169 | + "overwrite": False} | ||
1170 | + | ||
1171 | + | ||
1172 | +class CAOptions(wx.Panel): | ||
1173 | + ''' | ||
1174 | + Options related to Context aware algorithm: | ||
1175 | + Angle: The min angle to a vertex to be considered a staircase vertex; | ||
1176 | + Max distance: The max distance a normal vertex must be to calculate its | ||
1177 | + weighting; | ||
1178 | + Min Weighting: The min weight a vertex must have; | ||
1179 | + Steps: The number of iterations the smoothing algorithm have to do. | ||
1180 | + ''' | ||
1181 | + def __init__(self, parent): | ||
1182 | + wx.Panel.__init__(self, parent, -1) | ||
1183 | + self._build_widgets() | ||
1184 | + | ||
1185 | + def _build_widgets(self): | ||
1186 | + self.angle = floatspin.FloatSpin(self, -1, value=0.7, min_val=0.0, | ||
1187 | + max_val=1.0, increment=0.1, | ||
1188 | + digits=1) | ||
1189 | + | ||
1190 | + self.max_distance = floatspin.FloatSpin(self, -1, value=3.0, min_val=0.0, | ||
1191 | + max_val=100.0, increment=0.1, | ||
1192 | + digits=2) | ||
1193 | + | ||
1194 | + self.min_weight = floatspin.FloatSpin(self, -1, value=0.2, min_val=0.0, | ||
1195 | + max_val=1.0, increment=0.1, | ||
1196 | + digits=1) | ||
1197 | + | ||
1198 | + self.steps = wx.SpinCtrl(self, -1, value='10', min=1, max=100) | ||
1199 | + | ||
1200 | + layout_sizer = wx.FlexGridSizer(rows=4, cols=2, hgap=5, vgap=5) | ||
1201 | + layout_sizer.Add(wx.StaticText(self, -1, _(u'Angle:')), 0, wx.EXPAND) | ||
1202 | + layout_sizer.Add(self.angle, 0, wx.EXPAND) | ||
1203 | + layout_sizer.Add(wx.StaticText(self, -1, _(u'Max. distance:')), 0, wx.EXPAND) | ||
1204 | + layout_sizer.Add(self.max_distance, 0, wx.EXPAND) | ||
1205 | + layout_sizer.Add(wx.StaticText(self, -1, _(u'Min. weight:')), 0, wx.EXPAND) | ||
1206 | + layout_sizer.Add(self.min_weight, 0, wx.EXPAND) | ||
1207 | + layout_sizer.Add(wx.StaticText(self, -1, _(u'N. steps:')), 0, wx.EXPAND) | ||
1208 | + layout_sizer.Add(self.steps, 0, wx.EXPAND) | ||
1209 | + | ||
1210 | + self.main_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, _('Context aware options')), wx.VERTICAL) | ||
1211 | + self.main_sizer.Add(layout_sizer, 0, wx.EXPAND | wx.ALL, 5) | ||
1212 | + self.SetSizer(self.main_sizer) | ||
1213 | + | ||
1214 | +class SurfaceMethodPanel(wx.Panel): | ||
1215 | + ''' | ||
1216 | + This dialog is only shown when the mask whose surface will be generate was | ||
1217 | + edited. So far, the only options available are the choice of method to | ||
1218 | + generate the surface, Binary or `Context aware smoothing', and options from | ||
1219 | + `Context aware smoothing' | ||
1220 | + ''' | ||
1221 | + def __init__(self, parent, id, mask_edited=False): | ||
1222 | + wx.Panel.__init__(self, parent, id) | ||
1223 | + | ||
1224 | + self.mask_edited = mask_edited | ||
1225 | + self.alg_types = {_(u'Default'): 'Default', | ||
1226 | + _(u'Context aware smoothing'): 'ca_smoothing', | ||
1227 | + _(u'Binary'): 'Binary'} | ||
1228 | + self.edited_imp = [_(u'Default'), ] | ||
1229 | + | ||
1230 | + self._build_widgets() | ||
1231 | + self._bind_wx() | ||
982 | 1232 | ||
1233 | + def _build_widgets(self): | ||
983 | self.ca_options = CAOptions(self) | 1234 | self.ca_options = CAOptions(self) |
1235 | + self.cb_types = wx.ComboBox(self, -1, _(u'Default'), | ||
1236 | + choices=[i for i in sorted(self.alg_types) | ||
1237 | + if not (self.mask_edited and i in self.edited_imp)], | ||
1238 | + style=wx.CB_READONLY) | ||
1239 | + if self.mask_edited: | ||
1240 | + self.cb_types.SetValue(_(u'Context aware smoothing')) | ||
1241 | + self.ca_options.Enable() | ||
1242 | + else: | ||
1243 | + self.ca_options.Disable() | ||
1244 | + | ||
984 | 1245 | ||
985 | method_sizer = wx.BoxSizer(wx.HORIZONTAL) | 1246 | method_sizer = wx.BoxSizer(wx.HORIZONTAL) |
986 | method_sizer.Add(wx.StaticText(self, -1, u'Method:'), 0, | 1247 | method_sizer.Add(wx.StaticText(self, -1, u'Method:'), 0, |
@@ -990,7 +1251,6 @@ class SurfaceDialog(wx.Dialog): | @@ -990,7 +1251,6 @@ class SurfaceDialog(wx.Dialog): | ||
990 | self.main_sizer = wx.BoxSizer(wx.VERTICAL) | 1251 | self.main_sizer = wx.BoxSizer(wx.VERTICAL) |
991 | self.main_sizer.Add(method_sizer, 0, wx.EXPAND | wx.ALL, 5) | 1252 | self.main_sizer.Add(method_sizer, 0, wx.EXPAND | wx.ALL, 5) |
992 | self.main_sizer.Add(self.ca_options, 0, wx.EXPAND | wx.ALL, 5) | 1253 | self.main_sizer.Add(self.ca_options, 0, wx.EXPAND | wx.ALL, 5) |
993 | - self.main_sizer.Add(btn_sizer, 0, wx.EXPAND | wx.ALL, 5) | ||
994 | 1254 | ||
995 | self.SetSizer(self.main_sizer) | 1255 | self.SetSizer(self.main_sizer) |
996 | self.Fit() | 1256 | self.Fit() |
@@ -1000,7 +1260,7 @@ class SurfaceDialog(wx.Dialog): | @@ -1000,7 +1260,7 @@ class SurfaceDialog(wx.Dialog): | ||
1000 | 1260 | ||
1001 | def _set_cb_types(self, evt): | 1261 | def _set_cb_types(self, evt): |
1002 | print evt.GetString() | 1262 | print evt.GetString() |
1003 | - if self.alg_types[evt.GetSelection()] == self.alg_types[0]: | 1263 | + if self.alg_types[evt.GetString()] == 'ca_smoothing': |
1004 | self.ca_options.Enable() | 1264 | self.ca_options.Enable() |
1005 | else: | 1265 | else: |
1006 | self.ca_options.Disable() | 1266 | self.ca_options.Disable() |
@@ -1008,12 +1268,12 @@ class SurfaceDialog(wx.Dialog): | @@ -1008,12 +1268,12 @@ class SurfaceDialog(wx.Dialog): | ||
1008 | 1268 | ||
1009 | def GetAlgorithmSelected(self): | 1269 | def GetAlgorithmSelected(self): |
1010 | try: | 1270 | try: |
1011 | - return self.alg_types[self.cb_types.GetSelection()] | 1271 | + return self.alg_types[self.cb_types.GetValue()] |
1012 | except KeyError: | 1272 | except KeyError: |
1013 | return self.alg_types[0] | 1273 | return self.alg_types[0] |
1014 | 1274 | ||
1015 | def GetOptions(self): | 1275 | def GetOptions(self): |
1016 | - if self.GetAlgorithmSelected() == self.alg_types[0]: | 1276 | + if self.GetAlgorithmSelected() == 'ca_smoothing': |
1017 | options = {'angle': self.ca_options.angle.GetValue(), | 1277 | options = {'angle': self.ca_options.angle.GetValue(), |
1018 | 'max distance': self.ca_options.max_distance.GetValue(), | 1278 | 'max distance': self.ca_options.max_distance.GetValue(), |
1019 | 'min weight': self.ca_options.min_weight.GetValue(), | 1279 | 'min weight': self.ca_options.min_weight.GetValue(), |
@@ -1022,4 +1282,20 @@ class SurfaceDialog(wx.Dialog): | @@ -1022,4 +1282,20 @@ class SurfaceDialog(wx.Dialog): | ||
1022 | options = {} | 1282 | options = {} |
1023 | return options | 1283 | return options |
1024 | 1284 | ||
1285 | + def GetValue(self): | ||
1286 | + algorithm = self.GetAlgorithmSelected() | ||
1287 | + options = self.GetOptions() | ||
1025 | 1288 | ||
1289 | + return {"algorithm": algorithm, | ||
1290 | + "options": options} | ||
1291 | + | ||
1292 | + def ReloadMethodsOptions(self): | ||
1293 | + self.cb_types.Clear() | ||
1294 | + self.cb_types.AppendItems([i for i in sorted(self.alg_types) | ||
1295 | + if not (self.mask_edited and i in self.edited_imp)]) | ||
1296 | + if self.mask_edited: | ||
1297 | + self.cb_types.SetValue(_(u'Context aware smoothing')) | ||
1298 | + self.ca_options.Enable() | ||
1299 | + else: | ||
1300 | + self.cb_types.SetValue(_(u'Default')) | ||
1301 | + self.ca_options.Disable() |
invesalius/gui/task_slice.py
@@ -155,9 +155,19 @@ class InnerTaskPanel(wx.Panel): | @@ -155,9 +155,19 @@ class InnerTaskPanel(wx.Panel): | ||
155 | options = dlgs.GetOptions() | 155 | options = dlgs.GetOptions() |
156 | else: | 156 | else: |
157 | return | 157 | return |
158 | - Publisher.sendMessage('Create surface from index', | ||
159 | - (self.GetMaskSelected(), | ||
160 | - overwrite, algorithm, options)) | 158 | + |
159 | + mask_index = sl.current_mask.index | ||
160 | + method = {'algorithm': 'Default', | ||
161 | + 'options': options} | ||
162 | + srf_options = {"index": mask_index, | ||
163 | + "name": '', | ||
164 | + "quality": _('Optimal *'), | ||
165 | + "fill": False, | ||
166 | + "keep_largest": False, | ||
167 | + "overwrite": overwrite} | ||
168 | + | ||
169 | + Publisher.sendMessage('Create surface from index', | ||
170 | + {'method': method, 'options': srf_options}) | ||
161 | Publisher.sendMessage('Fold surface task') | 171 | Publisher.sendMessage('Fold surface task') |
162 | else: | 172 | else: |
163 | dlg.InexistentMask() | 173 | dlg.InexistentMask() |
invesalius/gui/task_surface.py
@@ -23,6 +23,7 @@ import wx.lib.hyperlink as hl | @@ -23,6 +23,7 @@ import wx.lib.hyperlink as hl | ||
23 | from wx.lib.pubsub import pub as Publisher | 23 | from wx.lib.pubsub import pub as Publisher |
24 | 24 | ||
25 | import constants as const | 25 | import constants as const |
26 | +import data.slice_ as slice_ | ||
26 | import gui.dialogs as dlg | 27 | import gui.dialogs as dlg |
27 | import gui.widgets.foldpanelbar as fpb | 28 | import gui.widgets.foldpanelbar as fpb |
28 | import gui.widgets.colourselect as csel | 29 | import gui.widgets.colourselect as csel |
@@ -30,7 +31,6 @@ import gui.widgets.platebtn as pbtn | @@ -30,7 +31,6 @@ import gui.widgets.platebtn as pbtn | ||
30 | import project as prj | 31 | import project as prj |
31 | import utils as utl | 32 | import utils as utl |
32 | 33 | ||
33 | - | ||
34 | #INTERPOLATION_MODE_LIST = ["Cubic", "Linear", "NearestNeighbor"] | 34 | #INTERPOLATION_MODE_LIST = ["Cubic", "Linear", "NearestNeighbor"] |
35 | MIN_TRANSPARENCY = 0 | 35 | MIN_TRANSPARENCY = 0 |
36 | MAX_TRANSPARENCY = 100 | 36 | MAX_TRANSPARENCY = 100 |
@@ -131,7 +131,10 @@ class InnerTaskPanel(wx.Panel): | @@ -131,7 +131,10 @@ class InnerTaskPanel(wx.Panel): | ||
131 | 131 | ||
132 | def OnLinkNewSurface(self, evt=None): | 132 | def OnLinkNewSurface(self, evt=None): |
133 | #import gui.dialogs as dlg | 133 | #import gui.dialogs as dlg |
134 | - dialog = dlg.NewSurfaceDialog(self, -1, _('InVesalius 3 - New surface')) | 134 | + sl = slice_.Slice() |
135 | + dialog = dlg.SurfaceCreationDialog(self, -1, | ||
136 | + _('InVesalius 3 - New surface'), | ||
137 | + mask_edited=sl.current_mask.was_edited) | ||
135 | 138 | ||
136 | try: | 139 | try: |
137 | if dialog.ShowModal() == wx.ID_OK: | 140 | if dialog.ShowModal() == wx.ID_OK: |
@@ -142,26 +145,29 @@ class InnerTaskPanel(wx.Panel): | @@ -142,26 +145,29 @@ class InnerTaskPanel(wx.Panel): | ||
142 | ok = 1 | 145 | ok = 1 |
143 | 146 | ||
144 | if (ok): | 147 | if (ok): |
145 | - # Retrieve information from dialog | ||
146 | - (mask_index, surface_name, surface_quality, fill_holes,\ | ||
147 | - keep_largest) = dialog.GetValue() | ||
148 | - | ||
149 | - # Retrieve information from mask | ||
150 | - proj = prj.Project() | ||
151 | - mask = proj.mask_dict[mask_index] | ||
152 | - | ||
153 | - # Send all information so surface can be created | ||
154 | - surface_data = [proj.imagedata, | ||
155 | - mask.colour, | ||
156 | - mask.threshold_range, | ||
157 | - mask.edited_points, | ||
158 | - False, # overwrite | ||
159 | - surface_name, | ||
160 | - surface_quality, | ||
161 | - fill_holes, | ||
162 | - keep_largest] | ||
163 | - | ||
164 | - Publisher.sendMessage('Create surface', surface_data) | 148 | + ## Retrieve information from dialog |
149 | + #(mask_index, surface_name, surface_quality, fill_holes,\ | ||
150 | + #keep_largest) = dialog.GetValue() | ||
151 | + | ||
152 | + ## Retrieve information from mask | ||
153 | + #proj = prj.Project() | ||
154 | + #mask = proj.mask_dict[mask_index] | ||
155 | + | ||
156 | + ## Send all information so surface can be created | ||
157 | + #surface_data = [proj.imagedata, | ||
158 | + #mask.colour, | ||
159 | + #mask.threshold_range, | ||
160 | + #mask.edited_points, | ||
161 | + #False, # overwrite | ||
162 | + #surface_name, | ||
163 | + #surface_quality, | ||
164 | + #fill_holes, | ||
165 | + #keep_largest] | ||
166 | + | ||
167 | + | ||
168 | + surface_options = dialog.GetValue() | ||
169 | + | ||
170 | + Publisher.sendMessage('Create surface from index', surface_options) | ||
165 | dialog.Destroy() | 171 | dialog.Destroy() |
166 | if evt: | 172 | if evt: |
167 | evt.Skip() | 173 | evt.Skip() |