Commit bba5864282eac415257c9cb2c0bb92cef314d195

Authored by tatiana
1 parent 0c9ee0bd

ENH: Surface connectivity (under devel)

invesalius/constants.py
@@ -180,6 +180,24 @@ MASK_COLOUR = [(0.33, 1, 0.33), @@ -180,6 +180,24 @@ MASK_COLOUR = [(0.33, 1, 0.33),
180 #(0.792156862745098, 1.0, 0.66666666666666663), # too "light" 180 #(0.792156862745098, 1.0, 0.66666666666666663), # too "light"
181 #(0.66666666666666663, 0.792156862745098, 1.0)] 181 #(0.66666666666666663, 0.792156862745098, 1.0)]
182 182
  183 +
  184 +SURFACE_COLOUR = [(0.33, 1, 0.33),
  185 + (1, 1, 0.33),
  186 + (0.33, 0.91, 1),
  187 + (1, 0.33, 1),
  188 + (1, 0.68, 0.33),
  189 + (1, 0.33, 0.33),
  190 + (0.33333333333333331, 0.33333333333333331, 1.0),
  191 + (1.0, 0.33333333333333331, 0.66666666666666663),
  192 + (0.74901960784313726, 1.0, 0.0),
  193 + (0.83529411764705885, 0.33333333333333331, 1.0),
  194 + (0.792156862745098, 0.66666666666666663, 1.0),
  195 + (1.0, 0.66666666666666663, 0.792156862745098),
  196 + (0.33333333333333331, 1.0, 0.83529411764705885),
  197 + (1.0, 0.792156862745098, 0.66666666666666663),
  198 + (0.792156862745098, 1.0, 0.66666666666666663),
  199 + (0.66666666666666663, 0.792156862745098, 1.0)]
  200 +
183 # Related to slice editor brush 201 # Related to slice editor brush
184 BRUSH_CIRCLE = 0 # 202 BRUSH_CIRCLE = 0 #
185 BRUSH_SQUARE = 1 203 BRUSH_SQUARE = 1
@@ -402,6 +420,7 @@ STATE_PAN = 1005 @@ -402,6 +420,7 @@ STATE_PAN = 1005
402 SLICE_STATE_CROSS = 1006 420 SLICE_STATE_CROSS = 1006
403 SLICE_STATE_SCROLL = 1007 421 SLICE_STATE_SCROLL = 1007
404 SLICE_STATE_EDITOR = 1008 422 SLICE_STATE_EDITOR = 1008
  423 +VOLUME_STATE_SEED = 2001
405 424
406 425
407 TOOL_STATES = [ STATE_WL, STATE_SPIN, STATE_ZOOM, 426 TOOL_STATES = [ STATE_WL, STATE_SPIN, STATE_ZOOM,
@@ -414,7 +433,7 @@ SLICE_STYLES = TOOL_STATES + TOOL_SLICE_STATES @@ -414,7 +433,7 @@ SLICE_STYLES = TOOL_STATES + TOOL_SLICE_STATES
414 SLICE_STYLES.append(STATE_DEFAULT) 433 SLICE_STYLES.append(STATE_DEFAULT)
415 SLICE_STYLES.append(SLICE_STATE_EDITOR) 434 SLICE_STYLES.append(SLICE_STATE_EDITOR)
416 435
417 -VOLUME_STYLES = TOOL_STATES + [] 436 +VOLUME_STYLES = TOOL_STATES + [VOLUME_STATE_SEED]
418 VOLUME_STYLES.append(STATE_DEFAULT) 437 VOLUME_STYLES.append(STATE_DEFAULT)
419 438
420 439
@@ -426,4 +445,5 @@ STYLE_LEVEL = {SLICE_STATE_EDITOR: 1, @@ -426,4 +445,5 @@ STYLE_LEVEL = {SLICE_STATE_EDITOR: 1,
426 STATE_SPIN: 2, 445 STATE_SPIN: 2,
427 STATE_ZOOM: 2, 446 STATE_ZOOM: 2,
428 STATE_ZOOM_SL: 2, 447 STATE_ZOOM_SL: 2,
429 - STATE_PAN:2} 448 + STATE_PAN:2,
  449 + VOLUME_STATE_SEED:1}
invesalius/data/polydata_utils.py
@@ -57,23 +57,7 @@ def ApplySmoothFilter(polydata, iterations, relaxation_factor): @@ -57,23 +57,7 @@ def ApplySmoothFilter(polydata, iterations, relaxation_factor):
57 57
58 return smoother.GetOutput() 58 return smoother.GetOutput()
59 59
60 -def SelectLargestSurface(polydata):  
61 - """  
62 - """  
63 - pass  
64 - return polydata  
65 60
66 -def SplitDisconectedSurfaces(polydata):  
67 - """  
68 - """  
69 - return []  
70 -  
71 -# TODO: style?  
72 -def SelectSurfaceByCell(polytada, list_index = []):  
73 - """  
74 - """  
75 - pass  
76 - return []  
77 61
78 def FillSurfaceHole(polydata): 62 def FillSurfaceHole(polydata):
79 """ 63 """
@@ -133,7 +117,7 @@ def Import(filename): @@ -133,7 +117,7 @@ def Import(filename):
133 reader.Update() 117 reader.Update()
134 return reader.GetOutput() 118 return reader.GetOutput()
135 119
136 -def SelectPolyDataPart(polydata, point): 120 +def JoinSeedsParts(polydata, point_id_list):
137 """ 121 """
138 The function require vtkPolyData and point id 122 The function require vtkPolyData and point id
139 from vtkPolyData. 123 from vtkPolyData.
@@ -141,7 +125,30 @@ def SelectPolyDataPart(polydata, point): @@ -141,7 +125,30 @@ def SelectPolyDataPart(polydata, point):
141 conn = vtk.vtkPolyDataConnectivityFilter() 125 conn = vtk.vtkPolyDataConnectivityFilter()
142 conn.SetInput(polydata) 126 conn.SetInput(polydata)
143 conn.SetExtractionModeToPointSeededRegions() 127 conn.SetExtractionModeToPointSeededRegions()
144 - conn.AddSeed(point) 128 + for seed in point_id_list:
  129 + conn.AddSeed(seed)
  130 + conn.Update()
  131 +
  132 + result = vtk.vtkPolyData()
  133 + result.DeepCopy(conn.GetOutput())
  134 + result.Update()
  135 + return result
  136 +
  137 +def SelectLargestPart(polydata):
  138 + """
  139 + """
  140 + conn = vtk.vtkPolyDataConnectivityFilter()
  141 + conn.SetInput(polydata)
  142 + conn.SetExtractionModeToLargestRegion()
145 conn.Update() 143 conn.Update()
146 144
147 - return conn.GetOutput() 145 + result = vtk.vtkPolyData()
  146 + result.DeepCopy(conn.GetOutput())
  147 + result.Update()
  148 + return result
  149 +
  150 +def SplitDisconectedParts(polydata):
  151 + """
  152 + """
  153 + return [polydata]
  154 +
invesalius/data/surface.py
@@ -20,6 +20,7 @@ @@ -20,6 +20,7 @@
20 import multiprocessing 20 import multiprocessing
21 import os 21 import os
22 import plistlib 22 import plistlib
  23 +import random
23 import sys 24 import sys
24 import tempfile 25 import tempfile
25 26
@@ -118,6 +119,81 @@ class SurfaceManager(): @@ -118,6 +119,81 @@ class SurfaceManager():
118 ps.Publisher().subscribe(self.OnLoadSurfaceDict, 'Load surface dict') 119 ps.Publisher().subscribe(self.OnLoadSurfaceDict, 'Load surface dict')
119 ps.Publisher().subscribe(self.OnCloseProject, 'Close project data') 120 ps.Publisher().subscribe(self.OnCloseProject, 'Close project data')
120 ps.Publisher().subscribe(self.OnSelectSurface, 'Change surface selected') 121 ps.Publisher().subscribe(self.OnSelectSurface, 'Change surface selected')
  122 + #----
  123 + ps.Publisher().subscribe(self.OnSplitSurface, 'Split surface')
  124 + ps.Publisher().subscribe(self.OnLargestSurface,
  125 + 'Create surface from largest region')
  126 + ps.Publisher().subscribe(self.OnSeedSurface, "Create surface from seeds")
  127 +
  128 + def OnSeedSurface(self, pubsub_evt):
  129 + index, points_id_list = pubsub_evt.data
  130 + proj = prj.Project()
  131 + surface = proj.surface_dict[index]
  132 +
  133 + new_polydata = pu.JoinSeedsParts(surface.polydata,
  134 + points_id_list)
  135 + self.CreateSurfaceFromPolydata(new_polydata)
  136 +
  137 + def OnSplitSurface(self, pubsub_evt):
  138 + index = pubsub_evt.data
  139 + proj = prj.Project()
  140 + surface = proj.surface_dict[index]
  141 +
  142 + new_polydata_list = pu.SplitDisconectedParts(surface.polydata)
  143 + for polydata in new_polydata_list:
  144 + self.CreateSurfaceFromPolydata(polydata)
  145 +
  146 + def OnLargestSurface(self, pubsub_evt):
  147 + index = pubsub_evt.data
  148 + proj = prj.Project()
  149 + surface = proj.surface_dict[index]
  150 +
  151 + new_polydata = pu.SelectLargestPart(surface.polydata)
  152 + self.CreateSurfaceFromPolydata(new_polydata)
  153 +
  154 + def CreateSurfaceFromPolydata(self, polydata, overwrite=False):
  155 + mapper = vtk.vtkPolyDataMapper()
  156 + mapper.SetInput(polydata)
  157 + mapper.ScalarVisibilityOff()
  158 +
  159 + actor = vtk.vtkActor()
  160 + actor.SetMapper(mapper)
  161 +
  162 + if overwrite:
  163 + surface = Surface(index = self.last_surface_index)
  164 + else:
  165 + surface = Surface()
  166 +
  167 + surface.colour = random.choice(const.SURFACE_COLOUR)
  168 + surface.polydata = polydata
  169 +
  170 + # Set actor colour and transparency
  171 + actor.GetProperty().SetColor(surface.colour)
  172 + actor.GetProperty().SetOpacity(1-surface.transparency)
  173 +
  174 + # Append surface into Project.surface_dict
  175 + proj = prj.Project()
  176 + if overwrite:
  177 + proj.ChangeSurface(surface)
  178 + else:
  179 + index = proj.AddSurface(surface)
  180 + surface.index = index
  181 + self.last_surface_index = index
  182 +
  183 + session = ses.Session()
  184 + session.ChangeProject()
  185 +
  186 + # The following lines have to be here, otherwise all volumes disappear
  187 + measured_polydata = vtk.vtkMassProperties()
  188 + measured_polydata.AddObserver("ProgressEvent", lambda obj,evt:
  189 + UpdateProgress(obj, _("Generating 3D surface...")))
  190 + measured_polydata.SetInput(polydata)
  191 + volume = measured_polydata.GetVolume()
  192 + surface.volume = volume
  193 + self.last_surface_index = surface.index
  194 +
  195 + ps.Publisher().sendMessage('Load surface actor into viewer', actor)
  196 +
121 197
122 def OnCloseProject(self, pubsub_evt): 198 def OnCloseProject(self, pubsub_evt):
123 self.CloseProject() 199 self.CloseProject()
invesalius/data/viewer_volume.py
@@ -83,6 +83,12 @@ class Viewer(wx.Panel): @@ -83,6 +83,12 @@ class Viewer(wx.Panel):
83 self.mouse_pressed = 0 83 self.mouse_pressed = 0
84 self.on_wl = False 84 self.on_wl = False
85 85
  86 + self.picker = vtk.vtkPointPicker()
  87 + interactor.SetPicker(self.picker)
  88 + self.seed_points = []
  89 + self.current_surface_index = 0
  90 +
  91 +
86 def __bind_events(self): 92 def __bind_events(self):
87 ps.Publisher().subscribe(self.LoadActor, 93 ps.Publisher().subscribe(self.LoadActor,
88 'Load surface actor into viewer') 94 'Load surface actor into viewer')
@@ -132,7 +138,19 @@ class Viewer(wx.Panel): @@ -132,7 +138,19 @@ class Viewer(wx.Panel):
132 138
133 ps.Publisher().subscribe(self.OnExportPicture,'Export picture to file') 139 ps.Publisher().subscribe(self.OnExportPicture,'Export picture to file')
134 140
  141 + ps.Publisher().subscribe(self.OnStartSeed,'Create surface by seeding - start')
  142 + ps.Publisher().subscribe(self.OnEndSeed,'Create surface by seeding - end')
135 143
  144 + def OnStartSeed(self, pubsub_evt):
  145 + index = pubsub_evt.data
  146 + self.current_surface_index = index
  147 + self.seed_points = []
  148 +
  149 + def OnEndSeed(self, pubsub_evt):
  150 + ps.Publisher().sendMessage("Create surface from seeds",
  151 + (self.current_surface_index ,
  152 + self.seed_points))
  153 +
136 154
137 def OnExportPicture(self, pubsub_evt): 155 def OnExportPicture(self, pubsub_evt):
138 ps.Publisher().sendMessage('Begin busy cursor') 156 ps.Publisher().sendMessage('Begin busy cursor')
@@ -189,13 +207,6 @@ class Viewer(wx.Panel): @@ -189,13 +207,6 @@ class Viewer(wx.Panel):
189 self.mouse_pressed = 0 207 self.mouse_pressed = 0
190 self.on_wl = False 208 self.on_wl = False
191 self.slice_plane = 0 209 self.slice_plane = 0
192 -  
193 - #if self.text:  
194 - # del self.text  
195 - # self.text = 0  
196 -  
197 -  
198 -  
199 210
200 def OnHideText(self, pubsub_evt): 211 def OnHideText(self, pubsub_evt):
201 self.text.Hide() 212 self.text.Hide()
@@ -238,6 +249,10 @@ class Viewer(wx.Panel): @@ -238,6 +249,10 @@ class Viewer(wx.Panel):
238 }, 249 },
239 const.STATE_DEFAULT: 250 const.STATE_DEFAULT:
240 { 251 {
  252 + },
  253 + const.VOLUME_STATE_SEED:
  254 + {
  255 + "LeftButtonPressEvent": self.OnInsertSeed
241 } 256 }
242 } 257 }
243 258
@@ -535,6 +550,12 @@ class Viewer(wx.Panel): @@ -535,6 +550,12 @@ class Viewer(wx.Panel):
535 def AppendActor(self, evt_pubsub=None): 550 def AppendActor(self, evt_pubsub=None):
536 self.ren.AddActor(evt_pubsub.data) 551 self.ren.AddActor(evt_pubsub.data)
537 552
  553 + def OnInsertSeed(self, obj, evt):
  554 + x,y = self.interactor.GetEventPosition()
  555 + #x,y = obj.GetLastEventPosition()
  556 + self.picker.Pick(x, y, 0, self.ren)
  557 + point_id = self.picker.GetPointId()
  558 + self.seed_points.append(point_id)
538 559
539 class SlicePlane: 560 class SlicePlane:
540 def __init__(self): 561 def __init__(self):
@@ -783,11 +804,4 @@ class SlicePlane: @@ -783,11 +804,4 @@ class SlicePlane:
783 del self.plane_y 804 del self.plane_y
784 del self.plane_z 805 del self.plane_z
785 806
786 - def PointId(self, evt, obj):  
787 - #TODO: add in the code  
788 - # picker = vtk.vtkPointPicker()  
789 - # interactor.SetPicker(picker)  
790 - # interactor.AddObserver("left...", self.PointId)  
791 - x,y = evt.GetLastEventPosition()  
792 - self.picker.Pick(x, y, 0, self.ren1)  
793 - point_id = self.picker.GetPointId() 807 +
invesalius/gui/task_surface.py
@@ -22,6 +22,7 @@ import wx @@ -22,6 +22,7 @@ import wx
22 import wx.lib.hyperlink as hl 22 import wx.lib.hyperlink as hl
23 import wx.lib.pubsub as ps 23 import wx.lib.pubsub as ps
24 24
  25 +import constants as const
25 import gui.dialogs as dlg 26 import gui.dialogs as dlg
26 import gui.widgets.foldpanelbar as fpb 27 import gui.widgets.foldpanelbar as fpb
27 import gui.widgets.colourselect as csel 28 import gui.widgets.colourselect as csel
@@ -335,11 +336,12 @@ class SurfaceTools(wx.Panel): @@ -335,11 +336,12 @@ class SurfaceTools(wx.Panel):
335 336
336 def SelectLargest(self): 337 def SelectLargest(self):
337 index = self.combo_surface_name.GetSelection() 338 index = self.combo_surface_name.GetSelection()
338 - ps.Publisher().sendMessage('Split surface by largest region', index) 339 + ps.Publisher().sendMessage('Split surface', index)
339 340
340 def SplitSurface(self): 341 def SplitSurface(self):
341 index = self.combo_surface_name.GetSelection() 342 index = self.combo_surface_name.GetSelection()
342 - ps.Publisher().sendMessage('Create surface by largest region', index) 343 + ps.Publisher().sendMessage('Create surface from largest region', index)
  344 + # surface_manager
343 345
344 def SelectSeed(self): 346 def SelectSeed(self):
345 if self.button_seeds.IsPressed(): 347 if self.button_seeds.IsPressed():
@@ -349,10 +351,13 @@ class SurfaceTools(wx.Panel): @@ -349,10 +351,13 @@ class SurfaceTools(wx.Panel):
349 351
350 def StartSeeding(self): 352 def StartSeeding(self):
351 index = self.combo_surface_name.GetSelection() 353 index = self.combo_surface_name.GetSelection()
352 - ps.Publisher().sendMessage('Create surface by seeding - start', index) 354 + ps.Publisher().sendMessage('Enable style', const.VOLUME_STATE_SEED)
  355 + ps.Publisher().sendMessage('Create surface by seeding - start', index)
353 356
354 def EndSeeding(self): 357 def EndSeeding(self):
355 - ps.Publisher().sendMessage('Create surface by seeding - end') 358 + ps.Publisher().sendMessage('Disable style', const.VOLUME_STATE_SEED)
  359 + ps.Publisher().sendMessage('Create surface by seeding - end')
  360 + # volume_viewer -> surface_manager
356 361
357 362
358 363