Commit 7b7b807a6b317cdece2e24c82330f9e83947e44b

Authored by tfmoraes
1 parent fe6a94e1

Merging improvement in editor branch to master

invesalius/data/cursor_actors.py
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 19
20 from math import * 20 from math import *
21 21
  22 +import numpy
22 import vtk 23 import vtk
23 import wx.lib.pubsub as ps 24 import wx.lib.pubsub as ps
24 from project import Project 25 from project import Project
@@ -89,50 +90,24 @@ class CursorCircle: @@ -89,50 +90,24 @@ class CursorCircle:
89 def __calculate_area_pixels(self): 90 def __calculate_area_pixels(self):
90 """ 91 """
91 Return the cursor's pixels. 92 Return the cursor's pixels.
92 - This method scans the circle line by line.  
93 - Extracted equation.  
94 - http://www.mathopenref.com/chord.html  
95 """ 93 """
96 - xc = 0.0  
97 - yc = 0.0  
98 - z = 0.0  
99 - xs, ys, zs = self.spacing  
100 -  
101 - proj = Project()  
102 - orig_orien = proj.original_orientation  
103 -  
104 - xy = (xs, ys)  
105 - yz = (ys, zs)  
106 - xz = (xs, zs)  
107 -  
108 - if (orig_orien == const.SAGITAL):  
109 - orientation_based_spacing = {"SAGITAL" : xy,  
110 - "AXIAL" : yz,  
111 - "CORONAL" : xz}  
112 - elif(orig_orien == const.CORONAL):  
113 - orientation_based_spacing = {"CORONAL" : xy,  
114 - "AXIAL" : xz,  
115 - "SAGITAL" : yz}  
116 - else:  
117 - orientation_based_spacing = {"AXIAL" : xy,  
118 - "SAGITAL" : yz,  
119 - "CORONAL" : xz}  
120 -  
121 - xs, ys = orientation_based_spacing[self.orientation]  
122 - self.pixel_list = []  
123 radius = self.radius 94 radius = self.radius
124 - for i in utils.frange(yc - radius, yc + radius, ys):  
125 - # distance from the line to the circle's center  
126 - d = yc - i  
127 - # line size  
128 - line = sqrt(radius**2 - d**2) * 2  
129 - # line initial x  
130 - xi = xc - line/2.0  
131 - # line final  
132 - xf = line/2.0 + xc  
133 - yi = i  
134 - for k in utils.frange(xi,xf,xs):  
135 - self.pixel_list.append((k, yi)) 95 + if self.orientation == 'AXIAL':
  96 + sx = self.spacing[0]
  97 + sy = self.spacing[1]
  98 + elif self.orientation == 'CORONAL':
  99 + sx = self.spacing[0]
  100 + sy = self.spacing[2]
  101 + elif self.orientation == 'SAGITAL':
  102 + sx = self.spacing[1]
  103 + sy = self.spacing[2]
  104 +
  105 + y,x = numpy.ogrid[-radius/sy:+radius/sy,
  106 + -radius/sx:+radius/sx]
  107 +
  108 + index = (y*sy)**2 + (x*sx)**2 <= radius**2
  109 + self.points = index
  110 +
136 111
137 def SetSize(self, diameter): 112 def SetSize(self, diameter):
138 radius = self.radius = diameter/2.0 113 radius = self.radius = diameter/2.0
@@ -185,38 +160,7 @@ class CursorCircle: @@ -185,38 +160,7 @@ class CursorCircle:
185 self.actor.VisibilityOff() 160 self.actor.VisibilityOff()
186 161
187 def GetPixels(self): 162 def GetPixels(self):
188 - px, py, pz = self.edition_position  
189 - orient = self.orientation  
190 - xs, ys, zs = self.spacing  
191 - proj = Project()  
192 - orig_orien = proj.original_orientation  
193 - xy1 = lambda x,y: (px + x / xs, py+(y/ys), pz)  
194 - xy2 = lambda x,y: (px+(x/xs), py, pz+(y/zs))  
195 - xy3 = lambda x,y: (px, py+(x/ys), pz+(y/zs))  
196 -  
197 - if (orig_orien == const.SAGITAL):  
198 - abs_pixel = {"SAGITAL": xy1,  
199 - "AXIAL": xy2,  
200 - "CORONAL": xy3}  
201 - elif(orig_orien == const.CORONAL):  
202 - abs_pixel = {"CORONAL": xy1,  
203 - "SAGITAL": xy3,  
204 - "AXIAL": xy2}  
205 - else:  
206 - abs_pixel = {"AXIAL": xy1,  
207 - "CORONAL": xy2,  
208 - "SAGITAL": xy3}  
209 -  
210 - function_orientation = abs_pixel[orient]  
211 -  
212 - for pixel_0,pixel_1 in self.pixel_list:  
213 - # The position of the pixels in this list is relative (based only on  
214 - # the area, and not the cursor position).  
215 - # Let's calculate the absolute position  
216 - # TODO: Optimize this!!!!  
217 - yield function_orientation(pixel_0, pixel_1)  
218 -  
219 - 163 + return self.points
220 164
221 165
222 class CursorRectangle: 166 class CursorRectangle:
@@ -228,6 +172,7 @@ class CursorRectangle: @@ -228,6 +172,7 @@ class CursorRectangle:
228 172
229 self.x_length = 30 173 self.x_length = 30
230 self.y_length = 30 174 self.y_length = 30
  175 + self.radius = 15
231 176
232 self.dimension = (self.x_length, self.y_length) 177 self.dimension = (self.x_length, self.y_length)
233 self.position = (0 ,0) 178 self.position = (0 ,0)
@@ -240,6 +185,7 @@ class CursorRectangle: @@ -240,6 +185,7 @@ class CursorRectangle:
240 def SetSize(self, size): 185 def SetSize(self, size):
241 self.x_length = size 186 self.x_length = size
242 self.y_length = size 187 self.y_length = size
  188 + self.radius = size / 2
243 retangle = self.retangle 189 retangle = self.retangle
244 retangle.SetXLength(size) 190 retangle.SetXLength(size)
245 retangle.SetYLength(size) 191 retangle.SetYLength(size)
@@ -311,67 +257,21 @@ class CursorRectangle: @@ -311,67 +257,21 @@ class CursorRectangle:
311 actor.SetVisibility(0) 257 actor.SetVisibility(0)
312 258
313 def __calculate_area_pixels(self): 259 def __calculate_area_pixels(self):
314 - xc = 0  
315 - yc = 0  
316 - z = 0  
317 - xs, ys, zs = self.spacing  
318 -  
319 - proj = Project()  
320 - orig_orien = proj.original_orientation  
321 -  
322 - xy = (xs, ys)  
323 - yz = (ys, zs)  
324 - xz = (xs, zs)  
325 -  
326 - if (orig_orien == const.SAGITAL):  
327 - orientation_based_spacing = {"SAGITAL" : xy,  
328 - "AXIAL" : yz,  
329 - "CORONAL" : xz}  
330 - elif(orig_orien == const.CORONAL):  
331 - orientation_based_spacing = {"CORONAL" : xy,  
332 - "AXIAL" : xz,  
333 - "SAGITAL" : yz}  
334 - else:  
335 - orientation_based_spacing = {"AXIAL" : xy,  
336 - "SAGITAL" : yz,  
337 - "CORONAL" : xz}  
338 -  
339 - xs, ys = orientation_based_spacing[self.orientation]  
340 - self.pixel_list = []  
341 - for i in utils.frange(yc - self.y_length/2, yc + self.y_length/2, ys):  
342 - for k in utils.frange(xc - self.x_length/2, xc + self.x_length/2, xs):  
343 - self.pixel_list.append((k, i))  
344 - 260 + if self.orientation == 'AXIAL':
  261 + sx = self.spacing[0]
  262 + sy = self.spacing[1]
  263 + elif self.orientation == 'CORONAL':
  264 + sx = self.spacing[0]
  265 + sy = self.spacing[2]
  266 + elif self.orientation == 'SAGITAL':
  267 + sx = self.spacing[1]
  268 + sy = self.spacing[2]
  269 + shape = (self.y_length/sy, self.x_length/sx)
  270 + self.points = numpy.empty(shape, dtype='bool')
  271 + self.points.fill(True)
345 272
346 def GetPixels(self): 273 def GetPixels(self):
347 """ 274 """
348 Return the points of the rectangle 275 Return the points of the rectangle
349 """ 276 """
350 - px, py, pz = self.edition_position  
351 - orient = self.orientation  
352 - xs, ys, zs = self.spacing  
353 - proj = Project()  
354 - orig_orien = proj.original_orientation  
355 - xy1 = lambda x,y: (px + x / xs, py+(y/ys), pz)  
356 - xy2 = lambda x,y: (px+(x/xs), py, pz+(y/zs))  
357 - xy3 = lambda x,y: (px, py+(x/ys), pz+(y/zs))  
358 -  
359 - if (orig_orien == const.SAGITAL):  
360 - abs_pixel = {"SAGITAL": xy1,  
361 - "AXIAL": xy2,  
362 - "CORONAL": xy3}  
363 - elif(orig_orien == const.CORONAL):  
364 - abs_pixel = {"CORONAL": xy1,  
365 - "SAGITAL": xy3,  
366 - "AXIAL": xy2}  
367 - else:  
368 - abs_pixel = {"AXIAL": xy1,  
369 - "CORONAL": xy2,  
370 - "SAGITAL": xy3}  
371 - function_orientation = abs_pixel[orient]  
372 - for pixel_0,pixel_1 in self.pixel_list:  
373 - # The position of the pixels in this list is relative (based only on  
374 - # the area, and not the cursor position).  
375 - # Let's calculate the absolute position  
376 - # TODO: Optimize this!!!!  
377 - yield function_orientation(pixel_0, pixel_1) 277 + return self.points
invesalius/data/slice_.py
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais 16 # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais
17 # detalhes. 17 # detalhes.
18 #-------------------------------------------------------------------------- 18 #--------------------------------------------------------------------------
  19 +import math
  20 +
19 import numpy 21 import numpy
20 import vtk 22 import vtk
21 import wx.lib.pubsub as ps 23 import wx.lib.pubsub as ps
@@ -101,11 +103,6 @@ class Slice(object): @@ -101,11 +103,6 @@ class Slice(object):
101 ps.Publisher().subscribe(self.__set_mask_name, 'Change mask name') 103 ps.Publisher().subscribe(self.__set_mask_name, 'Change mask name')
102 ps.Publisher().subscribe(self.__show_mask, 'Show mask') 104 ps.Publisher().subscribe(self.__show_mask, 'Show mask')
103 105
104 - # Operations related to slice editor  
105 - ps.Publisher().subscribe(self.__erase_mask_pixel, 'Erase mask pixel')  
106 - ps.Publisher().subscribe(self.__edit_mask_pixel, 'Edit mask pixel')  
107 - ps.Publisher().subscribe(self.__add_mask_pixel, 'Add mask pixel')  
108 -  
109 ps.Publisher().subscribe(self.__set_current_mask_threshold_limits, 106 ps.Publisher().subscribe(self.__set_current_mask_threshold_limits,
110 'Update threshold limits') 107 'Update threshold limits')
111 108
@@ -249,9 +246,7 @@ class Slice(object): @@ -249,9 +246,7 @@ class Slice(object):
249 #Clear edited points 246 #Clear edited points
250 self.current_mask.edited_points = {} 247 self.current_mask.edited_points = {}
251 self.num_gradient += 1 248 self.num_gradient += 1
252 - self.current_mask.matrix[0, :, :] = 0  
253 - self.current_mask.matrix[:, 0, :] = 0  
254 - self.current_mask.matrix[:, :, 0] = 0 249 + self.current_mask.matrix[:] = 0
255 250
256 def __set_current_mask_threshold_actual_slice(self, evt_pubsub): 251 def __set_current_mask_threshold_actual_slice(self, evt_pubsub):
257 threshold_range = evt_pubsub.data 252 threshold_range = evt_pubsub.data
@@ -288,20 +283,222 @@ class Slice(object): @@ -288,20 +283,222 @@ class Slice(object):
288 index, value = pubsub_evt.data 283 index, value = pubsub_evt.data
289 self.ShowMask(index, value) 284 self.ShowMask(index, value)
290 #--------------------------------------------------------------------------- 285 #---------------------------------------------------------------------------
291 - def __erase_mask_pixel(self, pubsub_evt):  
292 - positions = pubsub_evt.data  
293 - for position in positions:  
294 - self.ErasePixel(position) 286 + def erase_mask_pixel(self, index, position, radius, orientation):
  287 + mask = self.buffer_slices[orientation].mask
  288 + image = self.buffer_slices[orientation].image
  289 +
  290 + if hasattr(position, '__iter__'):
  291 + py, px = position
  292 + if orientation == 'AXIAL':
  293 + sx = self.spacing[0]
  294 + sy = self.spacing[1]
  295 + elif orientation == 'CORONAL':
  296 + sx = self.spacing[0]
  297 + sy = self.spacing[2]
  298 + elif orientation == 'SAGITAL':
  299 + sx = self.spacing[1]
  300 + sy = self.spacing[2]
  301 +
  302 + else:
  303 + if orientation == 'AXIAL':
  304 + sx = self.spacing[0]
  305 + sy = self.spacing[1]
  306 + py = position / mask.shape[1]
  307 + px = position % mask.shape[1]
  308 + elif orientation == 'CORONAL':
  309 + sx = self.spacing[0]
  310 + sy = self.spacing[2]
  311 + py = position / mask.shape[1]
  312 + px = position % mask.shape[1]
  313 + elif orientation == 'SAGITAL':
  314 + sx = self.spacing[1]
  315 + sy = self.spacing[2]
  316 + py = position / mask.shape[1]
  317 + px = position % mask.shape[1]
  318 +
  319 + xi = px - math.ceil(radius/sx)
  320 + xf = px + math.ceil(radius/sx)
  321 + yi = py - math.ceil(radius/sy)
  322 + yf = py + math.ceil(radius/sy)
  323 +
  324 + if yi < 0:
  325 + index = index[abs(yi):,:]
  326 + yi = 0
  327 + if yf > image.shape[0]:
  328 + index = index[:index.shape[0]-(yf-image.shape[0]), :]
  329 + yf = image.shape[0]
  330 +
  331 + if xi < 0:
  332 + index = index[:,abs(xi):]
  333 + xi = 0
  334 + if xf > image.shape[1]:
  335 + index = index[:,:index.shape[1]-(xf-image.shape[1])]
  336 + xf = image.shape[1]
  337 +
  338 + # Verifying if the points is over the image array.
  339 + if (not 0 < xi < image.shape[1] and not 0 < xf < image.shape[1]) or \
  340 + (not 0 < yi < image.shape[0] and not 0 < yf < image.shape[0]):
  341 + return
  342 +
  343 + roi_m = mask[yi:yf,xi:xf]
  344 + roi_i = image[yi:yf, xi:xf]
  345 +
  346 + roi_m[index] = 1
  347 + self.buffer_slices[orientation].discard_vtk_mask()
  348 +
  349 + def edit_mask_pixel(self, index, position, radius, orientation):
  350 + mask = self.buffer_slices[orientation].mask
  351 + image = self.buffer_slices[orientation].image
  352 + thresh_min, thresh_max = self.current_mask.edition_threshold_range
295 353
296 - def __edit_mask_pixel(self, pubsub_evt):  
297 - positions = pubsub_evt.data  
298 - for position in positions:  
299 - self.EditPixelBasedOnThreshold(position) 354 + if hasattr(position, '__iter__'):
  355 + py, px = position
  356 + if orientation == 'AXIAL':
  357 + sx = self.spacing[0]
  358 + sy = self.spacing[1]
  359 + elif orientation == 'CORONAL':
  360 + sx = self.spacing[0]
  361 + sy = self.spacing[2]
  362 + elif orientation == 'SAGITAL':
  363 + sx = self.spacing[1]
  364 + sy = self.spacing[2]
  365 +
  366 + else:
  367 + if orientation == 'AXIAL':
  368 + sx = self.spacing[0]
  369 + sy = self.spacing[1]
  370 + py = position / mask.shape[1]
  371 + px = position % mask.shape[1]
  372 + elif orientation == 'CORONAL':
  373 + sx = self.spacing[0]
  374 + sy = self.spacing[2]
  375 + py = position / mask.shape[1]
  376 + px = position % mask.shape[1]
  377 + elif orientation == 'SAGITAL':
  378 + sx = self.spacing[1]
  379 + sy = self.spacing[2]
  380 + py = position / mask.shape[1]
  381 + px = position % mask.shape[1]
  382 +
  383 + xi = px - math.ceil(radius/sx)
  384 + xf = px + math.ceil(radius/sx)
  385 + yi = py - math.ceil(radius/sy)
  386 + yf = py + math.ceil(radius/sy)
  387 +
  388 + if yi < 0:
  389 + index = index[abs(yi):,:]
  390 + yi = 0
  391 + if yf > image.shape[0]:
  392 + index = index[:index.shape[0]-(yf-image.shape[0]), :]
  393 + yf = image.shape[0]
  394 +
  395 + if xi < 0:
  396 + index = index[:,abs(xi):]
  397 + xi = 0
  398 + if xf > image.shape[1]:
  399 + index = index[:,:index.shape[1]-(xf-image.shape[1])]
  400 + xf = image.shape[1]
  401 +
  402 + # Verifying if the points is over the image array.
  403 + if (not 0 < xi < image.shape[1] and not 0 < xf < image.shape[1]) or \
  404 + (not 0 < yi < image.shape[0] and not 0 < yf < image.shape[0]):
  405 + return
  406 +
  407 + roi_m = mask[yi:yf,xi:xf]
  408 + roi_i = image[yi:yf, xi:xf]
  409 +
  410 + # It's a trick to make points between threshold gets value 254
  411 + # (1 * 253 + 1) and out ones gets value 1 (0 * 253 + 1).
  412 + roi_m[index] = (((roi_i[index] >= thresh_min)
  413 + & (roi_i[index] <= thresh_max)) * 253) + 1
  414 + self.buffer_slices[orientation].discard_vtk_mask()
  415 +
  416 + def add_mask_pixel(self, index, position, radius, orientation):
  417 + #mask = self.buffer_slices[orientation].mask
  418 + #if orientation == 'AXIAL':
  419 + #sx = self.spacing[0]
  420 + #sy = self.spacing[1]
  421 + #py = position / mask.shape[1]
  422 + #px = position % mask.shape[1]
  423 + #elif orientation == 'CORONAL':
  424 + #sx = self.spacing[0]
  425 + #sy = self.spacing[2]
  426 + #py = position / mask.shape[1]
  427 + #px = position % mask.shape[1]
  428 + #elif orientation == 'SAGITAL':
  429 + #sx = self.spacing[1]
  430 + #sy = self.spacing[2]
  431 + #py = position / mask.shape[1]
  432 + #px = position % mask.shape[1]
  433 +
  434 + #print "->px, py", px, py
  435 + #print "->position", position
  436 + #print '->shape', mask.shape
  437 +
  438 + #mask[py - radius / sy: py + radius / sy,
  439 + #px - radius / sx: px + radius / sx] = 255
  440 + #self.buffer_slices[orientation].discard_vtk_mask()
  441 + mask = self.buffer_slices[orientation].mask
  442 + image = self.buffer_slices[orientation].image
  443 +
  444 + if hasattr(position, '__iter__'):
  445 + py, px = position
  446 + if orientation == 'AXIAL':
  447 + sx = self.spacing[0]
  448 + sy = self.spacing[1]
  449 + elif orientation == 'CORONAL':
  450 + sx = self.spacing[0]
  451 + sy = self.spacing[2]
  452 + elif orientation == 'SAGITAL':
  453 + sx = self.spacing[1]
  454 + sy = self.spacing[2]
300 455
301 - def __add_mask_pixel(self, pubsub_evt):  
302 - positions = pubsub_evt.data  
303 - for position in positions:  
304 - self.DrawPixel(position) 456 + else:
  457 + if orientation == 'AXIAL':
  458 + sx = self.spacing[0]
  459 + sy = self.spacing[1]
  460 + py = position / mask.shape[1]
  461 + px = position % mask.shape[1]
  462 + elif orientation == 'CORONAL':
  463 + sx = self.spacing[0]
  464 + sy = self.spacing[2]
  465 + py = position / mask.shape[1]
  466 + px = position % mask.shape[1]
  467 + elif orientation == 'SAGITAL':
  468 + sx = self.spacing[1]
  469 + sy = self.spacing[2]
  470 + py = position / mask.shape[1]
  471 + px = position % mask.shape[1]
  472 +
  473 + xi = px - math.ceil(radius/sx)
  474 + xf = px + math.ceil(radius/sx)
  475 + yi = py - math.ceil(radius/sy)
  476 + yf = py + math.ceil(radius/sy)
  477 +
  478 + if yi < 0:
  479 + index = index[abs(yi):,:]
  480 + yi = 0
  481 + if yf > image.shape[0]:
  482 + index = index[:index.shape[0]-(yf-image.shape[0]), :]
  483 + yf = image.shape[0]
  484 +
  485 + if xi < 0:
  486 + index = index[:,abs(xi):]
  487 + xi = 0
  488 + if xf > image.shape[1]:
  489 + index = index[:,:index.shape[1]-(xf-image.shape[1])]
  490 + xf = image.shape[1]
  491 +
  492 + # Verifying if the points is over the image array.
  493 + if (not 0 < xi < image.shape[1] and not 0 < xf < image.shape[1]) or \
  494 + (not 0 < yi < image.shape[0] and not 0 < yf < image.shape[0]):
  495 + return
  496 +
  497 + roi_m = mask[yi:yf,xi:xf]
  498 + roi_i = image[yi:yf, xi:xf]
  499 +
  500 + roi_m[index] = 254
  501 + self.buffer_slices[orientation].discard_vtk_mask()
305 #--------------------------------------------------------------------------- 502 #---------------------------------------------------------------------------
306 # END PUBSUB_EVT METHODS 503 # END PUBSUB_EVT METHODS
307 #--------------------------------------------------------------------------- 504 #---------------------------------------------------------------------------
@@ -311,7 +508,7 @@ class Slice(object): @@ -311,7 +508,7 @@ class Slice(object):
311 if self.buffer_slices[orientation].vtk_image: 508 if self.buffer_slices[orientation].vtk_image:
312 image = self.buffer_slices[orientation].vtk_image 509 image = self.buffer_slices[orientation].vtk_image
313 else: 510 else:
314 - n_image = self.GetImageSlice(orientation, slice_number) 511 + n_image = self.get_image_slice(orientation, slice_number)
315 image = iu.to_vtk(n_image, self.spacing, slice_number, orientation) 512 image = iu.to_vtk(n_image, self.spacing, slice_number, orientation)
316 image = self.do_ww_wl(image) 513 image = self.do_ww_wl(image)
317 if self.current_mask and self.current_mask.is_shown: 514 if self.current_mask and self.current_mask.is_shown:
@@ -320,19 +517,22 @@ class Slice(object): @@ -320,19 +517,22 @@ class Slice(object):
320 mask = self.buffer_slices[orientation].vtk_mask 517 mask = self.buffer_slices[orientation].vtk_mask
321 else: 518 else:
322 print "Do not getting from buffer" 519 print "Do not getting from buffer"
323 - n_mask = self.GetMaskSlice(orientation, slice_number) 520 + n_mask = self.get_mask_slice(orientation, slice_number)
324 mask = iu.to_vtk(n_mask, self.spacing, slice_number, orientation) 521 mask = iu.to_vtk(n_mask, self.spacing, slice_number, orientation)
325 mask = self.do_colour_mask(mask) 522 mask = self.do_colour_mask(mask)
  523 + self.buffer_slices[orientation].mask = n_mask
326 final_image = self.do_blend(image, mask) 524 final_image = self.do_blend(image, mask)
  525 + self.buffer_slices[orientation].vtk_mask = mask
327 else: 526 else:
328 final_image = image 527 final_image = image
  528 + self.buffer_slices[orientation].vtk_image = image
329 else: 529 else:
330 - n_image = self.GetImageSlice(orientation, slice_number) 530 + n_image = self.get_image_slice(orientation, slice_number)
331 image = iu.to_vtk(n_image, self.spacing, slice_number, orientation) 531 image = iu.to_vtk(n_image, self.spacing, slice_number, orientation)
332 image = self.do_ww_wl(image) 532 image = self.do_ww_wl(image)
333 533
334 if self.current_mask and self.current_mask.is_shown: 534 if self.current_mask and self.current_mask.is_shown:
335 - n_mask = self.GetMaskSlice(orientation, slice_number) 535 + n_mask = self.get_mask_slice(orientation, slice_number)
336 mask = iu.to_vtk(n_mask, self.spacing, slice_number, orientation) 536 mask = iu.to_vtk(n_mask, self.spacing, slice_number, orientation)
337 mask = self.do_colour_mask(mask) 537 mask = self.do_colour_mask(mask)
338 final_image = self.do_blend(image, mask) 538 final_image = self.do_blend(image, mask)
@@ -349,7 +549,7 @@ class Slice(object): @@ -349,7 +549,7 @@ class Slice(object):
349 549
350 return final_image 550 return final_image
351 551
352 - def GetImageSlice(self, orientation, slice_number): 552 + def get_image_slice(self, orientation, slice_number):
353 if self.buffer_slices[orientation].index == slice_number \ 553 if self.buffer_slices[orientation].index == slice_number \
354 and self.buffer_slices[orientation].image is not None: 554 and self.buffer_slices[orientation].image is not None:
355 n_image = self.buffer_slices[orientation].image 555 n_image = self.buffer_slices[orientation].image
@@ -362,7 +562,7 @@ class Slice(object): @@ -362,7 +562,7 @@ class Slice(object):
362 n_image = numpy.array(self.matrix[..., ..., slice_number]) 562 n_image = numpy.array(self.matrix[..., ..., slice_number])
363 return n_image 563 return n_image
364 564
365 - def GetMaskSlice(self, orientation, slice_number): 565 + def get_mask_slice(self, orientation, slice_number):
366 """ 566 """
367 It gets the from actual mask the given slice from given orientation 567 It gets the from actual mask the given slice from given orientation
368 """ 568 """
@@ -376,25 +576,28 @@ class Slice(object): @@ -376,25 +576,28 @@ class Slice(object):
376 n = slice_number + 1 576 n = slice_number + 1
377 if orientation == 'AXIAL': 577 if orientation == 'AXIAL':
378 if self.current_mask.matrix[n, 0, 0] == 0: 578 if self.current_mask.matrix[n, 0, 0] == 0:
379 - self.current_mask.matrix[n, 1:, 1:] = \  
380 - self.do_threshold_to_a_slice(self.GetImageSlice(orientation,  
381 - slice_number)) 579 + mask = self.current_mask.matrix[n, 1:, 1:]
  580 + mask[:] = self.do_threshold_to_a_slice(self.get_image_slice(orientation,
  581 + slice_number),
  582 + mask)
382 self.current_mask.matrix[n, 0, 0] = 1 583 self.current_mask.matrix[n, 0, 0] = 1
383 n_mask = numpy.array(self.current_mask.matrix[n, 1:, 1:]) 584 n_mask = numpy.array(self.current_mask.matrix[n, 1:, 1:])
384 585
385 elif orientation == 'CORONAL': 586 elif orientation == 'CORONAL':
386 if self.current_mask.matrix[0, n, 0] == 0: 587 if self.current_mask.matrix[0, n, 0] == 0:
387 - self.current_mask.matrix[1:, n, 1:] = \  
388 - self.do_threshold_to_a_slice(self.GetImageSlice(orientation,  
389 - slice_number)) 588 + mask = self.current_mask.matrix[1:, n, 1:]
  589 + mask[:] = self.do_threshold_to_a_slice(self.get_image_slice(orientation,
  590 + slice_number),
  591 + mask)
390 self.current_mask.matrix[0, n, 0] = 1 592 self.current_mask.matrix[0, n, 0] = 1
391 n_mask = numpy.array(self.current_mask.matrix[1:, n, 1:]) 593 n_mask = numpy.array(self.current_mask.matrix[1:, n, 1:])
392 594
393 elif orientation == 'SAGITAL': 595 elif orientation == 'SAGITAL':
394 if self.current_mask.matrix[0, 0, n] == 0: 596 if self.current_mask.matrix[0, 0, n] == 0:
395 - self.current_mask.matrix[1:, 1:, n] = \  
396 - self.do_threshold_to_a_slice(self.GetImageSlice(orientation,  
397 - slice_number)) 597 + mask = self.current_mask.matrix[1:, 1:, n]
  598 + mask[:] = self.do_threshold_to_a_slice(self.get_image_slice(orientation,
  599 + slice_number),
  600 + mask)
398 self.current_mask.matrix[0, 0, n] = 1 601 self.current_mask.matrix[0, 0, n] = 1
399 n_mask = numpy.array(self.current_mask.matrix[1:, 1:, n]) 602 n_mask = numpy.array(self.current_mask.matrix[1:, 1:, n])
400 return n_mask 603 return n_mask
@@ -804,13 +1007,15 @@ class Slice(object): @@ -804,13 +1007,15 @@ class Slice(object):
804 1007
805 return colorer.GetOutput() 1008 return colorer.GetOutput()
806 1009
807 - def do_threshold_to_a_slice(self, slice_matrix): 1010 + def do_threshold_to_a_slice(self, slice_matrix, mask):
808 """ 1011 """
809 Based on the current threshold bounds generates a threshold mask to 1012 Based on the current threshold bounds generates a threshold mask to
810 given slice_matrix. 1013 given slice_matrix.
811 """ 1014 """
812 thresh_min, thresh_max = self.current_mask.threshold_range 1015 thresh_min, thresh_max = self.current_mask.threshold_range
813 - m= numpy.logical_and(slice_matrix >= thresh_min, slice_matrix <= thresh_max) * 255 1016 + m = (((slice_matrix >= thresh_min) & (slice_matrix <= thresh_max)) * 255)
  1017 + m[mask == 1] = 1
  1018 + m[mask == 254] = 254
814 return m 1019 return m
815 1020
816 def do_colour_mask(self, imagedata): 1021 def do_colour_mask(self, imagedata):
@@ -819,14 +1024,15 @@ class Slice(object): @@ -819,14 +1024,15 @@ class Slice(object):
819 1024
820 # map scalar values into colors 1025 # map scalar values into colors
821 lut_mask = vtk.vtkLookupTable() 1026 lut_mask = vtk.vtkLookupTable()
822 - lut_mask.SetNumberOfColors(255) 1027 + lut_mask.SetNumberOfColors(256)
823 lut_mask.SetHueRange(const.THRESHOLD_HUE_RANGE) 1028 lut_mask.SetHueRange(const.THRESHOLD_HUE_RANGE)
824 lut_mask.SetSaturationRange(1, 1) 1029 lut_mask.SetSaturationRange(1, 1)
825 - lut_mask.SetValueRange(0, 1) 1030 + lut_mask.SetValueRange(0, 255)
  1031 + lut_mask.SetRange(0, 255)
826 lut_mask.SetNumberOfTableValues(256) 1032 lut_mask.SetNumberOfTableValues(256)
827 lut_mask.SetTableValue(0, 0, 0, 0, 0.0) 1033 lut_mask.SetTableValue(0, 0, 0, 0, 0.0)
828 lut_mask.SetTableValue(1, 0, 0, 0, 0.0) 1034 lut_mask.SetTableValue(1, 0, 0, 0, 0.0)
829 - lut_mask.SetTableValue(2, 0, 0, 0, 0.0) 1035 + lut_mask.SetTableValue(254, r, g, b, 1.0)
830 lut_mask.SetTableValue(255, r, g, b, 1.0) 1036 lut_mask.SetTableValue(255, r, g, b, 1.0)
831 lut_mask.SetRampToLinear() 1037 lut_mask.SetRampToLinear()
832 lut_mask.Build() 1038 lut_mask.Build()
@@ -858,6 +1064,25 @@ class Slice(object): @@ -858,6 +1064,25 @@ class Slice(object):
858 1064
859 return blend_imagedata.GetOutput() 1065 return blend_imagedata.GetOutput()
860 1066
  1067 + def apply_slice_buffer_to_mask(self, orientation):
  1068 + """
  1069 + Apply the modifications (edition) in mask buffer to mask.
  1070 + """
  1071 + b_mask = self.buffer_slices[orientation].mask
  1072 + index = self.buffer_slices[orientation].index
  1073 + print "-> ORIENTATION", orientation, index, b_mask
  1074 + if orientation == 'AXIAL':
  1075 + self.current_mask.matrix[index+1,1:,1:] = b_mask
  1076 + elif orientation == 'CORONAL':
  1077 + self.current_mask.matrix[1:, index+1, 1:] = b_mask
  1078 + elif orientation == 'SAGITAL':
  1079 + self.current_mask.matrix[1:, 1:, index+1] = b_mask
  1080 +
  1081 + for o in self.buffer_slices:
  1082 + if o != orientation:
  1083 + self.buffer_slices[o].discard_mask()
  1084 + self.buffer_slices[o].discard_vtk_mask()
  1085 + ps.Publisher().sendMessage('Reload actual slice')
861 1086
862 def __build_mask(self, imagedata, create=True): 1087 def __build_mask(self, imagedata, create=True):
863 # create new mask instance and insert it into project 1088 # create new mask instance and insert it into project
invesalius/data/viewer_slice.py
@@ -90,7 +90,7 @@ class Viewer(wx.Panel): @@ -90,7 +90,7 @@ class Viewer(wx.Panel):
90 self.on_text = False 90 self.on_text = False
91 # VTK pipeline and actors 91 # VTK pipeline and actors
92 self.__config_interactor() 92 self.__config_interactor()
93 - self.pick = vtk.vtkPropPicker() 93 + self.pick = vtk.vtkWorldPointPicker()
94 self.cross_actor = vtk.vtkActor() 94 self.cross_actor = vtk.vtkActor()
95 95
96 96
@@ -180,6 +180,7 @@ class Viewer(wx.Panel): @@ -180,6 +180,7 @@ class Viewer(wx.Panel):
180 { 180 {
181 "MouseMoveEvent": self.OnBrushMove, 181 "MouseMoveEvent": self.OnBrushMove,
182 "LeftButtonPressEvent": self.OnBrushClick, 182 "LeftButtonPressEvent": self.OnBrushClick,
  183 + "LeftButtonReleaseEvent": self.OnBrushRelease,
183 "EnterEvent": self.OnEnterInteractor, 184 "EnterEvent": self.OnEnterInteractor,
184 "LeaveEvent": self.OnLeaveInteractor 185 "LeaveEvent": self.OnLeaveInteractor
185 }, 186 },
@@ -566,9 +567,9 @@ class Viewer(wx.Panel): @@ -566,9 +567,9 @@ class Viewer(wx.Panel):
566 567
567 def ChangeBrushSize(self, pubsub_evt): 568 def ChangeBrushSize(self, pubsub_evt):
568 size = pubsub_evt.data 569 size = pubsub_evt.data
569 - self._brush_cursor_size = size  
570 - for slice_data in self.slice_data_list:  
571 - slice_data.cursor.SetSize(size) 570 + #self._brush_cursor_size = size
  571 + #for slice_data in self.slice_data_list:
  572 + self.slice_data.cursor.SetSize(size)
572 573
573 def ChangeBrushColour(self, pubsub_evt): 574 def ChangeBrushColour(self, pubsub_evt):
574 vtk_colour = pubsub_evt.data[3] 575 vtk_colour = pubsub_evt.data[3]
@@ -587,60 +588,71 @@ class Viewer(wx.Panel): @@ -587,60 +588,71 @@ class Viewer(wx.Panel):
587 588
588 def ChangeBrushActor(self, pubsub_evt): 589 def ChangeBrushActor(self, pubsub_evt):
589 brush_type = pubsub_evt.data 590 brush_type = pubsub_evt.data
590 - for slice_data in self.slice_data_list:  
591 - self._brush_cursor_type = brush_type  
592 - #self.ren.RemoveActor(self.cursor.actor)  
593 -  
594 - if brush_type == const.BRUSH_SQUARE:  
595 - cursor = ca.CursorRectangle()  
596 - elif brush_type == const.BRUSH_CIRCLE:  
597 - cursor = ca.CursorCircle()  
598 - #self.cursor = cursor  
599 -  
600 - cursor.SetOrientation(self.orientation)  
601 - coordinates = {"SAGITAL": [slice_data.number, 0, 0],  
602 - "CORONAL": [0, slice_data.number, 0],  
603 - "AXIAL": [0, 0, slice_data.number]}  
604 - cursor.SetPosition(coordinates[self.orientation])  
605 - cursor.SetSpacing(self.imagedata.GetSpacing())  
606 - cursor.SetColour(self._brush_cursor_colour)  
607 - cursor.SetSize(self._brush_cursor_size)  
608 - slice_data.SetCursor(cursor)  
609 - #self.ren.AddActor(cursor.actor)  
610 - #self.ren.Render() 591 + slice_data = self.slice_data
  592 + self._brush_cursor_type = brush_type
  593 +
  594 + if brush_type == const.BRUSH_SQUARE:
  595 + cursor = ca.CursorRectangle()
  596 + elif brush_type == const.BRUSH_CIRCLE:
  597 + cursor = ca.CursorCircle()
  598 +
  599 + cursor.SetOrientation(self.orientation)
  600 + coordinates = {"SAGITAL": [slice_data.number, 0, 0],
  601 + "CORONAL": [0, slice_data.number, 0],
  602 + "AXIAL": [0, 0, slice_data.number]}
  603 + cursor.SetPosition(coordinates[self.orientation])
  604 + cursor.SetSpacing(self.slice_.spacing)
  605 + cursor.SetColour(self._brush_cursor_colour)
  606 + cursor.SetSize(self._brush_cursor_size)
  607 + slice_data.SetCursor(cursor)
611 self.interactor.Render() 608 self.interactor.Render()
612 - #self.cursor = cursor  
613 609
614 610
615 def OnBrushClick(self, evt, obj): 611 def OnBrushClick(self, evt, obj):
616 - 612 + self.__set_editor_cursor_visibility(1)
  613 +
617 mouse_x, mouse_y = self.interactor.GetEventPosition() 614 mouse_x, mouse_y = self.interactor.GetEventPosition()
618 render = self.interactor.FindPokedRenderer(mouse_x, mouse_y) 615 render = self.interactor.FindPokedRenderer(mouse_x, mouse_y)
619 slice_data = self.get_slice_data(render) 616 slice_data = self.get_slice_data(render)
620 - self.pick.Pick(mouse_x, mouse_y, 0, render)  
621 617
  618 + # TODO: Improve!
  619 + #for i in self.slice_data_list:
  620 + #i.cursor.Show(0)
  621 + self.slice_data.cursor.Show()
  622 +
  623 + self.pick.Pick(mouse_x, mouse_y, 0, render)
  624 +
622 coord = self.get_coordinate_cursor() 625 coord = self.get_coordinate_cursor()
623 slice_data.cursor.SetPosition(coord) 626 slice_data.cursor.SetPosition(coord)
624 slice_data.cursor.SetEditionPosition( 627 slice_data.cursor.SetEditionPosition(
625 self.get_coordinate_cursor_edition(slice_data)) 628 self.get_coordinate_cursor_edition(slice_data))
626 self.__update_cursor_position(slice_data, coord) 629 self.__update_cursor_position(slice_data, coord)
627 - #render.Render()  
628 630
629 - evt_msg = {const.BRUSH_ERASE: 'Erase mask pixel',  
630 - const.BRUSH_DRAW: 'Add mask pixel',  
631 - const.BRUSH_THRESH: 'Edit mask pixel'}  
632 - msg = evt_msg[self._brush_cursor_op] 631 + cursor = self.slice_data.cursor
  632 + position = self.slice_data.actor.GetInput().FindPoint(coord)
  633 + radius = cursor.radius
633 634
634 - pixels = itertools.ifilter(self.test_operation_position,  
635 - slice_data.cursor.GetPixels())  
636 - ps.Publisher().sendMessage(msg, pixels) 635 + if position < 0:
  636 + position = self.calculate_matrix_position(coord)
  637 +
  638 +
  639 + # TODO: Call slice_ functions instead of to use pubsub message,
  640 + # maybe we can get some performances improvements here.
  641 + if self._brush_cursor_op == const.BRUSH_ERASE:
  642 + self.slice_.erase_mask_pixel(cursor.GetPixels(), position, radius,
  643 + self.orientation)
  644 + elif self._brush_cursor_op == const.BRUSH_DRAW:
  645 + self.slice_.add_mask_pixel(cursor.GetPixels(), position, radius,
  646 + self.orientation)
  647 + elif self._brush_cursor_op == const.BRUSH_THRESH:
  648 + self.slice_.edit_mask_pixel(cursor.GetPixels(), position, radius,
  649 + self.orientation)
  650 +
  651 + # TODO: To create a new function to reload images to viewer.
  652 + self.OnScrollBar()
637 653
638 - # FIXME: This is idiot, but is the only way that brush operations are  
639 - # working when cross is disabled  
640 - ps.Publisher().sendMessage('Update slice viewer')  
641 654
642 def OnBrushMove(self, evt, obj): 655 def OnBrushMove(self, evt, obj):
643 -  
644 self.__set_editor_cursor_visibility(1) 656 self.__set_editor_cursor_visibility(1)
645 657
646 mouse_x, mouse_y = self.interactor.GetEventPosition() 658 mouse_x, mouse_y = self.interactor.GetEventPosition()
@@ -650,35 +662,50 @@ class Viewer(wx.Panel): @@ -650,35 +662,50 @@ class Viewer(wx.Panel):
650 # TODO: Improve! 662 # TODO: Improve!
651 #for i in self.slice_data_list: 663 #for i in self.slice_data_list:
652 #i.cursor.Show(0) 664 #i.cursor.Show(0)
653 - #slice_data.cursor.Show() 665 + self.slice_data.cursor.Show()
654 666
655 self.pick.Pick(mouse_x, mouse_y, 0, render) 667 self.pick.Pick(mouse_x, mouse_y, 0, render)
656 668
657 - if (self.pick.GetProp()):  
658 - self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK))  
659 - else:  
660 - self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) 669 + #if (self.pick.GetViewProp()):
  670 + #self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK))
  671 + #else:
  672 + #self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
661 673
662 coord = self.get_coordinate_cursor() 674 coord = self.get_coordinate_cursor()
663 slice_data.cursor.SetPosition(coord) 675 slice_data.cursor.SetPosition(coord)
664 slice_data.cursor.SetEditionPosition( 676 slice_data.cursor.SetEditionPosition(
665 self.get_coordinate_cursor_edition(slice_data)) 677 self.get_coordinate_cursor_edition(slice_data))
666 self.__update_cursor_position(slice_data, coord) 678 self.__update_cursor_position(slice_data, coord)
667 -  
668 - if self._brush_cursor_op == const.BRUSH_ERASE:  
669 - evt_msg = 'Erase mask pixel'  
670 - elif self._brush_cursor_op == const.BRUSH_DRAW:  
671 - evt_msg = 'Add mask pixel'  
672 - elif self._brush_cursor_op == const.BRUSH_THRESH:  
673 - evt_msg = 'Edit mask pixel'  
674 - 679 +
675 if (self.left_pressed): 680 if (self.left_pressed):
676 - pixels = itertools.ifilter(self.test_operation_position,  
677 - slice_data.cursor.GetPixels())  
678 - ps.Publisher().sendMessage(evt_msg, pixels)  
679 - ps.Publisher().sendMessage('Update slice viewer') 681 + cursor = self.slice_data.cursor
  682 + position = self.slice_data.actor.GetInput().FindPoint(coord)
  683 + radius = cursor.radius
680 684
681 - self.interactor.Render() 685 + if position < 0:
  686 + position = self.calculate_matrix_position(coord)
  687 +
  688 +
  689 + # TODO: Call slice_ functions instead of to use pubsub message,
  690 + # maybe we can get some performances improvements here.
  691 + if self._brush_cursor_op == const.BRUSH_ERASE:
  692 + self.slice_.erase_mask_pixel(cursor.GetPixels(), position, radius,
  693 + self.orientation)
  694 + elif self._brush_cursor_op == const.BRUSH_DRAW:
  695 + self.slice_.add_mask_pixel(cursor.GetPixels(), position, radius,
  696 + self.orientation)
  697 + elif self._brush_cursor_op == const.BRUSH_THRESH:
  698 + self.slice_.edit_mask_pixel(cursor.GetPixels(), position, radius,
  699 + self.orientation)
  700 +
  701 + # TODO: To create a new function to reload images to viewer.
  702 + self.OnScrollBar()
  703 +
  704 + else:
  705 + self.interactor.Render()
  706 +
  707 + def OnBrushRelease(self, evt, obj):
  708 + self.slice_.apply_slice_buffer_to_mask(self.orientation)
682 709
683 def OnCrossMouseClick(self, evt, obj): 710 def OnCrossMouseClick(self, evt, obj):
684 self.ChangeCrossPosition() 711 self.ChangeCrossPosition()
@@ -787,10 +814,24 @@ class Viewer(wx.Panel): @@ -787,10 +814,24 @@ class Viewer(wx.Panel):
787 # vtkImageData extent 814 # vtkImageData extent
788 return coord 815 return coord
789 816
  817 + def calculate_matrix_position(self, coord):
  818 + x, y, z = coord
  819 + xi, xf, yi, yf, zi, zf = self.slice_data.actor.GetBounds()
  820 + if self.orientation == 'AXIAL':
  821 + mx = round((x - xi)/self.slice_.spacing[0], 0)
  822 + my = round((y - yi)/self.slice_.spacing[1], 0)
  823 + elif self.orientation == 'CORONAL':
  824 + mx = round((x - xi)/self.slice_.spacing[0], 0)
  825 + my = round((z - zi)/self.slice_.spacing[2], 0)
  826 + elif self.orientation == 'SAGITAL':
  827 + mx = round((y - yi)/self.slice_.spacing[1], 0)
  828 + my = round((z - zi)/self.slice_.spacing[2], 0)
  829 + return my, mx
  830 +
790 def get_coordinate_cursor(self): 831 def get_coordinate_cursor(self):
791 # Find position 832 # Find position
792 x, y, z = self.pick.GetPickPosition() 833 x, y, z = self.pick.GetPickPosition()
793 - bounds = self.actor.GetBounds() 834 + bounds = self.slice_data.actor.GetBounds()
794 if bounds[0] == bounds[1]: 835 if bounds[0] == bounds[1]:
795 x = bounds[0] 836 x = bounds[0]
796 elif bounds[2] == bounds[3]: 837 elif bounds[2] == bounds[3]:
@@ -816,10 +857,10 @@ class Viewer(wx.Panel): @@ -816,10 +857,10 @@ class Viewer(wx.Panel):
816 dy = bound_yf - bound_yi 857 dy = bound_yf - bound_yi
817 dz = bound_zf - bound_zi 858 dz = bound_zf - bound_zi
818 859
819 - dimensions = self.imagedata.GetDimensions() 860 + dimensions = self.slice_.matrix.shape
820 861
821 try: 862 try:
822 - x = (x * dimensions[0]) / dx 863 + x = (x * dimensions[2]) / dx
823 except ZeroDivisionError: 864 except ZeroDivisionError:
824 x = slice_number 865 x = slice_number
825 try: 866 try:
@@ -827,7 +868,7 @@ class Viewer(wx.Panel): @@ -827,7 +868,7 @@ class Viewer(wx.Panel):
827 except ZeroDivisionError: 868 except ZeroDivisionError:
828 y = slice_number 869 y = slice_number
829 try: 870 try:
830 - z = (z * dimensions[2]) / dz 871 + z = (z * dimensions[0]) / dz
831 except ZeroDivisionError: 872 except ZeroDivisionError:
832 z = slice_number 873 z = slice_number
833 874
@@ -959,7 +1000,7 @@ class Viewer(wx.Panel): @@ -959,7 +1000,7 @@ class Viewer(wx.Panel):
959 self.slice_number = 0 1000 self.slice_number = 0
960 self.cursor = None 1001 self.cursor = None
961 self.wl_text = None 1002 self.wl_text = None
962 - self.pick = vtk.vtkPropPicker() 1003 + self.pick = vtk.vtkWorldPointPicker()
963 1004
964 1005
965 def OnSetInteractorStyle(self, pubsub_evt): 1006 def OnSetInteractorStyle(self, pubsub_evt):
@@ -1046,7 +1087,7 @@ class Viewer(wx.Panel): @@ -1046,7 +1087,7 @@ class Viewer(wx.Panel):
1046 cursor.SetOrientation(self.orientation) 1087 cursor.SetOrientation(self.orientation)
1047 #self.__update_cursor_position([i for i in actor_bound[1::2]]) 1088 #self.__update_cursor_position([i for i in actor_bound[1::2]])
1048 cursor.SetColour(self._brush_cursor_colour) 1089 cursor.SetColour(self._brush_cursor_colour)
1049 - cursor.SetSpacing(self.imagedata.GetSpacing()) 1090 + cursor.SetSpacing(self.slice_.spacing)
1050 cursor.Show(0) 1091 cursor.Show(0)
1051 self.cursor_ = cursor 1092 self.cursor_ = cursor
1052 return cursor 1093 return cursor
@@ -1060,6 +1101,7 @@ class Viewer(wx.Panel): @@ -1060,6 +1101,7 @@ class Viewer(wx.Panel):
1060 1101
1061 self.slice_data = self.create_slice_window() 1102 self.slice_data = self.create_slice_window()
1062 self.slice_data.actor.SetInput(imagedata) 1103 self.slice_data.actor.SetInput(imagedata)
  1104 + self.slice_data.SetCursor(self.__create_cursor())
1063 self.cam = self.slice_data.renderer.GetActiveCamera() 1105 self.cam = self.slice_data.renderer.GetActiveCamera()
1064 self.set_slice_number(0) 1106 self.set_slice_number(0)
1065 self.__update_camera() 1107 self.__update_camera()