Commit 81df591f40d115021e9ae7a6306daecfbdf4e764

Authored by Paulo Henrique Junqueira Amorim
1 parent 18130285

ADD: Implement surface generation in other process

invesalius/data/surface.py
@@ -17,20 +17,133 @@ @@ -17,20 +17,133 @@
17 # detalhes. 17 # detalhes.
18 #-------------------------------------------------------------------------- 18 #--------------------------------------------------------------------------
19 19
20 -import os  
21 -import plistlib  
22 -  
23 -import vtk  
24 -import wx.lib.pubsub as ps  
25 - 20 +from imagedata_utils import BuildEditedImage
26 import constants as const 21 import constants as const
27 import imagedata_utils as iu 22 import imagedata_utils as iu
  23 +import multiprocessing
  24 +import os
  25 +import plistlib
28 import polydata_utils as pu 26 import polydata_utils as pu
29 import project as prj 27 import project as prj
30 import session as ses 28 import session as ses
  29 +import tempfile
  30 +import vtk
31 import vtk_utils as vu 31 import vtk_utils as vu
32 -from imagedata_utils import BuildEditedImage 32 +import wx.lib.pubsub as ps
33 33
  34 +
  35 +#------------------------------------------------------------------
  36 +class SurfaceProcess(multiprocessing.Process):
  37 +
  38 + def __init__(self, conn, filename, mode, min_value, max_value,
  39 + decimate_reduction, smooth_relaxation_factor,
  40 + smooth_iterations):
  41 +
  42 + multiprocessing.Process.__init__(self)
  43 + self.conn = conn
  44 + self.filename = filename
  45 + self.mode = mode
  46 + self.min_value = min_value
  47 + self.max_value = max_value
  48 + self.decimate_reduction = decimate_reduction
  49 + self.smooth_relaxation_factor = smooth_relaxation_factor
  50 + self.smooth_iterations = smooth_iterations
  51 +
  52 + def run(self):
  53 + self.CreateSurface()
  54 +
  55 + def SendProgress(self, obj, msg):
  56 + prog = obj.GetProgress()
  57 + self.conn.send([prog, msg])
  58 +
  59 + def CreateSurface(self):
  60 +
  61 + reader = vtk.vtkXMLImageDataReader()
  62 + reader.SetFileName(self.filename)
  63 + reader.Update()
  64 +
  65 + # Flip original vtkImageData
  66 + flip = vtk.vtkImageFlip()
  67 + flip.SetInput(reader.GetOutput())
  68 + flip.SetFilteredAxis(1)
  69 + flip.FlipAboutOriginOn()
  70 +
  71 + # Create vtkPolyData from vtkImageData
  72 + if self.mode == "CONTOUR":
  73 + contour = vtk.vtkContourFilter()
  74 + contour.SetInput(flip.GetOutput())
  75 + contour.SetValue(0, self.min_value) # initial threshold
  76 + contour.SetValue(1, self.max_value) # final threshold
  77 + contour.GetOutput().ReleaseDataFlagOn()
  78 + contour.AddObserver("ProgressEvent", lambda obj,evt:
  79 + self.SendProgress(obj, "Generating 3D surface..."))
  80 + polydata = contour.GetOutput()
  81 + else: #mode == "GRAYSCALE":
  82 + mcubes = vtk.vtkMarchingCubes()
  83 + mcubes.SetInput(flip.GetOutput())
  84 + mcubes.SetValue(0, 255)
  85 + mcubes.ComputeScalarsOn()
  86 + mcubes.ComputeGradientsOn()
  87 + mcubes.ComputeNormalsOn()
  88 + mcubes.ThresholdBetween(self.min_value, self.max_value)
  89 + mcubes.GetOutput().ReleaseDataFlagOn()
  90 + mcubes.AddObserver("ProgressEvent", lambda obj,evt:
  91 + self.SendProgress(obj, "Generating 3D surface..."))
  92 + polydata = mcubes.GetOutput()
  93 +
  94 + if self.decimate_reduction:
  95 + decimation = vtk.vtkQuadricDecimation()
  96 + decimation.SetInput(polydata)
  97 + decimation.SetTargetReduction(self.decimate_reduction)
  98 + decimation.GetOutput().ReleaseDataFlagOn()
  99 + decimation.AddObserver("ProgressEvent", lambda obj,evt:
  100 + self.SendProgress(obj, "Reducing number of triangles..."))
  101 + polydata = decimation.GetOutput()
  102 +
  103 + if self.smooth_iterations and self.smooth_relaxation_factor:
  104 + smoother = vtk.vtkSmoothPolyDataFilter()
  105 + smoother.SetInput(polydata)
  106 + smoother.SetNumberOfIterations(self.smooth_iterations)
  107 + smoother.SetFeatureAngle(80)
  108 + smoother.SetRelaxationFactor(self.smooth_relaxation_factor)
  109 + smoother.FeatureEdgeSmoothingOn()
  110 + smoother.BoundarySmoothingOn()
  111 + smoother.GetOutput().ReleaseDataFlagOn()
  112 + smoother.AddObserver("ProgressEvent", lambda obj,evt:
  113 + self.SendProgress(obj, "Smoothing surface..."))
  114 + polydata = smoother.GetOutput()
  115 +
  116 + # Filter used to detect and fill holes. Only fill boundary edges holes.
  117 + #TODO: Hey! This piece of code is the same from
  118 + # polydata_utils.FillSurfaceHole, we need to review this.
  119 + filled_polydata = vtk.vtkFillHolesFilter()
  120 + filled_polydata.SetInput(polydata)
  121 + filled_polydata.SetHoleSize(500)
  122 + filled_polydata.AddObserver("ProgressEvent", lambda obj,evt:
  123 + self.SendProgress(obj, "Filling surface..."))
  124 + polydata = filled_polydata.GetOutput()
  125 + # Orient normals from inside to outside
  126 +
  127 + normals = vtk.vtkPolyDataNormals()
  128 + normals.SetInput(polydata)
  129 + normals.SetFeatureAngle(80)
  130 + normals.AutoOrientNormalsOn()
  131 + normals.GetOutput().ReleaseDataFlagOn()
  132 + normals.AddObserver("ProgressEvent", lambda obj,evt:
  133 + self.SendProgress(obj, "Orienting normals..."))
  134 + normals.UpdateInformation()
  135 + polydata = normals.GetOutput()
  136 +
  137 + filename = tempfile.mktemp()
  138 + writer = vtk.vtkXMLPolyDataWriter()
  139 + writer.SetInput(polydata)
  140 + writer.SetFileName(filename)
  141 + writer.Write()
  142 +
  143 + self.conn.send(None)
  144 + self.conn.send(filename)
  145 +
  146 +#----------------------------------------------------------------------------------------------
34 class Surface(): 147 class Surface():
35 """ 148 """
36 Represent both vtkPolyData and associated properties. 149 Represent both vtkPolyData and associated properties.
@@ -159,7 +272,7 @@ class SurfaceManager(): @@ -159,7 +272,7 @@ class SurfaceManager():
159 imagedata, colour, [min_value, max_value], edited_points = pubsub_evt.data 272 imagedata, colour, [min_value, max_value], edited_points = pubsub_evt.data
160 quality='Optimal' 273 quality='Optimal'
161 mode = 'CONTOUR' # 'GRAYSCALE' 274 mode = 'CONTOUR' # 'GRAYSCALE'
162 - 275 +
163 imagedata_tmp = None 276 imagedata_tmp = None
164 if (edited_points): 277 if (edited_points):
165 imagedata_tmp = vtk.vtkImageData() 278 imagedata_tmp = vtk.vtkImageData()
@@ -183,105 +296,43 @@ class SurfaceManager(): @@ -183,105 +296,43 @@ class SurfaceManager():
183 pipeline_size += 1 296 pipeline_size += 1
184 297
185 # Update progress value in GUI 298 # Update progress value in GUI
  299 +
  300 + filename = tempfile.mktemp()
  301 +
  302 + writer = vtk.vtkXMLImageDataWriter()
  303 + writer.SetFileName(filename)
  304 + writer.SetInput(imagedata)
  305 + writer.Write()
  306 +
  307 + pipeline_size = 5
186 UpdateProgress = vu.ShowProgress(pipeline_size) 308 UpdateProgress = vu.ShowProgress(pipeline_size)
187 -  
188 - # Flip original vtkImageData  
189 - flip = vtk.vtkImageFlip()  
190 - flip.SetInput(imagedata)  
191 - flip.SetFilteredAxis(1)  
192 - flip.FlipAboutOriginOn()  
193 -  
194 - # Create vtkPolyData from vtkImageData  
195 - if mode == "CONTOUR":  
196 - contour = vtk.vtkContourFilter()  
197 - contour.SetInput(flip.GetOutput())  
198 - contour.SetValue(0, min_value) # initial threshold  
199 - contour.SetValue(1, max_value) # final threshold  
200 - contour.GetOutput().ReleaseDataFlagOn()  
201 - contour.AddObserver("ProgressEvent", lambda obj,evt:  
202 - UpdateProgress(contour, "Generating 3D surface..."))  
203 - polydata = contour.GetOutput()  
204 - else: #mode == "GRAYSCALE":  
205 - mcubes = vtk.vtkMarchingCubes()  
206 - mcubes.SetInput(flip.GetOutput())  
207 - mcubes.SetValue(0, 255)  
208 - mcubes.ComputeScalarsOn()  
209 - mcubes.ComputeGradientsOn()  
210 - mcubes.ComputeNormalsOn()  
211 - mcubes.ThresholdBetween(min_value, max_value)  
212 - mcubes.GetOutput().ReleaseDataFlagOn()  
213 - mcubes.AddObserver("ProgressEvent", lambda obj, evt:  
214 - UpdateProgress(contour, "Generating 3D surface..."))  
215 - polydata = mcubes.GetOutput()  
216 -  
217 - # Reduce number of triangles (previous classes create a large amount)  
218 - # Important: vtkQuadricDecimation presented better results than  
219 - # vtkDecimatePro  
220 - if decimate_reduction:  
221 - decimation = vtk.vtkQuadricDecimation()  
222 - decimation.SetInput(polydata)  
223 - decimation.SetTargetReduction(decimate_reduction)  
224 - decimation.GetOutput().ReleaseDataFlagOn()  
225 - decimation.AddObserver("ProgressEvent", lambda obj, evt:  
226 - UpdateProgress(decimation, "Reducing number of triangles..."))  
227 - polydata = decimation.GetOutput()  
228 -  
229 - # TODO (Paulo): Why do we need this filter?  
230 - #triangle = vtk.vtkTriangleFilter()  
231 - #triangle.SetInput(polydata)  
232 - #triangle.PassLinesOn()  
233 - #triangle.PassVertsOn()  
234 - #triangle.GetOutput().ReleaseDataFlagOn()  
235 - #triangle.AddObserver("ProgressEvent",  
236 - # lambda obj, evt: self.__update_progress(obj))  
237 -  
238 - # Smooth surface without changing structures  
239 - # Important: vtkSmoothPolyDataFilter presented better results than  
240 - # vtkImageGaussianSmooth and vtkWindowedSincPolyDataFilter  
241 - if smooth_iterations and smooth_relaxation_factor:  
242 - smoother = vtk.vtkSmoothPolyDataFilter()  
243 - smoother.SetInput(polydata)  
244 - smoother.SetNumberOfIterations(smooth_iterations)  
245 - smoother.SetFeatureAngle(80)  
246 - smoother.SetRelaxationFactor(smooth_relaxation_factor)  
247 - smoother.FeatureEdgeSmoothingOn()  
248 - smoother.BoundarySmoothingOn()  
249 - smoother.GetOutput().ReleaseDataFlagOn()  
250 - smoother.AddObserver("ProgressEvent", lambda obj, evt:  
251 - UpdateProgress(smoother, "Smoothing surface..."))  
252 - polydata = smoother.GetOutput()  
253 -  
254 - # Filter used to detect and fill holes. Only fill boundary edges holes.  
255 - #TODO: Hey! This piece of code is the same from  
256 - # polydata_utils.FillSurfaceHole, we need to review this.  
257 - filled_polydata = vtk.vtkFillHolesFilter()  
258 - filled_polydata.SetInput(polydata)  
259 - filled_polydata.SetHoleSize(500)  
260 - filled_polydata.AddObserver("ProgressEvent", lambda obj, evt:  
261 - UpdateProgress(filled_polydata,  
262 - "Filling polydata..."))  
263 - polydata = filled_polydata.GetOutput()  
264 -  
265 - # Orient normals from inside to outside  
266 - normals = vtk.vtkPolyDataNormals()  
267 - normals.SetInput(polydata)  
268 - normals.SetFeatureAngle(80)  
269 - normals.AutoOrientNormalsOn()  
270 - normals.GetOutput().ReleaseDataFlagOn()  
271 - normals.AddObserver("ProgressEvent", lambda obj, evt:  
272 - UpdateProgress(normals, "Orienting normals..."))  
273 - polydata = normals.GetOutput()  
274 -  
275 -  
276 - # TODO (Paulo): Why do we need this filter?  
277 - # without this the volume does not appear 309 +
  310 + conn_in, conn_out = multiprocessing.Pipe()
  311 + sp = SurfaceProcess(conn_in, filename, mode, min_value, max_value,
  312 + decimate_reduction, smooth_relaxation_factor,
  313 + smooth_iterations)
  314 + sp.start()
  315 +
  316 + while 1:
  317 + msg = conn_out.recv()
  318 + if(msg is None):
  319 + break
  320 + UpdateProgress(msg[0],msg[1])
  321 +
  322 + filename = conn_out.recv()
  323 +
  324 + reader = vtk.vtkXMLPolyDataReader()
  325 + reader.SetFileName(filename)
  326 + reader.Update()
  327 +
  328 + polydata = reader.GetOutput()
  329 +
278 stripper = vtk.vtkStripper() 330 stripper = vtk.vtkStripper()
279 - stripper.SetInput(normals.GetOutput()) 331 + stripper.SetInput(polydata)
280 stripper.PassThroughCellIdsOn() 332 stripper.PassThroughCellIdsOn()
281 stripper.PassThroughPointIdsOn() 333 stripper.PassThroughPointIdsOn()
282 - stripper.AddObserver("ProgressEvent", lambda obj, evt:  
283 - UpdateProgress(stripper, "Stripping surface..."))  
284 - 334 +
  335 +
285 # Map polygonal data (vtkPolyData) to graphics primitives. 336 # Map polygonal data (vtkPolyData) to graphics primitives.
286 mapper = vtk.vtkPolyDataMapper() 337 mapper = vtk.vtkPolyDataMapper()
287 mapper.SetInput(stripper.GetOutput()) 338 mapper.SetInput(stripper.GetOutput())
invesalius/data/vtk_utils.py
@@ -56,7 +56,12 @@ def ShowProgress(number_of_filters = 1, @@ -56,7 +56,12 @@ def ShowProgress(number_of_filters = 1,
56 Show progress on GUI according to pipeline execution. 56 Show progress on GUI according to pipeline execution.
57 """ 57 """
58 # object progress is cummulative and is between 0.0 - 1.0 58 # object progress is cummulative and is between 0.0 - 1.0
59 - obj_progress = obj.GetProgress() 59 + # is necessary verify in case is sending the progress
  60 + #represented by number in case multiprocess, not vtk object
  61 + if isinstance(obj, float) or isinstance(obj, int):
  62 + obj_progress = obj
  63 + else:
  64 + obj_progress = obj.GetProgress()
60 65
61 # as it is cummulative, we need to compute the diference, to be 66 # as it is cummulative, we need to compute the diference, to be
62 # appended on the interface 67 # appended on the interface