Commit 5caf20b9d7ac4ed1b953a8c21f040d4ec8dee527
1 parent
2b5c9c39
Exists in
master
and in
6 other branches
FIX: Normals in STL and OBJ
Showing
2 changed files
with
37 additions
and
29 deletions
Show diff stats
invesalius/data/surface.py
@@ -35,12 +35,12 @@ import wx.lib.pubsub as ps | @@ -35,12 +35,12 @@ import wx.lib.pubsub as ps | ||
35 | #------------------------------------------------------------------ | 35 | #------------------------------------------------------------------ |
36 | class SurfaceProcess(multiprocessing.Process): | 36 | class SurfaceProcess(multiprocessing.Process): |
37 | 37 | ||
38 | - def __init__(self, conn, filename, mode, min_value, max_value, | 38 | + def __init__(self, pipe, filename, mode, min_value, max_value, |
39 | decimate_reduction, smooth_relaxation_factor, | 39 | decimate_reduction, smooth_relaxation_factor, |
40 | smooth_iterations): | 40 | smooth_iterations): |
41 | 41 | ||
42 | multiprocessing.Process.__init__(self) | 42 | multiprocessing.Process.__init__(self) |
43 | - self.conn = conn | 43 | + self.pipe = pipe |
44 | self.filename = filename | 44 | self.filename = filename |
45 | self.mode = mode | 45 | self.mode = mode |
46 | self.min_value = min_value | 46 | self.min_value = min_value |
@@ -54,7 +54,7 @@ class SurfaceProcess(multiprocessing.Process): | @@ -54,7 +54,7 @@ class SurfaceProcess(multiprocessing.Process): | ||
54 | 54 | ||
55 | def SendProgress(self, obj, msg): | 55 | def SendProgress(self, obj, msg): |
56 | prog = obj.GetProgress() | 56 | prog = obj.GetProgress() |
57 | - self.conn.send([prog, msg]) | 57 | + self.pipe.send([prog, msg]) |
58 | 58 | ||
59 | def CreateSurface(self): | 59 | def CreateSurface(self): |
60 | 60 | ||
@@ -97,7 +97,7 @@ class SurfaceProcess(multiprocessing.Process): | @@ -97,7 +97,7 @@ class SurfaceProcess(multiprocessing.Process): | ||
97 | decimation.SetTargetReduction(self.decimate_reduction) | 97 | decimation.SetTargetReduction(self.decimate_reduction) |
98 | decimation.GetOutput().ReleaseDataFlagOn() | 98 | decimation.GetOutput().ReleaseDataFlagOn() |
99 | decimation.AddObserver("ProgressEvent", lambda obj,evt: | 99 | decimation.AddObserver("ProgressEvent", lambda obj,evt: |
100 | - self.SendProgress(obj, "Reducing number of triangles...")) | 100 | + self.SendProgress(obj, "Generating 3D surface...")) |
101 | polydata = decimation.GetOutput() | 101 | polydata = decimation.GetOutput() |
102 | 102 | ||
103 | if self.smooth_iterations and self.smooth_relaxation_factor: | 103 | if self.smooth_iterations and self.smooth_relaxation_factor: |
@@ -110,7 +110,7 @@ class SurfaceProcess(multiprocessing.Process): | @@ -110,7 +110,7 @@ class SurfaceProcess(multiprocessing.Process): | ||
110 | smoother.BoundarySmoothingOn() | 110 | smoother.BoundarySmoothingOn() |
111 | smoother.GetOutput().ReleaseDataFlagOn() | 111 | smoother.GetOutput().ReleaseDataFlagOn() |
112 | smoother.AddObserver("ProgressEvent", lambda obj,evt: | 112 | smoother.AddObserver("ProgressEvent", lambda obj,evt: |
113 | - self.SendProgress(obj, "Smoothing surface...")) | 113 | + self.SendProgress(obj, "Generating 3D surface...")) |
114 | polydata = smoother.GetOutput() | 114 | polydata = smoother.GetOutput() |
115 | 115 | ||
116 | # Filter used to detect and fill holes. Only fill boundary edges holes. | 116 | # Filter used to detect and fill holes. Only fill boundary edges holes. |
@@ -120,19 +120,9 @@ class SurfaceProcess(multiprocessing.Process): | @@ -120,19 +120,9 @@ class SurfaceProcess(multiprocessing.Process): | ||
120 | filled_polydata.SetInput(polydata) | 120 | filled_polydata.SetInput(polydata) |
121 | filled_polydata.SetHoleSize(500) | 121 | filled_polydata.SetHoleSize(500) |
122 | filled_polydata.AddObserver("ProgressEvent", lambda obj,evt: | 122 | filled_polydata.AddObserver("ProgressEvent", lambda obj,evt: |
123 | - self.SendProgress(obj, "Filling surface...")) | 123 | + self.SendProgress(obj, "Generating 3D surface...")) |
124 | polydata = filled_polydata.GetOutput() | 124 | polydata = filled_polydata.GetOutput() |
125 | - # Orient normals from inside to outside | ||
126 | 125 | ||
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 | 126 | ||
137 | filename = tempfile.mktemp() | 127 | filename = tempfile.mktemp() |
138 | writer = vtk.vtkXMLPolyDataWriter() | 128 | writer = vtk.vtkXMLPolyDataWriter() |
@@ -140,8 +130,8 @@ class SurfaceProcess(multiprocessing.Process): | @@ -140,8 +130,8 @@ class SurfaceProcess(multiprocessing.Process): | ||
140 | writer.SetFileName(filename) | 130 | writer.SetFileName(filename) |
141 | writer.Write() | 131 | writer.Write() |
142 | 132 | ||
143 | - self.conn.send(None) | ||
144 | - self.conn.send(filename) | 133 | + self.pipe.send(None) |
134 | + self.pipe.send(filename) | ||
145 | 135 | ||
146 | #---------------------------------------------------------------------------------------------- | 136 | #---------------------------------------------------------------------------------------------- |
147 | class Surface(): | 137 | class Surface(): |
@@ -307,19 +297,19 @@ class SurfaceManager(): | @@ -307,19 +297,19 @@ class SurfaceManager(): | ||
307 | pipeline_size = 5 | 297 | pipeline_size = 5 |
308 | UpdateProgress = vu.ShowProgress(pipeline_size) | 298 | UpdateProgress = vu.ShowProgress(pipeline_size) |
309 | 299 | ||
310 | - conn_in, conn_out = multiprocessing.Pipe() | ||
311 | - sp = SurfaceProcess(conn_in, filename_img, mode, min_value, max_value, | 300 | + pipe_in, pipe_out = multiprocessing.Pipe() |
301 | + sp = SurfaceProcess(pipe_in, filename_img, mode, min_value, max_value, | ||
312 | decimate_reduction, smooth_relaxation_factor, | 302 | decimate_reduction, smooth_relaxation_factor, |
313 | smooth_iterations) | 303 | smooth_iterations) |
314 | sp.start() | 304 | sp.start() |
315 | 305 | ||
316 | while 1: | 306 | while 1: |
317 | - msg = conn_out.recv() | 307 | + msg = pipe_out.recv() |
318 | if(msg is None): | 308 | if(msg is None): |
319 | break | 309 | break |
320 | UpdateProgress(msg[0],msg[1]) | 310 | UpdateProgress(msg[0],msg[1]) |
321 | 311 | ||
322 | - filename_polydata = conn_out.recv() | 312 | + filename_polydata = pipe_out.recv() |
323 | 313 | ||
324 | reader = vtk.vtkXMLPolyDataReader() | 314 | reader = vtk.vtkXMLPolyDataReader() |
325 | reader.SetFileName(filename_polydata) | 315 | reader.SetFileName(filename_polydata) |
@@ -447,16 +437,19 @@ class SurfaceManager(): | @@ -447,16 +437,19 @@ class SurfaceManager(): | ||
447 | def OnExportSurface(self, pubsub_evt): | 437 | def OnExportSurface(self, pubsub_evt): |
448 | filename, filetype = pubsub_evt.data | 438 | filename, filetype = pubsub_evt.data |
449 | if (filetype == const.FILETYPE_STL) or\ | 439 | if (filetype == const.FILETYPE_STL) or\ |
450 | - (filetype == const.FILETYPE_VTP): | 440 | + (filetype == const.FILETYPE_VTP) or\ |
441 | + (filetype == const.FILETYPE_PLY) : | ||
451 | 442 | ||
452 | # First we identify all surfaces that are selected | 443 | # First we identify all surfaces that are selected |
453 | # (if any) | 444 | # (if any) |
454 | proj = prj.Project() | 445 | proj = prj.Project() |
455 | polydata_list = [] | 446 | polydata_list = [] |
447 | + | ||
456 | for index in proj.surface_dict: | 448 | for index in proj.surface_dict: |
457 | surface = proj.surface_dict[index] | 449 | surface = proj.surface_dict[index] |
458 | if surface.is_shown: | 450 | if surface.is_shown: |
459 | polydata_list.append(surface.polydata) | 451 | polydata_list.append(surface.polydata) |
452 | + | ||
460 | if len(polydata_list) == 0: | 453 | if len(polydata_list) == 0: |
461 | print "oops - no polydata" | 454 | print "oops - no polydata" |
462 | return | 455 | return |
@@ -473,8 +466,8 @@ class SurfaceManager(): | @@ -473,8 +466,8 @@ class SurfaceManager(): | ||
473 | writer.SetFileTypeToBinary() | 466 | writer.SetFileTypeToBinary() |
474 | elif filetype == const.FILETYPE_VTP: | 467 | elif filetype == const.FILETYPE_VTP: |
475 | writer = vtk.vtkXMLPolyDataWriter() | 468 | writer = vtk.vtkXMLPolyDataWriter() |
476 | - elif filetype == const.FILETYPE_IV: | ||
477 | - writer = vtk.vtkIVWriter() | 469 | + #elif filetype == const.FILETYPE_IV: |
470 | + # writer = vtk.vtkIVWriter() | ||
478 | elif filetype == const.FILETYPE_PLY: | 471 | elif filetype == const.FILETYPE_PLY: |
479 | writer = vtk.vtkPLYWriter() | 472 | writer = vtk.vtkPLYWriter() |
480 | writer.SetFileTypeToBinary() | 473 | writer.SetFileTypeToBinary() |
@@ -482,6 +475,16 @@ class SurfaceManager(): | @@ -482,6 +475,16 @@ class SurfaceManager(): | ||
482 | #writer.SetColorModeToUniformCellColor() | 475 | #writer.SetColorModeToUniformCellColor() |
483 | #writer.SetColor(255, 0, 0) | 476 | #writer.SetColor(255, 0, 0) |
484 | 477 | ||
478 | + if filetype == const.FILETYPE_STL: | ||
479 | + # Invert normals | ||
480 | + normals = vtk.vtkPolyDataNormals() | ||
481 | + normals.SetInput(polydata) | ||
482 | + normals.SetFeatureAngle(80) | ||
483 | + normals.AutoOrientNormalsOn() | ||
484 | + normals.GetOutput().ReleaseDataFlagOn() | ||
485 | + normals.UpdateInformation() | ||
486 | + polydata = normals.GetOutput() | ||
487 | + | ||
485 | writer.SetFileName(filename) | 488 | writer.SetFileName(filename) |
486 | writer.SetInput(polydata) | 489 | writer.SetInput(polydata) |
487 | writer.Write() | 490 | writer.Write() |
invesalius/data/viewer_volume.py
@@ -279,25 +279,30 @@ class Viewer(wx.Panel): | @@ -279,25 +279,30 @@ class Viewer(wx.Panel): | ||
279 | 279 | ||
280 | def OnExportSurface(self, pubsub_evt): | 280 | def OnExportSurface(self, pubsub_evt): |
281 | filename, filetype = pubsub_evt.data | 281 | filename, filetype = pubsub_evt.data |
282 | + fileprefix = filename.split(".")[-2] | ||
282 | renwin = self.interactor.GetRenderWindow() | 283 | renwin = self.interactor.GetRenderWindow() |
283 | 284 | ||
284 | if filetype == const.FILETYPE_RIB: | 285 | if filetype == const.FILETYPE_RIB: |
285 | writer = vtk.vtkRIBExporter() | 286 | writer = vtk.vtkRIBExporter() |
286 | - writer.SetFileName(filename) | 287 | + writer.SetFilePrefix(fileprefix) |
288 | + writer.SetTexturePrefix(fileprefix) | ||
287 | writer.SetInput(renwin) | 289 | writer.SetInput(renwin) |
290 | + writer.Write() | ||
288 | elif filetype == const.FILETYPE_VRML: | 291 | elif filetype == const.FILETYPE_VRML: |
289 | writer = vtk.vtkVRMLExporter() | 292 | writer = vtk.vtkVRMLExporter() |
290 | writer.SetFileName(filename) | 293 | writer.SetFileName(filename) |
291 | writer.SetInput(renwin) | 294 | writer.SetInput(renwin) |
295 | + writer.Write() | ||
292 | elif filetype == const.FILETYPE_OBJ: | 296 | elif filetype == const.FILETYPE_OBJ: |
293 | writer = vtk.vtkOBJExporter() | 297 | writer = vtk.vtkOBJExporter() |
294 | - writer.SetFilePrefix(filename.split(".")[-2]) | 298 | + writer.SetFilePrefix(fileprefix) |
295 | writer.SetInput(renwin) | 299 | writer.SetInput(renwin) |
296 | - else: # const.FILETYPE_IV: | 300 | + writer.Write() |
301 | + elif filetype == const.FILETYPE_IV: | ||
297 | writer = vtk.vtkIVExporter() | 302 | writer = vtk.vtkIVExporter() |
298 | writer.SetFileName(filename) | 303 | writer.SetFileName(filename) |
299 | writer.SetInput(renwin) | 304 | writer.SetInput(renwin) |
300 | - writer.Write() | 305 | + writer.Write() |
301 | 306 | ||
302 | def OnEnableBrightContrast(self, pubsub_evt): | 307 | def OnEnableBrightContrast(self, pubsub_evt): |
303 | style = self.style | 308 | style = self.style |