def marchingCubes( self, image, ijkToRasMatrix, threshold ): transformIJKtoRAS = vtk.vtkTransform() transformIJKtoRAS.SetMatrix( ijkToRasMatrix ) marchingCubes = vtk.vtkMarchingCubes() marchingCubes.SetInput( image ) marchingCubes.SetValue( 0, threshold ) marchingCubes.ComputeScalarsOn() marchingCubes.ComputeGradientsOn() marchingCubes.ComputeNormalsOn() marchingCubes.GetOutput().ReleaseDataFlagOn() marchingCubes.Update() if transformIJKtoRAS.GetMatrix().Determinant() < 0: reverser = vtk.vtkReverseSense() reverser.SetInput( marchingCubes.GetOutput() ) reverser.ReverseNormalsOn() reverser.GetOutput().ReleaseDataFlagOn() reverser.Update() correctedOutput = reverser.GetOutput() else: correctedOutput = marchingCubes.GetOutput() transformer = vtk.vtkTransformPolyDataFilter() transformer.SetInput( correctedOutput ) transformer.SetTransform( transformIJKtoRAS ) transformer.GetOutput().ReleaseDataFlagOn() transformer.Update() normals = vtk.vtkPolyDataNormals() normals.ComputePointNormalsOn() normals.SetInput( transformer.GetOutput() ) normals.SetFeatureAngle( 60 ) normals.SetSplitting( 1 ) normals.GetOutput().ReleaseDataFlagOn() normals.Update() stripper = vtk.vtkStripper() stripper.SetInput( normals.GetOutput() ) stripper.GetOutput().ReleaseDataFlagOff() stripper.Update() stripper.GetOutput().Update() result = vtk.vtkPolyData() result.DeepCopy( stripper.GetOutput() ) result.Update() return result
def marchingCubes(self,image,ijkToRasMatrix,threshold): transformIJKtoRAS = vtk.vtkTransform() transformIJKtoRAS.SetMatrix(ijkToRasMatrix) marchingCubes = vtk.vtkMarchingCubes() marchingCubes.SetInput(image) marchingCubes.SetValue(0,threshold) marchingCubes.ComputeScalarsOn() marchingCubes.ComputeGradientsOn() marchingCubes.ComputeNormalsOn() marchingCubes.GetOutput().ReleaseDataFlagOn() marchingCubes.Update() if transformIJKtoRAS.GetMatrix().Determinant() < 0: reverser = vtk.vtkReverseSense() reverser.SetInput(marchingCubes.GetOutput()) reverser.ReverseNormalsOn() reverser.GetOutput().ReleaseDataFlagOn() reverser.Update() correctedOutput = reverser.GetOutput() else: correctedOutput = marchingCubes.GetOutput() transformer = vtk.vtkTransformPolyDataFilter() transformer.SetInput(correctedOutput) transformer.SetTransform(transformIJKtoRAS) transformer.GetOutput().ReleaseDataFlagOn() transformer.Update() normals = vtk.vtkPolyDataNormals() normals.ComputePointNormalsOn() normals.SetInput(transformer.GetOutput()) normals.SetFeatureAngle(60) normals.SetSplitting(1) normals.GetOutput().ReleaseDataFlagOn() normals.Update() stripper = vtk.vtkStripper() stripper.SetInput(normals.GetOutput()) stripper.GetOutput().ReleaseDataFlagOff() stripper.Update() stripper.GetOutput().Update() result = vtk.vtkPolyData() result.DeepCopy(stripper.GetOutput()) result.Update() return result
def PointsToSurfacePolyData( self, inPoints, reverse ): # Create a polydata object from the points pointsPolyData = vtk.vtkPolyData() pointsPolyData.SetPoints( inPoints ) # Create the surface filter from the polydata surfaceFilter = vtk.vtkSurfaceReconstructionFilter() surfaceFilter.SetInputData( pointsPolyData ) surfaceFilter.Update() # Do the contouring filter, and reverse to ensure it works properly contourFilter = vtk.vtkContourFilter() contourFilter.SetValue( 0, 0.0 ) contourFilter.SetInputData( surfaceFilter.GetOutput() ) contourFilter.Update() # Reverse the normals if necessary reverseFilter = vtk.vtkReverseSense() reverseFilter.SetInputData( contourFilter.GetOutput() ) reverseFilter.SetReverseCells( reverse ) reverseFilter.SetReverseNormals( reverse ) reverseFilter.Update() # Reset the scaling to let the surface match the points fiducialBounds = [ 0, 0, 0, 0, 0, 0 ] pointsPolyData.GetBounds( fiducialBounds ) tissueBounds = [ 0, 0, 0, 0, 0, 0 ] reverseFilter.GetOutput().GetBounds( tissueBounds ) scaleX = ( fiducialBounds[1] - fiducialBounds[0] ) / ( tissueBounds[1] - tissueBounds[0] ) scaleY = ( fiducialBounds[3] - fiducialBounds[2] ) / ( tissueBounds[3] - tissueBounds[2] ) scaleZ = ( fiducialBounds[5] - fiducialBounds[4] ) / ( tissueBounds[5] - tissueBounds[4] ) transform = vtk.vtkTransform() transform.Translate( fiducialBounds[0], fiducialBounds[2], fiducialBounds[4] ) transform.Scale( scaleX, scaleY, scaleZ ) transform.Translate( - tissueBounds[0], - tissueBounds[2], - tissueBounds[4] ) transformFilter = vtk.vtkTransformPolyDataFilter() transformFilter.SetInputData( reverseFilter.GetOutput() ) transformFilter.SetTransform( transform ) transformFilter.Update() return transformFilter.GetOutput()
def updateModelFromMarkup(self, inputMarkup, outputModel): """ Update model to enclose all points in the input markup list """ # Delaunay triangulation is robust and creates nice smooth surfaces from a small number of points, # however it can only generate convex surfaces robustly. useDelaunay = True # Create polydata point set from markup points points = vtk.vtkPoints() cellArray = vtk.vtkCellArray() numberOfPoints = inputMarkup.GetNumberOfFiducials() # Surface generation algorithms behave unpredictably when there are not enough points # return if there are very few points if useDelaunay: if numberOfPoints < 3: return else: if numberOfPoints < 10: return points.SetNumberOfPoints(numberOfPoints) new_coord = [0.0, 0.0, 0.0] for i in range(numberOfPoints): inputMarkup.GetNthFiducialPosition(i, new_coord) points.SetPoint(i, new_coord) cellArray.InsertNextCell(numberOfPoints) for i in range(numberOfPoints): cellArray.InsertCellPoint(i) pointPolyData = vtk.vtkPolyData() pointPolyData.SetLines(cellArray) pointPolyData.SetPoints(points) # Create surface from point set if useDelaunay: delaunay = vtk.vtkDelaunay3D() delaunay.SetInputData(pointPolyData) surfaceFilter = vtk.vtkDataSetSurfaceFilter() surfaceFilter.SetInputConnection(delaunay.GetOutputPort()) smoother = vtk.vtkButterflySubdivisionFilter() smoother.SetInputConnection(surfaceFilter.GetOutputPort()) smoother.SetNumberOfSubdivisions(3) smoother.Update() outputModel.SetPolyDataConnection(smoother.GetOutputPort()) else: surf = vtk.vtkSurfaceReconstructionFilter() surf.SetInputData(pointPolyData) surf.SetNeighborhoodSize(20) surf.SetSampleSpacing( 80 ) # lower value follows the small details more closely but more dense pointset is needed as input cf = vtk.vtkContourFilter() cf.SetInputConnection(surf.GetOutputPort()) cf.SetValue(0, 0.0) # Sometimes the contouring algorithm can create a volume whose gradient # vector and ordering of polygon (using the right hand rule) are # inconsistent. vtkReverseSense cures this problem. reverse = vtk.vtkReverseSense() reverse.SetInputConnection(cf.GetOutputPort()) reverse.ReverseCellsOff() reverse.ReverseNormalsOff() outputModel.SetPolyDataConnection(reverse.GetOutputPort()) # Create default model display node if does not exist yet if not outputModel.GetDisplayNode(): modelDisplayNode = slicer.mrmlScene.CreateNodeByClass( "vtkMRMLModelDisplayNode") modelDisplayNode.SetColor(0, 0, 1) # Blue modelDisplayNode.BackfaceCullingOff() modelDisplayNode.SliceIntersectionVisibilityOn() modelDisplayNode.SetOpacity(0.3) # Between 0-1, 1 being opaque slicer.mrmlScene.AddNode(modelDisplayNode) outputModel.SetAndObserveDisplayNodeID(modelDisplayNode.GetID()) outputModel.GetDisplayNode().SliceIntersectionVisibilityOn() outputModel.Modified()
def run( self, markupNode, depth, fitPlane, flip ): """ Run the actual algorithm """ # Get all of the fiducial nodes and convert to vtkPoints points = vtk.vtkPoints() for i in range( 0, markupNode.GetNumberOfFiducials() ): currentCoordinates = [ 0, 0, 0 ] markupNode.GetNthFiducialPosition( i, currentCoordinates ) points.InsertNextPoint( currentCoordinates ) # Check that there is non-zero range in all coordinate directions pointsBounds = [ 0, 0, 0, 0, 0, 0 ] points.GetBounds( pointsBounds ) if ( pointsBounds[0] == pointsBounds [1] or pointsBounds[2] == pointsBounds [3] or pointsBounds[4] == pointsBounds [5] ): print "Tissue Model Creator: Points have no extent in one or more coordinate directions." return False # Create a polydata object from the points # The reversiness doesn't matter - we will fix it later if it os wrong if ( fitPlane ): surfacePolyData = self.PointsToPlanePolyData( points, True ) else: surfacePolyData = self.PointsToSurfacePolyData( points, True ) surfaceCleaner = vtk.vtkCleanPolyData() surfaceCleaner.SetInputData( surfacePolyData ) surfaceCleaner.Update() surfacePolyData = surfaceCleaner.GetOutput() mean = self.CalculateMean( points ) surfaceBase = [ 0, 0, 0 ] surfaceDir1 = [ 0, 0, 0 ] surfaceDir2 = [ 0, 0, 0 ] surfaceNormal = [ 0, 0, 0 ] self.CalculatePlane( surfacePolyData.GetPoints(), surfaceBase, surfaceDir1, surfaceDir2, surfaceNormal ) if ( flip == True ): surfaceNormal[ 0 ] = - surfaceNormal[ 0 ] surfaceNormal[ 1 ] = - surfaceNormal[ 1 ] surfaceNormal[ 2 ] = - surfaceNormal[ 2 ] extremePointIndex = self.FindExtremePoint( surfacePolyData.GetPoints(), surfaceBase, surfaceNormal ) reverse = self.ReverseNormals( extremePointIndex, surfacePolyData, surfaceNormal ) # Reverse the normals if necessary reverseFilter = vtk.vtkReverseSense() reverseFilter.SetInputData( surfacePolyData ) reverseFilter.SetReverseCells( reverse ) reverseFilter.SetReverseNormals( reverse ) reverseFilter.Update() surfacePolyData = reverseFilter.GetOutput() untransDeepPolyData = vtk.vtkPolyData() untransDeepPolyData.DeepCopy( surfacePolyData ) # Make the normals opposite the surface's normals reverseFilter = vtk.vtkReverseSense() reverseFilter.SetInputData( untransDeepPolyData ) reverseFilter.SetReverseCells( True ) reverseFilter.SetReverseNormals( True ) reverseFilter.Update() untransDeepPolyData = reverseFilter.GetOutput() deepTransform = vtk.vtkTransform() deepTransform.Translate( depth * surfaceNormal[0], depth * surfaceNormal[1], depth * surfaceNormal[2] ) deepTransformFilter = vtk.vtkTransformPolyDataFilter() deepTransformFilter.SetInputData( untransDeepPolyData ) deepTransformFilter.SetTransform( deepTransform ) deepTransformFilter.Update() deepPolyData = deepTransformFilter.GetOutput() surfaceHullPoints = self.GetBoundaryPoints( surfacePolyData ) deepHullPoints = self.GetBoundaryPoints( deepPolyData ) # Apparently, the joining needs an offset for the plane, but not for the surface reconstruction if ( fitPlane ): jointHullPolyData = self.JoinBoundaryPoints( surfaceHullPoints, deepHullPoints, 1 ) else: jointHullPolyData = self.JoinBoundaryPoints( surfaceHullPoints, deepHullPoints, 0 ) # Append all of the polydata together tissuePolyDataAppend = vtk.vtkAppendPolyData() tissuePolyDataAppend.AddInputData( surfacePolyData ) tissuePolyDataAppend.AddInputData( deepPolyData ) tissuePolyDataAppend.AddInputData( jointHullPolyData ) tissuePolyDataAppend.Update() # Clean up so the surface is closed tissueCleaner = vtk.vtkCleanPolyData() tissueCleaner.SetInputData( tissuePolyDataAppend.GetOutput() ) tissueCleaner.Update() tissueModelPolyData = tissueCleaner.GetOutput() # Add the data to a model tissueModel = slicer.mrmlScene.CreateNodeByClass( "vtkMRMLModelNode" ) slicer.mrmlScene.AddNode( tissueModel ) tissueModel.SetName( "TissueModel" ) tissueModel.SetAndObservePolyData( tissueModelPolyData ) tissueModel.SetScene( slicer.mrmlScene ) # Finally display the model tissueModelDisplay = slicer.mrmlScene.CreateNodeByClass( "vtkMRMLModelDisplayNode" ) slicer.mrmlScene.AddNode( tissueModelDisplay ) tissueModel.SetAndObserveDisplayNodeID( tissueModelDisplay.GetID() ) tissueModelDisplay.SetScene( slicer.mrmlScene ) tissueModelDisplay.SetInputPolyDataConnection( tissueModel.GetPolyDataConnection() ) # Check to make sure the model is a closed surface edgesFilter = vtk.vtkFeatureEdges() edgesFilter.FeatureEdgesOff() edgesFilter.BoundaryEdgesOn() edgesFilter.NonManifoldEdgesOn() edgesFilter.SetInputData( tissueModel.GetPolyData() ) edgesFilter.Update() if ( edgesFilter.GetOutput().GetNumberOfCells() != 0 ): print "Tissue Model Creator: Surface is not closed." return False return True