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 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 21 import constants as const
27 22 import imagedata_utils as iu
  23 +import multiprocessing
  24 +import os
  25 +import plistlib
28 26 import polydata_utils as pu
29 27 import project as prj
30 28 import session as ses
  29 +import tempfile
  30 +import vtk
31 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 147 class Surface():
35 148 """
36 149 Represent both vtkPolyData and associated properties.
... ... @@ -159,7 +272,7 @@ class SurfaceManager():
159 272 imagedata, colour, [min_value, max_value], edited_points = pubsub_evt.data
160 273 quality='Optimal'
161 274 mode = 'CONTOUR' # 'GRAYSCALE'
162   -
  275 +
163 276 imagedata_tmp = None
164 277 if (edited_points):
165 278 imagedata_tmp = vtk.vtkImageData()
... ... @@ -183,105 +296,43 @@ class SurfaceManager():
183 296 pipeline_size += 1
184 297  
185 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 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 330 stripper = vtk.vtkStripper()
279   - stripper.SetInput(normals.GetOutput())
  331 + stripper.SetInput(polydata)
280 332 stripper.PassThroughCellIdsOn()
281 333 stripper.PassThroughPointIdsOn()
282   - stripper.AddObserver("ProgressEvent", lambda obj, evt:
283   - UpdateProgress(stripper, "Stripping surface..."))
284   -
  334 +
  335 +
285 336 # Map polygonal data (vtkPolyData) to graphics primitives.
286 337 mapper = vtk.vtkPolyDataMapper()
287 338 mapper.SetInput(stripper.GetOutput())
... ...
invesalius/data/vtk_utils.py
... ... @@ -56,7 +56,12 @@ def ShowProgress(number_of_filters = 1,
56 56 Show progress on GUI according to pipeline execution.
57 57 """
58 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 66 # as it is cummulative, we need to compute the diference, to be
62 67 # appended on the interface
... ...