plannar适合odrpack

时间:2018-01-05 14:20:55

标签: python scipy data-fitting

我正在尝试使用scipy.odr为某些x,y,z点获得最佳拟合平面。

我将平面方程隐式定义为ax + by + cz + d = 0,并执行最小二乘(使用scipy.linalg.lstsq)为odr提供初始估计。

由odr返回的β矢量的组成部分(其中beta = [a,b,c,d])的幅度在1e167和1e172之间...这样的结果值得信赖吗?我觉得这些数字很荒谬......

请注意,这些点来自于相对平坦的面的3D扫描,该面几乎平行于xz平面(几乎垂直)。

这是odr结果的pprint():

'
Beta: [  3.14570111e-170   3.21821458e-169   4.49232028e-172   4.49374557e-167]
Beta Std Error: [ 0.  0.  0.  0.]
Beta Covariance: [[  6.37459471e-10  -8.57690019e-09  -2.18092934e-11  -1.13009384e-06]
 [ -8.57690019e-09   5.11732570e-07   1.30123070e-09   6.74263262e-05]
 [ -2.18092934e-11   1.30123070e-09   5.22674068e-12   1.70799469e-07]
 [ -1.13009384e-06   6.74263262e-05   1.70799469e-07   8.88444676e-03]]
Residual Variance: 0.0
Inverse Condition #: 0.0010484041422201213
Reason(s) for Halting:
  Sum of squares convergence
None
'

我正在使用的代码:

import numpy as np
import scipy.linalg
from scipy import odr
import pickle

def planar_fit(points):
    # best-fit linear plane
    a = np.c_[points[:, 0], points[:, 1], np.ones(points.shape[0])]
    c, _, _, _ = scipy.linalg.lstsq(a, points[:, 2])  # coefficients
    # The coefficients are returned as an array beta=[a, b, c, d] from the implicit form 'a*x + b*y + c*z + d = 0'.
    beta = np.r_[c[0], c[1], -1, c[2]] / c[2]
    return beta


def odr_planar_fit(points):
    def f_3(beta, xyz):
        """ implicit definition of the plane"""
        return beta[0] * xyz[0] + beta[1] * xyz[1] + beta[2] * xyz[2] + beta[3]

    # # Coordinates of the 2D points
    x = points[:, 0]
    y = points[:, 1]
    z = points[:, 2]

    # Use least squares for initial estimate.
    beta0 = planar_fit(points)

    # Create the data object for the odr. The equation is given in the implicit form 'a*x + b*y + c*z + d = 0' and
    # beta=[a, b, c, d] (beta is the vector to be fitted). The positional argument y=1 means that the dimensionality
    # of the fitting is 1.
    lsc_data = odr.Data(np.row_stack([x, y, z]), y=1)
    # Create the odr model
    lsc_model = odr.Model(f_3, implicit=True)
    # Create the odr object based on the data, the model and the first estimation vector.
    lsc_odr = odr.ODR(lsc_data, lsc_model, beta0)
    # run the regression.
    lsc_out = lsc_odr.run()

    return lsc_out, beta0

def main():
    #import from pickle.
    with open('./points.pkl', 'rb') as f:
        points = np.array(pickle.load(f))

    # Perform the ODR
    odr_out, lstsq = odr_planar_fit(points)
    print(lstsq)
    print(odr_out.pprint())

main()

The pickle containing my points.

2 个答案:

答案 0 :(得分:0)

据我所知Traceback (most recent call last): File "C:/Users/zahid/PycharmProjects/untitled3/test.py", line 1, in <module> from pywinauto.application import Application ModuleNotFoundError: No module named 'pywinauto' 它不是为3D数据制作的,但我可能在这里错了。由于这是一个简单的平面拟合,我建议使用简单的odr。此外,请注意您实际上没有4个免费参数,因为您可以划分leastsq,例如a * x + b * y + c * z + d = 0提供d(如果a' * x + b' * y + c' * z + 1 = 0不为零)。

如果我们用以下形式编写飞机:所有点d P我们已经免费获得了odr函数。可以通过假设平面偏移向量(P - p0) * n = 0是缩放的法向量来简化。像这样有3个自由参数,比例p0 = s * n和法线向量s的方向角。

根据拟合如下

(theta, phi)

提供了

Fit

蓝点是噪声数据,紫色是拟合平面,红色是偏移矢量。

很容易看出,import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np from scipy.optimize import leastsq from random import random # for rotating test data def y_mx( theta ): out = np.array( [ np.cos( theta ),0, np.sin( theta ), 0, 1, 0, -np.sin( theta ),0, np.cos( theta ) ] ) return out.reshape( 3, 3) # for rotating test data def z_mx( theta ): out = np.array( [ np.cos( theta ), np.sin( theta ), 0, -np.sin( theta ), np.cos( theta ), 0, 0, 0, 1 ] ) return out.reshape( 3, 3) # for test data def make_plane( theta, phi, px, py, pz, n=100 ): points=[] for i in range( n ): x = 1 - 2 * random( ) y = 1 - 2 * random( ) z = 0.15 * ( 1 - 2 * random() ) points += [ np.array( [ x, y, z] ) ] points = np.array( points) points = [ np.array( [px, py, pz ] ) + np.dot( z_mx( phi ), np.dot( y_mx( theta ) , p ) ) for p in points ] return np.array( points ) # residual function for leastsq # note the plane equation is (P - p0) n = 0 if P is member of plane # and n is normal vector of plane directly provides the normal distance function # moreover p0 can be chosen to be s * n def residuals( params, points ): scale, theta, phi = params nVector = np.array( [ np.sin( theta ) * np.cos( phi ), np.sin( theta ) * np.sin( phi ), np.cos( theta ) ] ) p0 = scale * nVector diff = [ np.dot( p - p0, nVector ) for p in points] return diff # some test data pnts = make_plane( 1.5, 1.49, .15, .2, .33) #and the fit guess=[ 0, 0, 0 ] bestfit, err = leastsq( residuals, guess, pnts ) #the resulting normal vectot and offset nVectorFit = np.array( [ np.sin( bestfit[1] ) * np.cos( bestfit[2] ), np.sin( bestfit[1] ) * np.sin( bestfit[2] ), np.cos( bestfit[1] ) ] ) p0Fit = bestfit[0] * nVectorFit # converting to standard plane equation a = nVectorFit[0] / nVectorFit[1] c = nVectorFit[2] / nVectorFit[1] d = bestfit[0] / nVectorFit[1] # plane equation data X = np.linspace( -.6, .6, 20 ) Z = np.linspace( -.6, .6, 20 ) XX, ZZ = np.meshgrid( X, Z ) YY = -a * XX - c * ZZ + d #plotting fig = plt.figure() ax = fig.add_subplot( 1, 1, 1, projection='3d') # original data ax.scatter( pnts[:,0], pnts[:,1] , pnts[:,2]) # offset vector ax.plot( [0, p0Fit[0] ], [0, p0Fit[1] ], [0, p0Fit[2] ], color = 'r') # fitted plane ax.plot_wireframe(XX, YY, ZZ , color = '#9900bb') ax.set_xlim( [-1,1] ) ax.set_ylim( [-1,1] ) ax.set_zlim( [-1,1] ) ax.set_xlabel("x") ax.set_ylabel("y") ax.set_zlabel("z") plt.show() y = a * x + c * z + d的情况是从拟合结果中直接计算的。

答案 1 :(得分:0)

ODR完全适合多维数据,您的方向正确。

您只是选择不好,将ODR的隐式版本与f_3定义一起使用。问题是您有一个函数A*X=0,在没有任何其他约束的情况下尝试将其最小化。当然,优化器可以做的最好的事情就是将A的幅度最小化为零-从而最大程度地减少错误!为了使隐式优化起作用,您需要以某种方式引入对A的大小的约束,例如除以最后一个数字:

def f_3(beta, xyz):
    """ implicit definition of the plane"""
    return beta[0]/beta[3] * xyz[0] + beta[1]/beta[3] * xyz[1] + beta[2]/beta[3] * xyz[2] + 1.0

这样,优化器除了执行您想要的操作之外别无选择:)

或者,您也可以将模型转换为显式形式y = ax + cz + d,该形式不会受到幅度问题的困扰(一直以来都是b == 1)。

当然,通过将点移至原点并将其缩放为距离的单位差异,可以获得更高的精度。

由于我也将要使用ODR,所以我对它的属性感到好奇,因此我四处探索以发现它的精确度和灵敏性,结果如下:https://gist.github.com/peci1/fb1cea77c41fe8ace6c0db8ef82539a3

我测试了隐式和显式ODR(有无标准化),并通过LSQ或较差的结果进行了初步猜测(以查看其对猜测的敏感程度)。您的数据看起来像这样: ODR tests 基本上,黄色和灰色平面是未经归一化的隐式拟合,结果非常糟糕,而其他ODR拟合大致相同。您会看到ODR拟合与(淡蓝色)LSQ拟合(预期的)有些不同。