Commit 81df591f40d115021e9ae7a6306daecfbdf4e764
1 parent
18130285
Exists in
master
and in
68 other branches
ADD: Implement surface generation in other process
Showing
2 changed files
with
160 additions
and
104 deletions
Show diff stats
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 | ... | ... |