Commit 620a3f2d7666f1d2bc09d7605ebc334ccecc29aa

Authored by Paulo Henrique Junqueira Amorim
1 parent 6a51200e

ADD: Create 3D Surface from Edited Mask

invesalius/data/mask.py
... ... @@ -13,4 +13,4 @@ class Mask():
13 13 self.name = const.MASK_NAME_PATTERN %(Mask.general_index+1)
14 14 self.edition_threshold_range = const.THRESHOLD_RANGE
15 15 self.is_shown = 1
16   -
17 16 \ No newline at end of file
  17 + self.edited_points = {}
... ...
invesalius/data/slice_.py
... ... @@ -20,14 +20,14 @@ class Slice(object):
20 20 self.current_mask = None
21 21 self.blend_filter = None
22 22 self.__bind_events()
23   -
  23 +
24 24 def __bind_events(self):
25 25 # Slice properties
26 26 ps.Publisher().subscribe(self.UpdateCursorPosition,
27 27 'Update cursor position in slice')
28 28 ps.Publisher().subscribe(self.UpdateCursorPositionSingleAxis,
29 29 'Update cursor single position in slice')
30   -
  30 +
31 31 # General slice control
32 32 ps.Publisher().subscribe(self.CreateSurfaceFromIndex,
33 33 'Create surface from index')
... ... @@ -35,7 +35,7 @@ class Slice(object):
35 35 ps.Publisher().subscribe(self.__add_mask, 'Create new mask')
36 36 ps.Publisher().subscribe(self.__select_current_mask,
37 37 'Change mask selected')
38   - # Mask properties
  38 + # Mask properties
39 39 ps.Publisher().subscribe(self.__set_current_mask_edition_threshold,
40 40 'Set edition threshold values')
41 41 ps.Publisher().subscribe(self.__set_current_mask_threshold,
... ... @@ -44,34 +44,34 @@ class Slice(object):
44 44 'Change mask colour')
45 45 ps.Publisher().subscribe(self.__set_mask_name, 'Change mask name')
46 46 ps.Publisher().subscribe(self.__show_mask, 'Show mask')
47   -
  47 +
48 48 # Operations related to slice editor
49 49 ps.Publisher().subscribe(self.__erase_mask_pixel, 'Erase mask pixel')
50 50 ps.Publisher().subscribe(self.__edit_mask_pixel, 'Edit mask pixel')
51   - ps.Publisher().subscribe(self.__add_mask_pixel, 'Add mask pixel')
52   -
  51 + ps.Publisher().subscribe(self.__add_mask_pixel, 'Add mask pixel')
  52 +
53 53 #---------------------------------------------------------------------------
54 54 # BEGIN PUBSUB_EVT METHODS
55 55 #---------------------------------------------------------------------------
56 56 def __get_mask_data_for_surface_creation(self, pubsub_evt):
57 57 mask_index = pubsub_evt.data
58 58 CreateSurfaceFromIndex
59   -
  59 +
60 60 def __add_mask(self, pubsub_evt):
61 61 mask_name = pubsub_evt.data
62 62 self.CreateMask(name=mask_name)
63 63 self.SetMaskColour(self.current_mask.index, self.current_mask.colour)
64   -
  64 +
65 65 def __select_current_mask(self, pubsub_evt):
66 66 mask_index = pubsub_evt.data
67 67 self.SelectCurrentMask(mask_index)
68   - #---------------------------------------------------------------------------
  68 + #---------------------------------------------------------------------------
69 69 def __set_current_mask_edition_threshold(self, evt_pubsub):
70 70 if self.current_mask:
71 71 threshold_range = evt_pubsub.data
72 72 index = self.current_mask.index
73 73 self.SetMaskEditionThreshold(index, threshold_range)
74   -
  74 +
75 75 def __set_current_mask_threshold(self, evt_pubsub):
76 76 threshold_range = evt_pubsub.data
77 77 index = self.current_mask.index
... ... @@ -99,24 +99,24 @@ class Slice(object):
99 99 def __erase_mask_pixel(self, pubsub_evt):
100 100 position = pubsub_evt.data
101 101 self.ErasePixel(position)
102   -
  102 +
103 103 def __edit_mask_pixel(self, pubsub_evt):
104 104 position = pubsub_evt.data
105 105 self.EditPixelBasedOnThreshold(position)
106 106  
107 107 def __add_mask_pixel(self, pubsub_evt):
108 108 position = pubsub_evt.data
109   - self.DrawPixel(position)
  109 + self.DrawPixel(position)
110 110 #---------------------------------------------------------------------------
111 111 # END PUBSUB_EVT METHODS
112 112 #---------------------------------------------------------------------------
113   -
114   -
  113 +
  114 +
115 115 def SetMaskColour(self, index, colour, update=True):
116 116 "Set a mask colour given its index and colour (RGB 0-1 values)"
117 117 proj = Project()
118 118 proj.mask_dict[index].colour = colour
119   -
  119 +
120 120 (r,g,b) = colour
121 121 scalar_range = int(self.imagedata.GetScalarRange()[1])
122 122 self.lut_mask.SetTableValue(1, r, g, b, 1.0)
... ... @@ -128,7 +128,7 @@ class Slice(object):
128 128 ps.Publisher().sendMessage('Set GUI items colour', colour_wx)
129 129 if update:
130 130 ps.Publisher().sendMessage('Update slice viewer')
131   -
  131 +
132 132 def SetMaskName(self, index, name):
133 133 "Rename a mask given its index and the new name"
134 134 proj = Project()
... ... @@ -145,25 +145,25 @@ class Slice(object):
145 145 threshold values.
146 146 """
147 147 thresh_min, thresh_max = threshold_range
148   -
  148 +
149 149 if self.current_mask.index == index:
150 150 # Update pipeline (this must be here, so pipeline is not broken)
151 151 self.img_thresh_mask.SetInput(self.imagedata)
152 152 self.img_thresh_mask.ThresholdBetween(float(thresh_min),
153 153 float(thresh_max))
154 154 self.img_thresh_mask.Update()
155   -
  155 +
156 156 # Create imagedata copy so the pipeline is not broken
157 157 imagedata = self.img_thresh_mask.GetOutput()
158 158 self.current_mask.imagedata.DeepCopy(imagedata)
159 159 self.current_mask.threshold_range = threshold_range
160   -
  160 +
161 161 # Update pipeline (this must be here, so pipeline is not broken)
162 162 self.img_colours_mask.SetInput(self.current_mask.imagedata)
163   -
  163 +
164 164 # Update viewer
165 165 ps.Publisher().sendMessage('Update slice viewer')
166   -
  166 +
167 167 # Update data notebook (GUI)
168 168 ps.Publisher().sendMessage('Set mask threshold in notebook',
169 169 (self.current_mask.index,
... ... @@ -171,7 +171,7 @@ class Slice(object):
171 171 else:
172 172 proj = Project()
173 173 proj.mask_dict[index].threshold_range = threshold_range
174   -
  174 +
175 175 def ShowMask(self, index, value):
176 176 "Show a mask given its index and 'show' value (0: hide, other: show)"
177 177 proj = Project()
... ... @@ -180,7 +180,7 @@ class Slice(object):
180 180 if value:
181 181 self.blend_filter.SetOpacity(1, self.current_mask.opacity)
182 182 else:
183   - self.blend_filter.SetOpacity(1, 0)
  183 + self.blend_filter.SetOpacity(1, 0)
184 184 self.blend_filter.Update()
185 185 ps.Publisher().sendMessage('Update slice viewer')
186 186 #---------------------------------------------------------------------------
... ... @@ -191,22 +191,23 @@ class Slice(object):
191 191 imagedata = self.current_mask.imagedata
192 192 imagedata.SetScalarComponentFromDouble(x, y, z, 0, colour)
193 193 imagedata.Update()
  194 + self.current_mask.edited_points[(x, y, z)] = colour
194 195  
195 196 def DrawPixel(self, position, colour=None):
196 197 "Draw pixel, based on x, y and z position coordinates."
197 198 x, y, z = position
198   - if not colour:
199   - colour = self.imagedata.GetScalarRange()[1]
  199 + #if not colour:
  200 + colour = self.imagedata.GetScalarRange()[1]
200 201 imagedata = self.current_mask.imagedata
201 202 imagedata.SetScalarComponentFromDouble(x, y, z, 0, colour)
202 203 imagedata.Update()
  204 + self.current_mask.edited_points[(x, y, z)] = colour
203 205  
204 206 def EditPixelBasedOnThreshold(self, position):
205 207 "Erase or draw pixel based on edition threshold range."
206 208 x, y, z = position
207 209 colour = self.imagedata.GetScalarComponentAsDouble(x, y, z, 0)
208 210 thresh_min, thresh_max = self.current_mask.edition_threshold_range
209   -
210 211 if (colour >= thresh_min) and (colour <= thresh_max):
211 212 self.DrawPixel(position, colour)
212 213 else:
... ... @@ -234,8 +235,8 @@ class Slice(object):
234 235 if self.current_mask.is_shown:
235 236 self.blend_filter.SetOpacity(1, self.current_mask.opacity)
236 237 else:
237   -
238   - self.blend_filter.SetOpacity(1, 0)
  238 +
  239 + self.blend_filter.SetOpacity(1, 0)
239 240 self.blend_filter.Update()
240 241  
241 242 ps.Publisher().sendMessage('Set mask threshold in notebook',
... ... @@ -254,31 +255,32 @@ class Slice(object):
254 255  
255 256 def CreateSurfaceFromIndex(self, pubsub_evt):
256 257 mask_index = pubsub_evt.data
257   -
  258 +
258 259  
259 260 proj = Project()
260 261 mask = proj.mask_dict[mask_index]
261   -
  262 +
262 263 # This is very important. Do not use masks' imagedata. It would mess up
263 264 # surface quality event when using contour
264 265 imagedata = self.imagedata
265   -
  266 +
266 267 colour = mask.colour
267 268 threshold = mask.threshold_range
  269 + edited_points = mask.edited_points
268 270  
269 271 ps.Publisher().sendMessage('Create surface',
270   - (imagedata,colour,threshold))
  272 + (imagedata,colour,threshold, edited_points))
  273 +
271 274  
272 275  
273 276  
274 277  
275 278  
276   -
277 279  
278 280  
279 281 def GetOutput(self):
280 282 return self.cast_filter.GetOutput()
281   -
  283 +
282 284  
283 285  
284 286 def SetInput(self, imagedata):
... ... @@ -314,19 +316,19 @@ class Slice(object):
314 316  
315 317 cross = vtk.vtkImageCursor3D()
316 318 cross.GetOutput().ReleaseDataFlagOn()
317   - cross.SetInput(blend_filter.GetOutput())
  319 + cross.SetInput(blend_filter.GetOutput())
318 320 cross.SetCursorPosition(CURSOR_X, CURSOR_Y, CURSOR_Z)
319 321 cross.SetCursorValue(CURSOR_VALUE)
320   - cross.SetCursorRadius(CURSOR_RADIUS)
  322 + cross.SetCursorRadius(CURSOR_RADIUS)
321 323 cross.Modified()
322 324 self.cross = cross
323   -
324   - cast = vtk.vtkImageCast()
  325 +
  326 + cast = vtk.vtkImageCast()
325 327 cast.SetInput(cross.GetOutput())
326 328 cast.GetOutput().SetUpdateExtentToWholeExtent()
327   - cast.SetOutputScalarTypeToUnsignedChar()
  329 + cast.SetOutputScalarTypeToUnsignedChar()
328 330 cast.Update()
329   -
  331 +
330 332 self.cast_filter = cast
331 333  
332 334  
... ... @@ -337,7 +339,7 @@ class Slice(object):
337 339 self.cross.Modified()
338 340 self.cast_filter.Update()
339 341 ps.Publisher().sendMessage('Update slice viewer')
340   -
  342 +
341 343 def UpdateCursorPositionSingleAxis(self, pubsub_evt):
342 344 axis_pos = pubsub_evt.data
343 345 x, y, z = self.cross.GetCursorPosition()
... ... @@ -348,8 +350,8 @@ class Slice(object):
348 350 self.cross.Modified()
349 351 self.cast_filter.Update()
350 352 ps.Publisher().sendMessage('Update slice viewer')
351   -
352   -
  353 +
  354 +
353 355 def __create_background(self, imagedata):
354 356  
355 357 thresh_min, thresh_max = imagedata.GetScalarRange()
... ... @@ -414,11 +416,11 @@ class Slice(object):
414 416 future_mask.colour))
415 417  
416 418 self.current_mask = future_mask
417   -
  419 +
418 420 ps.Publisher().sendMessage('Change mask selected', future_mask.index)
419 421 #ps.Publisher().sendMessage('Show mask', (future_mask.index, 1))
420 422 ps.Publisher().sendMessage('Update slice viewer')
421   -
  423 +
422 424  
423 425  
424 426 def __create_mask(self, imagedata):
... ...
invesalius/data/surface.py
... ... @@ -6,6 +6,7 @@ import imagedata_utils as iu
6 6 from project import Project
7 7 import vtk_utils as vu
8 8 import polydata_utils as pu
  9 +from imagedata_utils import BuildEditedImage
9 10  
10 11 class Surface():
11 12 """
... ... @@ -53,10 +54,13 @@ class SurfaceManager():
53 54 """
54 55 Create surface actor, save into project and send it to viewer.
55 56 """
56   - imagedata, colour, [min_value, max_value] = pubsub_evt.data
  57 + imagedata, colour, [min_value, max_value], edited_points = pubsub_evt.data
57 58 quality='Optimal'
58 59 mode = 'CONTOUR' # 'GRAYSCALE'
59 60  
  61 + if (edited_points):
  62 + imagedata = BuildEditedImage(imagedata, edited_points)
  63 +
60 64 if quality in const.SURFACE_QUALITY.keys():
61 65 imagedata_resolution = const.SURFACE_QUALITY[quality][0]
62 66 smooth_iterations = const.SURFACE_QUALITY[quality][1]
... ...