def signedDistanceFromPointCloud(mesh, maxradius=None, bounds=None, dims=(20, 20, 20)): """ Compute signed distances over a volume from an input point cloud. The output is a ``Volume`` object whose voxels contains the signed distance from the cloud. :param float maxradius: how far out to propagate distance calculation :param list bounds: volume bounds. :param list dims: dimensions (nr. of voxels) of the output volume. """ if bounds is None: bounds = mesh.GetBounds() if maxradius is None: maxradius = mesh.diagonalSize() / 10. dist = vtk.vtkSignedDistance() dist.SetInputData(mesh.polydata(True)) dist.SetRadius(maxradius) dist.SetBounds(bounds) dist.SetDimensions(dims) dist.Update() vol = Volume(dist.GetOutput()) vol.name = "signedDistanceVolume" return vol
def recoSurface(points, bins=256, c='gold', alpha=1, wire=False, bc='t', legend=None): ''' Surface reconstruction from sparse points. [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/recosurface.py) ![reco](https://user-images.githubusercontent.com/32848391/46817107-b3263880-cd7e-11e8-985d-f5d158992f0c.png) ''' if isinstance(points, vtk.vtkActor): points = points.coordinates() N = len(points) if N < 50: print('recoSurface: Use at least 50 points.') return None points = np.array(points) ptsSource = vtk.vtkPointSource() ptsSource.SetNumberOfPoints(N) ptsSource.Update() vpts = ptsSource.GetOutput().GetPoints() for i, p in enumerate(points): vpts.SetPoint(i, p) polyData = ptsSource.GetOutput() distance = vtk.vtkSignedDistance() f = 0.1 x0, x1, y0, y1, z0, z1 = polyData.GetBounds() distance.SetBounds(x0-(x1-x0)*f, x1+(x1-x0)*f, y0-(y1-y0)*f, y1+(y1-y0)*f, z0-(z1-z0)*f, z1+(z1-z0)*f) if polyData.GetPointData().GetNormals(): distance.SetInputData(polyData) else: normals = vtk.vtkPCANormalEstimation() normals.SetInputData(polyData) normals.SetSampleSize(int(N/50)) normals.SetNormalOrientationToGraphTraversal() distance.SetInputConnection(normals.GetOutputPort()) print('Recalculating normals for', N, 'points, sample size=', int(N/50)) b = polyData.GetBounds() diagsize = np.sqrt((b[1]-b[0])**2 + (b[3]-b[2])**2 + (b[5]-b[4])**2) radius = diagsize/bins*5 distance.SetRadius(radius) distance.SetDimensions(bins, bins, bins) distance.Update() print('Calculating mesh from points with R =', radius) surface = vtk.vtkExtractSurface() surface.SetRadius(radius * .99) surface.HoleFillingOn() surface.ComputeNormalsOff() surface.ComputeGradientsOff() surface.SetInputConnection(distance.GetOutputPort()) surface.Update() return Actor(surface.GetOutput(), c, alpha, wire, bc, legend)
def recoSurface(points, bins=256, c='gold', alpha=1, wire=False, bc='t', edges=False, legend=None): ''' Surface reconstruction from sparse points. ''' if isinstance(points, vtk.vtkActor): points = vu.coordinates(points) N = len(points) if N < 50: print('recoSurface: Use at least 50 points.') return None points = np.array(points) ptsSource = vtk.vtkPointSource() ptsSource.SetNumberOfPoints(N) ptsSource.Update() vpts = ptsSource.GetOutput().GetPoints() for i, p in enumerate(points): vpts.SetPoint(i, p) polyData = ptsSource.GetOutput() distance = vtk.vtkSignedDistance() f = 0.1 x0, x1, y0, y1, z0, z1 = polyData.GetBounds() distance.SetBounds(x0 - (x1 - x0) * f, x1 + (x1 - x0) * f, y0 - (y1 - y0) * f, y1 + (y1 - y0) * f, z0 - (z1 - z0) * f, z1 + (z1 - z0) * f) if polyData.GetPointData().GetNormals(): distance.SetInputData(polyData) vu.setInput(distance, polyData) else: normals = vtk.vtkPCANormalEstimation() vu.setInput(normals, polyData) normals.SetSampleSize(int(N / 50)) normals.SetNormalOrientationToGraphTraversal() distance.SetInputConnection(normals.GetOutputPort()) print('Recalculating normals for', N, 'points, sample size=', int(N / 50)) radius = vu.diagonalSize(polyData) / bins * 5 distance.SetRadius(radius) distance.SetDimensions(bins, bins, bins) distance.Update() print('Calculating mesh from points with R =', radius) surface = vtk.vtkExtractSurface() surface.SetRadius(radius * .99) surface.HoleFillingOn() surface.ComputeNormalsOff() surface.ComputeGradientsOff() surface.SetInputConnection(distance.GetOutputPort()) surface.Update() return vu.makeActor(surface.GetOutput(), c, alpha, wire, bc, edges, legend)
def __init__(self, infile, grid, radius=2, offset=(0., 0., 0.), toggle_normals=False, sample=20): # Catch non-3D grids if np.shape(grid.dimensions) != (3, ): dim_count = np.shape(grid.dimensions)[0] dim_err = "The specified grid has {:n} dimensions, 3 are required" raise ValueError(dim_err.format(dim_count)) self._grid = grid self._offset = offset self._reader = PolyReader(infile) self._norms = NormalCalculator(self._reader, toggle_normals, sample) self._dist = vtk.vtkSignedDistance() self._dist.SetInputConnection(self._norms.GetOutputPort()) self._dist.SetRadius(radius * grid.spacing[0]) self._dist.SetBounds(self._get_bounds()) self._dist.SetDimensions(self._grid.shape) self._dist.Update() # Get the SDF as an array sdf_array = dsa.WrapDataObject(self._dist.GetOutput()) # Reshape the array to the grid sdf_cube = np.reshape(sdf_array.PointData['ImageScalars'], self._grid.shape[::-1]) # Swap axis to get [x, y, z] order self._array = np.swapaxes(sdf_cube, 0, 2)
A mixed example with class vtkSignedDistance: generate a scalar field by the signed distance from a polydata, save it to stack.tif file, then extract an isosurface from the 3d image. """ from vtkplotter import Plotter, Points, Text, datadir vp = Plotter(verbose=0) act = vp.load(datadir + "290.vtk").normalize().subdivide().computeNormals() # Generate signed distance function and contour it import vtk dist = vtk.vtkSignedDistance() dist.SetInputData(act.polydata()) dist.SetRadius(0.2) # how far out to propagate distance calculation dist.SetBounds(-2, 2, -2, 2, -2, 2) dist.SetDimensions(80, 80, 80) dist.Update() # vp.write(dist.GetOutput(), 'stack.tif') fe = vtk.vtkExtractSurface() fe.SetInputConnection(dist.GetOutputPort()) fe.SetRadius(0.2) # this should match the signed distance radius fe.Update() pts = Points(act.coordinates())
norms.FlipNormalsOff() norms.SetNormalOrientationToGraphTraversal() #norms.SetNormalOrientationToPoint() #norms.SetOrientationPoint(0.3,0.3,0.3) norms.Update() subMapper = vtk.vtkPointGaussianMapper() subMapper.SetInputConnection(extract.GetOutputPort()) subMapper.EmissiveOff() subMapper.SetScaleFactor(0.0) subActor = vtk.vtkActor() subActor.SetMapper(subMapper) # Generate signed distance function and contour it dist = vtk.vtkSignedDistance() dist.SetInputConnection(norms.GetOutputPort()) dist.SetRadius(0.1) #how far out to propagate distance calculation dist.SetBounds(-1,1, -1,1, -1,1) dist.SetDimensions(50,50,50) # Extract the surface with modified flying edges #fe = vtk.vtkFlyingEdges3D() #fe.SetValue(0,0.0) fe = vtk.vtkExtractSurface() fe.SetInputConnection(dist.GetOutputPort()) fe.SetRadius(0.1) # this should match the signed distance radius # Time the execution timer = vtk.vtkTimerLog() timer.StartTimer()
def main(argv): colors = vtk.vtkNamedColors() polyData = ReadPolyData({True: argv[1], False: ""}[len(argv) > 1]) bounds = polyData.GetBounds() rng = [0, 0, 0] for i in range(3): rng[i] = bounds[2 * i + 1] - bounds[2 * i] sampleSize = polyData.GetNumberOfPoints() * .00005 if (sampleSize < 10): sampleSize = 10 print("Sample size is: %d" % (sampleSize)) normals = vtk.vtkPCANormalEstimation() normals.SetInputData(polyData) normals.SetSampleSize(sampleSize) normals.SetNormalOrientationToGraphTraversal() normals.FlipNormalsOn() print("Range: %f, %f, %f" % (rng[0], rng[1], rng[2])) dimension = 256 dimension = 128 radius = rng[0] / float(dimension) * 5 # ~5 voxels print("Radius: %f" % (radius)) distance = vtk.vtkSignedDistance() distance.SetInputConnection(normals.GetOutputPort()) distance.SetRadius(radius) distance.SetDimensions(dimension, dimension, dimension) distance.SetBounds(bounds[0] - rng[0] * 0.1, bounds[1] + rng[0] * 0.1, bounds[2] - rng[1] * 0.1, bounds[3] + rng[1] * 0.1, bounds[4] - rng[2] * 0.1, bounds[5] + rng[2] * 0.1) # Create a lookup table that consists of the full hue circle # (from HSV). belowRangeColor = colors.GetColor4d("Black") belowRangeColor[3] = 0.2 aboveRangeColor = colors.GetColor4d("White") aboveRangeColor[3] = 0.2 hueLut = vtk.vtkLookupTable() hueLut.SetTableRange(-0.99 * radius, 0.99 * radius) hueLut.SetHueRange(0.667, 0) hueLut.SetSaturationRange(1, 1) hueLut.SetValueRange(1, 1) hueLut.UseBelowRangeColorOn() hueLut.SetBelowRangeColor(belowRangeColor) hueLut.UseAboveRangeColorOn() hueLut.SetAboveRangeColor(aboveRangeColor) hueLut.SetNumberOfColors(5) hueLut.Build() last = hueLut.GetTableValue(4) hueLut.SetAboveRangeColor(last[0], last[1], last[2], 0) sagittalColors = vtk.vtkImageMapToColors() sagittalColors.SetInputConnection(distance.GetOutputPort()) sagittalColors.SetLookupTable(hueLut) sagittalColors.Update() sagittal = vtk.vtkImageActor() sagittal.GetMapper().SetInputConnection(sagittalColors.GetOutputPort()) sagittal.SetDisplayExtent(dimension // 2, dimension // 2, 0, dimension - 1, 0, dimension - 1) sagittal.ForceOpaqueOn() axialColors = vtk.vtkImageMapToColors() axialColors.SetInputConnection(distance.GetOutputPort()) axialColors.SetLookupTable(hueLut) axialColors.Update() axial = vtk.vtkImageActor() axial.GetMapper().SetInputConnection(axialColors.GetOutputPort()) axial.SetDisplayExtent(0, dimension - 1, 0, dimension - 1, dimension // 2, dimension // 2) axial.ForceOpaqueOn() coronalColors = vtk.vtkImageMapToColors() coronalColors.SetInputConnection(distance.GetOutputPort()) coronalColors.SetLookupTable(hueLut) coronalColors.Update() coronal = vtk.vtkImageActor() coronal.GetMapper().SetInputConnection(coronalColors.GetOutputPort()) coronal.SetDisplayExtent(0, dimension - 1, dimension // 2, dimension // 2, 0, dimension - 1) coronal.ForceOpaqueOn() # Create a scalar bar scalarBar = vtk.vtkScalarBarActor() scalarBar.SetLookupTable(hueLut) scalarBar.SetTitle("Distance") scalarBar.SetNumberOfLabels(5) # Create graphics stuff # ren1 = vtk.vtkRenderer() ren1.SetBackground(colors.GetColor3d("CornflowerBlue")) renWin = vtk.vtkRenderWindow() renWin.AddRenderer(ren1) renWin.SetSize(600, 400) renWin.SetWindowName("SignedDistance") iren = vtk.vtkRenderWindowInteractor() iren.SetRenderWindow(renWin) # Add the actors to the renderer, set the background and size # ren1.AddActor(sagittal) ren1.AddActor(axial) ren1.AddActor(coronal) ren1.AddActor2D(scalarBar) # Generate an interesting view # ren1.ResetCamera() ren1.GetActiveCamera().Azimuth(120) ren1.GetActiveCamera().Elevation(30) ren1.GetActiveCamera().Dolly(1.5) ren1.ResetCameraClippingRange() renWin.Render() iren.Initialize() iren.Start() print("%f, %f" % (distance.GetOutput().GetScalarRange()[0], distance.GetOutput().GetScalarRange()[1])) return distance