剖析相机矩阵,第1部分:外在/内在分解
因此,您一直在使用新的计算机视觉库,并且已经成功校准了相机...现在如何处理它? 如果您可以拿到相机的位置或找出它的视场,它将大有用处。 您可以打开可信赖的Hartley和Zisserman副本,它告诉您如何将相机分解为内在和外在的矩阵-太好了! 但是当您查看结果时,并不完全正确。 也许您的旋转矩阵的行列式为-1,从而导致矩阵到四元数的函数变为barf。 也许您的焦距为负数,并且您不明白为什么。 也许您的平移向量( translation vector)错误地声称世界起源于相机的背后。 或最糟糕的是,一切看起来都很好,但是当您将其插入OpenGL时,您什么都看不到。
今天,我们将介绍将摄影机矩阵分解为内部矩阵和外部矩阵的过程,并尝试解决可能因不同的坐标约定而出现的问题。 在后面的文章中,我们将更详细地研究内在(intrinsic)和外在( extrinsic)矩阵,并且我将介绍如何将它们转换为OpenGL可用的形式。
这是该系列的第二篇文章“透视相机,互动之旅”。 要阅读本系列的其他文章,请转到简介页面。
序幕:获取相机矩阵(Prologue: Getting a Camera Matrix)
我假设您已经事先获得了相机矩阵,但是如果您需要相机校准方面的帮助,建议您进入Matlab的相机校准工具箱。 OpenCV似乎还具有一些有用的例程,可以根据一系列棋盘图像自动进行相机校准,尽管我个人并没有使用它们。 像往常一样,Hartley和Zisserman's对这个话题有很好的处理。
裁剪:相机分解(Cut 'em Up: Camera Decomposition)
首先,我们假设您的相机矩阵为3x4,它将矩阵3D世界坐标转换为矩阵2D图像坐标。 在Hartley和Zisserman之后,我们将矩阵表示为P,有时使用块形式会很有用:
其中M是3x3的可逆矩阵,C是代表摄像机在世界坐标中位置的列向量。 一些校准软件提供了4x4矩阵,该矩阵增加了额外的一行以保留z坐标。 在这种情况下,只需删除第三行即可获得3x4矩阵。
相机矩阵本身可用于将3D点投影到2D中,但是它有一些缺点:
- 它不会告诉您相机的姿势。
- 它不会告诉您相机的内部几何形状。
- 镜面照明是不可能的,因为您无法在相机坐标中获得表面法线。
为了解决这些缺点,可以将相机矩阵分解为两个矩阵的乘积:本征矩阵K和非本征矩阵[R | -RC]:
矩阵K是一个3x3的上三角矩阵,用于描述相机的内部参数(如焦距)。 R是一个3x3旋转矩阵,其列是相机参考系中世界轴的方向。 向量C是世界坐标中的相机中心; 向量t = -RC给出世界原点在相机坐标中的位置。 我们将在后面的文章中更详细地研究每个矩阵,今天我们将讨论如何从P中获取它们。
恢复相机中心C很简单。 请注意,P的最后一列是-MC,因此只需将其乘以-M-1。
在RQ-ze我之前...(Before You RQ-ze Me... )
为了恢复R和K,我们注意到R由于是旋转矩阵而正交,而K是上三角。 通过使用RQ分解,可以将任何满秩矩阵分解为上三角矩阵和正交矩阵的乘积。 不幸的是,RQ分解在包括Matlab在内的许多库中均不可用,但幸运的是,它通常是QR分解的朋友。 Solem的愿景博客中有一篇不错的文章,其中介绍了一些矩阵翻转来实现缺少的功能的方法。 这是Matlab版本(感谢Solem让我重新发布了它!):
function [R Q] = rq(M) [Q,R] = qr(flipud(M)') R = flipud(R'); R = fliplr(R); Q = Q'; Q = flipud(Q);
简单!
我看到了双重...四个分解!(I'm seeing double... FOUR decompositions!)
只有一个问题:RQ分解的结果不是唯一的。 要看到这一点,请尝试取反K的任何列和R的相应行:生成的相机矩阵不变。 大多数人只是简单地强迫K的对角元素为正,如果满足两个条件,这是正确的方法:
- 图像的X / Y轴指向与相机X / Y轴相同的方向。
- 您的相机朝着正Z方向看。
Solem的博客以三行代码优雅地为我们提供了正对角线条目:
# make diagonal of K positive T = diag(sign(diag(K))); K = K * T; R = T * R; # (T is its own inverse)
实际上,相机和图像轴不会一致,并且K的对角元素不应为正。 强迫他们积极可能会导致不良后果,包括:
- 物体出现在相机的反面。
- 旋转矩阵的行列式为-1而不是1。
- 镜面照明不正确。
- 由于w坐标为负,因此无法呈现可见的几何图形。
在这种情况下,您需要进行一些修复。 首先确保您的相机和世界坐标都具有相同的惯用性。 然后记下校准相机时使用的轴约定。 图像y轴指向哪个方向,向上还是向下? X轴? 现在考虑相机的坐标轴。 相机是否向下看Z轴的负向(OpenGL风格)? 正Z(例如Hartley和Zisserman)? x轴指向左还是右? y轴? 好吧,好吧,你明白了。
从全正对角线开始,请遵循以下四个步骤:
- 如果图像x轴和相机x轴指向相反的方向,则取反K的第一列和R的第一行。
- 如果图像y轴和相机y轴指向相反的方向,则取反K的第二列和R的第二行。
- 如果相机向下看Z轴的负方向,则取反K的第三列。保持R不变。 编辑:也取反R的第三列。
- 如果R的行列式为-1,则取反。
请注意,这些步骤中的每一个都不会更改组合的相机矩阵。 最后一步等效于将整个相机矩阵P乘以-1。 由于P在齐次坐标上运算,因此将其乘以任何常数均无效。
关于第3步,Hartley和Zisserman的摄影机朝Z轴正方向看,但是在某些实际系统(例如OpenGL)中,摄影机朝Z轴负方向看。 这样可以使x和y轴指向右上方,从而形成一个坐标系,在仍然右手使用时感觉很自然。 上面的步骤3通过在z为负时使w为正来对此进行校正。 您可能会对K3,3为负这一事实不屑一顾,但是OpenGL要求使用此参数进行适当的裁剪。 我们将在以后的文章中进一步讨论OpenGL。
您可以通过检查矢量t = -RC再次检查结果,t = -RC是世界原点在相机坐标中的位置。 如果一切正确,则tx,ty,tz的符号应反映出世界原点出现在相机中的位置(中心的左/右,中心的上方/下方,前后的相机)。
谁拍了我的斧头?(Who Flipped my Axes?)
到目前为止,我们对2D坐标约定的讨论都涉及到校准期间使用的坐标。 如果您的应用程序使用其他2D坐标约定,则需要使用2D平移和反射来变换K。
例如,考虑一个经过校准的相机矩阵,其原点在左上角,y轴指向下方,但您更喜欢左下角的原点,y轴指向上方。 要进行转换,您将首先取反图像的y坐标,然后按图像高度h向上平移。 所得的固有矩阵K'由下式给出:
Summary
无论使用哪种坐标约定,以上步骤都应为您提供正确的摄像机分解。 在我自己的研究中,我已经在一些场景中对其进行了测试,并且到目前为止,它一直有效。 当然,如果您对这种方法有任何疑问,我很想听听他们的看法,只需在评论中留言或给我发电子邮件。
在下一篇文章中,我们将通过交互式演示更详细地研究外部矩阵。