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,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 |