三点的法向量

10

嘿,数学狂人们,我有一个问题让我纠结了一段时间,这是为了一个个人项目。

我有三个点:红、绿、蓝。它们位于硬纸板上,红点在左下角(0,0),蓝点在右下角(1,0),绿点在左上角。想象一下向后退并从一个角度拍摄卡片的照片。如果你要在图片中找到每个点的中心(假设单位为像素),你将如何找到图片中卡片表面(相对于相机)的法线向量?

现在我已经解决了这个问题:

  1. 这些点(在“实际生活”中)总是成直角。在图片中,只有当相机沿着“轴”(轴是由红色和蓝色或红色和绿色点创建的线)围绕红色点旋转时,它们才会成直角。
  2. 卡片只有一侧有点。因此,你知道你永远不会看到它的背面。
  3. 卡片与相机的距离是无关紧要的。如果我知道每个点的深度,这将变得简单得多(仅是简单的叉积,对吗?)。
  4. 卡片的旋转与我所寻找的东西无关。在我试图理解这个问题的过程中,旋转可以在最后通过法向量的帮助下找到。旋转是否是查找法向量的一部分或结果对我来说是未知的。

希望有人能帮我解决这个问题或者是数学天才。我有两个朋友在帮我,但我们到目前为止都没有成功。


你提到了圆圈和点......抱歉,我没看出两者之间的关系。 - Hamish Grubijan
让我问一下,我是否理解正确? 所以您有一些绑定在纸板上的二维笛卡尔坐标,绑定在车道平面上的二维笛卡尔坐标,并且需要将它们转换为三维笛卡尔坐标? - Maciek Sawicki
为了澄清这个问题: 您将针垂直放置在代表卡片法向量的纸板上。 您拍摄卡片的照片。 您正在测量中心点的位置(在角度的情况下,它们是椭圆),并从位置的输入(以及知道点通常位于哪里的知识)中获取卡片的法向量?如果您想要这个,不,卡片距离无关紧要(但对于更远的距离可能差异可以忽略不计)。 - Thorsten S.
Ipthnc:抱歉,改成只有“点”。实际上,它们是我找到中心点的“颜色斑点”。Maciek:纸板实际上是一个被投射到另一个平面上的平面。我的目标是根据“投影”找到纸板平面的法向量。Thorsten:没错,但我不明白距离如何相关。无论纸板“平面”是否靠近相机或距离相机10米,法向量都是相同的。但除此之外,你已经理解了我想要做的事情。 - mattbasta
4
不应该将此设为社区wiki。 - BlueRaja - Danny Pflughoeft
6个回答

16

我在旧版本的MathCAD中解决了它:

alt text

编辑:MathCAD截图中的措辞错误:“已知:gb相互垂直

在MathCAD中,我忘记了执行叉积的最后一步,我将从早期答案中复制并粘贴它在这里:

Now we've solved for the X-Y-Z of the translated g and b points, your original question wanted the normal of the plane.

If cross g x b, we'll get the vector normal to both:

        | u1  u2  u3 |
g x b = | g1  g2  g3 |
        | b1  b2  b3 |  

      = (g2b3 - b2g3)u1 + (b1g3 - b3g1)u2 + (g1b2 - b1g2)u3

All the values are known, plug them in (i won't write out the version with g3 and b3 substituted in, since it's just too long and ugly to be helpful.

但实际上,我认为您需要通过数值方法来解决它,调整gzbz以最好地适应以下条件:

g · b = 0

|g| = |b|

因为像素并不是代数上完美的。

示例

使用阿波罗13号宇航员在LEM中调整指令模块的方形氢氧化锂罐之一的图片,我找到了角落:

alt text

使用它们作为我的X-Y平面基础:

alt text

我用Photoshop记录了像素位置,其中正X指向右侧,正Y指向下方(保持Z的右手定则“进入”图片):

g = (79.5,-48.5,gz)

b = (-110.8,-62.8,bz)

将这两个起始公式输入Excel,并使用分析工具包通过调整gzbz来“最小化”误差,得出了两个Z值:

g = (79.5,-48.5,102.5)

b = (-110.8,-62.8,56.2)

这样就可以计算其他有趣的值。 gb在像素中的长度:

|g| = 138.5

|b| = 139.2

法向量:

g x b = (3710,-15827,-10366)

单位法向量(长度为1):

uN = (0.1925, -0.8209, -0.5377)

将法线按与gb相同的长度(138.9像素)进行缩放:

法线=(26.7,-114.0,-74.7)

现在我有了与gb相同长度的法线,我将它们绘制在同一张图片上:

alt text

我觉得你将会面临一个新问题:由相机镜头引入的畸变。三个点不完美地投影到二维照片平面上。有一种球形畸变使得直线不再直,等长线段不再等长,法线也稍微偏离正常。
微软研究院有一种算法可以纠正相机的畸变: 一种灵活的相机校准新技术 但这超出了我的能力:
我们提出了一种灵活的新技术来轻松校准相机。它非常适合没有专业3D几何或计算机视觉知识的使用。该技术只需要相机观察到在至少两个不同方向上显示的平面图案。可以自由移动相机或平面图案。运动不需要知道。径向镜头畸变被建模。所提出的程序由闭式解决方案组成,然后进行基于最大似然标准的非线性细化。已经使用计算机模拟和实际数据来测试所提出的技术,并获得了非常好的结果。与使用两个或三个正交平面等昂贵设备的传统技术相比,所提出的技术易于使用和灵活。它将3D计算机视觉从实验室环境推进到实际使用中。
他们有一个样本图像,您可以看到畸变:

alt text
(来源: microsoft.com)

注意

  • 你不知道你看到的是纸板的“顶部”还是“底部”,所以法向量可能会垂直镜像(即z = -z)

更新

一个人在推导代数公式时发现了一个错误。修正后的公式我认为没有简单的闭合形式。这并不太糟糕,因为它无法完全解决;但可以通过数值方法。

这是Excel中的屏幕截图,我从两个已知规则开始:

g · b = 0

|g| = |b|

将第二个规则写成差异(“错误”数量),然后将两者相加并使用该值作为数字,使Excel的求解器最小化

alt text

这意味着你需要编写自己的数值迭代求解器。我正在看着我大学的《工程数值方法》教材;我知道它包含用于解决没有简单闭式形式的递归方程的算法。

@BlueRaja,Matt最初只想要一个垂直于平面的向量。因此,r、g、b的“深度”并不重要,因为简单的平移不会改变平面或其法线的方向。此外,我认为应该保留旧答案,因为它包含了一些有用的思考过程,而且由于你指出了“单位长度”的问题,原始答案可以展示最终答案的演变。 - Ian Boyd
此外,如果我们知道“单位”在像素上的长度,那么另一个答案也可以应用。因此,也许有人会发现它有用。 - Ian Boyd
@Ian:我知道深度并不重要,这就是为什么我给你的正确答案加了+1 :) 我只是强调了这一点。然而,关于旧答案:由于任何一组像素的“长度”取决于物体的深度,任何深度发生变化的物体(即相对于相机的角度)都不会有一个一致的单位/像素,因此旧方程仍然没有太大帮助。 - BlueRaja - Danny Pflughoeft
否则,我认为这是一个很棒的答案(尤其是图片),谢谢! - BlueRaja - Danny Pflughoeft
@David Heffernan 确实如此;但是人们不应该接受那些没有回答问题的答案 - 无论付出了多少努力。 - Ian Boyd
显示剩余12条评论

2
根据您的描述,您有三个点p1p2p3来定义一个平面,您想要找到该平面的法向量。
将这些点表示为从原点出发的向量,法向量的方程将会是
n = (p2 - p1)x(p3 - p1)
(其中 x 是两个向量的叉积)
如果您希望该向量从卡片的正面指向外部,那么可以根据右手定则设置如下:
p1 = 红色(左下角)点
p2 = 蓝色(右下角)点
p3 = 绿色(左上角)点

问题在于我有的点不是三维的,而是二维的。我需要使用2D点来确定原始平面的法向量。例如,如果我拍摄纸板的照片并规范化图片中纸板的坐标,使得红点位于(0,0),即裁剪图片,并知道这些点代表"现实生活"中形成一个等腰直角三角形的三个点,那么我如何确定纸板"现实生活"中的法向量。 - mattbasta
3
对于每个大于等于一的整数m,所有n维点也是n+m维点;只需将额外的分量设置为0即可。 - Ignacio Vazquez-Abrams
1
有一些仿射映射(http://en.wikipedia.org/wiki/Affine_transformation),它将你的纸板平面上的点映射到你的观察平面上的点。假设你知道纸板的宽度和高度,你可以创建一组线性方程来找到它。每个方程看起来像 m * (x,y) + b = p,其中 m 是一个线性变换(3x2 矩阵),(x,y) 是原始纸板平面上点的坐标,b 是偏移量(一个 3x1 矩阵),而 p 是其中一个点。这给出了 9 个方程和 9 个未知数,可以求解。 - BlueRaja - Danny Pflughoeft
4
如果这样做,计算出来的“法向量”始终沿着z轴,这是错误的。 - dmckee --- ex-moderator kitten

2
@Ian Boyd...我喜欢你的解释,但是在第二步时卡住了,当你说要解出bz时,你的答案中仍然有bz,我认为你的答案中不应该有bz...

bz应该是+/- gx2 + gy2 + gz2 - bx2 - by2的平方根

我自己尝试后发现,将bz代入你求解gz的第一个方程式中非常困难,因为当代入bz时,你会得到:

gz = -(gxbx + gyby) / sqrt( gx2 + gy2 + gz2 - bx2 - by2 )

这部分让问题变得困难的是平方根中有gz,所以你必须将其分离并将gz组合在一起,然后解出gz。我尝试了,但我认为我的解法不正确,因为当我编写程序计算gz时,我使用了你的gxgy值来查看我的答案是否与你的相符,但结果并不相同。

所以我想知道你能否帮助我,因为我真的需要在我的一个项目中让它工作。谢谢!


你说得对,我确实有一个错误。我已经纠正了它,并且我也遇到了gzgz的形式递归问题。我采用了简单的方法,在Excel中使用数值方法数值求解了gzbz。我创建了两个起始假设:1)g和b是正常的:g*b=0 2)g和b具有相同的长度(|g|=|b|)。然后我让Excel最小化满足这些条件的误差,并解决了g=(79.5,-48.5,103.3) b=(-110.8,-62.8,55.8)。考虑到公式是错误的,结果非常接近原始答案。这是你得到的值吗? - Ian Boyd
不,我无法获得这些值,因为我用的是C++编写的代码,所以当我尝试解决g_z时,使用的公式是:g_z = -(g_xb_x + g_yb_y) / sqrt( g_x^2 + g_y^2 + g_z^2 - b_x^2 - b_y^2 )...你可以看到在这个公式中,我仍然需要g_z的值才能解决g_z...而且通常情况下,在方程中包含未知变量时,你永远无法获得未知变量的值...所以我从未能够获得正确的g_z值。 - Samir Silbak
但我可以像你一样编写一个递归函数,在我的程序中暴力解决这些值,所以我可能会这样做,因为解决g_z太困难了。 - Samir Silbak

1

这里只是我的一些脑补。

你需要的是表观比率 RB/RG [+], 表观角度 BRG 和 (比如说) RB 相对于屏幕坐标 y 轴的角度 (我有没有遗漏什么?)。你需要得出标准法向量的分量,我相信只有两个独立的值 (但如果卡牌是透明的,则前后方向存在歧义)。[++]

所以我猜这是可能的...

从这里开始,我假设 RB 的视角始终为 0,并且我们可以稍后围绕 z 轴旋转最终解决方案。

首先将卡片放置在与视平面平行的位置,并以“自然”的方式定位 (即尊重你的上下和左右分配)。我们可以通过绕初始 x 轴旋转 theta (-\pi/2 < \theta < \pi/2) ,然后绕初始 y 轴旋转 phi (-\pi/2 < \phi < \pi/2) 来达到卡片的所有有趣位置。请注意,我们保留了 RB 向量的表观方向。

下一步是计算表观比率和表观角度,以\theta\phi为变量,并反转结果。

对于R_i绕轴i的原始旋转矩阵,法线将为R_y(\phi)R_x(\theta)(0, 0, 1)

[+] 绝对长度不重要,因为这只告诉您到卡片的距离。

[++] 另一个假设:卡片到视平面的距离远大于卡片的大小。

[+++] 这里使用的从三维空间到视平面的投影很重要。这是难点,但除非您说明使用的投影方式,否则我们无法为您完成。如果您使用的是真实相机,则这是透视投影,并且在任何关于3D图形的书中都有介绍。


0

对的,法向量不会因为距离而改变,但是纸板在图片上的投影会因为距离而改变(简单来说:如果你有一张小纸板,什么都不会改变。如果你有一张长宽各为1英里的纸板,并将其旋转使得一侧更近,另一侧更远,那么近侧在图片上会被放大,远侧则被缩短。你可以立刻看到一个矩形不再是矩形,而是一个梯形)

对于小角度和相机居中的情况,最准确的方法是测量“正常”图像和中线角度图像之间宽度/高度的比率(因为它们没有扭曲)。

我们定义x为从左到右,y为从下到上,z为从远到近。

然后
x = arcsin(measuredWidth/normWidth) 红蓝
y = arcsin(measuredHeight/normHeight) 红绿
z = sqrt(1.0-x^2-y^2)

我明天会计算一个更精确的解决方案,但现在我太累了...


我觉得这只有在你知道卡片范围的情况下才能起作用(或者如果你使用平行投影(以及小角度限制),因为你已经假设你知道预期的高度和宽度)。 - dmckee --- ex-moderator kitten
@Thorsten:我已经查看了你的工作,看起来很简单。我正在测试一些Java代码的原型,它会给我这样的点集。我将插入您的公式,并告诉您结果如何。@dmckee:对于这个项目的小规模,可以假定使用平行投影。由此引起的误差对于此实验的目的而言是微不足道的。 - mattbasta

0

您可以使用u、v、n坐标。将视点设置为“眼睛”或“相机”的位置,然后将x、y、z坐标转换为u、v、n坐标。从那里,您可以确定法线,以及透视和可见表面(如果需要则为u'、v'、n')。此外,请记住2D = 3D,其中z = 0。最后,请确保使用齐次坐标。


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