更新:我修改了Optimize and Eigen和Solve方法以反映更改。所有现在都返回“相同”向量,允许机器精度。 我仍然对Eigen方法感到难过。具体如何/为什么我选择特征向量切片没有意义。这只是试验和错误,直到正常情况与其他解决方案相匹配。如果有人能够纠正/解释我真正应该做的事情,或者为什么我做了什么工作我会很感激。。
谢谢 亚历山大克莱默,解释我为什么要切片,只能选择一个正确的答案
我有一张深度图片。我想计算深度图像中像素的粗糙表面法线。我考虑周围像素,在最简单的情况下是3x3矩阵,并将平面拟合到这些点,并计算该平面的法向单位矢量。
听起来很简单,但最好先考虑平面拟合算法。搜索SO和其他各种网站,我看到了使用最小二乘法,单值分解,特征向量/值等的方法。
虽然我不完全理解数学,但我已经能够使各种片段/示例起作用。我遇到的问题是,我对每种方法都有不同的答案。我期待各种答案是相似的(不完全),但它们似乎有很大的不同。也许有些方法不适合我的数据,但不确定为什么我会得到不同的结果。有什么想法吗?
以下是代码的 更新输出 :
LTSQ: [ -8.10792259e-17 7.07106781e-01 -7.07106781e-01]
SVD: [ 0. 0.70710678 -0.70710678]
Eigen: [ 0. 0.70710678 -0.70710678]
Solve: [ 0. 0.70710678 0.70710678]
Optim: [ -1.56069661e-09 7.07106781e-01 7.07106782e-01]
以下代码实现了五种不同的方法来计算平面的曲面法线。算法/代码来自互联网上的各种论坛。
import numpy as np
import scipy.optimize
def fitPLaneLTSQ(XYZ):
# Fits a plane to a point cloud,
# Where Z = aX + bY + c ----Eqn #1
# Rearanging Eqn1: aX + bY -Z +c =0
# Gives normal (a,b,-1)
# Normal = (a,b,-1)
[rows,cols] = XYZ.shape
G = np.ones((rows,3))
G[:,0] = XYZ[:,0] #X
G[:,1] = XYZ[:,1] #Y
Z = XYZ[:,2]
(a,b,c),resid,rank,s = np.linalg.lstsq(G,Z)
normal = (a,b,-1)
nn = np.linalg.norm(normal)
normal = normal / nn
return normal
def fitPlaneSVD(XYZ):
[rows,cols] = XYZ.shape
# Set up constraint equations of the form AB = 0,
# where B is a column vector of the plane coefficients
# in the form b(1)*X + b(2)*Y +b(3)*Z + b(4) = 0.
p = (np.ones((rows,1)))
AB = np.hstack([XYZ,p])
[u, d, v] = np.linalg.svd(AB,0)
B = v[3,:]; # Solution is last column of v.
nn = np.linalg.norm(B[0:3])
B = B / nn
return B[0:3]
def fitPlaneEigen(XYZ):
# Works, in this case but don't understand!
average=sum(XYZ)/XYZ.shape[0]
covariant=np.cov(XYZ - average)
eigenvalues,eigenvectors = np.linalg.eig(covariant)
want_max = eigenvectors[:,eigenvalues.argmax()]
(c,a,b) = want_max[3:6] # Do not understand! Why 3:6? Why (c,a,b)?
normal = np.array([a,b,c])
nn = np.linalg.norm(normal)
return normal / nn
def fitPlaneSolve(XYZ):
X = XYZ[:,0]
Y = XYZ[:,1]
Z = XYZ[:,2]
npts = len(X)
A = np.array([ [sum(X*X), sum(X*Y), sum(X)],
[sum(X*Y), sum(Y*Y), sum(Y)],
[sum(X), sum(Y), npts] ])
B = np.array([ [sum(X*Z), sum(Y*Z), sum(Z)] ])
normal = np.linalg.solve(A,B.T)
nn = np.linalg.norm(normal)
normal = normal / nn
return normal.ravel()
def fitPlaneOptimize(XYZ):
def residiuals(parameter,f,x,y):
return [(f[i] - model(parameter,x[i],y[i])) for i in range(len(f))]
def model(parameter, x, y):
a, b, c = parameter
return a*x + b*y + c
X = XYZ[:,0]
Y = XYZ[:,1]
Z = XYZ[:,2]
p0 = [1., 1.,1.] # initial guess
result = scipy.optimize.leastsq(residiuals, p0, args=(Z,X,Y))[0]
normal = result[0:3]
nn = np.linalg.norm(normal)
normal = normal / nn
return normal
if __name__=="__main__":
XYZ = np.array([
[0,0,1],
[0,1,2],
[0,2,3],
[1,0,1],
[1,1,2],
[1,2,3],
[2,0,1],
[2,1,2],
[2,2,3]
])
print "Solve: ", fitPlaneSolve(XYZ)
print "Optim: ",fitPlaneOptimize(XYZ)
print "SVD: ",fitPlaneSVD(XYZ)
print "LTSQ: ",fitPLaneLTSQ(XYZ)
print "Eigen: ",fitPlaneEigen(XYZ)
答案 0 :(得分:5)
平面的法向量a * x + b * y + c * z = 0,等于(a,b,c)
优化方法找到a和b的值,使得a * x + b * y~z(〜表示近似值)省略在计算中使用c的值。我没有在这台机器上安装numpy,但我希望将模型更改为(a * x + b * y)/ c应该修复此方法。它不会为所有数据集提供相同的结果。此方法将始终假设一个穿过原点的平面。
产生相同的结果。 (不同之处在于机器精度的大小)。
选择了错误的特征向量。对应于最大特征值(lambda = 1.50
)的特征向量是x=[0, sqrt(2)/2, sqrt(2)/2]
,就像在SVD和LTSQ中一样。
我不知道这应该如何运作。
答案 1 :(得分:5)
特征解中平面的法向量是最小特征值的特征向量。一些Eigen实现对特征值和特征向量进行排序,而另一些则没有。因此,在某些实现中,将第一个(或最后一个)特征向量用于正常就足够了。在其他实现中,您必须先对它们进行排序。另一方面,大多数SVD实现提供排序值,因此它是简单的第一个(或最后一个)向量。