Commit bc5cd630e6aa6813d60500c7740e63b6a34db836

Authored by tatiana
1 parent 4efd8d3f

ADD: Raycasting window and level control by cursor

invesalius/constants.py
... ... @@ -2,6 +2,11 @@ import os.path
2 2 import wx
3 3 from project import Project
4 4  
  5 +# VTK text
  6 +TEXT_SIZE = 12
  7 +TEXT_COLOUR = (1,1,1)
  8 +TEXT_POSITION = (0.03, 0.97)
  9 +
5 10 # Slice orientation
6 11 AXIAL = 0
7 12 CORONAL = 1
... ...
invesalius/data/viewer_volume.py
... ... @@ -23,8 +23,10 @@ import wx
23 23 import vtk
24 24 from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor
25 25 import wx.lib.pubsub as ps
  26 +
26 27 import constants as const
27 28 import project as prj
  29 +import data.vtk_utils as vtku
28 30  
29 31 class Viewer(wx.Panel):
30 32 def __init__(self, parent):
... ... @@ -32,36 +34,57 @@ class Viewer(wx.Panel):
32 34 self.SetBackgroundColour(wx.Colour(0, 0, 0))
33 35  
34 36 style = vtk.vtkInteractorStyleTrackballCamera()
  37 + self.style = style
35 38  
36   - iren = wxVTKRenderWindowInteractor(self, -1, size = self.GetSize())
37   - iren.SetInteractorStyle(style)
  39 + interactor = wxVTKRenderWindowInteractor(self, -1, size = self.GetSize())
  40 + interactor.SetInteractorStyle(style)
38 41  
39 42 sizer = wx.BoxSizer(wx.VERTICAL)
40   - sizer.Add(iren, 1, wx.EXPAND)
  43 + sizer.Add(interactor, 1, wx.EXPAND)
41 44 self.SetSizer(sizer)
42 45 self.Layout()
43 46  
44   - # It would be more correct (API-wise) to call iren.Initialize() and
45   - # iren.Start() here, but Initialize() calls RenderWindow.Render().
  47 + # It would be more correct (API-wise) to call interactor.Initialize() and
  48 + # interactor.Start() here, but Initialize() calls RenderWindow.Render().
46 49 # That Render() call will get through before we can setup the
47 50 # RenderWindow() to render via the wxWidgets-created context; this
48 51 # causes flashing on some platforms and downright breaks things on
49 52 # other platforms. Instead, we call widget.Enable(). This means
50 53 # that the RWI::Initialized ivar is not set, but in THIS SPECIFIC CASE,
51 54 # that doesn't matter.
52   - iren.Enable(1)
  55 + interactor.Enable(1)
53 56  
54 57 ren = vtk.vtkRenderer()
55   - iren.GetRenderWindow().AddRenderer(ren)
  58 + interactor.GetRenderWindow().AddRenderer(ren)
56 59  
57   - self.iren = iren
  60 + self.interactor = interactor
58 61 self.ren = ren
59 62  
  63 + self.onclick = False
  64 +
60 65 self.__bind_events()
61 66 self.__bind_events_wx()
62 67  
63 68 self.view_angle = None
64 69  
  70 + def OnMove(self, obj, evt):
  71 + if self.onclick:
  72 + mouse_x, mouse_y = self.interactor.GetEventPosition()
  73 + diff_x = mouse_x - self.last_x
  74 + diff_y = mouse_y - self.last_y
  75 + self.last_x, self.last_y = mouse_x, mouse_y
  76 + ps.Publisher().sendMessage('Set raycasting relative window and level',
  77 + (diff_x, diff_y))
  78 + self.interactor.Render()
  79 +
  80 + def OnClick(self, obj, evt):
  81 + self.onclick = True
  82 + mouse_x, mouse_y = self.interactor.GetEventPosition()
  83 + self.last_x, self.last_y = mouse_x, mouse_y
  84 +
  85 + def OnRelease(self, obj, evt):
  86 + self.onclick = False
  87 +
65 88 def __bind_events(self):
66 89 ps.Publisher().subscribe(self.LoadActor, 'Load surface actor into viewer')
67 90 ps.Publisher().subscribe(self.UpdateRender, 'Render volume viewer')
... ... @@ -73,25 +96,52 @@ class Viewer(wx.Panel):
73 96 'Set Widget Interactor')
74 97 ps.Publisher().subscribe(self.OnSetViewAngle,
75 98 'Set volume view angle')
76   -
  99 + ps.Publisher().subscribe(self.OnSetWindowLevelText,
  100 + 'Set volume window and level text')
  101 + ps.Publisher().subscribe(self.OnEnableBrightContrast,
  102 + 'Bright and contrast adjustment')
  103 + ps.Publisher().subscribe(self.OnDisableBrightContrast,
  104 + 'Set Editor Mode')
77 105  
78 106 def __bind_events_wx(self):
79 107 #self.Bind(wx.EVT_SIZE, self.OnSize)
80 108 pass
81   -
  109 +
  110 +
  111 + def OnEnableBrightContrast(self, pubsub_evt):
  112 + style = self.style
  113 + style.AddObserver("MouseMoveEvent", self.OnMove)
  114 + style.AddObserver("LeftButtonPressEvent", self.OnClick)
  115 + style.AddObserver("LeftButtonReleaseEvent", self.OnRelease)
  116 +
  117 +
  118 + def OnDisableBrightContrast(self, pubsub_evt):
  119 + style = vtk.vtkInteractorStyleTrackballCamera()
  120 + self.interactor.SetInteractorStyle(style)
  121 + self.style = style
  122 +
  123 +
82 124 def OnSize(self, evt):
83   - print "viewer_volume :: OnSize"
84 125 self.UpdateRender()
85 126 self.Refresh()
86   - print dir(self.iren)
87   - self.iren.UpdateWindowUI()
88   - self.iren.Update()
  127 + self.interactor.UpdateWindowUI()
  128 + self.interactor.Update()
89 129 evt.Skip()
90 130  
  131 + def OnSetWindowLevelText(self, pubsub_evt):
  132 + ww, wl = pubsub_evt.data
  133 + self.text.SetValue("WL: %d WW: %d"%(wl, ww))
  134 +
91 135 def LoadVolume(self, pubsub_evt):
92   - volume, colour = pubsub_evt.data
  136 + volume = pubsub_evt.data[0]
93 137 self.light = self.ren.GetLights().GetNextItem()
  138 +
  139 + text = vtku.Text()
  140 + self.text = text
  141 +
94 142 self.ren.AddVolume(volume)
  143 + self.ren.AddActor(text.actor)
  144 +
95 145 if not (self.view_angle):
96 146 self.SetViewAngle(const.VOL_FRONT)
97 147 else:
... ... @@ -117,7 +167,7 @@ class Viewer(wx.Panel):
117 167 ren.ResetCamera()
118 168 ren.ResetCameraClippingRange()
119 169  
120   - self.iren.Render()
  170 + self.interactor.Render()
121 171  
122 172 def OnSetViewAngle(self, evt_pubsub):
123 173 view = evt_pubsub.data
... ... @@ -139,21 +189,21 @@ class Viewer(wx.Panel):
139 189  
140 190 self.ren.ResetCameraClippingRange()
141 191 self.ren.ResetCamera()
142   - self.iren.Render()
  192 + self.interactor.Render()
143 193  
144 194 def UpdateRender(self, evt_pubsub=None):
145   - self.iren.Render()
  195 + self.interactor.Render()
146 196  
147 197 def CreatePlanes(self):
148 198  
149 199 imagedata = self.imagedata
150 200 ren = self.ren
151   - iren = self.iren
  201 + interactor = self.interactor
152 202  
153 203 import ivVolumeWidgets as vw
154 204 axial_plane = vw.Plane()
155 205 axial_plane.SetRender(ren)
156   - axial_plane.SetInteractor(iren)
  206 + axial_plane.SetInteractor(interactor)
157 207 axial_plane.SetOrientation(vw.AXIAL)
158 208 axial_plane.SetInput(imagedata)
159 209 axial_plane.Show()
... ... @@ -161,7 +211,7 @@ class Viewer(wx.Panel):
161 211  
162 212 coronal_plane = vw.Plane()
163 213 coronal_plane.SetRender(ren)
164   - coronal_plane.SetInteractor(iren)
  214 + coronal_plane.SetInteractor(interactor)
165 215 coronal_plane.SetOrientation(vw.CORONAL)
166 216 coronal_plane.SetInput(imagedata)
167 217 coronal_plane.Show()
... ... @@ -169,14 +219,14 @@ class Viewer(wx.Panel):
169 219  
170 220 sagital_plane = vw.Plane()
171 221 sagital_plane.SetRender(ren)
172   - sagital_plane.SetInteractor(iren)
  222 + sagital_plane.SetInteractor(interactor)
173 223 sagital_plane.SetOrientation(vw.SAGITAL)
174 224 sagital_plane.SetInput(imagedata)
175 225 sagital_plane.Show()
176 226 sagital_plane.Update()
177 227  
178 228 def SetWidgetInteractor(self, evt_pubsub=None):
179   - evt_pubsub.data.SetInteractor(self.iren._Iren)
  229 + evt_pubsub.data.SetInteractor(self.interactor._Iren)
180 230  
181 231 def AppendActor(self, evt_pubsub=None):
182 232 self.ren.AddActor(evt_pubsub.data)
... ...
invesalius/data/volume.py
... ... @@ -72,6 +72,9 @@ class Volume():
72 72 self.exist = None
73 73 self.color_transfer = None
74 74 self.opacity_transfer_func = None
  75 + self.ww = None
  76 + self.wl = None
  77 + self.n = 0
75 78  
76 79 self.__bind_events()
77 80  
... ... @@ -83,10 +86,12 @@ class Volume():
83 86 'Hide raycasting volume')
84 87 ps.Publisher().subscribe(self.SetRaycastPreset,
85 88 'Set raycasting preset')
86   - ps.Publisher().subscribe(self.SetWWWL,
  89 + ps.Publisher().subscribe(self.OnSetWindowLevel,
87 90 'Set raycasting wwwl')
88 91 ps.Publisher().subscribe(self.Refresh,
89 92 'Set raycasting refresh')
  93 + ps.Publisher().subscribe(self.OnSetRelativeWindowLevel,
  94 + 'Set raycasting relative window and level')
90 95  
91 96 def OnLoadVolume(self, pubsub_evt):
92 97 label = pubsub_evt.data
... ... @@ -95,7 +100,6 @@ class Volume():
95 100  
96 101 def LoadConfig(self):
97 102 self.config = Project().raycasting_preset
98   - #print path
99 103  
100 104 def OnHideVolume(self, pubsub_evt):
101 105 self.volume.SetVisibility(0)
... ... @@ -126,11 +130,24 @@ class Volume():
126 130 self.Create8bColorTable(self.scale)
127 131 self.Create8bOpacityTable(self.scale)
128 132  
129   - def SetWWWL(self, pubsub_evt):
  133 + def OnSetRelativeWindowLevel(self, pubsub_evt):
  134 + diff_ww, diff_wl = pubsub_evt.data
  135 + ww = self.ww + diff_ww
  136 + wl = self.wl + diff_wl
  137 + ps.Publisher().sendMessage('Set volume window and level text',
  138 + (ww, wl))
  139 + self.SetWWWL(ww, wl)
  140 + self.ww = ww
  141 + self.wl = wl
  142 +
  143 + def OnSetWindowLevel(self, pubsub_evt):
130 144 ww, wl, n = pubsub_evt.data
131   - print "Setting ww, wl", ww, wl
  145 + self.SetWWWL(ww,wl,n)
  146 +
  147 + def SetWWWL(self, ww, wl):
  148 +
132 149 if self.config['advancedCLUT']:
133   - curve = self.config['16bitClutCurves'][n]
  150 + curve = self.config['16bitClutCurves'][self.n]
134 151  
135 152 p1 = curve[0]
136 153 p2 = curve[-1]
... ... @@ -155,7 +172,7 @@ class Volume():
155 172 self.config['ww'] = ww
156 173  
157 174 self.__config_preset()
158   - ps.Publisher().sendMessage('Render volume viewer', None)
  175 + #ps.Publisher().sendMessage('Render volume viewer', None)
159 176  
160 177 def Refresh(self, pubsub_evt):
161 178 self.__config_preset()
... ... @@ -167,7 +184,6 @@ class Volume():
167 184 else:
168 185 color_transfer = vtk.vtkColorTransferFunction()
169 186 color_transfer.RemoveAllPoints()
170   - print self.config
171 187 curve_table = self.config['16bitClutCurves']
172 188 color_table = self.config['16bitClutColors']
173 189 colors = []
... ... @@ -191,12 +207,10 @@ class Volume():
191 207 color_transfer = vtk.vtkColorTransferFunction()
192 208 color_transfer.RemoveAllPoints()
193 209 color_preset = self.config['CLUT']
194   - print ">>>", color_preset
195 210 if color_preset != "No CLUT":
196 211 p = plistlib.readPlist(
197 212 os.path.join(const.RAYCASTING_PRESETS_DIRECTORY,
198 213 'color_list', color_preset + '.plist'))
199   - print "nome clut", p
200 214 r = p['Red']
201 215 g = p['Green']
202 216 b = p['Blue']
... ... @@ -205,7 +219,6 @@ class Volume():
205 219 wl = self.TranslateScale(scale, self.config['wl'])
206 220 inc = ww / 254.0
207 221 for i,rgb in enumerate(colors):
208   - print i,inc, ww, wl - ww/2 + i * inc, rgb
209 222 color_transfer.AddRGBPoint((wl - ww/2) + (i * inc), *[i/255.0 for i in rgb])
210 223 self.color_transfer = color_transfer
211 224 return color_transfer
... ... @@ -221,6 +234,8 @@ class Volume():
221 234  
222 235 ww = self.config['ww']
223 236 wl = self.config['wl']
  237 + self.ww = ww
  238 + self.wl = wl
224 239  
225 240 l1 = wl - ww/2.0
226 241 l2 = wl + ww/2.0
... ... @@ -255,16 +270,12 @@ class Volume():
255 270 ww = self.config['ww']
256 271 wl = self.TranslateScale(scale, self.config['wl'])
257 272  
258   - print ww, wl
259   -
260 273 l1 = wl - ww/2.0
261 274 l2 = wl + ww/2.0
262 275  
263 276 opacity_transfer_func.RemoveAllPoints()
264 277 opacity_transfer_func.AddSegment(0, 0, 2**16-1, 0)
265 278  
266   - print "l1, l2", l1, l2
267   -
268 279 k1 = 0.0
269 280 k2 = 1.0
270 281  
... ...
invesalius/data/vtk_utils.py
1 1 import wx.lib.pubsub as ps
  2 +import vtk
  3 +
  4 +import constants as const
2 5  
3 6 # If you are frightened by the code bellow, or think it must have been result of
4 7 # an identation error, lookup at:
... ... @@ -47,4 +50,41 @@ def ShowProgress(number_of_filters = 1):
47 50 (progress[0], label))
48 51 return progress[0]
49 52  
50   - return UpdateProgress
51 53 \ No newline at end of file
  54 + return UpdateProgress
  55 +
  56 +class Text(object):
  57 + def __init__(self):
  58 +
  59 + property = vtk.vtkTextProperty()
  60 + property.SetFontSize(const.TEXT_SIZE)
  61 + property.SetFontFamilyToArial()
  62 + property.BoldOff()
  63 + property.ItalicOff()
  64 + property.ShadowOn()
  65 + property.SetJustificationToLeft()
  66 + property.SetVerticalJustificationToTop()
  67 + property.SetColor(const.TEXT_COLOUR)
  68 + self.property = property
  69 +
  70 + mapper = vtk.vtkTextMapper()
  71 + mapper.SetTextProperty(property)
  72 + self.mapper = mapper
  73 +
  74 + x, y = const.TEXT_POSITION
  75 + actor = vtk.vtkActor2D()
  76 + actor.SetMapper(mapper)
  77 + actor.GetPositionCoordinate().SetCoordinateSystemToNormalizedDisplay()
  78 + actor.GetPositionCoordinate().SetValue(x,y)
  79 + self.actor = actor
  80 +
  81 + def SetValue(self, value):
  82 + self.mapper.SetInput(str(value))
  83 +
  84 + def SetPosition(self, position):
  85 + self.actor.SetPositionCoordinate().SetValue(position)
  86 +
  87 + def Show(self, value=1):
  88 + if value:
  89 + self.actor.VisibilityOn()
  90 + else:
  91 + self.actor.VisibilityOff()
... ...