예제 #1
2
    def Execute(self):
        if self.Surface == None:
            self.PrintError('Error: No input surface.')

        # feature edges are used to find any holes in the surface. 
        fedges = vtk.vtkFeatureEdges()
        fedges.BoundaryEdgesOn()
        fedges.FeatureEdgesOff()
        fedges.ManifoldEdgesOff()
        fedges.SetInputData(self.Surface)
        fedges.Update()
        ofedges = fedges.GetOutput()

        # if numEdges is not 0, then there are holes which need to be capped
        numEdges = ofedges.GetNumberOfPoints()
        if numEdges != 0:
            tempcapper = vmtksurfacecapper.vmtkSurfaceCapper()
            tempcapper.Surface = self.Surface
            tempcapper.Interactive = 0
            tempcapper.Execute()
            
            networkSurface = tempcapper.Surface
        else:
            networkSurface = self.Surface

        # randomly select one cell to delete so that there is an opening for
        # vmtkNetworkExtraction to use.
        numCells = self.Surface.GetNumberOfCells()
        cellToDelete = random.randrange(0, numCells-1)
        networkSurface.BuildLinks()
        networkSurface.DeleteCell(cellToDelete)
        networkSurface.RemoveDeletedCells()

        # extract the network of approximated centerlines
        net = vmtknetworkextraction.vmtkNetworkExtraction()
        net.Surface = networkSurface
        net.AdvancementRatio = 1.001
        net.Execute()
        network = net.Network

        convert = vmtkcenterlinestonumpy.vmtkCenterlinesToNumpy()
        convert.Centerlines = network
        convert.LogOn = False
        convert.Execute()
        ad = convert.ArrayDict
        cellDataTopology = ad['CellData']['Topology']

        # the network topology identifies an the input segment with the "0" id.
        # since we artificially created this segment, we don't want to use the
        # ends of the segment as source/target points of the centerline calculation
        nodeIndexToIgnore = np.where(cellDataTopology[:,0] == 0)[0][0]
        keepCellConnectivityList = []
        pointIdxToKeep = np.array([])
        # we remove the cell, points, and point data which are associated with the
        # segment we want to ignore
        for loopIdx, cellConnectivityList in enumerate(ad['CellData']['CellPointIds']):
            if loopIdx == nodeIndexToIgnore:
                removeCellStartIdx = cellConnectivityList[0]
                removeCellEndIdx = cellConnectivityList[-1]
                removeCellLength = cellConnectivityList.size
                if (removeCellEndIdx + 1) - removeCellStartIdx != removeCellLength:
                    raise(ValueError)
                continue
            else:
                rescaledCellConnectivity = np.subtract(cellConnectivityList, removeCellLength, where=cellConnectivityList >= removeCellLength)
                keepCellConnectivityList.append(rescaledCellConnectivity)
                pointIdxToKeep = np.concatenate((pointIdxToKeep, cellConnectivityList)).astype(np.int)
        newPoints = ad['Points'][pointIdxToKeep]
        newRadius = ad['PointData']['Radius'][pointIdxToKeep]

        # precompute the delaunay tessellation for the whole surface. 
        tessalation = vmtkdelaunayvoronoi.vmtkDelaunayVoronoi()
        tessalation.Surface = networkSurface
        tessalation.Execute()
        self.DelaunayTessellation = tessalation.DelaunayTessellation
        self.VoronoiDiagram = tessalation.VoronoiDiagram
        self.PoleIds = tessalation.PoleIds

        # vtk objects cannot be serialized in python. Instead of converting the inputs to numpy arrays and having
        # to reconstruct the vtk object each time the loop executes (a slow process), we can just pass in the
        # memory address of the data objects as a string, and use the vtk python bindings to create a python name 
        # referring to the data residing at that memory address. This works because joblib executes each loop
        # iteration in a fork of the original process, providing access to the original memory space. 
        # However, the process does not work for return arguments, since the original process will not have access to
        # the memory space of the fork. To return results we use the vmtkCenterlinesToNumpy converter.
        networkSurfaceMemoryAddress = networkSurface.__this__
        delaunayMemoryAddress = tessalation.DelaunayTessellation.__this__
        voronoiMemoryAddress = tessalation.VoronoiDiagram.__this__
        poleIdsMemoryAddress = tessalation.PoleIds.__this__

        # When using Joblib Under Windows, it is important to protect the main loop of code to avoid recursive spawning
        # of subprocesses. Since we cannot guarantee no code will run outside of “if __name__ == ‘__main__’” blocks 
        # (only imports and definitions), we make Joblib execute each loop iteration serially on windows. 
        # On unix-like os's (linux, Mac) we execute each loop iteration independently on as many cores as the system has. 
        if (sys.platform == 'win32') or (sys.platform == 'win64') or (sys.platform == 'cygwin'):
            self.PrintLog('Centerlines extraction on windows computer will execute serially.')
            self.PrintLog('To speed up execution, please run vmtk on unix-like operating system')
            numParallelJobs = 1
        else:
            numParallelJobs = -1

        self.PrintLog('Computing Centerlines ...')
        # note about the verbose function: while Joblib can print a progress bar output (set verbose = 20),
        # it does not implement a callback function as of version 0.11, so we cannot report progress to the user
        # if we are redirecting standard out with the self.PrintLog method. 
        outlist = Parallel(n_jobs=numParallelJobs, backend='multiprocessing', verbose=0)(
            delayed(_compute_centerlines_network)(networkSurfaceMemoryAddress,
                                          delaunayMemoryAddress,
                                          voronoiMemoryAddress,
                                          poleIdsMemoryAddress,
                                          cell,
                                          newPoints) for cell in keepCellConnectivityList)
        out = []
        for item in outlist:
            npConvert = vmtknumpytocenterlines.vmtkNumpyToCenterlines()
            npConvert.ArrayDict = item
            npConvert.LogOn = 0
            npConvert.Execute()
            out.append(npConvert.Centerlines)

        # Append each segment's polydata into a single polydata object
        centerlineAppender = vtk.vtkAppendPolyData()
        for data in out:
            centerlineAppender.AddInputData(data)
        centerlineAppender.Update()

        # clean and strip the output centerlines so that redundant points are merged and tracts are combined
        centerlineCleaner = vtk.vtkCleanPolyData()
        centerlineCleaner.SetInputData(centerlineAppender.GetOutput())
        centerlineCleaner.Update()

        centerlineStripper = vtk.vtkStripper()
        centerlineStripper.SetInputData(centerlineCleaner.GetOutput())
        centerlineStripper.JoinContiguousSegmentsOn()
        centerlineStripper.Update()

        self.Centerlines = centerlineStripper.GetOutput()
예제 #2
0
def test_methods_with_default_params(aorta_surface_open_ends, method, paramid, compare_surfaces, write_surface):
    name = __name__ + '_test_methods_with_default_params_' + paramid + '.vtp'
    capper = surfacecapper.vmtkSurfaceCapper()
    capper.Surface = aorta_surface_open_ends
    capper.Method = method
    capper.Interactive = 0
    capper.Execute()
    write_surface(capper.Surface)

    assert compare_surfaces(capper.Surface, name) == True
예제 #3
0
    def Execute(self):
        if self.Surface == None:
            self.PrintError('Error: No Input Surface.')

        # Step 0: Check if the surface has any unfilled holes in it. if it does, cap them.
        fedges = vtk.vtkFeatureEdges()
        fedges.BoundaryEdgesOn()
        fedges.FeatureEdgesOff()
        fedges.ManifoldEdgesOff()
        fedges.SetInputData(self.Surface)
        fedges.Update()

        ofedges = fedges.GetOutput()
        # if the following is not == 0, then the surface contains unfilled holes.
        numEdges = ofedges.GetNumberOfPoints()
        if numEdges >= 1:
            self.PrintLog('Capping unclosed holes in surface.')
            tempcapper = vmtksurfacecapper.vmtkSurfaceCapper()
            tempcapper.Surface = self.Surface
            tempcapper.LogOn = 0
            tempcapper.Interactive = 0
            tempcapper.Execute()
            self.Surface = tempcapper.Surface

        # Step 1: Convert the input surface into an image mask of unsigned char type and spacing = PolyDataToImageDataSpacing
        #         Where voxels lying inside the surface are set to 255 and voxels outside the image are set to value 0.

        # since we are creating a new image container from nothing, calculate the origin, extent, and dimensions for the
        # vtkImageDataObject from the surface parameters.

        self.PrintLog('Converting Surface to Image Mask')
        binaryImageFilter = vmtksurfacetobinaryimage.vmtkSurfaceToBinaryImage()
        binaryImageFilter.Surface = self.Surface
        binaryImageFilter.InsideValue = 255
        binaryImageFilter.LogOn = 0
        binaryImageFilter.OutsideValue = 0
        binaryImageFilter.PolyDataToImageDataSpacing = self.PolyDataToImageDataSpacing
        binaryImageFilter.Execute()

        # Step 2: Feed into the vtkvmtkMedialCurveFilter
        #         This takes the binary image and computes the average outward flux of the image. This is then
        #         used to compute the skeleton image. It returns a binary image where values of 1 are skeleton points
        #         and values of 0 are outside the skeleton. The execution speed of this algorithm is fairly sensetive to
        #         the extent of the input image.
        self.PrintLog('Extracting Centerline Skeleton from Image Mask...')
        medialCurveFilter = vtkvmtk.vtkvmtkMedialCurveFilter()
        medialCurveFilter.SetInputData(binaryImageFilter.Image)
        medialCurveFilter.SetThreshold(self.Threshold)
        medialCurveFilter.SetSigma(self.Sigma)
        medialCurveFilter.Update()

        self.Image = medialCurveFilter.GetOutput()
예제 #4
0
    def Execute(self):
        if self.Surface == None:
            self.PrintError('Error: No Input Surface.')

        # Step 0: Check if the surface has any unfilled holes in it. if it does, cap them. 
        fedges = vtk.vtkFeatureEdges()
        fedges.BoundaryEdgesOn()
        fedges.FeatureEdgesOff()
        fedges.ManifoldEdgesOff()
        fedges.SetInputData(self.Surface)
        fedges.Update()

        ofedges = fedges.GetOutput()
        # if the following is not == 0, then the surface contains unfilled holes. 
        numEdges = ofedges.GetNumberOfPoints()
        if numEdges >= 1:
            self.PrintLog('Capping unclosed holes in surface.')
            tempcapper = vmtksurfacecapper.vmtkSurfaceCapper()
            tempcapper.Surface = self.Surface
            tempcapper.LogOn = 0
            tempcapper.Interactive = 0
            tempcapper.Execute()
            self.Surface = tempcapper.Surface

        # Step 1: Convert the input surface into an image mask of unsigned char type and spacing = PolyDataToImageDataSpacing
        #         Where voxels lying inside the surface are set to 255 and voxels outside the image are set to value 0. 

        # since we are creating a new image container from nothing, calculate the origin, extent, and dimensions for the
        # vtkImageDataObject from the surface parameters.

        self.PrintLog('Converting Surface to Image Mask')
        binaryImageFilter = vmtksurfacetobinaryimage.vmtkSurfaceToBinaryImage()
        binaryImageFilter.Surface = self.Surface
        binaryImageFilter.InsideValue = 255
        binaryImageFilter.LogOn = 0
        binaryImageFilter.OutsideValue = 0
        binaryImageFilter.PolyDataToImageDataSpacing = self.PolyDataToImageDataSpacing
        binaryImageFilter.Execute()

        # Step 2: Feed into the vtkvmtkMedialCurveFilter
        #         This takes the binary image and computes the average outward flux of the image. This is then
        #         used to compute the skeleton image. It returns a binary image where values of 1 are skeleton points
        #         and values of 0 are outside the skeleton. The execution speed of this algorithm is fairly sensetive to
        #         the extent of the input image.
        self.PrintLog('Extracting Centerline Skeleton from Image Mask...')
        medialCurveFilter = vtkvmtk.vtkvmtkMedialCurveFilter()
        medialCurveFilter.SetInputData(binaryImageFilter.Image)
        medialCurveFilter.SetThreshold(self.Threshold)
        medialCurveFilter.SetSigma(self.Sigma)
        medialCurveFilter.Update()

        self.Image = medialCurveFilter.GetOutput()
예제 #5
0
def test_smooth_method_with_changing_params(aorta_surface_open_ends, constraint, rings,
                                            paramid, compare_surfaces, write_surface):
    name = __name__ + '_test_smooth_method_with_changing_params_' + paramid + '.vtp'
    capper = surfacecapper.vmtkSurfaceCapper()
    capper.Surface = aorta_surface_open_ends
    capper.Method = 'smooth'
    capper.ConstraintFactor = constraint
    capper.NumberOfRings = rings
    capper.Interactive = 0
    capper.Execute()
    write_surface(capper.Surface)

    assert compare_surfaces(capper.Surface, name) == True
예제 #6
0
def surface_capper(input_file, output_file):
    '''
    将网格变得端口封住
    '''
    reader = sr.vmtkSurfaceReader()
    reader.InputFileName = input_file
    reader.Execute()

    capper = sc.vmtkSurfaceCapper()
    capper.Surface = reader.Surface
    capper.Execute()

    writer = sw.vmtkSurfaceWriter()
    writer.OutputFileName = output_file
    writer.Surface = capper.Surface
    writer.Mode = 'ascii'
    writer.Execute()
예제 #7
0
    def Execute(self):
        if self.Surface == None:
            self.PrintError('Error: No input surface.')

        # feature edges are used to find any holes in the surface.
        fedges = vtk.vtkFeatureEdges()
        fedges.BoundaryEdgesOn()
        fedges.FeatureEdgesOff()
        fedges.ManifoldEdgesOff()
        fedges.SetInputData(self.Surface)
        fedges.Update()
        ofedges = fedges.GetOutput()

        # if numEdges is not 0, then there are holes which need to be capped
        numEdges = ofedges.GetNumberOfPoints()
        if numEdges != 0:
            tempcapper = vmtksurfacecapper.vmtkSurfaceCapper()
            tempcapper.Surface = self.Surface
            tempcapper.Interactive = 0
            tempcapper.Execute()

            networkSurface = tempcapper.Surface
        else:
            networkSurface = self.Surface

        # randomly select one cell to delete so that there is an opening for
        # vmtkNetworkExtraction to use.
        numCells = networkSurface.GetNumberOfCells()
        cellToDelete = random.randrange(0, numCells-1)
        networkSurface.BuildLinks()
        networkSurface.DeleteCell(cellToDelete)
        networkSurface.RemoveDeletedCells()

        # extract the network of approximated centerlines
        net = vmtknetworkextraction.vmtkNetworkExtraction()
        net.Surface = networkSurface
        net.AdvancementRatio = 1.001
        net.Execute()
        network = net.Network

        convert = vmtkcenterlinestonumpy.vmtkCenterlinesToNumpy()
        convert.Centerlines = network
        convert.LogOn = False
        convert.Execute()
        ad = convert.ArrayDict
        cellDataTopology = ad['CellData']['Topology']

        # the network topology identifies an the input segment with the "0" id.
        # since we artificially created this segment, we don't want to use the
        # ends of the segment as source/target points of the centerline calculation
        nodeIndexToIgnore = np.where(cellDataTopology[:,0] == 0)[0][0]
        keepCellConnectivityList = []
        pointIdxToKeep = np.array([])
        removeCellLength = 0
        # we remove the cell, points, and point data which are associated with the
        # segment we want to ignore
        for loopIdx, cellConnectivityList in enumerate(ad['CellData']['CellPointIds']):
            if loopIdx == nodeIndexToIgnore:
                removeCellStartIdx = cellConnectivityList[0]
                removeCellEndIdx = cellConnectivityList[-1]
                removeCellLength = cellConnectivityList.size
                if (removeCellEndIdx + 1) - removeCellStartIdx != removeCellLength:
                    raise(ValueError)
                continue
            else:
                rescaledCellConnectivity = np.subtract(cellConnectivityList, removeCellLength, where=cellConnectivityList >= removeCellLength)
                keepCellConnectivityList.append(rescaledCellConnectivity)
                pointIdxToKeep = np.concatenate((pointIdxToKeep, cellConnectivityList)).astype(np.int)
        newPoints = ad['Points'][pointIdxToKeep]
        newRadius = ad['PointData']['Radius'][pointIdxToKeep]

        # precompute the delaunay tessellation for the whole surface.
        tessalation = vmtkdelaunayvoronoi.vmtkDelaunayVoronoi()
        tessalation.Surface = networkSurface
        tessalation.Execute()
        self.DelaunayTessellation = tessalation.DelaunayTessellation
        self.VoronoiDiagram = tessalation.VoronoiDiagram
        self.PoleIds = tessalation.PoleIds

        # vtk objects cannot be serialized in python. Instead of converting the inputs to numpy arrays and having
        # to reconstruct the vtk object each time the loop executes (a slow process), we can just pass in the
        # memory address of the data objects as a string, and use the vtk python bindings to create a python name
        # referring to the data residing at that memory address. This works because joblib executes each loop
        # iteration in a fork of the original process, providing access to the original memory space.
        # However, the process does not work for return arguments, since the original process will not have access to
        # the memory space of the fork. To return results we use the vmtkCenterlinesToNumpy converter.
        networkSurfaceMemoryAddress = networkSurface.__this__
        delaunayMemoryAddress = tessalation.DelaunayTessellation.__this__
        voronoiMemoryAddress = tessalation.VoronoiDiagram.__this__
        poleIdsMemoryAddress = tessalation.PoleIds.__this__

        # When using Joblib Under Windows, it is important to protect the main loop of code to avoid recursive spawning
        # of subprocesses. Since we cannot guarantee no code will run outside of “if __name__ == ‘__main__’” blocks
        # (only imports and definitions), we make Joblib execute each loop iteration serially on windows.
        # On unix-like os's (linux, Mac) we execute each loop iteration independently on as many cores as the system has.
        if (sys.platform == 'win32') or (sys.platform == 'win64') or (sys.platform == 'cygwin'):
            self.PrintLog('Centerlines extraction on windows computer will execute serially.')
            self.PrintLog('To speed up execution, please run vmtk on unix-like operating system')
            numParallelJobs = 1
        else:
            numParallelJobs = -1

        self.PrintLog('Computing Centerlines ...')
        # note about the verbose function: while Joblib can print a progress bar output (set verbose = 20),
        # it does not implement a callback function as of version 0.11, so we cannot report progress to the user
        # if we are redirecting standard out with the self.PrintLog method.
        outlist = Parallel(n_jobs=numParallelJobs, backend='multiprocessing', verbose=0)(
            delayed(_compute_centerlines_network)(networkSurfaceMemoryAddress,
                                          delaunayMemoryAddress,
                                          voronoiMemoryAddress,
                                          poleIdsMemoryAddress,
                                          cell,
                                          newPoints) for cell in keepCellConnectivityList)
        out = []
        for item in outlist:
            npConvert = vmtknumpytocenterlines.vmtkNumpyToCenterlines()
            npConvert.ArrayDict = item
            npConvert.LogOn = 0
            npConvert.Execute()
            out.append(npConvert.Centerlines)

        # Append each segment's polydata into a single polydata object
        centerlineAppender = vtk.vtkAppendPolyData()
        for data in out:
            centerlineAppender.AddInputData(data)
        centerlineAppender.Update()

        # clean and strip the output centerlines so that redundant points are merged and tracts are combined
        centerlineCleaner = vtk.vtkCleanPolyData()
        centerlineCleaner.SetInputData(centerlineAppender.GetOutput())
        centerlineCleaner.Update()

        centerlineStripper = vtk.vtkStripper()
        centerlineStripper.SetInputData(centerlineCleaner.GetOutput())
        centerlineStripper.JoinContiguousSegmentsOn()
        centerlineStripper.Update()

        self.Centerlines = centerlineStripper.GetOutput()