diff --git a/.gitattributes b/.gitattributes index 7333d98..979a516 100644 --- a/.gitattributes +++ b/.gitattributes @@ -116,6 +116,7 @@ invesalius/data/slice_.py -text invesalius/data/slice_data.py -text invesalius/data/styles.py -text invesalius/data/surface.py -text +invesalius/data/surface_process.py -text invesalius/data/viewer.py -text invesalius/data/viewer_slice.py -text invesalius/data/viewer_volume.py -text diff --git a/invesalius/data/surface.py b/invesalius/data/surface.py index 8174ff9..88e24df 100644 --- a/invesalius/data/surface.py +++ b/invesalius/data/surface.py @@ -31,110 +31,9 @@ import imagedata_utils as iu import polydata_utils as pu import project as prj import session as ses +import surface_process import vtk_utils as vu -#------------------------------------------------------------------ -class SurfaceProcess(multiprocessing.Process): - - def __init__(self, pipe, filename, mode, min_value, max_value, - decimate_reduction, smooth_relaxation_factor, - smooth_iterations): - - multiprocessing.Process.__init__(self) - self.pipe = pipe - self.filename = filename - self.mode = mode - self.min_value = min_value - self.max_value = max_value - self.decimate_reduction = decimate_reduction - self.smooth_relaxation_factor = smooth_relaxation_factor - self.smooth_iterations = smooth_iterations - - def run(self): - self.CreateSurface() - - def SendProgress(self, obj, msg): - prog = obj.GetProgress() - self.pipe.send([prog, msg]) - - def CreateSurface(self): - - reader = vtk.vtkXMLImageDataReader() - reader.SetFileName(self.filename) - reader.Update() - - # Flip original vtkImageData - flip = vtk.vtkImageFlip() - flip.SetInput(reader.GetOutput()) - flip.SetFilteredAxis(1) - flip.FlipAboutOriginOn() - - # Create vtkPolyData from vtkImageData - if self.mode == "CONTOUR": - contour = vtk.vtkContourFilter() - contour.SetInput(flip.GetOutput()) - contour.SetValue(0, self.min_value) # initial threshold - contour.SetValue(1, self.max_value) # final threshold - contour.GetOutput().ReleaseDataFlagOn() - contour.AddObserver("ProgressEvent", lambda obj,evt: - self.SendProgress(obj, "Generating 3D surface...")) - polydata = contour.GetOutput() - else: #mode == "GRAYSCALE": - mcubes = vtk.vtkMarchingCubes() - mcubes.SetInput(flip.GetOutput()) - mcubes.SetValue(0, 255) - mcubes.ComputeScalarsOn() - mcubes.ComputeGradientsOn() - mcubes.ComputeNormalsOn() - mcubes.ThresholdBetween(self.min_value, self.max_value) - mcubes.GetOutput().ReleaseDataFlagOn() - mcubes.AddObserver("ProgressEvent", lambda obj,evt: - self.SendProgress(obj, "Generating 3D surface...")) - polydata = mcubes.GetOutput() - - if self.decimate_reduction: - decimation = vtk.vtkQuadricDecimation() - decimation.SetInput(polydata) - decimation.SetTargetReduction(self.decimate_reduction) - decimation.GetOutput().ReleaseDataFlagOn() - decimation.AddObserver("ProgressEvent", lambda obj,evt: - self.SendProgress(obj, "Generating 3D surface...")) - polydata = decimation.GetOutput() - - if self.smooth_iterations and self.smooth_relaxation_factor: - smoother = vtk.vtkSmoothPolyDataFilter() - smoother.SetInput(polydata) - smoother.SetNumberOfIterations(self.smooth_iterations) - smoother.SetFeatureAngle(80) - smoother.SetRelaxationFactor(self.smooth_relaxation_factor) - smoother.FeatureEdgeSmoothingOn() - smoother.BoundarySmoothingOn() - smoother.GetOutput().ReleaseDataFlagOn() - smoother.AddObserver("ProgressEvent", lambda obj,evt: - self.SendProgress(obj, "Generating 3D surface...")) - polydata = smoother.GetOutput() - - # Filter used to detect and fill holes. Only fill boundary edges holes. - #TODO: Hey! This piece of code is the same from - # polydata_utils.FillSurfaceHole, we need to review this. - filled_polydata = vtk.vtkFillHolesFilter() - filled_polydata.SetInput(polydata) - filled_polydata.SetHoleSize(500) - filled_polydata.AddObserver("ProgressEvent", lambda obj,evt: - self.SendProgress(obj, "Generating 3D surface...")) - polydata = filled_polydata.GetOutput() - - - filename = tempfile.mktemp() - writer = vtk.vtkXMLPolyDataWriter() - writer.SetInput(polydata) - writer.SetFileName(filename) - writer.Write() - - self.pipe.send(None) - self.pipe.send(filename) - -#---------------------------------------------------------------------------------------------- class Surface(): """ Represent both vtkPolyData and associated properties. @@ -161,8 +60,8 @@ class Surface(): surface[key] = {'$vtp': os.path.split(img_name)[1]} else: surface[key] = d[key] - - + + plistlib.writePlist(surface, filename + '.plist') return os.path.split(filename)[1] + '.plist' @@ -225,18 +124,18 @@ class SurfaceManager(): for key in surface_dict: surface = surface_dict[key] # Map polygonal data (vtkPolyData) to graphics primitives. - + normals = vtk.vtkPolyDataNormals() normals.SetInput(surface.polydata) normals.SetFeatureAngle(80) normals.AutoOrientNormalsOn() normals.GetOutput().ReleaseDataFlagOn() - + stripper = vtk.vtkStripper() stripper.SetInput(normals.GetOutput()) stripper.PassThroughCellIdsOn() stripper.PassThroughPointIdsOn() - + mapper = vtk.vtkPolyDataMapper() mapper.SetInput(stripper.GetOutput()) mapper.ScalarVisibilityOff() @@ -257,7 +156,7 @@ class SurfaceManager(): ps.Publisher().sendMessage('Update status text in GUI', "Surface created.") - + # The following lines have to be here, otherwise all volumes disappear ps.Publisher().sendMessage('Update surface info in GUI', @@ -275,7 +174,7 @@ class SurfaceManager(): imagedata, colour, [min_value, max_value], edited_points = pubsub_evt.data quality=_('Optimal *') mode = 'CONTOUR' # 'GRAYSCALE' - + imagedata_tmp = None if (edited_points): imagedata_tmp = vtk.vtkImageData() @@ -299,49 +198,51 @@ class SurfaceManager(): pipeline_size += 1 # Update progress value in GUI - + filename_img = tempfile.mktemp() - + writer = vtk.vtkXMLImageDataWriter() writer.SetFileName(filename_img) writer.SetInput(imagedata) writer.Write() - + #pipeline_size = 4 UpdateProgress = vu.ShowProgress(pipeline_size) - + + language = ses.Session().language + pipe_in, pipe_out = multiprocessing.Pipe() - sp = SurfaceProcess(pipe_in, filename_img, mode, min_value, max_value, - decimate_reduction, smooth_relaxation_factor, - smooth_iterations) + sp = surface_process.SurfaceProcess(pipe_in, filename_img, mode, min_value, max_value, + decimate_reduction, smooth_relaxation_factor, + smooth_iterations, language) sp.start() - + while 1: msg = pipe_out.recv() if(msg is None): break UpdateProgress(msg[0],msg[1]) - + filename_polydata = pipe_out.recv() - + reader = vtk.vtkXMLPolyDataReader() reader.SetFileName(filename_polydata) reader.Update() - + polydata = reader.GetOutput() - + # Orient normals from inside to outside normals = vtk.vtkPolyDataNormals() normals.SetInput(polydata) normals.SetFeatureAngle(80) normals.AutoOrientNormalsOn() normals.GetOutput().ReleaseDataFlagOn() - + stripper = vtk.vtkStripper() stripper.SetInput(normals.GetOutput()) stripper.PassThroughCellIdsOn() stripper.PassThroughPointIdsOn() - + # Map polygonal data (vtkPolyData) to graphics primitives. mapper = vtk.vtkPolyDataMapper() mapper.SetInput(stripper.GetOutput()) @@ -360,8 +261,8 @@ class SurfaceManager(): # Set actor colour and transparency actor.GetProperty().SetColor(colour) actor.GetProperty().SetOpacity(1-surface.transparency) - - # Remove temporary files + + # Remove temporary files if sys.platform == "win32": try: os.remove(filename_img) @@ -371,7 +272,7 @@ class SurfaceManager(): else: os.remove(filename_img) os.remove(filename_polydata) - + # Append surface into Project.surface_dict proj = prj.Project() index = proj.AddSurface(surface) @@ -494,12 +395,12 @@ class SurfaceManager(): writer = vtk.vtkXMLPolyDataWriter() #elif filetype == const.FILETYPE_IV: # writer = vtk.vtkIVWriter() - elif filetype == const.FILETYPE_PLY: + elif filetype == const.FILETYPE_PLY: writer = vtk.vtkPLYWriter() writer.SetFileTypeToBinary() writer.SetDataByteOrderToLittleEndian() #writer.SetColorModeToUniformCellColor() - #writer.SetColor(255, 0, 0) + #writer.SetColor(255, 0, 0) if filetype == const.FILETYPE_STL: # Invert normals @@ -508,7 +409,7 @@ class SurfaceManager(): normals.SetFeatureAngle(80) normals.AutoOrientNormalsOn() normals.GetOutput().ReleaseDataFlagOn() - normals.UpdateInformation() + normals.UpdateInformation() polydata = normals.GetOutput() writer.SetFileName(filename) diff --git a/invesalius/data/surface_process.py b/invesalius/data/surface_process.py new file mode 100644 index 0000000..da2c9ec --- /dev/null +++ b/invesalius/data/surface_process.py @@ -0,0 +1,107 @@ +import vtk +import multiprocessing +import tempfile + +import i18n + +class SurfaceProcess(multiprocessing.Process): + + def __init__(self, pipe, filename, mode, min_value, max_value, + decimate_reduction, smooth_relaxation_factor, + smooth_iterations, language): + + multiprocessing.Process.__init__(self) + self.pipe = pipe + self.filename = filename + self.mode = mode + self.min_value = min_value + self.max_value = max_value + self.decimate_reduction = decimate_reduction + self.smooth_relaxation_factor = smooth_relaxation_factor + self.smooth_iterations = smooth_iterations + _ = i18n.InstallLanguage(language) + + + def run(self): + self.CreateSurface() + + def SendProgress(self, obj, msg): + prog = obj.GetProgress() + self.pipe.send([prog, msg]) + + def CreateSurface(self): + + reader = vtk.vtkXMLImageDataReader() + reader.SetFileName(self.filename) + reader.Update() + + # Flip original vtkImageData + flip = vtk.vtkImageFlip() + flip.SetInput(reader.GetOutput()) + flip.SetFilteredAxis(1) + flip.FlipAboutOriginOn() + + # Create vtkPolyData from vtkImageData + if self.mode == "CONTOUR": + contour = vtk.vtkContourFilter() + contour.SetInput(flip.GetOutput()) + contour.SetValue(0, self.min_value) # initial threshold + contour.SetValue(1, self.max_value) # final threshold + contour.GetOutput().ReleaseDataFlagOn() + contour.AddObserver("ProgressEvent", lambda obj,evt: + self.SendProgress(obj, _("Generating 3D surface..."))) + polydata = contour.GetOutput() + else: #mode == "GRAYSCALE": + mcubes = vtk.vtkMarchingCubes() + mcubes.SetInput(flip.GetOutput()) + mcubes.SetValue(0, 255) + mcubes.ComputeScalarsOn() + mcubes.ComputeGradientsOn() + mcubes.ComputeNormalsOn() + mcubes.ThresholdBetween(self.min_value, self.max_value) + mcubes.GetOutput().ReleaseDataFlagOn() + mcubes.AddObserver("ProgressEvent", lambda obj,evt: + self.SendProgress(obj, _("Generating 3D surface..."))) + polydata = mcubes.GetOutput() + + if self.decimate_reduction: + decimation = vtk.vtkQuadricDecimation() + decimation.SetInput(polydata) + decimation.SetTargetReduction(self.decimate_reduction) + decimation.GetOutput().ReleaseDataFlagOn() + decimation.AddObserver("ProgressEvent", lambda obj,evt: + self.SendProgress(obj, _("Generating 3D surface..."))) + polydata = decimation.GetOutput() + + if self.smooth_iterations and self.smooth_relaxation_factor: + smoother = vtk.vtkSmoothPolyDataFilter() + smoother.SetInput(polydata) + smoother.SetNumberOfIterations(self.smooth_iterations) + smoother.SetFeatureAngle(80) + smoother.SetRelaxationFactor(self.smooth_relaxation_factor) + smoother.FeatureEdgeSmoothingOn() + smoother.BoundarySmoothingOn() + smoother.GetOutput().ReleaseDataFlagOn() + smoother.AddObserver("ProgressEvent", lambda obj,evt: + self.SendProgress(obj, _("Generating 3D surface..."))) + polydata = smoother.GetOutput() + + # Filter used to detect and fill holes. Only fill boundary edges holes. + #TODO: Hey! This piece of code is the same from + # polydata_utils.FillSurfaceHole, we need to review this. + filled_polydata = vtk.vtkFillHolesFilter() + filled_polydata.SetInput(polydata) + filled_polydata.SetHoleSize(500) + filled_polydata.AddObserver("ProgressEvent", lambda obj,evt: + self.SendProgress(obj, _("Generating 3D surface..."))) + polydata = filled_polydata.GetOutput() + + + filename = tempfile.mktemp() + writer = vtk.vtkXMLPolyDataWriter() + writer.SetInput(polydata) + writer.SetFileName(filename) + writer.Write() + + self.pipe.send(None) + self.pipe.send(filename) \ No newline at end of file -- libgit2 0.21.2