Commit f5d0049f3c1f7c22f6839fa0a54443fc83b8618c
1 parent
2fb6b86d
Exists in
master
and in
68 other branches
FIX: Error generate 3D surface FIX #98
Showing
3 changed files
with
138 additions
and
129 deletions
Show diff stats
.gitattributes
... | ... | @@ -116,6 +116,7 @@ invesalius/data/slice_.py -text |
116 | 116 | invesalius/data/slice_data.py -text |
117 | 117 | invesalius/data/styles.py -text |
118 | 118 | invesalius/data/surface.py -text |
119 | +invesalius/data/surface_process.py -text | |
119 | 120 | invesalius/data/viewer.py -text |
120 | 121 | invesalius/data/viewer_slice.py -text |
121 | 122 | invesalius/data/viewer_volume.py -text | ... | ... |
invesalius/data/surface.py
... | ... | @@ -31,110 +31,9 @@ import imagedata_utils as iu |
31 | 31 | import polydata_utils as pu |
32 | 32 | import project as prj |
33 | 33 | import session as ses |
34 | +import surface_process | |
34 | 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 | 37 | class Surface(): |
139 | 38 | """ |
140 | 39 | Represent both vtkPolyData and associated properties. |
... | ... | @@ -161,8 +60,8 @@ class Surface(): |
161 | 60 | surface[key] = {'$vtp': os.path.split(img_name)[1]} |
162 | 61 | else: |
163 | 62 | surface[key] = d[key] |
164 | - | |
165 | - | |
63 | + | |
64 | + | |
166 | 65 | plistlib.writePlist(surface, filename + '.plist') |
167 | 66 | return os.path.split(filename)[1] + '.plist' |
168 | 67 | |
... | ... | @@ -225,18 +124,18 @@ class SurfaceManager(): |
225 | 124 | for key in surface_dict: |
226 | 125 | surface = surface_dict[key] |
227 | 126 | # Map polygonal data (vtkPolyData) to graphics primitives. |
228 | - | |
127 | + | |
229 | 128 | normals = vtk.vtkPolyDataNormals() |
230 | 129 | normals.SetInput(surface.polydata) |
231 | 130 | normals.SetFeatureAngle(80) |
232 | 131 | normals.AutoOrientNormalsOn() |
233 | 132 | normals.GetOutput().ReleaseDataFlagOn() |
234 | - | |
133 | + | |
235 | 134 | stripper = vtk.vtkStripper() |
236 | 135 | stripper.SetInput(normals.GetOutput()) |
237 | 136 | stripper.PassThroughCellIdsOn() |
238 | 137 | stripper.PassThroughPointIdsOn() |
239 | - | |
138 | + | |
240 | 139 | mapper = vtk.vtkPolyDataMapper() |
241 | 140 | mapper.SetInput(stripper.GetOutput()) |
242 | 141 | mapper.ScalarVisibilityOff() |
... | ... | @@ -257,7 +156,7 @@ class SurfaceManager(): |
257 | 156 | |
258 | 157 | ps.Publisher().sendMessage('Update status text in GUI', |
259 | 158 | "Surface created.") |
260 | - | |
159 | + | |
261 | 160 | # The following lines have to be here, otherwise all volumes disappear |
262 | 161 | |
263 | 162 | ps.Publisher().sendMessage('Update surface info in GUI', |
... | ... | @@ -275,7 +174,7 @@ class SurfaceManager(): |
275 | 174 | imagedata, colour, [min_value, max_value], edited_points = pubsub_evt.data |
276 | 175 | quality=_('Optimal *') |
277 | 176 | mode = 'CONTOUR' # 'GRAYSCALE' |
278 | - | |
177 | + | |
279 | 178 | imagedata_tmp = None |
280 | 179 | if (edited_points): |
281 | 180 | imagedata_tmp = vtk.vtkImageData() |
... | ... | @@ -299,49 +198,51 @@ class SurfaceManager(): |
299 | 198 | pipeline_size += 1 |
300 | 199 | |
301 | 200 | # Update progress value in GUI |
302 | - | |
201 | + | |
303 | 202 | filename_img = tempfile.mktemp() |
304 | - | |
203 | + | |
305 | 204 | writer = vtk.vtkXMLImageDataWriter() |
306 | 205 | writer.SetFileName(filename_img) |
307 | 206 | writer.SetInput(imagedata) |
308 | 207 | writer.Write() |
309 | - | |
208 | + | |
310 | 209 | #pipeline_size = 4 |
311 | 210 | UpdateProgress = vu.ShowProgress(pipeline_size) |
312 | - | |
211 | + | |
212 | + language = ses.Session().language | |
213 | + | |
313 | 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 | 218 | sp.start() |
318 | - | |
219 | + | |
319 | 220 | while 1: |
320 | 221 | msg = pipe_out.recv() |
321 | 222 | if(msg is None): |
322 | 223 | break |
323 | 224 | UpdateProgress(msg[0],msg[1]) |
324 | - | |
225 | + | |
325 | 226 | filename_polydata = pipe_out.recv() |
326 | - | |
227 | + | |
327 | 228 | reader = vtk.vtkXMLPolyDataReader() |
328 | 229 | reader.SetFileName(filename_polydata) |
329 | 230 | reader.Update() |
330 | - | |
231 | + | |
331 | 232 | polydata = reader.GetOutput() |
332 | - | |
233 | + | |
333 | 234 | # Orient normals from inside to outside |
334 | 235 | normals = vtk.vtkPolyDataNormals() |
335 | 236 | normals.SetInput(polydata) |
336 | 237 | normals.SetFeatureAngle(80) |
337 | 238 | normals.AutoOrientNormalsOn() |
338 | 239 | normals.GetOutput().ReleaseDataFlagOn() |
339 | - | |
240 | + | |
340 | 241 | stripper = vtk.vtkStripper() |
341 | 242 | stripper.SetInput(normals.GetOutput()) |
342 | 243 | stripper.PassThroughCellIdsOn() |
343 | 244 | stripper.PassThroughPointIdsOn() |
344 | - | |
245 | + | |
345 | 246 | # Map polygonal data (vtkPolyData) to graphics primitives. |
346 | 247 | mapper = vtk.vtkPolyDataMapper() |
347 | 248 | mapper.SetInput(stripper.GetOutput()) |
... | ... | @@ -360,8 +261,8 @@ class SurfaceManager(): |
360 | 261 | # Set actor colour and transparency |
361 | 262 | actor.GetProperty().SetColor(colour) |
362 | 263 | actor.GetProperty().SetOpacity(1-surface.transparency) |
363 | - | |
364 | - # Remove temporary files | |
264 | + | |
265 | + # Remove temporary files | |
365 | 266 | if sys.platform == "win32": |
366 | 267 | try: |
367 | 268 | os.remove(filename_img) |
... | ... | @@ -371,7 +272,7 @@ class SurfaceManager(): |
371 | 272 | else: |
372 | 273 | os.remove(filename_img) |
373 | 274 | os.remove(filename_polydata) |
374 | - | |
275 | + | |
375 | 276 | # Append surface into Project.surface_dict |
376 | 277 | proj = prj.Project() |
377 | 278 | index = proj.AddSurface(surface) |
... | ... | @@ -494,12 +395,12 @@ class SurfaceManager(): |
494 | 395 | writer = vtk.vtkXMLPolyDataWriter() |
495 | 396 | #elif filetype == const.FILETYPE_IV: |
496 | 397 | # writer = vtk.vtkIVWriter() |
497 | - elif filetype == const.FILETYPE_PLY: | |
398 | + elif filetype == const.FILETYPE_PLY: | |
498 | 399 | writer = vtk.vtkPLYWriter() |
499 | 400 | writer.SetFileTypeToBinary() |
500 | 401 | writer.SetDataByteOrderToLittleEndian() |
501 | 402 | #writer.SetColorModeToUniformCellColor() |
502 | - #writer.SetColor(255, 0, 0) | |
403 | + #writer.SetColor(255, 0, 0) | |
503 | 404 | |
504 | 405 | if filetype == const.FILETYPE_STL: |
505 | 406 | # Invert normals |
... | ... | @@ -508,7 +409,7 @@ class SurfaceManager(): |
508 | 409 | normals.SetFeatureAngle(80) |
509 | 410 | normals.AutoOrientNormalsOn() |
510 | 411 | normals.GetOutput().ReleaseDataFlagOn() |
511 | - normals.UpdateInformation() | |
412 | + normals.UpdateInformation() | |
512 | 413 | polydata = normals.GetOutput() |
513 | 414 | |
514 | 415 | writer.SetFileName(filename) | ... | ... |
... | ... | @@ -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 | 108 | \ No newline at end of file | ... | ... |