예제 #1
0
def pca(points, pvalue=.95, c='c', alpha=0.5, pcaAxes=False, legend=None):
    '''
    Show the oriented PCA ellipsoid that contains fraction pvalue of points.
        axes = True, show the 3 PCA semi axes
    Extra info is stored in actor.sphericity, actor.va, actor.vb, actor.vc
    (sphericity = 1 for a perfect sphere)
    '''
    try:
        from scipy.stats import f
    except:
        vc.printc("Error in ellipsoid(): scipy not installed. Skip.",1)
        return None
    if isinstance(points, vtk.vtkActor): points=vu.coordinates(points)
    if len(points) == 0: return None
    P = np.array(points, ndmin=2, dtype=float)
    cov = np.cov(P, rowvar=0)      # covariance matrix
    U, s, R = np.linalg.svd(cov)   # singular value decomposition
    p, n = s.size, P.shape[0]
    fppf = f.ppf(pvalue, p, n-p)*(n-1)*p*(n+1)/n/(n-p) # f % point function
    ua,ub,uc = np.sqrt(s*fppf)*2   # semi-axes (largest first)
    center = np.mean(P, axis=0)    # centroid of the hyperellipsoid
    sphericity = ( ((ua-ub)/(ua+ub))**2
                 + ((ua-uc)/(ua+uc))**2
                 + ((ub-uc)/(ub+uc))**2 )/3. *4.
    elliSource = vtk.vtkSphereSource()
    elliSource.SetThetaResolution(48)
    elliSource.SetPhiResolution(48)
    matri = vtk.vtkMatrix4x4()
    matri.DeepCopy((R[0][0] *ua, R[1][0] *ub, R[2][0] *uc, center[0],
                    R[0][1] *ua, R[1][1] *ub, R[2][1] *uc, center[1],
                    R[0][2] *ua, R[1][2] *ub, R[2][2] *uc, center[2], 0,0,0,1))
    vtra = vtk.vtkTransform()
    vtra.SetMatrix(matri)
    ftra = vtk.vtkTransformFilter()
    ftra.SetTransform(vtra)
    ftra.SetInputConnection(elliSource.GetOutputPort())
    ftra.Update()
    actor_elli = vu.makeActor(ftra.GetOutput(), c, alpha, legend=legend)
    actor_elli.GetProperty().BackfaceCullingOn()
    actor_elli.GetProperty().SetInterpolationToPhong()
    if pcaAxes:
        axs = []
        for ax in ([1,0,0], [0,1,0], [0,0,1]):
            l = vtk.vtkLineSource()
            l.SetPoint1([0,0,0])
            l.SetPoint2(ax)
            l.Update()
            t = vtk.vtkTransformFilter()
            t.SetTransform(vtra)
            vu.setInput(t, l.GetOutput())
            t.Update()
            axs.append(vu.makeActor(t.GetOutput(), c, alpha))
        finact = vu.makeAssembly([actor_elli]+axs, legend=legend)
    else : 
        finact = actor_elli
    setattr(finact, 'sphericity', sphericity)
    setattr(finact, 'va', ua)
    setattr(finact, 'vb', ub)
    setattr(finact, 'vc', uc)
    return finact
예제 #2
0
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 = vu.coordinates(a)    
        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, 'jet') # use a colormap to associate a color to time
    return act
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 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)
예제 #5
0
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 extractLines(actor, n=5):

    coords = vu.coordinates(actor)
    poly = vu.polydata(actor, True)
    vpts = poly.GetPoints()
    locator = vtk.vtkPointLocator()
    locator.SetDataSet(poly)
    locator.BuildLocator()
    vtklist = vtk.vtkIdList()
    spts = []
    for i, p in enumerate(coords):
        locator.FindClosestNPoints(n, p, vtklist)
        points = []
        for j in range(vtklist.GetNumberOfIds()):
            trgp = [0, 0, 0]
            vpts.GetPoint(vtklist.GetId(j), trgp)
            if (p - trgp).any(): points.append(trgp)
        p0 = points.pop() - p
        dots = [np.dot(p0, ps - p) for ps in points]
        if len(np.unique(np.sign(dots))) == 1:
            spts.append(p)
    return np.array(spts)
예제 #7
0
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 = vu.coordinates(actor)
    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 = vu.polydata(actor, 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])

    setattr(actor, 'variances', np.array(variances))

    if showNPlanes:
        apts = vs.points(newsurf, c='r 0.6', r=2)
        ass = vu.makeAssembly([apts]+acts)
        return ass  # NB: a demo actor is returned

    return actor  # NB: original actor is modified