def smoothMLS1D(actor, f=0.2, showNLines=0): ''' Smooth actor or points with a Moving Least Squares variant. The list actor.variances contain the residue calculated for each point. Input actor's polydata is modified. f, smoothing factor - typical range s [0,2] showNLines, build an actor showing the fitting line for N random points ''' coords = vu.coordinates(actor) ncoords = len(coords) Ncp = int(ncoords * f / 10) nshow = int(ncoords) if showNLines: ndiv = int(nshow / showNLines) if Ncp < 3: vc.printc('Please choose a higher fraction than ' + str(f), 1) Ncp = 3 poly = vu.polydata(actor, True) vpts = poly.GetPoints() locator = vtk.vtkPointLocator() locator.SetDataSet(poly) locator.BuildLocator() vtklist = vtk.vtkIdList() variances, newline, acts = [], [], [] for i, p in enumerate(coords): locator.FindClosestNPoints(Ncp, p, vtklist) points = [] for j in range(vtklist.GetNumberOfIds()): trgp = [0, 0, 0] vpts.GetPoint(vtklist.GetId(j), trgp) points.append(trgp) if len(points) < 2: continue points = np.array(points) pointsmean = points.mean(axis=0) # plane center uu, dd, vv = np.linalg.svd(points - pointsmean) newp = np.dot(p - pointsmean, vv[0]) * vv[0] + pointsmean variances.append(dd[1] + dd[2]) newline.append(newp) if showNLines and not i % ndiv: fline = fitLine(points, lw=4, alpha=1) # fitting plane iapts = vs.points(points) # blue points acts += [fline, iapts] for i in range(ncoords): vpts.SetPoint(i, newline[i]) if showNLines: apts = vs.points(newline, c='r 0.6', r=2) ass = vu.makeAssembly([apts] + acts) return ass #NB: a demo actor is returned setattr(actor, 'variances', np.array(variances)) return actor #NB: original actor is modified
def removeOutliers(points, radius, c='k', alpha=1, legend=None): ''' Remove outliers from a cloud of points within radius search ''' isactor = False if isinstance(points, vtk.vtkActor): isactor = True poly = vu.polydata(points) else: src = vtk.vtkPointSource() src.SetNumberOfPoints(len(points)) src.Update() vpts = src.GetOutput().GetPoints() for i, p in enumerate(points): vpts.SetPoint(i, p) poly = src.GetOutput() removal = vtk.vtkRadiusOutlierRemoval() vu.setInput(removal, poly) removal.SetRadius(radius) removal.SetNumberOfNeighbors(5) removal.GenerateOutliersOff() removal.Update() rpoly = removal.GetOutput() print("# of removed outlier points: ", removal.GetNumberOfPointsRemoved(), '/', poly.GetNumberOfPoints()) outpts = [] for i in range(rpoly.GetNumberOfPoints()): outpts.append(list(rpoly.GetPoint(i))) outpts = np.array(outpts) if not isactor: return outpts actor = vs.points(outpts, c=c, alpha=alpha, legend=legend) return actor # return same obj for concatenation
def smoothMLS3D(actors, neighbours=10): ''' A time sequence of actors is being smoothed in 4D using a MLS (Moving Least Squares) variant. Time assciated to an actor must be specified in advance with actor.time(t). Data itself can suggest a meaningful time separation based on the spatial distribution of points. neighbours, fixed nr of neighbours in space-time to take into account in fit. ''' from scipy.spatial import KDTree coords4d = [] for a in actors: # build the list of 4d coordinates coords3d = a.coordinates() n = len(coords3d) pttimes = [[a.time()]]*n coords4d += np.append(coords3d, pttimes, axis=1).tolist() avedt = float(actors[-1].time()-actors[0].time())/len(actors) print("Average time separation between actors dt =", round(avedt, 3)) coords4d = np.array(coords4d) newcoords4d = [] kd = KDTree(coords4d, leafsize=neighbours) suggest='' pb = vio.ProgressBar(0, len(coords4d)) for i in pb.range(): mypt = coords4d[i] #dr = np.sqrt(3*dx**2+dt**2) #iclosest = kd.query_ball_point(mypt, r=dr) #dists, iclosest = kd.query(mypt, k=None, distance_upper_bound=dr) dists, iclosest = kd.query(mypt, k=neighbours) closest = coords4d[iclosest] nc = len(closest) if nc >= neighbours and nc > 5: m = np.linalg.lstsq(closest, [1.]*nc, rcond=None)[0] vers = m/np.linalg.norm(m) hpcenter = np.mean(closest, axis=0) # hyperplane center dist = np.dot(mypt-hpcenter, vers) projpt = mypt - dist*vers newcoords4d.append(projpt) if not i%1000: # work out some stats v = np.std(closest, axis=0) vx = round((v[0]+v[1]+v[2])/3, 3) suggest='data suggest dt='+str(vx) pb.print(suggest) newcoords4d = np.array(newcoords4d) ctimes = newcoords4d[:, 3] ccoords3d = np.delete(newcoords4d, 3, axis=1) # get rid of time act = vs.points(ccoords3d) act.pointColors(ctimes, cmap='jet') # use a colormap to associate a color to time return act
def spline(points, smooth=0.5, degree=2, s=2, c='b', alpha=1., nodes=False, legend=None, res=20): ''' Return a vtkActor for a spline that doesnt necessarly pass exactly throught all points. Options: smooth, smoothing factor: 0 = interpolate points exactly, 1 = average point positions degree = degree of the spline (1<degree<5) nodes = True shows also original the points [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/tutorial.py) ![rspline](https://user-images.githubusercontent.com/32848391/35976041-15781de8-0cdf-11e8-997f-aeb725bc33cc.png) ''' try: from scipy.interpolate import splprep, splev except ImportError: vc.printc( 'Warning: ..scipy not installed, using vtkCardinalSpline instead.', c=5) return _vtkspline(points, s, c, alpha, nodes, legend, res) Nout = len(points)*res # Number of points on the spline points = np.array(points) minx, miny, minz = np.min(points, axis=0) maxx, maxy, maxz = np.max(points, axis=0) maxb = max(maxx-minx, maxy-miny, maxz-minz) smooth *= maxb/2 # must be in absolute units x, y, z = points[:, 0], points[:, 1], points[:, 2] tckp, _ = splprep([x, y, z], task=0, s=smooth, k=degree) # find the knots # evaluate spline, including interpolated points: xnew, ynew, znew = splev(np.linspace(0, 1, Nout), tckp) ppoints = vtk.vtkPoints() # Generate the polyline for the spline profileData = vtk.vtkPolyData() for i in range(Nout): ppoints.InsertPoint(i, xnew[i], ynew[i], znew[i]) lines = vtk.vtkCellArray() # Create the polyline lines.InsertNextCell(Nout) for i in range(Nout): lines.InsertCellPoint(i) profileData.SetPoints(ppoints) profileData.SetLines(lines) actline = vu.makeActor(profileData, c=c, alpha=alpha, legend=legend) actline.GetProperty().SetLineWidth(s) if nodes: actnodes = vs.points(points, r=5, c=c, alpha=alpha) ass = vu.makeAssembly([actline, actnodes], legend=legend) return ass else: return actline
def cluster(points, radius, legend=None): ''' Clustering of points in space. radius, is the radius of local search. Individual subsets can be accessed through actor.clusters [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/clustering.py) ![cluster](https://user-images.githubusercontent.com/32848391/46817286-2039ce00-cd7f-11e8-8b29-42925e03c974.png) ''' if isinstance(points, vtk.vtkActor): poly = points.polydata() else: src = vtk.vtkPointSource() src.SetNumberOfPoints(len(points)) src.Update() vpts = src.GetOutput().GetPoints() for i, p in enumerate(points): vpts.SetPoint(i, p) poly = src.GetOutput() cluster = vtk.vtkEuclideanClusterExtraction() cluster.SetInputData(poly) cluster.SetExtractionModeToAllClusters() cluster.SetRadius(radius) cluster.ColorClustersOn() cluster.Update() idsarr = cluster.GetOutput().GetPointData().GetArray('ClusterId') Nc = cluster.GetNumberOfExtractedClusters() sets = [[] for i in range(Nc)] for i, p in enumerate(points): sets[idsarr.GetValue(i)].append(p) acts = [] for i, aset in enumerate(sets): acts.append(vs.points(aset, c=i)) actor = Assembly(acts, legend=legend) actor.info['clusters'] = sets print('Nr. of extracted clusters', Nc) if Nc > 10: print('First ten:') for i in range(Nc): if i > 9: print('...') break print('Cluster #'+str(i)+', N =', len(sets[i])) print('Access individual clusters through attribute: actor.cluster') return actor
def cluster(points, radius, legend=None): ''' Clustering of points in space. radius, is the radius of local search. Individual subsets can be accessed through actor.clusters ''' if isinstance(points, vtk.vtkActor): poly = vu.polydata(points) else: src = vtk.vtkPointSource() src.SetNumberOfPoints(len(points)) src.Update() vpts = src.GetOutput().GetPoints() for i, p in enumerate(points): vpts.SetPoint(i, p) poly = src.GetOutput() cluster = vtk.vtkEuclideanClusterExtraction() vu.setInput(cluster, poly) cluster.SetExtractionModeToAllClusters() cluster.SetRadius(radius) cluster.ColorClustersOn() cluster.Update() idsarr = cluster.GetOutput().GetPointData().GetArray('ClusterId') Nc = cluster.GetNumberOfExtractedClusters() sets = [[] for i in range(Nc)] for i, p in enumerate(points): sets[idsarr.GetValue(i)].append(p) acts = [] for i, aset in enumerate(sets): acts.append(vs.points(aset, c=i)) actor = vu.makeAssembly(acts, legend=legend) setattr(actor, 'clusters', sets) print('Nr. of extracted clusters', Nc) if Nc > 10: print('First ten:') for i in range(Nc): if i > 9: print('...') break print('Cluster #' + str(i) + ', N =', len(sets[i])) print('Access individual clusters through attribute: actor.cluster') return actor
def _vtkspline(points, s, c, alpha, nodes, legend, res): numberOfOutputPoints = len(points)*res # Number of points on the spline numberOfInputPoints = len(points) # One spline for each direction. aSplineX = vtk.vtkCardinalSpline() # interpolate the x values aSplineY = vtk.vtkCardinalSpline() # interpolate the y values aSplineZ = vtk.vtkCardinalSpline() # interpolate the z values inputPoints = vtk.vtkPoints() for i in range(0, numberOfInputPoints): x = points[i][0] y = points[i][1] z = points[i][2] aSplineX.AddPoint(i, x) aSplineY.AddPoint(i, y) aSplineZ.AddPoint(i, z) inputPoints.InsertPoint(i, x, y, z) inputData = vtk.vtkPolyData() inputData.SetPoints(inputPoints) points = vtk.vtkPoints() profileData = vtk.vtkPolyData() for i in range(0, numberOfOutputPoints): t = (numberOfInputPoints-1.)/(numberOfOutputPoints-1.)*i x, y, z = aSplineX.Evaluate( t), aSplineY.Evaluate(t), aSplineZ.Evaluate(t) points.InsertPoint(i, x, y, z) lines = vtk.vtkCellArray() # Create the polyline. lines.InsertNextCell(numberOfOutputPoints) for i in range(0, numberOfOutputPoints): lines.InsertCellPoint(i) profileData.SetPoints(points) profileData.SetLines(lines) actline = vu.makeActor(profileData, c=c, alpha=alpha, legend=legend) actline.GetProperty().SetLineWidth(s) actline.GetProperty().SetInterpolationToPhong() if nodes: pts = vu.coordinates(inputData) actnodes = vs.points(pts, r=s*1.5, c=c, alpha=alpha) ass = vu.makeAssembly([actline, actnodes], legend=legend) return ass else: return actline
def smoothMLS1D(actor, f=0.2, showNLines=0): ''' Smooth actor or points with a Moving Least Squares variant. The list actor.variances contain the residue calculated for each point. Input actor's polydata is modified. Options: f, smoothing factor - typical range s [0,2] showNLines, build an actor showing the fitting line for N random points [**Example1**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/moving_least_squares1D.py) [**Example2**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/skeletonize.py) ![skel](https://user-images.githubusercontent.com/32848391/46820954-c5f13b00-cd87-11e8-87aa-286528a09de8.png) ''' coords = actor.coordinates() ncoords = len(coords) Ncp = int(ncoords*f/10) nshow = int(ncoords) if showNLines: ndiv = int(nshow/showNLines) if Ncp < 3: vc.printc('Please choose a higher fraction than '+str(f), c=1) Ncp = 3 poly = actor.polydata(True) vpts = poly.GetPoints() locator = vtk.vtkPointLocator() locator.SetDataSet(poly) locator.BuildLocator() vtklist = vtk.vtkIdList() variances, newline, acts = [], [], [] for i, p in enumerate(coords): locator.FindClosestNPoints(Ncp, p, vtklist) points = [] for j in range(vtklist.GetNumberOfIds()): trgp = [0, 0, 0] vpts.GetPoint(vtklist.GetId(j), trgp) points.append(trgp) if len(points) < 2: continue points = np.array(points) pointsmean = points.mean(axis=0) # plane center uu, dd, vv = np.linalg.svd(points-pointsmean) newp = np.dot(p-pointsmean, vv[0])*vv[0] + pointsmean variances.append(dd[1]+dd[2]) newline.append(newp) if showNLines and not i % ndiv: fline = fitLine(points, lw=4, alpha=1) # fitting plane iapts = vs.points(points) # blue points acts += [fline, iapts] for i in range(ncoords): vpts.SetPoint(i, newline[i]) if showNLines: apts = vs.points(newline, c='r 0.6', r=2) ass = Assembly([apts]+acts) return ass # NB: a demo actor is returned actor.info['variances'] = np.array(variances) return actor # NB: original actor is modified
def smoothMLS2D(actor, f=0.2, decimate=1, recursive=0, showNPlanes=0): ''' Smooth actor or points with a Moving Least Squares variant. The list actor.variances contain the residue calculated for each point. Input actor's polydata is modified. Options: f, smoothing factor - typical range s [0,2] decimate, decimation factor (an integer number) recursive, move points while algorithm proceedes showNPlanes, build an actor showing the fitting plane for N random points [**Example1**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/mesh_smoothers.py) [**Example2**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/moving_least_squares2D.py) [**Example3**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/recosurface.py) ''' coords = actor.coordinates() ncoords = len(coords) Ncp = int(ncoords*f/100) nshow = int(ncoords/decimate) if showNPlanes: ndiv = int(nshow/showNPlanes*decimate) if Ncp < 5: vc.printc('Please choose a higher fraction than '+str(f), c=1) Ncp = 5 print('smoothMLS: Searching #neighbours, #pt:', Ncp, ncoords) poly = actor.polydata(True) vpts = poly.GetPoints() locator = vtk.vtkPointLocator() locator.SetDataSet(poly) locator.BuildLocator() vtklist = vtk.vtkIdList() variances, newsurf, acts = [], [], [] pb = vio.ProgressBar(0, ncoords) for i, p in enumerate(coords): pb.print('smoothing...') if i % decimate: continue locator.FindClosestNPoints(Ncp, p, vtklist) points = [] for j in range(vtklist.GetNumberOfIds()): trgp = [0, 0, 0] vpts.GetPoint(vtklist.GetId(j), trgp) points.append(trgp) if len(points) < 5: continue points = np.array(points) pointsmean = points.mean(axis=0) # plane center uu, dd, vv = np.linalg.svd(points-pointsmean) a, b, c = np.cross(vv[0], vv[1]) # normal d, e, f = pointsmean # plane center x, y, z = p t = (a*d - a*x + b*e - b*y + c*f - c*z) # /(a*a+b*b+c*c) newp = [x+t*a, y+t*b, z+t*c] variances.append(dd[2]) newsurf.append(newp) if recursive: vpts.SetPoint(i, newp) if showNPlanes and not i % ndiv: plane = fitPlane(points, alpha=0.3) # fitting plane iapts = vs.points(points) # blue points acts += [plane, iapts] if decimate == 1 and not recursive: for i in range(ncoords): vpts.SetPoint(i, newsurf[i]) actor.info['variances'] = np.array(variances) if showNPlanes: apts = vs.points(newsurf, c='r 0.6', r=2) ass = Assembly([apts]+acts) return ass # NB: a demo actor is returned return actor # NB: original actor is modified
def fxy(z='sin(3*x)*log(x-y)/3', x=[0, 3], y=[0, 3], zlimits=[None, None], showNan=True, zlevels=10, wire=False, c='b', bc='aqua', alpha=1, legend=True, texture=None, res=100): ''' Build a surface representing the 3D function specified as a string or as a reference to an external function. Red points indicate where the function does not exist (showNan). zlevels will draw the specified number of z-levels contour lines. [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/fxy.py) ![fxy](https://user-images.githubusercontent.com/32848391/36611824-fd524fac-18d4-11e8-8c76-d3d1b1bb3954.png) ''' if isinstance(z, str): try: z = z.replace('math.', '').replace('np.', '') namespace = locals() code = "from math import*\ndef zfunc(x,y): return "+z exec(code, namespace) z = namespace['zfunc'] except: vc.printc('Syntax Error in fxy()', c=1) return None ps = vtk.vtkPlaneSource() ps.SetResolution(res, res) ps.SetNormal([0, 0, 1]) ps.Update() poly = ps.GetOutput() dx = x[1]-x[0] dy = y[1]-y[0] todel, nans = [], [] if zlevels: tf = vtk.vtkTriangleFilter() tf.SetInputData(poly) tf.Update() poly = tf.GetOutput() for i in range(poly.GetNumberOfPoints()): px, py, _ = poly.GetPoint(i) xv = (px+.5)*dx+x[0] yv = (py+.5)*dy+y[0] try: zv = z(xv, yv) poly.GetPoints().SetPoint(i, [xv, yv, zv]) except: todel.append(i) nans.append([xv, yv, 0]) if len(todel): cellIds = vtk.vtkIdList() poly.BuildLinks() for i in todel: poly.GetPointCells(i, cellIds) for j in range(cellIds.GetNumberOfIds()): poly.DeleteCell(cellIds.GetId(j)) # flag cell poly.RemoveDeletedCells() cl = vtk.vtkCleanPolyData() cl.SetInputData(poly) cl.Update() poly = cl.GetOutput() if not poly.GetNumberOfPoints(): vc.printc('Function is not real in the domain', c=1) return None if zlimits[0]: tmpact1 = Actor(poly) a = tmpact1.cutPlane((0, 0, zlimits[0]), (0, 0, 1)) poly = a.polydata() if zlimits[1]: tmpact2 = Actor(poly) a = tmpact2.cutPlane((0, 0, zlimits[1]), (0, 0, -1)) poly = a.polydata() if c is None: elev = vtk.vtkElevationFilter() elev.SetInputData(poly) elev.Update() poly = elev.GetOutput() actor = Actor(poly, c=c, bc=bc, alpha=alpha, wire=wire, legend=legend, texture=texture) acts = [actor] if zlevels: elevation = vtk.vtkElevationFilter() elevation.SetInputData(poly) bounds = poly.GetBounds() elevation.SetLowPoint( 0, 0, bounds[4]) elevation.SetHighPoint(0, 0, bounds[5]) elevation.Update() bcf = vtk.vtkBandedPolyDataContourFilter() bcf.SetInputData(elevation.GetOutput()) bcf.SetScalarModeToValue() bcf.GenerateContourEdgesOn() bcf.GenerateValues(zlevels, elevation.GetScalarRange()) bcf.Update() zpoly = bcf.GetContourEdgesOutput() zbandsact = Actor(zpoly, c='k', alpha=alpha) zbandsact.GetProperty().SetLineWidth(1.5) acts.append(zbandsact) if showNan and len(todel): bb = actor.GetBounds() zm = (bb[4]+bb[5])/2 nans = np.array(nans)+[0, 0, zm] nansact = vs.points(nans, c='red', alpha=alpha/2) acts.append(nansact) if len(acts) > 1: asse = Assembly(acts) return asse else: return actor
def fxy(z='sin(3*x)*log(x-y)/3', x=[0, 3], y=[0, 3], zlimits=[None, None], showNan=True, zlevels=10, wire=False, c='b', bc='aqua', alpha=1, legend=True, texture=None, res=100): ''' Build a surface representing the 3D function specified as a string or as a reference to an external function. Red points indicate where the function does not exist (showNan). zlevels will draw the specified number of z-levels contour lines. Examples: vp = vtkplotter.Plotter() vp.fxy('sin(3*x)*log(x-y)/3') or def z(x,y): return math.sin(x*y) vp.fxy(z) # or equivalently: vp.fxy(lambda x,y: math.sin(x*y)) ''' if isinstance(z, str): try: z = z.replace('math.', '').replace('np.', '') namespace = locals() code = "from math import*\ndef zfunc(x,y): return " + z exec(code, namespace) z = namespace['zfunc'] except: vc.printc('Syntax Error in fxy()', 1) return None ps = vtk.vtkPlaneSource() ps.SetResolution(res, res) ps.SetNormal([0, 0, 1]) ps.Update() poly = ps.GetOutput() dx = x[1] - x[0] dy = y[1] - y[0] todel, nans = [], [] if zlevels: tf = vtk.vtkTriangleFilter() vu.setInput(tf, poly) tf.Update() poly = tf.GetOutput() for i in range(poly.GetNumberOfPoints()): px, py, _ = poly.GetPoint(i) xv = (px + .5) * dx + x[0] yv = (py + .5) * dy + y[0] try: zv = z(xv, yv) poly.GetPoints().SetPoint(i, [xv, yv, zv]) except: todel.append(i) nans.append([xv, yv, 0]) if len(todel): cellIds = vtk.vtkIdList() poly.BuildLinks() for i in todel: poly.GetPointCells(i, cellIds) for j in range(cellIds.GetNumberOfIds()): poly.DeleteCell(cellIds.GetId(j)) #flag cell poly.RemoveDeletedCells() cl = vtk.vtkCleanPolyData() vu.setInput(cl, poly) cl.Update() poly = cl.GetOutput() if not poly.GetNumberOfPoints(): vc.printc('Function is not real in the domain', 1) return vtk.vtkActor() if zlimits[0]: a = vu.cutPlane(poly, (0, 0, zlimits[0]), (0, 0, 1)) poly = vu.polydata(a) if zlimits[1]: a = vu.cutPlane(poly, (0, 0, zlimits[1]), (0, 0, -1)) poly = vu.polydata(a) if c is None: elev = vtk.vtkElevationFilter() vu.setInput(elev, poly) elev.Update() poly = elev.GetOutput() actor = vu.makeActor(poly, c=c, bc=bc, alpha=alpha, wire=wire, legend=legend, texture=texture) acts = [actor] if zlevels: elevation = vtk.vtkElevationFilter() vu.setInput(elevation, poly) bounds = poly.GetBounds() elevation.SetLowPoint(0, 0, bounds[4]) elevation.SetHighPoint(0, 0, bounds[5]) elevation.Update() bcf = vtk.vtkBandedPolyDataContourFilter() vu.setInput(bcf, elevation.GetOutput()) bcf.SetScalarModeToValue() bcf.GenerateContourEdgesOn() bcf.GenerateValues(zlevels, elevation.GetScalarRange()) bcf.Update() zpoly = bcf.GetContourEdgesOutput() zbandsact = vu.makeActor(zpoly, c='k', alpha=alpha) zbandsact.GetProperty().SetLineWidth(1.5) acts.append(zbandsact) if showNan and len(todel): bb = actor.GetBounds() zm = (bb[4] + bb[5]) / 2 nans = np.array(nans) + [0, 0, zm] nansact = vs.points(nans, c='red', alpha=alpha / 2) acts.append(nansact) if len(acts) > 1: asse = vu.makeAssembly(acts) return asse else: return actor