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 | 596 | #--------------------------------------------------------------------------- |
597 | 597 | |
598 | 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 | 602 | proj = Project() |
602 | - mask = proj.mask_dict[mask_index] | |
603 | + mask = proj.mask_dict[surface_parameters['options']['index']] | |
603 | 604 | |
604 | 605 | # This is very important. Do not use masks' imagedata. It would mess up |
605 | 606 | # surface quality event when using contour |
... | ... | @@ -611,12 +612,8 @@ class Slice(object): |
611 | 612 | |
612 | 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 | 617 | def GetOutput(self): |
621 | 618 | return self.blend_filter.GetOutput() |
622 | 619 | ... | ... |
invesalius/data/surface.py
... | ... | @@ -369,31 +369,29 @@ class SurfaceManager(): |
369 | 369 | """ |
370 | 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 | 385 | mode = 'CONTOUR' # 'GRAYSCALE' |
392 | - quality=_('Optimal *') | |
393 | - keep_largest = False | |
394 | - surface_name = "" | |
386 | + min_value, max_value = mask.threshold_range | |
395 | 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 | 395 | if quality in const.SURFACE_QUALITY.keys(): |
398 | 396 | imagedata_resolution = const.SURFACE_QUALITY[quality][0] |
399 | 397 | smooth_iterations = const.SURFACE_QUALITY[quality][1] |
... | ... | @@ -448,8 +446,9 @@ class SurfaceManager(): |
448 | 446 | smooth_relaxation_factor, |
449 | 447 | smooth_iterations, language, |
450 | 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 | 452 | p.append(sp) |
454 | 453 | sp.start() |
455 | 454 | |
... | ... | @@ -501,7 +500,7 @@ class SurfaceManager(): |
501 | 500 | polydata.SetSource(None) |
502 | 501 | del polydata_append |
503 | 502 | |
504 | - if algorithm == u'Context aware smoothing': | |
503 | + if algorithm == 'ca_smoothing': | |
505 | 504 | normals = vtk.vtkPolyDataNormals() |
506 | 505 | normals_ref = weakref.ref(normals) |
507 | 506 | normals_ref().AddObserver("ProgressEvent", lambda obj,evt: |
... | ... | @@ -542,21 +541,23 @@ class SurfaceManager(): |
542 | 541 | polydata.DebugOn() |
543 | 542 | |
544 | 543 | else: |
545 | - smoother = vtk.vtkWindowedSincPolyDataFilter() | |
544 | + #smoother = vtk.vtkWindowedSincPolyDataFilter() | |
545 | + smoother = vtk.vtkSmoothPolyDataFilter() | |
546 | 546 | smoother_ref = weakref.ref(smoother) |
547 | 547 | smoother_ref().AddObserver("ProgressEvent", lambda obj,evt: |
548 | 548 | UpdateProgress(smoother_ref(), _("Generating 3D surface..."))) |
549 | 549 | smoother.SetInput(polydata) |
550 | 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 | 555 | smoother.BoundarySmoothingOn() |
554 | - smoother.SetPassBand(0.1) | |
556 | + smoother.FeatureEdgeSmoothingOn() | |
557 | + #smoother.NormalizeCoordinatesOn() | |
558 | + #smoother.NonManifoldSmoothingOn() | |
555 | 559 | smoother.ReleaseDataFlagOn() |
556 | 560 | smoother.GetOutput().ReleaseDataFlagOn() |
557 | - #smoother.FeatureEdgeSmoothingOn() | |
558 | - #smoother.NonManifoldSmoothingOn() | |
559 | - #smoother.NormalizeCoordinatesOn() | |
560 | 561 | smoother.Update() |
561 | 562 | del polydata |
562 | 563 | polydata = smoother.GetOutput() | ... | ... |
invesalius/data/surface_process.py
... | ... | @@ -7,6 +7,8 @@ import vtk |
7 | 7 | |
8 | 8 | import i18n |
9 | 9 | import converters |
10 | +import imagedata_utils as iu | |
11 | + | |
10 | 12 | from scipy import ndimage |
11 | 13 | |
12 | 14 | class SurfaceProcess(multiprocessing.Process): |
... | ... | @@ -15,7 +17,7 @@ class SurfaceProcess(multiprocessing.Process): |
15 | 17 | mask_shape, mask_dtype, spacing, mode, min_value, max_value, |
16 | 18 | decimate_reduction, smooth_relaxation_factor, |
17 | 19 | smooth_iterations, language, flip_image, q_in, q_out, |
18 | - from_binary, algorithm): | |
20 | + from_binary, algorithm, imagedata_resolution): | |
19 | 21 | |
20 | 22 | multiprocessing.Process.__init__(self) |
21 | 23 | self.pipe = pipe |
... | ... | @@ -35,6 +37,7 @@ class SurfaceProcess(multiprocessing.Process): |
35 | 37 | self.shape = shape |
36 | 38 | self.from_binary = from_binary |
37 | 39 | self.algorithm = algorithm |
40 | + self.imagedata_resolution = imagedata_resolution | |
38 | 41 | |
39 | 42 | self.mask_filename = mask_filename |
40 | 43 | self.mask_shape = mask_shape |
... | ... | @@ -96,6 +99,9 @@ class SurfaceProcess(multiprocessing.Process): |
96 | 99 | "AXIAL") |
97 | 100 | del a_image |
98 | 101 | |
102 | + if self.imagedata_resolution: | |
103 | + image = iu.ResampleImage3D(image, self.imagedata_resolution) | |
104 | + | |
99 | 105 | flip = vtk.vtkImageFlip() |
100 | 106 | flip.SetInput(image) |
101 | 107 | flip.SetFilteredAxis(1) | ... | ... |
invesalius/gui/data_notebook.py
... | ... | @@ -29,6 +29,7 @@ import wx.lib.platebtn as pbtn |
29 | 29 | from wx.lib.pubsub import pub as Publisher |
30 | 30 | |
31 | 31 | import constants as const |
32 | +import data.slice_ as slice_ | |
32 | 33 | import gui.dialogs as dlg |
33 | 34 | import gui.widgets.listctrl as listmix |
34 | 35 | import utils as ul |
... | ... | @@ -615,9 +616,10 @@ class SurfaceButtonControlPanel(wx.Panel): |
615 | 616 | self.OnDuplicate() |
616 | 617 | |
617 | 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 | 623 | try: |
622 | 624 | if dialog.ShowModal() == wx.ID_OK: |
623 | 625 | ok = 1 |
... | ... | @@ -627,25 +629,10 @@ class SurfaceButtonControlPanel(wx.Panel): |
627 | 629 | ok = 1 |
628 | 630 | |
629 | 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 | 637 | def OnRemove(self): |
651 | 638 | self.parent.listctrl.RemoveSurfaces() | ... | ... |
invesalius/gui/default_tasks.py
... | ... | @@ -260,7 +260,7 @@ class UpperTaskPanel(wx.Panel): |
260 | 260 | Publisher.subscribe(self.OnFoldExport, 'Fold export task') |
261 | 261 | |
262 | 262 | def OnOverwrite(self, pubsub_evt): |
263 | - self.overwrite = pubsub_evt.data[1] | |
263 | + self.overwrite = pubsub_evt.data['options']['overwrite'] | |
264 | 264 | |
265 | 265 | def OnFoldSurface(self, pubsub_evt): |
266 | 266 | if not self.overwrite: | ... | ... |
invesalius/gui/dialogs.py
... | ... | @@ -34,6 +34,14 @@ import project as proj |
34 | 34 | import session as ses |
35 | 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 | 45 | class NumberDialog(wx.Dialog): |
38 | 46 | def __init__(self, message, value=0): |
39 | 47 | pre = wx.PreDialog() |
... | ... | @@ -964,7 +972,6 @@ class SurfaceDialog(wx.Dialog): |
964 | 972 | def __init__(self): |
965 | 973 | wx.Dialog.__init__(self, None, -1, u'Surface generation options') |
966 | 974 | self._build_widgets() |
967 | - self._bind_wx() | |
968 | 975 | self.CenterOnScreen() |
969 | 976 | |
970 | 977 | def _build_widgets(self): |
... | ... | @@ -975,12 +982,266 @@ class SurfaceDialog(wx.Dialog): |
975 | 982 | btn_sizer.AddButton(btn_cancel) |
976 | 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 | 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 | 1246 | method_sizer = wx.BoxSizer(wx.HORIZONTAL) |
986 | 1247 | method_sizer.Add(wx.StaticText(self, -1, u'Method:'), 0, |
... | ... | @@ -990,7 +1251,6 @@ class SurfaceDialog(wx.Dialog): |
990 | 1251 | self.main_sizer = wx.BoxSizer(wx.VERTICAL) |
991 | 1252 | self.main_sizer.Add(method_sizer, 0, wx.EXPAND | wx.ALL, 5) |
992 | 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 | 1255 | self.SetSizer(self.main_sizer) |
996 | 1256 | self.Fit() |
... | ... | @@ -1000,7 +1260,7 @@ class SurfaceDialog(wx.Dialog): |
1000 | 1260 | |
1001 | 1261 | def _set_cb_types(self, evt): |
1002 | 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 | 1264 | self.ca_options.Enable() |
1005 | 1265 | else: |
1006 | 1266 | self.ca_options.Disable() |
... | ... | @@ -1008,12 +1268,12 @@ class SurfaceDialog(wx.Dialog): |
1008 | 1268 | |
1009 | 1269 | def GetAlgorithmSelected(self): |
1010 | 1270 | try: |
1011 | - return self.alg_types[self.cb_types.GetSelection()] | |
1271 | + return self.alg_types[self.cb_types.GetValue()] | |
1012 | 1272 | except KeyError: |
1013 | 1273 | return self.alg_types[0] |
1014 | 1274 | |
1015 | 1275 | def GetOptions(self): |
1016 | - if self.GetAlgorithmSelected() == self.alg_types[0]: | |
1276 | + if self.GetAlgorithmSelected() == 'ca_smoothing': | |
1017 | 1277 | options = {'angle': self.ca_options.angle.GetValue(), |
1018 | 1278 | 'max distance': self.ca_options.max_distance.GetValue(), |
1019 | 1279 | 'min weight': self.ca_options.min_weight.GetValue(), |
... | ... | @@ -1022,4 +1282,20 @@ class SurfaceDialog(wx.Dialog): |
1022 | 1282 | options = {} |
1023 | 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 | 155 | options = dlgs.GetOptions() |
156 | 156 | else: |
157 | 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 | 171 | Publisher.sendMessage('Fold surface task') |
162 | 172 | else: |
163 | 173 | dlg.InexistentMask() | ... | ... |
invesalius/gui/task_surface.py
... | ... | @@ -23,6 +23,7 @@ import wx.lib.hyperlink as hl |
23 | 23 | from wx.lib.pubsub import pub as Publisher |
24 | 24 | |
25 | 25 | import constants as const |
26 | +import data.slice_ as slice_ | |
26 | 27 | import gui.dialogs as dlg |
27 | 28 | import gui.widgets.foldpanelbar as fpb |
28 | 29 | import gui.widgets.colourselect as csel |
... | ... | @@ -30,7 +31,6 @@ import gui.widgets.platebtn as pbtn |
30 | 31 | import project as prj |
31 | 32 | import utils as utl |
32 | 33 | |
33 | - | |
34 | 34 | #INTERPOLATION_MODE_LIST = ["Cubic", "Linear", "NearestNeighbor"] |
35 | 35 | MIN_TRANSPARENCY = 0 |
36 | 36 | MAX_TRANSPARENCY = 100 |
... | ... | @@ -131,7 +131,10 @@ class InnerTaskPanel(wx.Panel): |
131 | 131 | |
132 | 132 | def OnLinkNewSurface(self, evt=None): |
133 | 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 | 139 | try: |
137 | 140 | if dialog.ShowModal() == wx.ID_OK: |
... | ... | @@ -142,26 +145,29 @@ class InnerTaskPanel(wx.Panel): |
142 | 145 | ok = 1 |
143 | 146 | |
144 | 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 | 171 | dialog.Destroy() |
166 | 172 | if evt: |
167 | 173 | evt.Skip() | ... | ... |