Python:将 3D 椭球体(扁圆形/长圆形)拟合到 3D 点

标签 python computational-geometry ellipse geometry-surface

亲爱的 stackoverflow 用户,

我面临如下问题:我想在我的 python 脚本中将 3D 椭球拟合到 3D 数据点。

起始数据是一组 x、y 和 z 坐标(笛卡尔坐标)。我想得到的是 3D 数据点凸包的最佳拟合椭球的定义方程中的 a 和 c。

在正确旋转和平移的坐标系中,方程为:

ellipsoid equation

所以我最想做的任务是:

  • 查找 3D 数据点的凸包
  • 将最佳拟合椭球拟合到凸包并得到 a 和 c

  • 你知道是否有一些库允许用最少的代码在 Python 中做到这一点?或者我是否必须用我有限的数学知识(在找到最适合的椭球体时基本上等于零)来明确地对每个步骤进行编码?

    在此先感谢您的帮助,祝您有美好的一天!

    最佳答案

    好吧,我通过将 scipy 的凸包算法与在 this website 上找到的一些 python 函数相结合找到了我的解决方案。 .
    让我们假设你得到一个 x 坐标的 numpy 向量、y 坐标的一个 numpy 向量和 z 坐标的一个 numpy 向量,命名为 x、y 和 z。这对我有用:

    from   scipy.spatial            
    import ConvexHull, convex_hull_plot_2d
    import numpy as np
    from   numpy.linalg import eig, inv
    
    def ls_ellipsoid(xx,yy,zz):                                  
        #finds best fit ellipsoid. Found at http://www.juddzone.com/ALGORITHMS/least_squares_3D_ellipsoid.html
        #least squares fit to a 3D-ellipsoid
        #  Ax^2 + By^2 + Cz^2 +  Dxy +  Exz +  Fyz +  Gx +  Hy +  Iz  = 1
        #
        # Note that sometimes it is expressed as a solution to
        #  Ax^2 + By^2 + Cz^2 + 2Dxy + 2Exz + 2Fyz + 2Gx + 2Hy + 2Iz  = 1
        # where the last six terms have a factor of 2 in them
        # This is in anticipation of forming a matrix with the polynomial coefficients.
        # Those terms with factors of 2 are all off diagonal elements.  These contribute
        # two terms when multiplied out (symmetric) so would need to be divided by two
        
        # change xx from vector of length N to Nx1 matrix so we can use hstack
        x = xx[:,np.newaxis]
        y = yy[:,np.newaxis]
        z = zz[:,np.newaxis]
        
        #  Ax^2 + By^2 + Cz^2 +  Dxy +  Exz +  Fyz +  Gx +  Hy +  Iz = 1
        J = np.hstack((x*x,y*y,z*z,x*y,x*z,y*z, x, y, z))
        K = np.ones_like(x) #column of ones
        
        #np.hstack performs a loop over all samples and creates
        #a row in J for each x,y,z sample:
        # J[ix,0] = x[ix]*x[ix]
        # J[ix,1] = y[ix]*y[ix]
        # etc.
        
        JT=J.transpose()
        JTJ = np.dot(JT,J)
        InvJTJ=np.linalg.inv(JTJ);
        ABC= np.dot(InvJTJ, np.dot(JT,K))
    
        # Rearrange, move the 1 to the other side
        #  Ax^2 + By^2 + Cz^2 +  Dxy +  Exz +  Fyz +  Gx +  Hy +  Iz - 1 = 0
        #    or
        #  Ax^2 + By^2 + Cz^2 +  Dxy +  Exz +  Fyz +  Gx +  Hy +  Iz + J = 0
        #  where J = -1
        eansa=np.append(ABC,-1)
    
        return (eansa)
    
    def polyToParams3D(vec,printMe):                             
        #gets 3D parameters of an ellipsoid. Found at http://www.juddzone.com/ALGORITHMS/least_squares_3D_ellipsoid.html
        # convert the polynomial form of the 3D-ellipsoid to parameters
        # center, axes, and transformation matrix
        # vec is the vector whose elements are the polynomial
        # coefficients A..J
        # returns (center, axes, rotation matrix)
        
        #Algebraic form: X.T * Amat * X --> polynomial form
        
        if printMe: print('\npolynomial\n',vec)
        
        Amat=np.array(
        [
        [ vec[0],     vec[3]/2.0, vec[4]/2.0, vec[6]/2.0 ],
        [ vec[3]/2.0, vec[1],     vec[5]/2.0, vec[7]/2.0 ],
        [ vec[4]/2.0, vec[5]/2.0, vec[2],     vec[8]/2.0 ],
        [ vec[6]/2.0, vec[7]/2.0, vec[8]/2.0, vec[9]     ]
        ])
        
        if printMe: print('\nAlgebraic form of polynomial\n',Amat)
        
        #See B.Bartoni, Preprint SMU-HEP-10-14 Multi-dimensional Ellipsoidal Fitting
        # equation 20 for the following method for finding the center
        A3=Amat[0:3,0:3]
        A3inv=inv(A3)
        ofs=vec[6:9]/2.0
        center=-np.dot(A3inv,ofs)
        if printMe: print('\nCenter at:',center)
        
        # Center the ellipsoid at the origin
        Tofs=np.eye(4)
        Tofs[3,0:3]=center
        R = np.dot(Tofs,np.dot(Amat,Tofs.T))
        if printMe: print('\nAlgebraic form translated to center\n',R,'\n')
        
        R3=R[0:3,0:3]
        R3test=R3/R3[0,0]
        # print('normed \n',R3test)
        s1=-R[3, 3]
        R3S=R3/s1
        (el,ec)=eig(R3S)
        
        recip=1.0/np.abs(el)
        axes=np.sqrt(recip)
        if printMe: print('\nAxes are\n',axes  ,'\n')
        
        inve=inv(ec) #inverse is actually the transpose here
        if printMe: print('\nRotation matrix\n',inve)
        return (center,axes,inve)
    
    
    #let us assume some definition of x, y and z
    
    #get convex hull
    surface  = np.stack((conf.x,conf.y,conf.z), axis=-1)
    hullV    = ConvexHull(surface)
    lH       = len(hullV.vertices)
    hull     = np.zeros((lH,3))
    for i in range(len(hullV.vertices)):
        hull[i] = surface[hullV.vertices[i]]
    hull     = np.transpose(hull)         
                
    #fit ellipsoid on convex hull
    eansa            = ls_ellipsoid(hull[0],hull[1],hull[2]) #get ellipsoid polynomial coefficients
    print("coefficients:"  , eansa)
    center,axes,inve = polyToParams3D(eansa,False)   #get ellipsoid 3D parameters
    print("center:"        , center)
    print("axes:"          , axes)
    print("rotationMatrix:", inve)
    

    关于Python:将 3D 椭球体(扁圆形/长圆形)拟合到 3D 点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58501545/

    相关文章:

    Java:从两个焦点的坐标开始绘制Ellipse2D

    python - 在 python 中遍历元组的语法错误

    python - python的Elasticsearch客户端,无解

    python - 为 Youtube 自动生成的字幕自动打开脚本

    c# - 生成复杂(非凸)多面体 UV 贴图

    arrays - 在数组中找到具有最小差异的一对的有效方法

    graphics - 使用 PCA 的 3D 面向对象边界框

    python - pandas 计算行内填充单元格的数量

    c# - 用多边形逼近椭圆

    latex - 如何在PGF/TikZ中找到带有椭圆的交点