Commit a2be4d98b0fd159de5f1a9726ab7e64d35bee9e3

Authored by tatiana
1 parent f532e845

ADD: New surface dialog (fix #127)

invesalius/constants.py
@@ -202,6 +202,7 @@ SURFACE_QUALITY = { @@ -202,6 +202,7 @@ SURFACE_QUALITY = {
202 _("High"): (0, 1, 0.3000, 0.1), 202 _("High"): (0, 1, 0.3000, 0.1),
203 _("Optimal *"): (0, 2, 0.3000, 0.4)} 203 _("Optimal *"): (0, 2, 0.3000, 0.4)}
204 DEFAULT_SURFACE_QUALITY = _("Optimal *") 204 DEFAULT_SURFACE_QUALITY = _("Optimal *")
  205 +SURFACE_QUALITY_LIST = [_("Low"),_("Medium"),_("High"),_("Optimal *")]
205 206
206 207
207 # Surface properties 208 # Surface properties
invesalius/data/surface.py
@@ -39,7 +39,7 @@ class Surface(): @@ -39,7 +39,7 @@ class Surface():
39 Represent both vtkPolyData and associated properties. 39 Represent both vtkPolyData and associated properties.
40 """ 40 """
41 general_index = -1 41 general_index = -1
42 - def __init__(self, index=None): 42 + def __init__(self, index=None, name=""):
43 Surface.general_index += 1 43 Surface.general_index += 1
44 if index is None: 44 if index is None:
45 self.index = Surface.general_index 45 self.index = Surface.general_index
@@ -51,7 +51,10 @@ class Surface(): @@ -51,7 +51,10 @@ class Surface():
51 self.transparency = const.SURFACE_TRANSPARENCY 51 self.transparency = const.SURFACE_TRANSPARENCY
52 self.volume = 0 52 self.volume = 0
53 self.is_shown = 1 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 def SavePlist(self, filename): 59 def SavePlist(self, filename):
57 surface = {} 60 surface = {}
@@ -186,17 +189,31 @@ class SurfaceManager(): @@ -186,17 +189,31 @@ class SurfaceManager():
186 (surface.index, surface.name, 189 (surface.index, surface.name,
187 surface.colour, surface.volume, 190 surface.colour, surface.volume,
188 surface.transparency)) 191 surface.transparency))
  192 +
  193 + ####
  194 + #(mask_index, surface_name, quality, fill_holes, keep_largest)
  195 +
189 def AddNewActor(self, pubsub_evt): 196 def AddNewActor(self, pubsub_evt):
190 """ 197 """
191 Create surface actor, save into project and send it to viewer. 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 mode = 'CONTOUR' # 'GRAYSCALE' 215 mode = 'CONTOUR' # 'GRAYSCALE'
  216 +
200 ps.Publisher().sendMessage('Begin busy cursor') 217 ps.Publisher().sendMessage('Begin busy cursor')
201 imagedata_tmp = None 218 imagedata_tmp = None
202 if (edited_points): 219 if (edited_points):
@@ -214,11 +231,15 @@ class SurfaceManager(): @@ -214,11 +231,15 @@ class SurfaceManager():
214 if imagedata_resolution: 231 if imagedata_resolution:
215 imagedata = iu.ResampleImage3D(imagedata, imagedata_resolution) 232 imagedata = iu.ResampleImage3D(imagedata, imagedata_resolution)
216 233
217 - pipeline_size = 4 234 + pipeline_size = 3
218 if decimate_reduction: 235 if decimate_reduction:
219 pipeline_size += 1 236 pipeline_size += 1
220 if (smooth_iterations and smooth_relaxation_factor): 237 if (smooth_iterations and smooth_relaxation_factor):
221 pipeline_size += 1 238 pipeline_size += 1
  239 + if fill_holes:
  240 + pipeline_size += 1
  241 + if keep_largest:
  242 + pipeline_size += 1
222 243
223 # Update progress value in GUI 244 # Update progress value in GUI
224 UpdateProgress = vu.ShowProgress(pipeline_size) 245 UpdateProgress = vu.ShowProgress(pipeline_size)
@@ -236,7 +257,7 @@ class SurfaceManager(): @@ -236,7 +257,7 @@ class SurfaceManager():
236 pipe_in, pipe_out = multiprocessing.Pipe() 257 pipe_in, pipe_out = multiprocessing.Pipe()
237 sp = surface_process.SurfaceProcess(pipe_in, filename_img, mode, min_value, max_value, 258 sp = surface_process.SurfaceProcess(pipe_in, filename_img, mode, min_value, max_value,
238 decimate_reduction, smooth_relaxation_factor, 259 decimate_reduction, smooth_relaxation_factor,
239 - smooth_iterations, language) 260 + smooth_iterations, language, fill_holes, keep_largest)
240 sp.start() 261 sp.start()
241 262
242 while 1: 263 while 1:
@@ -282,28 +303,27 @@ class SurfaceManager(): @@ -282,28 +303,27 @@ class SurfaceManager():
282 if overwrite: 303 if overwrite:
283 surface = Surface(index = self.last_surface_index) 304 surface = Surface(index = self.last_surface_index)
284 else: 305 else:
285 - surface = Surface() 306 + surface = Surface(name=surface_name)
286 surface.colour = colour 307 surface.colour = colour
287 surface.polydata = polydata 308 surface.polydata = polydata
288 309
289 -  
290 # Set actor colour and transparency 310 # Set actor colour and transparency
291 actor.GetProperty().SetColor(colour) 311 actor.GetProperty().SetColor(colour)
292 actor.GetProperty().SetOpacity(1-surface.transparency) 312 actor.GetProperty().SetOpacity(1-surface.transparency)
293 313
294 # Remove temporary files 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 # Append surface into Project.surface_dict 328 # Append surface into Project.surface_dict
309 proj = prj.Project() 329 proj = prj.Project()
invesalius/data/surface_process.py
@@ -8,7 +8,7 @@ class SurfaceProcess(multiprocessing.Process): @@ -8,7 +8,7 @@ class SurfaceProcess(multiprocessing.Process):
8 8
9 def __init__(self, pipe, filename, mode, min_value, max_value, 9 def __init__(self, pipe, filename, mode, min_value, max_value,
10 decimate_reduction, smooth_relaxation_factor, 10 decimate_reduction, smooth_relaxation_factor,
11 - smooth_iterations, language): 11 + smooth_iterations, language, fill_holes, keep_largest):
12 12
13 multiprocessing.Process.__init__(self) 13 multiprocessing.Process.__init__(self)
14 self.pipe = pipe 14 self.pipe = pipe
@@ -20,6 +20,9 @@ class SurfaceProcess(multiprocessing.Process): @@ -20,6 +20,9 @@ class SurfaceProcess(multiprocessing.Process):
20 self.smooth_relaxation_factor = smooth_relaxation_factor 20 self.smooth_relaxation_factor = smooth_relaxation_factor
21 self.smooth_iterations = smooth_iterations 21 self.smooth_iterations = smooth_iterations
22 self.language = language 22 self.language = language
  23 + self.fill_holes = fill_holes
  24 + self.keep_largest = keep_largest
  25 +
23 26
24 def run(self): 27 def run(self):
25 self.CreateSurface() 28 self.CreateSurface()
@@ -86,15 +89,26 @@ class SurfaceProcess(multiprocessing.Process): @@ -86,15 +89,26 @@ class SurfaceProcess(multiprocessing.Process):
86 self.SendProgress(obj, _("Generating 3D surface..."))) 89 self.SendProgress(obj, _("Generating 3D surface...")))
87 polydata = smoother.GetOutput() 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 # Filter used to detect and fill holes. Only fill boundary edges holes. 101 # Filter used to detect and fill holes. Only fill boundary edges holes.
90 #TODO: Hey! This piece of code is the same from 102 #TODO: Hey! This piece of code is the same from
91 # polydata_utils.FillSurfaceHole, we need to review this. 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 filename = tempfile.mktemp() 114 filename = tempfile.mktemp()
@@ -104,4 +118,4 @@ class SurfaceProcess(multiprocessing.Process): @@ -104,4 +118,4 @@ class SurfaceProcess(multiprocessing.Process):
104 writer.Write() 118 writer.Write()
105 119
106 self.pipe.send(None) 120 self.pipe.send(None)
107 - self.pipe.send(filename)  
108 \ No newline at end of file 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,13 +393,13 @@ def ShowSavePresetDialog(default_filename="raycasting"):
393 393
394 return filename 394 return filename
395 395
396 -MASK_LIST = []  
397 class NewSurfaceDialog(wx.Dialog): 396 class NewSurfaceDialog(wx.Dialog):
398 def __init__(self, parent, ID, title, size=wx.DefaultSize, 397 def __init__(self, parent, ID, title, size=wx.DefaultSize,
399 pos=wx.DefaultPosition, style=wx.DEFAULT_DIALOG_STYLE, 398 pos=wx.DefaultPosition, style=wx.DEFAULT_DIALOG_STYLE,
400 useMetal=False): 399 useMetal=False):
401 - import data.surface as surface  
402 import constants as const 400 import constants as const
  401 + import data.surface as surface
  402 + import project as prj
403 403
404 # Instead of calling wx.Dialog.__init__ we precreate the dialog 404 # Instead of calling wx.Dialog.__init__ we precreate the dialog
405 # so we can set an extra style that must be set before 405 # so we can set an extra style that must be set before
@@ -420,57 +420,98 @@ class NewSurfaceDialog(wx.Dialog): @@ -420,57 +420,98 @@ class NewSurfaceDialog(wx.Dialog):
420 420
421 self.CenterOnScreen() 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 style=wx.CB_DROPDOWN|wx.CB_READONLY) 447 style=wx.CB_DROPDOWN|wx.CB_READONLY)
432 - combo_surface_name.SetSelection(0) 448 + combo_mask.SetSelection(len(self.mask_list)-1)
433 if sys.platform != 'win32': 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 btnsizer.Realize() 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 self.SetSizer(sizer) 505 self.SetSizer(sizer)
470 sizer.Fit(self) 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 INDEX_TO_EXTENSION = {0: "bmp", 1: "jpg", 2: "png", 3: "ps", 4:"povray", 5:"tiff"} 516 INDEX_TO_EXTENSION = {0: "bmp", 1: "jpg", 2: "png", 3: "ps", 4:"povray", 5:"tiff"}
476 WILDCARD_SAVE_PICTURE = _("BMP image")+" (*.bmp)|*.bmp|"+\ 517 WILDCARD_SAVE_PICTURE = _("BMP image")+" (*.bmp)|*.bmp|"+\
invesalius/gui/task_surface.py
@@ -26,6 +26,7 @@ import wx.lib.pubsub as ps @@ -26,6 +26,7 @@ import wx.lib.pubsub as ps
26 import gui.dialogs as dlg 26 import gui.dialogs as dlg
27 import gui.widgets.foldpanelbar as fpb 27 import gui.widgets.foldpanelbar as fpb
28 import gui.widgets.colourselect as csel 28 import gui.widgets.colourselect as csel
  29 +import project as prj
29 import utils as utl 30 import utils as utl
30 31
31 #INTERPOLATION_MODE_LIST = ["Cubic", "Linear", "NearestNeighbor"] 32 #INTERPOLATION_MODE_LIST = ["Cubic", "Linear", "NearestNeighbor"]
@@ -130,7 +131,27 @@ class InnerTaskPanel(wx.Panel): @@ -130,7 +131,27 @@ class InnerTaskPanel(wx.Panel):
130 #import gui.dialogs as dlg 131 #import gui.dialogs as dlg
131 dialog = dlg.NewSurfaceDialog(self, -1, _('InVesalius 3 - New surface')) 132 dialog = dlg.NewSurfaceDialog(self, -1, _('InVesalius 3 - New surface'))
132 if dialog.ShowModal() == wx.ID_OK: 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 dialog.Destroy() 155 dialog.Destroy()
135 if evt: 156 if evt:
136 evt.Skip() 157 evt.Skip()
invesalius/project.py
@@ -266,8 +266,6 @@ class Project(object): @@ -266,8 +266,6 @@ class Project(object):
266 self.surface_dict[s.index] = s 266 self.surface_dict[s.index] = s
267 else: 267 else:
268 setattr(self, key, project[key]) 268 setattr(self, key, project[key])
269 - #print "depois", self.__dict__  
270 -  
271 269
272 270
273 def Compress(folder, filename): 271 def Compress(folder, filename):