将3D点投影到2D平面

45

设A为一个点,它有3D坐标x、y、z,而我想将它们转换成2D坐标:x、y。投影将垂直于由给定法线定义的平面。当法线实际上是其中之一轴时,这个简单情况很容易解决,只需消去一个坐标即可,但其他更可能发生的情况该怎么办?


4
这个问题似乎不适合在这里发布,因为它涉及到几何学(请尝试访问http://math.stackexchange.com)。 - Oliver Charlesworth
2D平面中的(0,0)点在哪里定义?这个点是离3D原点最近的点吗?如何定义平面的x和y轴方向? - John Alexiou
2个回答

44

如果您有目标点P及其坐标为r_P = (x,y,z),并且有一个法向量为n=(nx,ny,nz)的平面,您需要在该平面上定义一个原点以及两个正交方向xy。例如,如果您的原点位于r_O = (ox, oy, oz)处,并且平面内的两个坐标轴由e_1 = (ex_1,ey_1,ez_1)e_2 = (ex_2,ey_2,ez_2)定义,则正交性概念表述为:Dot(n,e_1)=0Dot(n,e_2)=0Dot(e_1,e_2)=0(即向量点积)。请注意,所有方向向量都应该被规范化(模长应该为一)。

您的目标点P必须满足以下等式:

r_P = r_O + t_1*e_1 + t_2*e_2 + s*n

其中t_1t_2是沿着e_1e_2的二维坐标,s是平面与点之间的正常分离(距离)。

这些标量通过投影找到:

s = Dot(n, r_P-r_O)
t_1 = Dot(e_1, r_P-r_O)    
t_2 = Dot(e_2, r_P-r_O)

以一个平面原点 r_O = (-1,3,1) 和法向量为例:

n = r_O/|r_O| = (-1/√11, 3/√11, 1/√11)

你必须为2D坐标选择正交方向,例如:

e_1 = (1/√2, 0 ,1/√2)
e_2 = (-3/√22, -2/√22, 3/√22)

使得 Dot(n,e_1) = 0Dot(n,e_2) = 0,以及 Dot(e_1, e_2) = 0

一个点P的二维坐标是 r_P=(1,7,-3)

t_1 = Dot(e_1, r_P-r_O) = ( 1/√2,0,1/√2)·( (1,7,-3)-(-1,3,1) ) =  -√2
t_2 = Dot(e_2, r_P-r_O) = (-3/√22, -2/√22, 3/√22)·( (1,7,-3)-(-1,3,1) ) = -26/√22

并且在垂直于平面方向的分离:

s = Dot(n, r_P-r_O) = 6/√11

非常感谢,我认为你的回答比第一个更好。我自己也在考虑类似的事情,但你为我澄清了一些问题。还有一个问题:如何计算e_1和e_2? - Lighthink
1
请参阅 https://dev59.com/4WTWa4cB1Zd3GeqPAB0G#10703674。 - John Alexiou
或者,您可以根据从三维原点(投影到平面上)的径向和切向矢量,或通过平面与XYYZZX平面的交点来定义您的方向。 - John Alexiou
定义法线向量的正交向量有无数种方法。这应该在平面被定义时决定,而不是在测量点时决定。 - John Alexiou
这些向量不应该是正交的,而应该是_标准正交_的吗? - polkovnikov.ph
哎呀,我从来没有提到过“e_1”和“e_n”应该被标准化。我的错。 - John Alexiou

10

找出 A 在法向量上的投影。然后从A中减去该投影。剩下的部分就是A在正交平面上的投影。

单位法向量n的投影可以通过以下公式计算:

(A · n) n

如果 A = (x, y, z),单位法向量为 n = (nx, ny, nz),那么 A 在 n 上的投影是
(x*nx + y*ny + z*nz) n

因此,A在正交平面上的投影为。
A - (A · n) n
= (x, y, z) - (x*nx + y*ny + z*nz) (nx, ny, nz)

例如,如果A = (1,2,3),n是方向为(4,5,6)的单位法向量,则:
In [12]: A
Out[12]: array([1, 2, 3])
In [17]: d
Out[17]: array([4, 5, 6])

In [20]: n = d/sqrt(4*4 + 5*5 + 6*6)   # make n a unit vector
In [13]: n
Out[13]: array([ 0.45584231,  0.56980288,  0.68376346])

所以A在正交平面上的投影是
In [15]: A - np.dot(A,n)*n
Out[15]: array([-0.66233766, -0.07792208,  0.50649351])

如何找到二维坐标:
您需要在正交平面上定义一个二维坐标系。换句话说,您需要定义x轴y轴的位置。例如,您可以将x轴定义为(1,0,0)在正交平面上的投影(使用上面显示的计算)。这将适用于除了(1,0,0)与平面垂直的退化情况。
一旦您有了xy轴方向的单位向量,那么您可以直接将A投影到xy上。这些向量的大小就是二维坐标。
例如,这是(1,0,0)在平面上的投影。我们将其视为x轴方向:
In [42]: x = np.array([1,0,0])    
In [45]: x = x - np.dot(x, n) * n
In [52]: x /= sqrt((x**2).sum())   # make x a unit vector    
In [53]: x
Out[53]: array([ 0.89006056, -0.29182313, -0.35018776])

在这里,我们计算y轴的方向:y轴方向必须垂直于法线方向nx。因此,我们可以定义ynx叉积

In [68]: y = np.cross(n, x)

In [69]: y
Out[69]: array([ -2.77555756e-17,   7.68221280e-01,  -6.40184400e-01])

所以这里是平面上 A 的坐标:

In [70]: np.dot(A, x), np.dot(A, y)
Out[70]: (-0.74414898890755965, -0.38411063979868798)

谢谢您的回答,但是现在我该如何将它们转换为二维坐标呢?我的意思是去掉z坐标,就像将其投影到屏幕上一样。 - Lighthink
我认为你把事情复杂化了。你只需要使用点积来获取投影距离(坐标)。 - John Alexiou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接