Commit f5d0049f3c1f7c22f6839fa0a54443fc83b8618c

Authored by Paulo Henrique Junqueira Amorim
1 parent 2fb6b86d

FIX: Error generate 3D surface FIX #98

.gitattributes
@@ -116,6 +116,7 @@ invesalius/data/slice_.py -text @@ -116,6 +116,7 @@ invesalius/data/slice_.py -text
116 invesalius/data/slice_data.py -text 116 invesalius/data/slice_data.py -text
117 invesalius/data/styles.py -text 117 invesalius/data/styles.py -text
118 invesalius/data/surface.py -text 118 invesalius/data/surface.py -text
  119 +invesalius/data/surface_process.py -text
119 invesalius/data/viewer.py -text 120 invesalius/data/viewer.py -text
120 invesalius/data/viewer_slice.py -text 121 invesalius/data/viewer_slice.py -text
121 invesalius/data/viewer_volume.py -text 122 invesalius/data/viewer_volume.py -text
invesalius/data/surface.py
@@ -31,110 +31,9 @@ import imagedata_utils as iu @@ -31,110 +31,9 @@ import imagedata_utils as iu
31 import polydata_utils as pu 31 import polydata_utils as pu
32 import project as prj 32 import project as prj
33 import session as ses 33 import session as ses
  34 +import surface_process
34 import vtk_utils as vu 35 import vtk_utils as vu
35 36
36 -#------------------------------------------------------------------  
37 -class SurfaceProcess(multiprocessing.Process):  
38 -  
39 - def __init__(self, pipe, filename, mode, min_value, max_value,  
40 - decimate_reduction, smooth_relaxation_factor,  
41 - smooth_iterations):  
42 -  
43 - multiprocessing.Process.__init__(self)  
44 - self.pipe = pipe  
45 - self.filename = filename  
46 - self.mode = mode  
47 - self.min_value = min_value  
48 - self.max_value = max_value  
49 - self.decimate_reduction = decimate_reduction  
50 - self.smooth_relaxation_factor = smooth_relaxation_factor  
51 - self.smooth_iterations = smooth_iterations  
52 -  
53 - def run(self):  
54 - self.CreateSurface()  
55 -  
56 - def SendProgress(self, obj, msg):  
57 - prog = obj.GetProgress()  
58 - self.pipe.send([prog, msg])  
59 -  
60 - def CreateSurface(self):  
61 -  
62 - reader = vtk.vtkXMLImageDataReader()  
63 - reader.SetFileName(self.filename)  
64 - reader.Update()  
65 -  
66 - # Flip original vtkImageData  
67 - flip = vtk.vtkImageFlip()  
68 - flip.SetInput(reader.GetOutput())  
69 - flip.SetFilteredAxis(1)  
70 - flip.FlipAboutOriginOn()  
71 -  
72 - # Create vtkPolyData from vtkImageData  
73 - if self.mode == "CONTOUR":  
74 - contour = vtk.vtkContourFilter()  
75 - contour.SetInput(flip.GetOutput())  
76 - contour.SetValue(0, self.min_value) # initial threshold  
77 - contour.SetValue(1, self.max_value) # final threshold  
78 - contour.GetOutput().ReleaseDataFlagOn()  
79 - contour.AddObserver("ProgressEvent", lambda obj,evt:  
80 - self.SendProgress(obj, "Generating 3D surface..."))  
81 - polydata = contour.GetOutput()  
82 - else: #mode == "GRAYSCALE":  
83 - mcubes = vtk.vtkMarchingCubes()  
84 - mcubes.SetInput(flip.GetOutput())  
85 - mcubes.SetValue(0, 255)  
86 - mcubes.ComputeScalarsOn()  
87 - mcubes.ComputeGradientsOn()  
88 - mcubes.ComputeNormalsOn()  
89 - mcubes.ThresholdBetween(self.min_value, self.max_value)  
90 - mcubes.GetOutput().ReleaseDataFlagOn()  
91 - mcubes.AddObserver("ProgressEvent", lambda obj,evt:  
92 - self.SendProgress(obj, "Generating 3D surface..."))  
93 - polydata = mcubes.GetOutput()  
94 -  
95 - if self.decimate_reduction:  
96 - decimation = vtk.vtkQuadricDecimation()  
97 - decimation.SetInput(polydata)  
98 - decimation.SetTargetReduction(self.decimate_reduction)  
99 - decimation.GetOutput().ReleaseDataFlagOn()  
100 - decimation.AddObserver("ProgressEvent", lambda obj,evt:  
101 - self.SendProgress(obj, "Generating 3D surface..."))  
102 - polydata = decimation.GetOutput()  
103 -  
104 - if self.smooth_iterations and self.smooth_relaxation_factor:  
105 - smoother = vtk.vtkSmoothPolyDataFilter()  
106 - smoother.SetInput(polydata)  
107 - smoother.SetNumberOfIterations(self.smooth_iterations)  
108 - smoother.SetFeatureAngle(80)  
109 - smoother.SetRelaxationFactor(self.smooth_relaxation_factor)  
110 - smoother.FeatureEdgeSmoothingOn()  
111 - smoother.BoundarySmoothingOn()  
112 - smoother.GetOutput().ReleaseDataFlagOn()  
113 - smoother.AddObserver("ProgressEvent", lambda obj,evt:  
114 - self.SendProgress(obj, "Generating 3D surface..."))  
115 - polydata = smoother.GetOutput()  
116 -  
117 - # Filter used to detect and fill holes. Only fill boundary edges holes.  
118 - #TODO: Hey! This piece of code is the same from  
119 - # polydata_utils.FillSurfaceHole, we need to review this.  
120 - filled_polydata = vtk.vtkFillHolesFilter()  
121 - filled_polydata.SetInput(polydata)  
122 - filled_polydata.SetHoleSize(500)  
123 - filled_polydata.AddObserver("ProgressEvent", lambda obj,evt:  
124 - self.SendProgress(obj, "Generating 3D surface..."))  
125 - polydata = filled_polydata.GetOutput()  
126 -  
127 -  
128 - filename = tempfile.mktemp()  
129 - writer = vtk.vtkXMLPolyDataWriter()  
130 - writer.SetInput(polydata)  
131 - writer.SetFileName(filename)  
132 - writer.Write()  
133 -  
134 - self.pipe.send(None)  
135 - self.pipe.send(filename)  
136 -  
137 -#----------------------------------------------------------------------------------------------  
138 class Surface(): 37 class Surface():
139 """ 38 """
140 Represent both vtkPolyData and associated properties. 39 Represent both vtkPolyData and associated properties.
@@ -161,8 +60,8 @@ class Surface(): @@ -161,8 +60,8 @@ class Surface():
161 surface[key] = {'$vtp': os.path.split(img_name)[1]} 60 surface[key] = {'$vtp': os.path.split(img_name)[1]}
162 else: 61 else:
163 surface[key] = d[key] 62 surface[key] = d[key]
164 -  
165 - 63 +
  64 +
166 plistlib.writePlist(surface, filename + '.plist') 65 plistlib.writePlist(surface, filename + '.plist')
167 return os.path.split(filename)[1] + '.plist' 66 return os.path.split(filename)[1] + '.plist'
168 67
@@ -225,18 +124,18 @@ class SurfaceManager(): @@ -225,18 +124,18 @@ class SurfaceManager():
225 for key in surface_dict: 124 for key in surface_dict:
226 surface = surface_dict[key] 125 surface = surface_dict[key]
227 # Map polygonal data (vtkPolyData) to graphics primitives. 126 # Map polygonal data (vtkPolyData) to graphics primitives.
228 - 127 +
229 normals = vtk.vtkPolyDataNormals() 128 normals = vtk.vtkPolyDataNormals()
230 normals.SetInput(surface.polydata) 129 normals.SetInput(surface.polydata)
231 normals.SetFeatureAngle(80) 130 normals.SetFeatureAngle(80)
232 normals.AutoOrientNormalsOn() 131 normals.AutoOrientNormalsOn()
233 normals.GetOutput().ReleaseDataFlagOn() 132 normals.GetOutput().ReleaseDataFlagOn()
234 - 133 +
235 stripper = vtk.vtkStripper() 134 stripper = vtk.vtkStripper()
236 stripper.SetInput(normals.GetOutput()) 135 stripper.SetInput(normals.GetOutput())
237 stripper.PassThroughCellIdsOn() 136 stripper.PassThroughCellIdsOn()
238 stripper.PassThroughPointIdsOn() 137 stripper.PassThroughPointIdsOn()
239 - 138 +
240 mapper = vtk.vtkPolyDataMapper() 139 mapper = vtk.vtkPolyDataMapper()
241 mapper.SetInput(stripper.GetOutput()) 140 mapper.SetInput(stripper.GetOutput())
242 mapper.ScalarVisibilityOff() 141 mapper.ScalarVisibilityOff()
@@ -257,7 +156,7 @@ class SurfaceManager(): @@ -257,7 +156,7 @@ class SurfaceManager():
257 156
258 ps.Publisher().sendMessage('Update status text in GUI', 157 ps.Publisher().sendMessage('Update status text in GUI',
259 "Surface created.") 158 "Surface created.")
260 - 159 +
261 # The following lines have to be here, otherwise all volumes disappear 160 # The following lines have to be here, otherwise all volumes disappear
262 161
263 ps.Publisher().sendMessage('Update surface info in GUI', 162 ps.Publisher().sendMessage('Update surface info in GUI',
@@ -275,7 +174,7 @@ class SurfaceManager(): @@ -275,7 +174,7 @@ class SurfaceManager():
275 imagedata, colour, [min_value, max_value], edited_points = pubsub_evt.data 174 imagedata, colour, [min_value, max_value], edited_points = pubsub_evt.data
276 quality=_('Optimal *') 175 quality=_('Optimal *')
277 mode = 'CONTOUR' # 'GRAYSCALE' 176 mode = 'CONTOUR' # 'GRAYSCALE'
278 - 177 +
279 imagedata_tmp = None 178 imagedata_tmp = None
280 if (edited_points): 179 if (edited_points):
281 imagedata_tmp = vtk.vtkImageData() 180 imagedata_tmp = vtk.vtkImageData()
@@ -299,49 +198,51 @@ class SurfaceManager(): @@ -299,49 +198,51 @@ class SurfaceManager():
299 pipeline_size += 1 198 pipeline_size += 1
300 199
301 # Update progress value in GUI 200 # Update progress value in GUI
302 - 201 +
303 filename_img = tempfile.mktemp() 202 filename_img = tempfile.mktemp()
304 - 203 +
305 writer = vtk.vtkXMLImageDataWriter() 204 writer = vtk.vtkXMLImageDataWriter()
306 writer.SetFileName(filename_img) 205 writer.SetFileName(filename_img)
307 writer.SetInput(imagedata) 206 writer.SetInput(imagedata)
308 writer.Write() 207 writer.Write()
309 - 208 +
310 #pipeline_size = 4 209 #pipeline_size = 4
311 UpdateProgress = vu.ShowProgress(pipeline_size) 210 UpdateProgress = vu.ShowProgress(pipeline_size)
312 - 211 +
  212 + language = ses.Session().language
  213 +
313 pipe_in, pipe_out = multiprocessing.Pipe() 214 pipe_in, pipe_out = multiprocessing.Pipe()
314 - sp = SurfaceProcess(pipe_in, filename_img, mode, min_value, max_value,  
315 - decimate_reduction, smooth_relaxation_factor,  
316 - smooth_iterations) 215 + sp = surface_process.SurfaceProcess(pipe_in, filename_img, mode, min_value, max_value,
  216 + decimate_reduction, smooth_relaxation_factor,
  217 + smooth_iterations, language)
317 sp.start() 218 sp.start()
318 - 219 +
319 while 1: 220 while 1:
320 msg = pipe_out.recv() 221 msg = pipe_out.recv()
321 if(msg is None): 222 if(msg is None):
322 break 223 break
323 UpdateProgress(msg[0],msg[1]) 224 UpdateProgress(msg[0],msg[1])
324 - 225 +
325 filename_polydata = pipe_out.recv() 226 filename_polydata = pipe_out.recv()
326 - 227 +
327 reader = vtk.vtkXMLPolyDataReader() 228 reader = vtk.vtkXMLPolyDataReader()
328 reader.SetFileName(filename_polydata) 229 reader.SetFileName(filename_polydata)
329 reader.Update() 230 reader.Update()
330 - 231 +
331 polydata = reader.GetOutput() 232 polydata = reader.GetOutput()
332 - 233 +
333 # Orient normals from inside to outside 234 # Orient normals from inside to outside
334 normals = vtk.vtkPolyDataNormals() 235 normals = vtk.vtkPolyDataNormals()
335 normals.SetInput(polydata) 236 normals.SetInput(polydata)
336 normals.SetFeatureAngle(80) 237 normals.SetFeatureAngle(80)
337 normals.AutoOrientNormalsOn() 238 normals.AutoOrientNormalsOn()
338 normals.GetOutput().ReleaseDataFlagOn() 239 normals.GetOutput().ReleaseDataFlagOn()
339 - 240 +
340 stripper = vtk.vtkStripper() 241 stripper = vtk.vtkStripper()
341 stripper.SetInput(normals.GetOutput()) 242 stripper.SetInput(normals.GetOutput())
342 stripper.PassThroughCellIdsOn() 243 stripper.PassThroughCellIdsOn()
343 stripper.PassThroughPointIdsOn() 244 stripper.PassThroughPointIdsOn()
344 - 245 +
345 # Map polygonal data (vtkPolyData) to graphics primitives. 246 # Map polygonal data (vtkPolyData) to graphics primitives.
346 mapper = vtk.vtkPolyDataMapper() 247 mapper = vtk.vtkPolyDataMapper()
347 mapper.SetInput(stripper.GetOutput()) 248 mapper.SetInput(stripper.GetOutput())
@@ -360,8 +261,8 @@ class SurfaceManager(): @@ -360,8 +261,8 @@ class SurfaceManager():
360 # Set actor colour and transparency 261 # Set actor colour and transparency
361 actor.GetProperty().SetColor(colour) 262 actor.GetProperty().SetColor(colour)
362 actor.GetProperty().SetOpacity(1-surface.transparency) 263 actor.GetProperty().SetOpacity(1-surface.transparency)
363 -  
364 - # Remove temporary files 264 +
  265 + # Remove temporary files
365 if sys.platform == "win32": 266 if sys.platform == "win32":
366 try: 267 try:
367 os.remove(filename_img) 268 os.remove(filename_img)
@@ -371,7 +272,7 @@ class SurfaceManager(): @@ -371,7 +272,7 @@ class SurfaceManager():
371 else: 272 else:
372 os.remove(filename_img) 273 os.remove(filename_img)
373 os.remove(filename_polydata) 274 os.remove(filename_polydata)
374 - 275 +
375 # Append surface into Project.surface_dict 276 # Append surface into Project.surface_dict
376 proj = prj.Project() 277 proj = prj.Project()
377 index = proj.AddSurface(surface) 278 index = proj.AddSurface(surface)
@@ -494,12 +395,12 @@ class SurfaceManager(): @@ -494,12 +395,12 @@ class SurfaceManager():
494 writer = vtk.vtkXMLPolyDataWriter() 395 writer = vtk.vtkXMLPolyDataWriter()
495 #elif filetype == const.FILETYPE_IV: 396 #elif filetype == const.FILETYPE_IV:
496 # writer = vtk.vtkIVWriter() 397 # writer = vtk.vtkIVWriter()
497 - elif filetype == const.FILETYPE_PLY: 398 + elif filetype == const.FILETYPE_PLY:
498 writer = vtk.vtkPLYWriter() 399 writer = vtk.vtkPLYWriter()
499 writer.SetFileTypeToBinary() 400 writer.SetFileTypeToBinary()
500 writer.SetDataByteOrderToLittleEndian() 401 writer.SetDataByteOrderToLittleEndian()
501 #writer.SetColorModeToUniformCellColor() 402 #writer.SetColorModeToUniformCellColor()
502 - #writer.SetColor(255, 0, 0) 403 + #writer.SetColor(255, 0, 0)
503 404
504 if filetype == const.FILETYPE_STL: 405 if filetype == const.FILETYPE_STL:
505 # Invert normals 406 # Invert normals
@@ -508,7 +409,7 @@ class SurfaceManager(): @@ -508,7 +409,7 @@ class SurfaceManager():
508 normals.SetFeatureAngle(80) 409 normals.SetFeatureAngle(80)
509 normals.AutoOrientNormalsOn() 410 normals.AutoOrientNormalsOn()
510 normals.GetOutput().ReleaseDataFlagOn() 411 normals.GetOutput().ReleaseDataFlagOn()
511 - normals.UpdateInformation() 412 + normals.UpdateInformation()
512 polydata = normals.GetOutput() 413 polydata = normals.GetOutput()
513 414
514 writer.SetFileName(filename) 415 writer.SetFileName(filename)
invesalius/data/surface_process.py 0 → 100644
@@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
  1 +import vtk
  2 +import multiprocessing
  3 +import tempfile
  4 +
  5 +import i18n
  6 +
  7 +class SurfaceProcess(multiprocessing.Process):
  8 +
  9 + def __init__(self, pipe, filename, mode, min_value, max_value,
  10 + decimate_reduction, smooth_relaxation_factor,
  11 + smooth_iterations, language):
  12 +
  13 + multiprocessing.Process.__init__(self)
  14 + self.pipe = pipe
  15 + self.filename = filename
  16 + self.mode = mode
  17 + self.min_value = min_value
  18 + self.max_value = max_value
  19 + self.decimate_reduction = decimate_reduction
  20 + self.smooth_relaxation_factor = smooth_relaxation_factor
  21 + self.smooth_iterations = smooth_iterations
  22 + _ = i18n.InstallLanguage(language)
  23 +
  24 +
  25 + def run(self):
  26 + self.CreateSurface()
  27 +
  28 + def SendProgress(self, obj, msg):
  29 + prog = obj.GetProgress()
  30 + self.pipe.send([prog, msg])
  31 +
  32 + def CreateSurface(self):
  33 +
  34 + reader = vtk.vtkXMLImageDataReader()
  35 + reader.SetFileName(self.filename)
  36 + reader.Update()
  37 +
  38 + # Flip original vtkImageData
  39 + flip = vtk.vtkImageFlip()
  40 + flip.SetInput(reader.GetOutput())
  41 + flip.SetFilteredAxis(1)
  42 + flip.FlipAboutOriginOn()
  43 +
  44 + # Create vtkPolyData from vtkImageData
  45 + if self.mode == "CONTOUR":
  46 + contour = vtk.vtkContourFilter()
  47 + contour.SetInput(flip.GetOutput())
  48 + contour.SetValue(0, self.min_value) # initial threshold
  49 + contour.SetValue(1, self.max_value) # final threshold
  50 + contour.GetOutput().ReleaseDataFlagOn()
  51 + contour.AddObserver("ProgressEvent", lambda obj,evt:
  52 + self.SendProgress(obj, _("Generating 3D surface...")))
  53 + polydata = contour.GetOutput()
  54 + else: #mode == "GRAYSCALE":
  55 + mcubes = vtk.vtkMarchingCubes()
  56 + mcubes.SetInput(flip.GetOutput())
  57 + mcubes.SetValue(0, 255)
  58 + mcubes.ComputeScalarsOn()
  59 + mcubes.ComputeGradientsOn()
  60 + mcubes.ComputeNormalsOn()
  61 + mcubes.ThresholdBetween(self.min_value, self.max_value)
  62 + mcubes.GetOutput().ReleaseDataFlagOn()
  63 + mcubes.AddObserver("ProgressEvent", lambda obj,evt:
  64 + self.SendProgress(obj, _("Generating 3D surface...")))
  65 + polydata = mcubes.GetOutput()
  66 +
  67 + if self.decimate_reduction:
  68 + decimation = vtk.vtkQuadricDecimation()
  69 + decimation.SetInput(polydata)
  70 + decimation.SetTargetReduction(self.decimate_reduction)
  71 + decimation.GetOutput().ReleaseDataFlagOn()
  72 + decimation.AddObserver("ProgressEvent", lambda obj,evt:
  73 + self.SendProgress(obj, _("Generating 3D surface...")))
  74 + polydata = decimation.GetOutput()
  75 +
  76 + if self.smooth_iterations and self.smooth_relaxation_factor:
  77 + smoother = vtk.vtkSmoothPolyDataFilter()
  78 + smoother.SetInput(polydata)
  79 + smoother.SetNumberOfIterations(self.smooth_iterations)
  80 + smoother.SetFeatureAngle(80)
  81 + smoother.SetRelaxationFactor(self.smooth_relaxation_factor)
  82 + smoother.FeatureEdgeSmoothingOn()
  83 + smoother.BoundarySmoothingOn()
  84 + smoother.GetOutput().ReleaseDataFlagOn()
  85 + smoother.AddObserver("ProgressEvent", lambda obj,evt:
  86 + self.SendProgress(obj, _("Generating 3D surface...")))
  87 + polydata = smoother.GetOutput()
  88 +
  89 + # Filter used to detect and fill holes. Only fill boundary edges holes.
  90 + #TODO: Hey! This piece of code is the same from
  91 + # polydata_utils.FillSurfaceHole, we need to review this.
  92 + filled_polydata = vtk.vtkFillHolesFilter()
  93 + filled_polydata.SetInput(polydata)
  94 + filled_polydata.SetHoleSize(500)
  95 + filled_polydata.AddObserver("ProgressEvent", lambda obj,evt:
  96 + self.SendProgress(obj, _("Generating 3D surface...")))
  97 + polydata = filled_polydata.GetOutput()
  98 +
  99 +
  100 + filename = tempfile.mktemp()
  101 + writer = vtk.vtkXMLPolyDataWriter()
  102 + writer.SetInput(polydata)
  103 + writer.SetFileName(filename)
  104 + writer.Write()
  105 +
  106 + self.pipe.send(None)
  107 + self.pipe.send(filename)
0 \ No newline at end of file 108 \ No newline at end of file