Commit 32a72e8b400238d37779453dcf05084a5402256d
1 parent
7853c073
Exists in
master
and in
6 other branches
ENH: Cursor actor is being shown over slice. Not working when cursor is moved.
Showing
1 changed file
with
75 additions
and
60 deletions
Show diff stats
invesalius/data/viewer_slice.py
@@ -29,34 +29,34 @@ import project | @@ -29,34 +29,34 @@ import project | ||
29 | import cursor_actors as ca | 29 | import cursor_actors as ca |
30 | 30 | ||
31 | class Viewer(wx.Panel): | 31 | class Viewer(wx.Panel): |
32 | - | 32 | + |
33 | def __init__(self, prnt, orientation='AXIAL'): | 33 | def __init__(self, prnt, orientation='AXIAL'): |
34 | wx.Panel.__init__(self, prnt, size=wx.Size(320, 300)) | 34 | wx.Panel.__init__(self, prnt, size=wx.Size(320, 300)) |
35 | - | 35 | + |
36 | colour = [255*c for c in const.ORIENTATION_COLOUR[orientation]] | 36 | colour = [255*c for c in const.ORIENTATION_COLOUR[orientation]] |
37 | self.SetBackgroundColour(colour) | 37 | self.SetBackgroundColour(colour) |
38 | - | 38 | + |
39 | # Interactor aditional style | 39 | # Interactor aditional style |
40 | self.modes = ['DEFAULT'] | 40 | self.modes = ['DEFAULT'] |
41 | self.mouse_pressed = 0 | 41 | self.mouse_pressed = 0 |
42 | - | 42 | + |
43 | self.__init_gui() | 43 | self.__init_gui() |
44 | 44 | ||
45 | self.orientation = orientation | 45 | self.orientation = orientation |
46 | self.slice_number = 0 | 46 | self.slice_number = 0 |
47 | - | 47 | + |
48 | self._brush_cursor_op = 'Draw' | 48 | self._brush_cursor_op = 'Draw' |
49 | self.brush_cursor_size = 30 | 49 | self.brush_cursor_size = 30 |
50 | - | 50 | + self.cursor = None |
51 | # VTK pipeline and actors | 51 | # VTK pipeline and actors |
52 | self.__config_interactor() | 52 | self.__config_interactor() |
53 | self.pick = vtk.vtkCellPicker() | 53 | self.pick = vtk.vtkCellPicker() |
54 | - | 54 | + |
55 | self.__bind_events() | 55 | self.__bind_events() |
56 | self.__bind_events_wx() | 56 | self.__bind_events_wx() |
57 | 57 | ||
58 | def __init_gui(self): | 58 | def __init_gui(self): |
59 | - | 59 | + |
60 | interactor = wxVTKRenderWindowInteractor(self, -1, size=self.GetSize()) | 60 | interactor = wxVTKRenderWindowInteractor(self, -1, size=self.GetSize()) |
61 | 61 | ||
62 | scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) | 62 | scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) |
@@ -78,7 +78,7 @@ class Viewer(wx.Panel): | @@ -78,7 +78,7 @@ class Viewer(wx.Panel): | ||
78 | self.interactor = interactor | 78 | self.interactor = interactor |
79 | 79 | ||
80 | def __config_interactor(self): | 80 | def __config_interactor(self): |
81 | - | 81 | + |
82 | ren = vtk.vtkRenderer() | 82 | ren = vtk.vtkRenderer() |
83 | 83 | ||
84 | interactor = self.interactor | 84 | interactor = self.interactor |
@@ -86,14 +86,13 @@ class Viewer(wx.Panel): | @@ -86,14 +86,13 @@ class Viewer(wx.Panel): | ||
86 | 86 | ||
87 | self.cam = ren.GetActiveCamera() | 87 | self.cam = ren.GetActiveCamera() |
88 | self.ren = ren | 88 | self.ren = ren |
89 | - | ||
90 | - self.AppendMode('EDITOR') | ||
91 | - | 89 | + |
90 | + | ||
92 | def AppendMode(self, mode): | 91 | def AppendMode(self, mode): |
93 | - | 92 | + |
94 | # Retrieve currently set modes | 93 | # Retrieve currently set modes |
95 | self.modes.append(mode) | 94 | self.modes.append(mode) |
96 | - | 95 | + |
97 | # All modes and bindings | 96 | # All modes and bindings |
98 | action = {'DEFAULT': { | 97 | action = {'DEFAULT': { |
99 | "MouseMoveEvent": self.OnCrossMove, | 98 | "MouseMoveEvent": self.OnCrossMove, |
@@ -111,7 +110,7 @@ class Viewer(wx.Panel): | @@ -111,7 +110,7 @@ class Viewer(wx.Panel): | ||
111 | style = vtk.vtkInteractorStyleImage() | 110 | style = vtk.vtkInteractorStyleImage() |
112 | self.style = style | 111 | self.style = style |
113 | self.interactor.SetInteractorStyle(style) | 112 | self.interactor.SetInteractorStyle(style) |
114 | - | 113 | + |
115 | # Check all modes set by user | 114 | # Check all modes set by user |
116 | for mode in self.modes: | 115 | for mode in self.modes: |
117 | # Check each event available for each mode | 116 | # Check each event available for each mode |
@@ -120,17 +119,7 @@ class Viewer(wx.Panel): | @@ -120,17 +119,7 @@ class Viewer(wx.Panel): | ||
120 | style.AddObserver(event, | 119 | style.AddObserver(event, |
121 | action[mode][event]) | 120 | action[mode][event]) |
122 | 121 | ||
123 | - # Insert cursor | ||
124 | - cursor = ca.CursorCircle() | ||
125 | - cursor.SetOrientation(self.orientation) | ||
126 | - coordinates = {"SAGITAL": [self.slice_number, 0, 0], | ||
127 | - "CORONAL": [0, self.slice_number, 0], | ||
128 | - "AXIAL": [0, 0, self.slice_number]} | ||
129 | - cursor.SetPosition(coordinates[self.orientation]) | ||
130 | - self.ren.AddActor(cursor.actor) | ||
131 | - self.ren.Render() | ||
132 | - | ||
133 | - self.cursor = cursor | 122 | + |
134 | 123 | ||
135 | def ChangeBrushSize(self, pubsub_evt): | 124 | def ChangeBrushSize(self, pubsub_evt): |
136 | size = pubsub_evt.data | 125 | size = pubsub_evt.data |
@@ -140,23 +129,24 @@ class Viewer(wx.Panel): | @@ -140,23 +129,24 @@ class Viewer(wx.Panel): | ||
140 | self.interactor.Render() | 129 | self.interactor.Render() |
141 | 130 | ||
142 | def ChangeBrushColour(self, pubsub_evt): | 131 | def ChangeBrushColour(self, pubsub_evt): |
143 | - vtk_colour = pubsub_evt.data[3] | ||
144 | - self.cursor.SetColour(vtk_colour) | ||
145 | - self._brush_cursor_colour = vtk_colour | ||
146 | - self.ren.Render() | ||
147 | - self.interactor.Render() | ||
148 | - | 132 | + if (self.cursor): |
133 | + vtk_colour = pubsub_evt.data[3] | ||
134 | + self.cursor.SetColour(vtk_colour) | ||
135 | + self._brush_cursor_colour = vtk_colour | ||
136 | + self.ren.Render() | ||
137 | + self.interactor.Render() | ||
138 | + | ||
149 | 139 | ||
150 | def ChangeBrushActor(self, pubsub_evt): | 140 | def ChangeBrushActor(self, pubsub_evt): |
151 | brush_type = pubsub_evt.data | 141 | brush_type = pubsub_evt.data |
152 | self.ren.RemoveActor(self.cursor.actor) | 142 | self.ren.RemoveActor(self.cursor.actor) |
153 | - | 143 | + |
154 | if brush_type == 'square': | 144 | if brush_type == 'square': |
155 | cursor = ca.CursorRectangle() | 145 | cursor = ca.CursorRectangle() |
156 | elif brush_type == 'circle': | 146 | elif brush_type == 'circle': |
157 | cursor = ca.CursorCircle() | 147 | cursor = ca.CursorCircle() |
158 | self.cursor = cursor | 148 | self.cursor = cursor |
159 | - | 149 | + |
160 | cursor.SetOrientation(self.orientation) | 150 | cursor.SetOrientation(self.orientation) |
161 | coordinates = {"SAGITAL": [self.slice_number, 0, 0], | 151 | coordinates = {"SAGITAL": [self.slice_number, 0, 0], |
162 | "CORONAL": [0, self.slice_number, 0], | 152 | "CORONAL": [0, self.slice_number, 0], |
@@ -187,21 +177,21 @@ class Viewer(wx.Panel): | @@ -187,21 +177,21 @@ class Viewer(wx.Panel): | ||
187 | self.cursor.SetPosition(coord) | 177 | self.cursor.SetPosition(coord) |
188 | self.cursor.SetEditionPosition(self.GetCoordinateCursorEdition()) | 178 | self.cursor.SetEditionPosition(self.GetCoordinateCursorEdition()) |
189 | self.ren.Render() | 179 | self.ren.Render() |
190 | - | 180 | + |
191 | if self._brush_cursor_op == 'Erase': | 181 | if self._brush_cursor_op == 'Erase': |
192 | evt_msg = 'Erase mask pixel' | 182 | evt_msg = 'Erase mask pixel' |
193 | elif self._brush_cursor_op == 'Draw': | 183 | elif self._brush_cursor_op == 'Draw': |
194 | evt_msg = 'Add mask pixel' | 184 | evt_msg = 'Add mask pixel' |
195 | elif self._brush_cursor_op == 'Threshold': | 185 | elif self._brush_cursor_op == 'Threshold': |
196 | evt_msg = 'Edit mask pixel' | 186 | evt_msg = 'Edit mask pixel' |
197 | - | 187 | + |
198 | if self.mouse_pressed: | 188 | if self.mouse_pressed: |
199 | print "Edit pixel region based on origin:", coord | 189 | print "Edit pixel region based on origin:", coord |
200 | pixels = self.cursor.GetPixels() | 190 | pixels = self.cursor.GetPixels() |
201 | for coord in pixels: | 191 | for coord in pixels: |
202 | ps.Publisher().sendMessage(evt_msg, coord) | 192 | ps.Publisher().sendMessage(evt_msg, coord) |
203 | self.interactor.Render() | 193 | self.interactor.Render() |
204 | - | 194 | + |
205 | def OnCrossMove(self, obj, evt_vtk): | 195 | def OnCrossMove(self, obj, evt_vtk): |
206 | coord = self.GetCoordinate() | 196 | coord = self.GetCoordinate() |
207 | # Update position in other slices | 197 | # Update position in other slices |
@@ -214,34 +204,34 @@ class Viewer(wx.Panel): | @@ -214,34 +204,34 @@ class Viewer(wx.Panel): | ||
214 | coord[1]) | 204 | coord[1]) |
215 | ps.Publisher().sendMessage(('Set scroll position', 'AXIAL'), | 205 | ps.Publisher().sendMessage(('Set scroll position', 'AXIAL'), |
216 | coord[2]) | 206 | coord[2]) |
217 | - | ||
218 | - | 207 | + |
208 | + | ||
219 | def GetCoordinate(self): | 209 | def GetCoordinate(self): |
220 | - | 210 | + |
221 | # Find position | 211 | # Find position |
222 | mouse_x, mouse_y = self.interactor.GetEventPosition() | 212 | mouse_x, mouse_y = self.interactor.GetEventPosition() |
223 | self.pick.Pick(mouse_x, mouse_y, 0, self.ren) | 213 | self.pick.Pick(mouse_x, mouse_y, 0, self.ren) |
224 | x, y, z = self.pick.GetPickPosition() | 214 | x, y, z = self.pick.GetPickPosition() |
225 | - | 215 | + |
226 | # First we fix the position origin, based on vtkActor bounds | 216 | # First we fix the position origin, based on vtkActor bounds |
227 | bounds = self.actor.GetBounds() | 217 | bounds = self.actor.GetBounds() |
228 | bound_xi, bound_xf, bound_yi, bound_yf, bound_zi, bound_zf = bounds | 218 | bound_xi, bound_xf, bound_yi, bound_yf, bound_zi, bound_zf = bounds |
229 | x = float(x - bound_xi) | 219 | x = float(x - bound_xi) |
230 | y = float(y - bound_yi) | 220 | y = float(y - bound_yi) |
231 | z = float(z - bound_zi) | 221 | z = float(z - bound_zi) |
232 | - | 222 | + |
233 | # Then we fix the porpotion, based on vtkImageData spacing | 223 | # Then we fix the porpotion, based on vtkImageData spacing |
234 | spacing_x, spacing_y, spacing_z = self.imagedata.GetSpacing() | 224 | spacing_x, spacing_y, spacing_z = self.imagedata.GetSpacing() |
235 | x = x/spacing_x | 225 | x = x/spacing_x |
236 | y = y/spacing_y | 226 | y = y/spacing_y |
237 | z = z/spacing_z | 227 | z = z/spacing_z |
238 | - | 228 | + |
239 | # Based on the current orientation, we define 3D position | 229 | # Based on the current orientation, we define 3D position |
240 | coordinates = {"SAGITAL": [self.slice_number, y, z], | 230 | coordinates = {"SAGITAL": [self.slice_number, y, z], |
241 | "CORONAL": [x, self.slice_number, z], | 231 | "CORONAL": [x, self.slice_number, z], |
242 | "AXIAL": [x, y, self.slice_number]} | 232 | "AXIAL": [x, y, self.slice_number]} |
243 | coord = [int(coord) for coord in coordinates[self.orientation]] | 233 | coord = [int(coord) for coord in coordinates[self.orientation]] |
244 | - | 234 | + |
245 | # According to vtkImageData extent, we limit min and max value | 235 | # According to vtkImageData extent, we limit min and max value |
246 | # If this is not done, a VTK Error occurs when mouse is pressed outside | 236 | # If this is not done, a VTK Error occurs when mouse is pressed outside |
247 | # vtkImageData extent | 237 | # vtkImageData extent |
@@ -254,24 +244,24 @@ class Viewer(wx.Panel): | @@ -254,24 +244,24 @@ class Viewer(wx.Panel): | ||
254 | elif coord[index] < extent_min[index]: | 244 | elif coord[index] < extent_min[index]: |
255 | coord[index] = extent_min[index] | 245 | coord[index] = extent_min[index] |
256 | #print "New coordinate: ", coord | 246 | #print "New coordinate: ", coord |
257 | - | 247 | + |
258 | return coord | 248 | return coord |
259 | - | 249 | + |
260 | def GetCoordinateCursor(self): | 250 | def GetCoordinateCursor(self): |
261 | - | 251 | + |
262 | # Find position | 252 | # Find position |
263 | mouse_x, mouse_y = self.interactor.GetEventPosition() | 253 | mouse_x, mouse_y = self.interactor.GetEventPosition() |
264 | self.pick.Pick(mouse_x, mouse_y, 0, self.ren) | 254 | self.pick.Pick(mouse_x, mouse_y, 0, self.ren) |
265 | x, y, z = self.pick.GetPickPosition() | 255 | x, y, z = self.pick.GetPickPosition() |
266 | return x, y, z | 256 | return x, y, z |
267 | - | 257 | + |
268 | def GetCoordinateCursorEdition(self): | 258 | def GetCoordinateCursorEdition(self): |
269 | - | 259 | + |
270 | # Find position | 260 | # Find position |
271 | mouse_x, mouse_y = self.interactor.GetEventPosition() | 261 | mouse_x, mouse_y = self.interactor.GetEventPosition() |
272 | self.pick.Pick(mouse_x, mouse_y, 0, self.ren) | 262 | self.pick.Pick(mouse_x, mouse_y, 0, self.ren) |
273 | x, y, z = self.pick.GetPickPosition() | 263 | x, y, z = self.pick.GetPickPosition() |
274 | - | 264 | + |
275 | # First we fix the position origin, based on vtkActor bounds | 265 | # First we fix the position origin, based on vtkActor bounds |
276 | bounds = self.actor.GetBounds() | 266 | bounds = self.actor.GetBounds() |
277 | bound_xi, bound_xf, bound_yi, bound_yf, bound_zi, bound_zf = bounds | 267 | bound_xi, bound_xf, bound_yi, bound_yf, bound_zi, bound_zf = bounds |
@@ -299,21 +289,21 @@ class Viewer(wx.Panel): | @@ -299,21 +289,21 @@ class Viewer(wx.Panel): | ||
299 | z = self.slice_number | 289 | z = self.slice_number |
300 | 290 | ||
301 | return x,y,z | 291 | return x,y,z |
302 | - | 292 | + |
303 | def __bind_events(self): | 293 | def __bind_events(self): |
304 | ps.Publisher().subscribe(self.LoadImagedata, 'Load slice to viewer') | 294 | ps.Publisher().subscribe(self.LoadImagedata, 'Load slice to viewer') |
305 | ps.Publisher().subscribe(self.SetColour, 'Change mask colour') | 295 | ps.Publisher().subscribe(self.SetColour, 'Change mask colour') |
306 | ps.Publisher().subscribe(self.UpdateRender, 'Update slice viewer') | 296 | ps.Publisher().subscribe(self.UpdateRender, 'Update slice viewer') |
307 | - ps.Publisher().subscribe(self.ChangeSliceNumber, ('Set scroll position', | 297 | + ps.Publisher().subscribe(self.ChangeSliceNumber, ('Set scroll position', |
308 | self.orientation)) | 298 | self.orientation)) |
309 | - | 299 | + |
310 | ### | 300 | ### |
311 | ps.Publisher().subscribe(self.ChangeBrushSize,'Set edition brush size') | 301 | ps.Publisher().subscribe(self.ChangeBrushSize,'Set edition brush size') |
312 | ps.Publisher().subscribe(self.ChangeBrushColour, 'Add mask') | 302 | ps.Publisher().subscribe(self.ChangeBrushColour, 'Add mask') |
313 | ps.Publisher().subscribe(self.ChangeBrushActor, 'Set brush format') | 303 | ps.Publisher().subscribe(self.ChangeBrushActor, 'Set brush format') |
314 | ps.Publisher().subscribe(self.ChangeBrushOperation, 'Set edition operation') | 304 | ps.Publisher().subscribe(self.ChangeBrushOperation, 'Set edition operation') |
315 | - | ||
316 | - | 305 | + |
306 | + | ||
317 | def ChangeBrushOperation(self, pubsub_evt): | 307 | def ChangeBrushOperation(self, pubsub_evt): |
318 | print pubsub_evt.data | 308 | print pubsub_evt.data |
319 | self._brush_cursor_op = pubsub_evt.data | 309 | self._brush_cursor_op = pubsub_evt.data |
@@ -339,6 +329,7 @@ class Viewer(wx.Panel): | @@ -339,6 +329,7 @@ class Viewer(wx.Panel): | ||
339 | 329 | ||
340 | actor = vtk.vtkImageActor() | 330 | actor = vtk.vtkImageActor() |
341 | actor.SetInput(slice_.GetOutput()) | 331 | actor.SetInput(slice_.GetOutput()) |
332 | + actor_bound = actor.GetBounds() | ||
342 | self.actor = actor | 333 | self.actor = actor |
343 | 334 | ||
344 | colour = const.ORIENTATION_COLOUR[self.orientation] | 335 | colour = const.ORIENTATION_COLOUR[self.orientation] |
@@ -363,7 +354,30 @@ class Viewer(wx.Panel): | @@ -363,7 +354,30 @@ class Viewer(wx.Panel): | ||
363 | self.scroll.SetScrollbar(wx.SB_VERTICAL, 1, max_slice_number, | 354 | self.scroll.SetScrollbar(wx.SB_VERTICAL, 1, max_slice_number, |
364 | max_slice_number) | 355 | max_slice_number) |
365 | self.SetScrollPosition(0) | 356 | self.SetScrollPosition(0) |
366 | - self.cursor.SetSpacing(imagedata.GetSpacing()) | 357 | + |
358 | + actor_bound = actor.GetBounds() | ||
359 | + | ||
360 | + # Insert cursor | ||
361 | + cursor = ca.CursorCircle() | ||
362 | + cursor.SetOrientation(self.orientation) | ||
363 | + self.__update_cursor_position() | ||
364 | + cursor.SetSpacing(imagedata.GetSpacing()) | ||
365 | + self.ren.AddActor(cursor.actor) | ||
366 | + self.ren.Render() | ||
367 | + | ||
368 | + self.cursor = cursor | ||
369 | + | ||
370 | + self.AppendMode('EDITOR') | ||
371 | + | ||
372 | + def __update_cursor_position(self, position = None): | ||
373 | + | ||
374 | + if (self.cursor): | ||
375 | + slice_number = self.slice_number | ||
376 | + actor_bound = self.actor.GetBounds() | ||
377 | + coordinates = {"SAGITAL": [actor_bound[1] + 1 + slice_number, actor_bound[3], actor_bound[5]], | ||
378 | + "CORONAL": [actor_bound[1], actor_bound[3] + 1 + slice_number, actor_bound[5]], | ||
379 | + "AXIAL": [actor_bound[1], actor_bound[3], actor_bound[5] + 1 + slice_number]} | ||
380 | + self.cursor.SetPosition(coordinates[self.orientation]) | ||
367 | 381 | ||
368 | def SetOrientation(self, orientation): | 382 | def SetOrientation(self, orientation): |
369 | self.orientation = orientation | 383 | self.orientation = orientation |
@@ -378,10 +392,10 @@ class Viewer(wx.Panel): | @@ -378,10 +392,10 @@ class Viewer(wx.Panel): | ||
378 | cam.SetViewUp(const.CAM_VIEW_UP[self.orientation]) | 392 | cam.SetViewUp(const.CAM_VIEW_UP[self.orientation]) |
379 | cam.ComputeViewPlaneNormal() | 393 | cam.ComputeViewPlaneNormal() |
380 | cam.OrthogonalizeViewUp() | 394 | cam.OrthogonalizeViewUp() |
381 | - cam.ParallelProjectionOn() | 395 | + cam.ParallelProjectionOn() |
382 | 396 | ||
383 | self.__update_display_extent() | 397 | self.__update_display_extent() |
384 | - | 398 | + |
385 | self.ren.ResetCamera() | 399 | self.ren.ResetCamera() |
386 | self.ren.Render() | 400 | self.ren.Render() |
387 | 401 | ||
@@ -389,11 +403,11 @@ class Viewer(wx.Panel): | @@ -389,11 +403,11 @@ class Viewer(wx.Panel): | ||
389 | 403 | ||
390 | pos = self.slice_number | 404 | pos = self.slice_number |
391 | e = self.imagedata.GetWholeExtent() | 405 | e = self.imagedata.GetWholeExtent() |
392 | - | 406 | + |
393 | new_extent = {"SAGITAL": (pos, pos, e[2], e[3], e[4], e[5]), | 407 | new_extent = {"SAGITAL": (pos, pos, e[2], e[3], e[4], e[5]), |
394 | "CORONAL": (e[0], e[1], pos, pos, e[4], e[5]), | 408 | "CORONAL": (e[0], e[1], pos, pos, e[4], e[5]), |
395 | "AXIAL": (e[0], e[1], e[2], e[3], pos, pos)} | 409 | "AXIAL": (e[0], e[1], e[2], e[3], pos, pos)} |
396 | - | 410 | + |
397 | self.actor.SetDisplayExtent(new_extent[self.orientation]) | 411 | self.actor.SetDisplayExtent(new_extent[self.orientation]) |
398 | self.ren.ResetCameraClippingRange() | 412 | self.ren.ResetCameraClippingRange() |
399 | self.ren.Render() | 413 | self.ren.Render() |
@@ -416,6 +430,7 @@ class Viewer(wx.Panel): | @@ -416,6 +430,7 @@ class Viewer(wx.Panel): | ||
416 | self.text_actor.SetInput(str(index)) | 430 | self.text_actor.SetInput(str(index)) |
417 | self.slice_number = index | 431 | self.slice_number = index |
418 | self.__update_display_extent() | 432 | self.__update_display_extent() |
433 | + self.__update_cursor_position() | ||
419 | 434 | ||
420 | def ChangeSliceNumber(self, pubsub_evt): | 435 | def ChangeSliceNumber(self, pubsub_evt): |
421 | index = pubsub_evt.data | 436 | index = pubsub_evt.data |