Commit a2be4d98b0fd159de5f1a9726ab7e64d35bee9e3

Authored by tatiana
1 parent f532e845

ADD: New surface dialog (fix #127)

invesalius/constants.py
... ... @@ -202,6 +202,7 @@ SURFACE_QUALITY = {
202 202 _("High"): (0, 1, 0.3000, 0.1),
203 203 _("Optimal *"): (0, 2, 0.3000, 0.4)}
204 204 DEFAULT_SURFACE_QUALITY = _("Optimal *")
  205 +SURFACE_QUALITY_LIST = [_("Low"),_("Medium"),_("High"),_("Optimal *")]
205 206  
206 207  
207 208 # Surface properties
... ...
invesalius/data/surface.py
... ... @@ -39,7 +39,7 @@ class Surface():
39 39 Represent both vtkPolyData and associated properties.
40 40 """
41 41 general_index = -1
42   - def __init__(self, index=None):
  42 + def __init__(self, index=None, name=""):
43 43 Surface.general_index += 1
44 44 if index is None:
45 45 self.index = Surface.general_index
... ... @@ -51,7 +51,10 @@ class Surface():
51 51 self.transparency = const.SURFACE_TRANSPARENCY
52 52 self.volume = 0
53 53 self.is_shown = 1
54   - self.name = const.SURFACE_NAME_PATTERN %(self.index+1)
  54 + if not name:
  55 + self.name = const.SURFACE_NAME_PATTERN %(self.index+1)
  56 + else:
  57 + self.name = name
55 58  
56 59 def SavePlist(self, filename):
57 60 surface = {}
... ... @@ -186,17 +189,31 @@ class SurfaceManager():
186 189 (surface.index, surface.name,
187 190 surface.colour, surface.volume,
188 191 surface.transparency))
  192 +
  193 + ####
  194 + #(mask_index, surface_name, quality, fill_holes, keep_largest)
  195 +
189 196 def AddNewActor(self, pubsub_evt):
190 197 """
191 198 Create surface actor, save into project and send it to viewer.
192 199 """
193   - imagedata, colour, [min_value, max_value], \
194   - edited_points, overwrite = pubsub_evt.data
195   -
  200 + surface_data = pubsub_evt.data
  201 +
  202 + if len(surface_data) == 5:
  203 + imagedata, colour, [min_value, max_value], \
  204 + edited_points, overwrite = pubsub_evt.data
  205 + quality=_('Optimal *')
  206 + surface_name = ""
  207 + fill_holes = True
  208 + keep_largest = False
  209 + else:
  210 + imagedata, colour, [min_value, max_value],\
  211 + edited_points, overwrite, surface_name,\
  212 + quality, fill_holes, keep_largest =\
  213 + pubsub_evt.data
196 214  
197   - print "---------------- OVERWRITE:",overwrite
198   - quality=_('Optimal *')
199 215 mode = 'CONTOUR' # 'GRAYSCALE'
  216 +
200 217 ps.Publisher().sendMessage('Begin busy cursor')
201 218 imagedata_tmp = None
202 219 if (edited_points):
... ... @@ -214,11 +231,15 @@ class SurfaceManager():
214 231 if imagedata_resolution:
215 232 imagedata = iu.ResampleImage3D(imagedata, imagedata_resolution)
216 233  
217   - pipeline_size = 4
  234 + pipeline_size = 3
218 235 if decimate_reduction:
219 236 pipeline_size += 1
220 237 if (smooth_iterations and smooth_relaxation_factor):
221 238 pipeline_size += 1
  239 + if fill_holes:
  240 + pipeline_size += 1
  241 + if keep_largest:
  242 + pipeline_size += 1
222 243  
223 244 # Update progress value in GUI
224 245 UpdateProgress = vu.ShowProgress(pipeline_size)
... ... @@ -236,7 +257,7 @@ class SurfaceManager():
236 257 pipe_in, pipe_out = multiprocessing.Pipe()
237 258 sp = surface_process.SurfaceProcess(pipe_in, filename_img, mode, min_value, max_value,
238 259 decimate_reduction, smooth_relaxation_factor,
239   - smooth_iterations, language)
  260 + smooth_iterations, language, fill_holes, keep_largest)
240 261 sp.start()
241 262  
242 263 while 1:
... ... @@ -282,28 +303,27 @@ class SurfaceManager():
282 303 if overwrite:
283 304 surface = Surface(index = self.last_surface_index)
284 305 else:
285   - surface = Surface()
  306 + surface = Surface(name=surface_name)
286 307 surface.colour = colour
287 308 surface.polydata = polydata
288 309  
289   -
290 310 # Set actor colour and transparency
291 311 actor.GetProperty().SetColor(colour)
292 312 actor.GetProperty().SetOpacity(1-surface.transparency)
293 313  
294 314 # Remove temporary files
295   - if sys.platform == "win32":
296   - try:
297   - os.remove(filename_img)
298   - os.remove(filename_polydata)
299   - except (WindowsError):
300   - print "Error while removing surface temporary file"
301   - else: # sys.platform == "linux2" or sys.platform == "darwin"
302   - try:
303   - os.remove(filename_img)
304   - os.remove(filename_polydata)
305   - except (OSError):
306   - print "Error while removing surface temporary file"
  315 + #if sys.platform == "win32":
  316 + # try:
  317 + # os.remove(filename_img)
  318 + # os.remove(filename_polydata)
  319 + # except (WindowsError):
  320 + # print "Error while removing surface temporary file"
  321 + #else: # sys.platform == "linux2" or sys.platform == "darwin"
  322 + # try:
  323 + # os.remove(filename_img)
  324 + # os.remove(filename_polydata)
  325 + # except (OSError):
  326 + # print "Error while removing surface temporary file"
307 327  
308 328 # Append surface into Project.surface_dict
309 329 proj = prj.Project()
... ...
invesalius/data/surface_process.py
... ... @@ -8,7 +8,7 @@ class SurfaceProcess(multiprocessing.Process):
8 8  
9 9 def __init__(self, pipe, filename, mode, min_value, max_value,
10 10 decimate_reduction, smooth_relaxation_factor,
11   - smooth_iterations, language):
  11 + smooth_iterations, language, fill_holes, keep_largest):
12 12  
13 13 multiprocessing.Process.__init__(self)
14 14 self.pipe = pipe
... ... @@ -20,6 +20,9 @@ class SurfaceProcess(multiprocessing.Process):
20 20 self.smooth_relaxation_factor = smooth_relaxation_factor
21 21 self.smooth_iterations = smooth_iterations
22 22 self.language = language
  23 + self.fill_holes = fill_holes
  24 + self.keep_largest = keep_largest
  25 +
23 26  
24 27 def run(self):
25 28 self.CreateSurface()
... ... @@ -86,15 +89,26 @@ class SurfaceProcess(multiprocessing.Process):
86 89 self.SendProgress(obj, _("Generating 3D surface...")))
87 90 polydata = smoother.GetOutput()
88 91  
  92 +
  93 + if self.keep_largest:
  94 + conn = vtk.vtkPolyDataConnectivityFilter()
  95 + conn.SetInput(polydata)
  96 + conn.SetExtractionModeToLargestRegion()
  97 + conn.AddObserver("ProgressEvent", lambda obj,evt:
  98 + self.SendProgress(obj, _("Generating 3D surface...")))
  99 + polydata = conn.GetOutput()
  100 +
89 101 # Filter used to detect and fill holes. Only fill boundary edges holes.
90 102 #TODO: Hey! This piece of code is the same from
91 103 # polydata_utils.FillSurfaceHole, we need to review this.
92   - filled_polydata = vtk.vtkFillHolesFilter()
93   - filled_polydata.SetInput(polydata)
94   - filled_polydata.SetHoleSize(500)
95   - filled_polydata.AddObserver("ProgressEvent", lambda obj,evt:
96   - self.SendProgress(obj, _("Generating 3D surface...")))
97   - polydata = filled_polydata.GetOutput()
  104 + if self.fill_holes:
  105 + filled_polydata = vtk.vtkFillHolesFilter()
  106 + filled_polydata.SetInput(polydata)
  107 + filled_polydata.SetHoleSize(300)
  108 + filled_polydata.AddObserver("ProgressEvent", lambda obj,evt:
  109 + self.SendProgress(obj, _("Generating 3D surface...")))
  110 + polydata = filled_polydata.GetOutput()
  111 +
98 112  
99 113  
100 114 filename = tempfile.mktemp()
... ... @@ -104,4 +118,4 @@ class SurfaceProcess(multiprocessing.Process):
104 118 writer.Write()
105 119  
106 120 self.pipe.send(None)
107   - self.pipe.send(filename)
108 121 \ No newline at end of file
  122 + self.pipe.send(filename)
... ...
invesalius/gui/dialogs.py
... ... @@ -393,13 +393,13 @@ def ShowSavePresetDialog(default_filename="raycasting"):
393 393  
394 394 return filename
395 395  
396   -MASK_LIST = []
397 396 class NewSurfaceDialog(wx.Dialog):
398 397 def __init__(self, parent, ID, title, size=wx.DefaultSize,
399 398 pos=wx.DefaultPosition, style=wx.DEFAULT_DIALOG_STYLE,
400 399 useMetal=False):
401   - import data.surface as surface
402 400 import constants as const
  401 + import data.surface as surface
  402 + import project as prj
403 403  
404 404 # Instead of calling wx.Dialog.__init__ we precreate the dialog
405 405 # so we can set an extra style that must be set before
... ... @@ -420,57 +420,98 @@ class NewSurfaceDialog(wx.Dialog):
420 420  
421 421 self.CenterOnScreen()
422 422  
423   - # Now continue with the normal construction of the dialog
424   - # contents
  423 + # LINE 1: Surface name
  424 +
  425 + label_surface = wx.StaticText(self, -1, _("New surface name:"))
  426 +
  427 + default_name = const.SURFACE_NAME_PATTERN %(surface.Surface.general_index+2)
  428 + text = wx.TextCtrl(self, -1, "", size=(80,-1))
  429 + text.SetHelpText(_("Name the surface to be created"))
  430 + text.SetValue(default_name)
  431 + self.text = text
425 432  
426   - # Label related to mask name
427   - label_mask = wx.StaticText(self, -1, _("Select mask to be used for creating 3D surface:"))
  433 + # LINE 2: Mask of reference
428 434  
429   - # Combo related to mask name
430   - combo_surface_name = wx.ComboBox(self, -1, "", choices= MASK_LIST,
  435 + # Informative label
  436 + label_mask = wx.StaticText(self, -1, _("Mask of reference:"))
  437 +
  438 + # Retrieve existing masks
  439 + project = prj.Project()
  440 + index_list = project.mask_dict.keys()
  441 + index_list.sort()
  442 + self.mask_list = [project.mask_dict[index].name for index in index_list]
  443 +
  444 +
  445 + # Mask selection combo
  446 + combo_mask = wx.ComboBox(self, -1, "", choices= self.mask_list,
431 447 style=wx.CB_DROPDOWN|wx.CB_READONLY)
432   - combo_surface_name.SetSelection(0)
  448 + combo_mask.SetSelection(len(self.mask_list)-1)
433 449 if sys.platform != 'win32':
434   - combo_surface_name.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
435   - self.combo_surface_name = combo_surface_name
  450 + combo_mask.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
  451 + self.combo_mask = combo_mask
436 452  
  453 + # LINE 3: Surface quality
  454 + label_quality = wx.StaticText(self, -1, _("Surface quality:"))
437 455  
438   - label_surface = wx.StaticText(self, -1, _("Set new surface name:"))
  456 + combo_quality = wx.ComboBox(self, -1, "", choices= const.SURFACE_QUALITY_LIST,
  457 + style=wx.CB_DROPDOWN|wx.CB_READONLY)
  458 + combo_quality.SetSelection(3)
  459 + if sys.platform != 'win32':
  460 + combo_quality.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
  461 + self.combo_quality = combo_quality
439 462  
440   - text = wx.TextCtrl(self, -1, "", size=(80,-1))
441   - text.SetHelpText(_("Name of the new surface to be created"))
442 463  
443   - default_name = const.SURFACE_NAME_PATTERN %(surface.Surface.general_index+2)
444   - text.SetValue(default_name)
445   - self.text = text
  464 + # OVERVIEW
  465 + # Sizer that joins content above
  466 + flag_link = wx.EXPAND|wx.GROW|wx.ALL
  467 + flag_button = wx.ALL | wx.EXPAND| wx.GROW
446 468  
447   - sizer = wx.BoxSizer(wx.VERTICAL)
448   - sizer.Add(label_mask, 0, wx.ALL|wx.GROW|wx.EXPAND, 5)
449   - sizer.Add(combo_surface_name, 1, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT, 10)
450   - sizer.Add(label_surface, 0, wx.ALL|wx.GROW|wx.EXPAND, 5)
451   - sizer.Add(text, 0, wx.GROW|wx.EXPAND|wx.RIGHT|wx.LEFT, 10)
  469 + fixed_sizer = wx.FlexGridSizer(rows=2, cols=2, hgap=10, vgap=0)
  470 + fixed_sizer.AddGrowableCol(0, 1)
  471 + fixed_sizer.AddMany([ (label_surface, 1, flag_link, 5),
  472 + (text, 1, flag_button, 2),
  473 + (label_mask, 1, flag_link, 5),
  474 + (combo_mask, 0, flag_button, 1),
  475 + (label_quality, 1, flag_link, 5),
  476 + (combo_quality, 0, flag_button, 1)])
452 477  
453   - btnsizer = wx.StdDialogButtonSizer()
454 478  
455   - #if wx.Platform != "__WXMSW__":
456   - # btn = wx.ContextHelpButton(self)
457   - # btnsizer.AddButton(btn)
  479 + # LINES 4 and 5: Checkboxes
  480 + check_box_holes = wx.CheckBox(self, -1, _("Fill holes"))
  481 + check_box_holes.SetValue(True)
  482 + self.check_box_holes = check_box_holes
  483 + check_box_largest = wx.CheckBox(self, -1, _("Keep largest region"))
  484 + self.check_box_largest = check_box_largest
458 485  
459   - btn = wx.Button(self, wx.ID_OK)
460   - btn.SetDefault()
461   - btnsizer.AddButton(btn)
  486 + # LINE 6: Buttons
462 487  
463   - btn = wx.Button(self, wx.ID_CANCEL)
464   - btnsizer.AddButton(btn)
  488 + btn_ok = wx.Button(self, wx.ID_OK)
  489 + btn_ok.SetDefault()
  490 + btn_cancel = wx.Button(self, wx.ID_CANCEL)
  491 +
  492 + btnsizer = wx.StdDialogButtonSizer()
  493 + btnsizer.AddButton(btn_ok)
  494 + btnsizer.AddButton(btn_cancel)
465 495 btnsizer.Realize()
466 496  
467   - sizer.Add(btnsizer, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
  497 + # OVERVIEW
  498 + # Merge all sizers and checkboxes
  499 + sizer = wx.BoxSizer(wx.VERTICAL)
  500 + sizer.Add(fixed_sizer, 0, wx.TOP|wx.RIGHT|wx.LEFT|wx.GROW|wx.EXPAND, 20)
  501 + sizer.Add(check_box_holes, 0, wx.RIGHT|wx.LEFT, 30)
  502 + sizer.Add(check_box_largest, 0, wx.RIGHT|wx.LEFT, 30)
  503 + sizer.Add(btnsizer, 0, wx.ALIGN_RIGHT|wx.ALL, 10)
468 504  
469 505 self.SetSizer(sizer)
470 506 sizer.Fit(self)
471 507  
472   - #def GetValue(self):
473   - # return self.text.GetValue() + _("| mask: ") + MASK_LIST[self.combo_surface_name.GetSelection()]
  508 + def GetValue(self):
  509 + mask_index = self.combo_mask.GetSelection()
  510 + surface_name = self.text.GetValue()
  511 + quality = const.SURFACE_QUALITY_LIST[self.combo_quality.GetSelection()]
  512 + fill_holes = self.check_box_holes.GetValue()
  513 + keep_largest = self.check_box_largest.GetValue()
  514 + return (mask_index, surface_name, quality, fill_holes, keep_largest)
474 515  
475 516 INDEX_TO_EXTENSION = {0: "bmp", 1: "jpg", 2: "png", 3: "ps", 4:"povray", 5:"tiff"}
476 517 WILDCARD_SAVE_PICTURE = _("BMP image")+" (*.bmp)|*.bmp|"+\
... ...
invesalius/gui/task_surface.py
... ... @@ -26,6 +26,7 @@ import wx.lib.pubsub as ps
26 26 import gui.dialogs as dlg
27 27 import gui.widgets.foldpanelbar as fpb
28 28 import gui.widgets.colourselect as csel
  29 +import project as prj
29 30 import utils as utl
30 31  
31 32 #INTERPOLATION_MODE_LIST = ["Cubic", "Linear", "NearestNeighbor"]
... ... @@ -130,7 +131,27 @@ class InnerTaskPanel(wx.Panel):
130 131 #import gui.dialogs as dlg
131 132 dialog = dlg.NewSurfaceDialog(self, -1, _('InVesalius 3 - New surface'))
132 133 if dialog.ShowModal() == wx.ID_OK:
133   - print "TODO: Send Signal - Create 3d surface %s \n" % dialog.GetValue()
  134 + # Retrieve information from dialog
  135 + (mask_index, surface_name, surface_quality, fill_holes,\
  136 + keep_largest) = dialog.GetValue()
  137 +
  138 + # Retrieve information from mask
  139 + proj = prj.Project()
  140 + mask = proj.mask_dict[mask_index]
  141 +
  142 + # Send all information so surface can be created
  143 + surface_data = [proj.imagedata,
  144 + mask.colour,
  145 + mask.threshold_range,
  146 + mask.edited_points,
  147 + False, # overwrite
  148 + surface_name,
  149 + surface_quality,
  150 + fill_holes,
  151 + keep_largest]
  152 +
  153 + ps.Publisher().sendMessage('Create surface', surface_data)
  154 + print "TODO: Send Signal - Create 3d surface %s \n" % surface_data
134 155 dialog.Destroy()
135 156 if evt:
136 157 evt.Skip()
... ...
invesalius/project.py
... ... @@ -266,8 +266,6 @@ class Project(object):
266 266 self.surface_dict[s.index] = s
267 267 else:
268 268 setattr(self, key, project[key])
269   - #print "depois", self.__dict__
270   -
271 269  
272 270  
273 271 def Compress(folder, filename):
... ...