与消失点和地平线相关的 3D 对象的变换

hyp*_*one 5 3d graphics perspectivecamera perspective camera-calibration

我正在尝试从图片的消失点和地平线开始计算 3D 对象的精确预期变换。

起始图像

我想要的是,固定图片的消失点和水平线,我想根据我从图片开始设置的消失点和水平线旋转和倾斜3D对象

最终结果低于我的预期。

结束图像

我怎样才能得到这个结果?

我可以使用什么样的转换?

在这个视频中可以看到我想要的结果。

https://www.youtube.com/watch?v=EsSerR-AjEk

Spe*_*tre 4

这远不是我喜欢的,所以要带着极端的偏见和遥远的解决方案来处理,只是一些起点提示......

首先,我们需要定义一些约束/假设才能使其发挥作用。

  • 用户选择代表 2 个垂直平面的 4 条线,这 2 个QUAD平面具有相同的高度和高度。此外,物体高度轴几乎与相机 y 轴相同(不是旋转图像)。
  • 透视以图像中心为中心,因此中心像素代表视图方向
  • 像素是正方形

所以你想要获得的是一个 4x4 的齐次矩阵,它将一些全局 3D 坐标转换为 2D 图像坐标 + 透视除法。

|x'|    | Xx Yx Zx Ox |  |x|
|y'| =  | Xy Yy Zy Oy | .|y|
|z'|    | Xz Yz Zz Oz |  |z|
|w'|    | a  b  c  1  |  |1| 
Run Code Online (Sandbox Code Playgroud)

其中(x,y,z)代表某个 3D 位置,(x'/z',y'/z')代表屏幕(图像)上的 2D 位置。为了简单起见,我们假设图像中心是(0,0)

要构建透视矩阵,您需要知道相机的FOV角度及其焦距znear。没有它,您可以根据图像上的已知内容来估计它......

另一种选择是拟合该矩阵直到点匹配。但由于有 15 个未知数,速度会非常慢(即使许多参数是相关的并且可以根据其他参数计算)。

[完整重新编辑] 简单的 C++ 方法示例

  1. 两个四人组

    我将从计算每个平面的四边形开始:

    四边形和重复

    为了简化以后的代码,这些点应该有特定的顺序。我以编程方式对它们进行排序,使它们逆时针旋转,并且每个四边形的第一个点位于右上角。第一个 QUAD 位于右侧(代表建筑物的 Z 轴或 YZ 平面),第二个位于左侧(代表建筑物的 X 轴或 XY 平面)。

    我还计算每个四边形的中点(平均点),并按屏幕 x 轴该点与排序点之间的角度对点进行排序。之后,需要进行位置校正(如果屏幕 x 轴与四边形水平轴碰撞,则将所有点移动 1),因此四边形的第一个点是右上角。

    现在我们需要将线变成 QUAD。为此,我们需要建筑物 y 轴方向...首先,我从 4 条线中的每条线投射 2D 法线,并将它们平均在一起。请注意,它们应该都在同一方向......因此,如果添加法线具有负点积,则在添加之前将平均值取反。该平均法线是 XY 平面上的 UP 矢量投影。

    法线和交点

    但后来我改变了这一点,我计算了相应的左右 QUAD 水平线之间的 2 个交点(获得 QUAD 之间的建筑物边缘的 UP 向量/方向)。事实证明,这更准确,也更容易计算。

    现在,要将线转换为四边形,只需找到线与从每个平面之一线的端点投射的法线之间的交点即可。之后,交叉点将与四角完全对齐,因此我们可以从现在开始使用它......

  2. 看法

    由于我们的建筑物很可能是一个平面之间具有直角的盒子,因此我们的 2 个四边形在 3D 中也应该彼此垂直。我们可以使用这个...就好像我们将它们的消失点与中点连接起来一样,3D 中的线也应该是 90 度直角。所以我们可以直接从中获得FOVx角度......

    消失点XZ 消失点 XY

    FOVx因此和之间的比率90.0deg与屏幕 x 分辨率和 2 个消失点距离(以像素为单位)之间的比率相同......因此:

    FOVx = 90.0*deg * image_x_resolution / intersections_x_distance
    
    Run Code Online (Sandbox Code Playgroud)

    我们也知道屏幕分辨率也znear可以直接计算。例如我使用<-1,+1>OpenGL 中的屏幕坐标:

    znear = 1.0/tan(0.5*FOVx)
    
    Run Code Online (Sandbox Code Playgroud)

    粗略地说,这会影响结果的整体规模,所以不要指望米单位......

    应该zfar明智地选择,以便建筑物实际上位于视锥体中。例如:

    zfar = 1000.0*znear
    
    Run Code Online (Sandbox Code Playgroud)

    它只影响相对于znear...的视图深度,但不会影响透视本身。

  3. 构建 3D 坐标

    QUAD 垂直线尺寸为我们提供了取决于深度的比例。这可以用来计算我们拥有的每个点的 Z 坐标......但为此我们需要知道 QUAD 的原始高度。对我们来说幸运的是,QUAD 到 3D 的未投影 2D 屏幕坐标应该形成直角。因此,如果我们使用 3 个点(四边形中点和它们之间的边的中点)并对未投影线方向进行点积,结果应该为零。所以我们得到了 4 个方程和 4 个可代数求解的未知数......

    深度关系如下:

    scale(z) = znear/z
    
    Run Code Online (Sandbox Code Playgroud)

    因此,如果我们计算我们所讨论的点所在位置的 QUAD 高度,我们就可以获得相对于原始 QUAD 高度的比例l...因为我们有 3 个点:

    z0 = znear*l0/l
    z1 = znear*l1/l
    z2 = znear*l2/l
    dot(pnt1-pnt0,pnt2-pnt0)=0
    
    Run Code Online (Sandbox Code Playgroud)

    其中未投影点:pnt0(x0,y0,z0)是 QUAD 之间的边的中点, 和pnt1(x1,y1,z1)pnt2(x2,y2,z2)QUAD 的中点。这l0,l1,l2是相应的高度尺寸。所以这里唯一的未知数是z0,z1,z2,l......

    顺便说一句,这些未投影的点直接为我们提供了 2 个基向量和建筑物坐标系的位置。所以我们也可以组成它的矩阵...第三个也可以不投影或使用叉积代替...

    这里是一个带有反向透视叠加的调试渲染立方体:

    调试立方体

正如您所看到的,拟合并不完美,这是由于我的 3D 视图中存在一些与查看窗口纵横比相关的错误。如果窗口是方形的(不是图像,只是 GL 窗口),那么拟合是完美的。如果我将纵横比添加到 3D 视图(比例),则拟合是完美的,但坐标系的基本向量在视觉上的大小不同...需要更多地考虑它来修复...它很可能是一些愚蠢的简单的事情根本与反转视角无关......这里是方形视图屏幕截图:

完美契合

这是我的实际 C++/GL 代码...但要注意我正在使用渲染引擎中的一些东西(如向量数学等...)

|x'|    | Xx Yx Zx Ox |  |x|
|y'| =  | Xy Yy Zy Oy | .|y|
|z'|    | Xz Yz Zz Oz |  |z|
|w'|    | a  b  c  1  |  |1| 
Run Code Online (Sandbox Code Playgroud)

这里重要的东西是计算函数,它将 QUAD 点反转为透视参数/矩阵和坐标系...其余的只是用于渲染/调整大小和鼠标处理...