NeRF神经辐射场中关于光线从世界坐标系转换为NDC坐标系 Representing Scenes as Neural Radiance Fields for View Synthesis

本文旨在回复一个粉丝的关于坐标系变换编程提问,并结合下面的一个代码进行解释(完整代码参考我前面的文章)

补充:[希望那个同学可以看见,因为公众号对话10天未互动默认无法再回复消息了。 ]

def get_ndc_rays(H, W, focal, near, rays_o, rays_d):
    """
    Transform rays from world coordinate to NDC.
    NDC: Space such that the canvas is a cube with sides [-1, 1] in each axis.
    For detailed derivation, please see:
    http://www.songho.ca/opengl/gl_projectionmatrix.html
    https://github.com/bmild/nerf/files/4451808/ndc_derivation.pdf

    In practice, use NDC "if and only if" the scene is unbounded (has a large depth).
    See https://github.com/bmild/nerf/issues/18

    Inputs:
        H, W, focal: image height, width and focal length
        near: (N_rays) or float, the depths of the near plane
        rays_o: (N_rays, 3), the origin of the rays in world coordinate
        rays_d: (N_rays, 3), the direction of the rays in world coordinate

    Outputs:
        rays_o: (N_rays, 3), the origin of the rays in NDC
        rays_d: (N_rays, 3), the direction of the rays in NDC

    """
    # 将光线从世界坐标转换为NDC。
    #
    # NDC: 这样的空间,画布是一个立方体,每个轴的边都是[- 1, 1]。
    #
    # 有关详细的推导,请参阅:
    #
    # http: // www.songho.ca / opengl / gl_projectionmatrix.html
    #
    #  M
    #
    # 在实践中,使用NDC“当且仅当”场景是无界的(有很大的深度)。
    #
    # 参见https: // github.com / bmild / nerf / issues / 18
    #
    # 输入:
    #
    # H、W、焦距: 图像高度、宽度和焦距
    #
    # near: (N_rays)
    # 或float,近平面的深度
    #
    # rays_o: (N_rays, 3),世界坐标中射线的原点
    #
    # rays_d: (N_rays, 3),射线的世界坐标方向
    #
    # 输出:
    #
    # rays_o: (N_rays, 3), NDC中射线的原点
    #
    # rays_d: (N_rays, 3), NDC中的射线方向
    # Shift ray origins to near plane
    t = -(near + rays_o[..., 2]) / rays_d[..., 2]
    rays_o = rays_o + t[..., None] * rays_d

    # Store some intermediate homogeneous results
    ox_oz = rays_o[..., 0] / rays_o[..., 2]
    oy_oz = rays_o[..., 1] / rays_o[..., 2]

    # Projection
    o0 = -1. / (W / (2. * focal)) * ox_oz
    o1 = -1. / (H / (2. * focal)) * oy_oz
    o2 = 1. + 2. * near / rays_o[..., 2]

    d0 = -1. / (W / (2. * focal)) * (rays_d[..., 0] / rays_d[..., 2] - ox_oz)
    d1 = -1. / (H / (2. * focal)) * (rays_d[..., 1] / rays_d[..., 2] - oy_oz)
    d2 = 1 - o2

    rays_o = torch.stack([o0, o1, o2], -1)  # (B, 3)
    rays_d = torch.stack([d0, d1, d2], -1)  # (B, 3)

    return rays_o, rays_d

提问如下:
在这里插入图片描述
回答:现在将进行数学公式的一个推断

START-------------------------START

NDC ray space derivation光线空间推导

齐次坐标的标准 3D 透视投影矩阵看起来像这样:
M = ( n r 0 0 0 0 n t 0 0 0 0 − ( f + n ) f − n − 2 f n f − n 0 0 − 1 0 ) M=\left(\begin{array}{cccc} \frac{n}{r} & 0 & 0 & 0 \\ 0 & \frac{n}{t} & 0 & 0 \\ 0 & 0 & \frac{-(f+n)}{f-n} & \frac{-2 f n}{f-n} \\ 0 & 0 & -1 & 0 \end{array}\right) M= rn0000tn0000fn(f+n)100fn2fn0
n , f n, f n,f 是near 平面和far clipping平面, r r r t t t是右 clipping平面和远 clipping平面,近cliping平面处场景的顶部边界.

1 { }^1 1 (Note 这是在相机朝 -z 方向的观察.)

投影点现在位于标准化设备坐标 (NDC) 空间中,其中原始视锥体已映射到立方体 ( x , y , z , 1 ) ⊤ (x, y, z, 1)^{\top} (x,y,z,1), 先左乘 M \mathrm{M} M 并且除以第4个坐标:

( n r 0 0 0 0 n t 0 0 0 0 − ( f + n ) f − n − 2 f n f − n 0 0 − 1 0 ) ( x y z 1 ) = ( n r x n t y − ( f + n ) f − n z − − 2 f n f − n − z )  project  → ( n r x − z n t y − z ( f + n ) f − n − 2 f n f − n 1 − z ) \begin{aligned} &\left(\begin{array}{cccc} \frac{n}{r} & 0 & 0 & 0 \\ 0 & \frac{n}{t} & 0 & 0 \\ 0 & 0 & \frac{-(f+n)}{f-n} & \frac{-2 f n}{f-n} \\ 0 & 0 & -1 & 0 \end{array}\right)\left(\begin{array}{l} x \\ y \\ z \\ 1 \end{array}\right)=\left(\begin{array}{c} \frac{n}{r} x \\ \frac{n}{t} y \\ \frac{-(f+n)}{f-n} z-\frac{-2 f n}{f-n} \\ -z \end{array}\right) \\ & \text { project } \rightarrow\left(\begin{array}{c} \frac{n}{r} \frac{x}{-z} \\ \frac{n}{t} \frac{y}{-z} \\ \frac{(f+n)}{f-n}-\frac{2 f n}{f-n} \frac{1}{-z} \end{array}\right) \end{aligned} rn0000tn0000fn(f+n)100fn2fn0 xyz1 = rnxtnyfn(f+n)zfn2fnz  project  rnzxtnzyfn(f+n)fn2fnz1
投影点现在位于标准化设备坐标 (NDC) 空间中,其中原始视锥体已映射到立方体 [ − 1 , 1 ] 3 [-1,1]^3 [1,1]3.

我们的目标是利用光线 o + t d \mathbf{o}+t \mathbf{d} o+td 并计算光线的起点 o ′ \mathbf{o}^{\prime} o 以及方向 d ′ \mathbf{d}^{\prime} d
在NDC空间,对于每一个 t t t,都存在一个对应的 t ′ t^{\prime} t 表达为 π ( o + t d ) = o ′ + t ′ d ′ \pi(\mathbf{o}+t \mathbf{d})=\mathbf{o}^{\prime}+t^{\prime} \mathbf{d}^{\prime} π(o+td)=o+td ( π \pi π 是使用上述矩阵的投影).

π 是使用上述矩阵的投影)。 换句话说,投影的原始光线和 NDC 空间光线追踪出相同的点(但不一定以相同的速率)。

我们将投影点重写为 ( a x x / z , a y y / z , a z + b z / z ) ⊤ \left(a_x x / z, a_y y / z, a_z+b_z / z\right)^{\top} (axx/z,ayy/z,az+bz/z) 使其不那么凌乱。现在将写出我们需要满足的所有约束:
( a x o x + t d x o z + t d z a y o y + t d y o z + t d z a z + b z o z + t d z ) = ( o x ′ + t ′ d x ′ o y ′ + t ′ d y ′ o z ′ + t ′ d z ′ ) \left(\begin{array}{c} a_x \frac{o_x+t d_x}{o_z+t d_z} \\ a_y \frac{o_y+t d_y}{o_z+t d_z} \\ a_z+\frac{b_z}{o_z+t d_z} \end{array}\right)=\left(\begin{array}{c} o_x^{\prime}+t^{\prime} d_x^{\prime} \\ o_y^{\prime}+t^{\prime} d_y^{\prime} \\ o_z^{\prime}+t^{\prime} d_z^{\prime} \end{array}\right) axoz+tdzox+tdxayoz+tdzoy+tdyaz+oz+tdzbz = ox+tdxoy+tdyoz+tdz
为了方便起见,我们将决定 t ′ = 0 t^{\prime}=0 t=0 and t = 0 t=0 t=0 应该映射到同一点。 这为我们提供了 NDC 空间 o ′ \mathbf{o}^{\prime} o
o ′ = ( o x ′ o y ′ o z ′ ) = ( a x o x o z a y o y o z a z + b z o − ) = π ( o ) \mathbf{o}^{\prime}=\left(\begin{array}{c} o_x^{\prime} \\ o_y^{\prime} \\ o_z^{\prime} \end{array}\right)=\left(\begin{array}{c} a_x \frac{o_x}{o_z} \\ a_y \frac{o_y}{o_z} \\ a_z+\frac{b_z}{o_{-}} \end{array}\right)=\pi(\mathbf{o}) o= oxoyoz = axozoxayozoyaz+obz =π(o)

这只是original ray 光线起点的投影 π ( o ) \pi(\mathbf{o}) π(o) . 现在我们可以计算 t ′ t^{\prime} t d ′ \mathbf{d}^{\prime} d .
( t ′ d x ′ t ′ d y ′ t ′ d z ′ ) = ( a x o x + t d x o z + t d z − a x o x o z a y o y + t d y o z + t d z − a y o y o z a z + b z o z + t d z − a z − b z o z ) = ( a x o z ( o x + t d x ) − o x ( o z + t d z ) ( o z + t d z ) o z a y o z ( o y + t d y ) − o y ( o z + t d z ) ( o z + t d z ) o z b z o z − ( o z + t d z ) ( o z + t d z ) o z ) = ( a x t d z o z + t d z ( d x d z − o x o z ) a y t d z o z + t d z ( d y d z − o y o z ) − b z t d z o z + t d z 1 o z ) \begin{aligned} \left(\begin{array}{l} t^{\prime} d_x^{\prime} \\ t^{\prime} d_y^{\prime} \\ t^{\prime} d_z^{\prime} \end{array}\right) & =\left(\begin{array}{c} a_x \frac{o_x+t d_x}{o_z+t d_z}-a_x \frac{o_x}{o_z} \\ a_y \frac{o_y+t d_y}{o_z+t d_z}-a_y \frac{o_y}{o_z} \\ a_z+\frac{b_z}{o_z+t d_z}-a_z-\frac{b_z}{o_z} \end{array}\right) \\ & =\left(\begin{array}{c} a_x \frac{o_z\left(o_x+t d_x\right)-o_x\left(o_z+t d_z\right)}{\left(o_z+t d_z\right) o_z} \\ a_y \frac{o_z\left(o_y+t d_y\right)-o_y\left(o_z+t d_z\right)}{\left(o_z+t d_z\right) o_z} \\ b_z \frac{o_z-\left(o_z+t d_z\right)}{\left(o_z+t d_z\right) o_z} \end{array}\right) \\ & =\left(\begin{array}{c} a_x \frac{t d_z}{o_z+t d_z}\left(\frac{d_x}{d_z}-\frac{o_x}{o_z}\right) \\ a_y \frac{t d_z}{o_z+t d_z}\left(\frac{d_y}{d_z}-\frac{o_y}{o_z}\right) \\ -b_z \frac{t d_z}{o_z+t d_z} \frac{1}{o_z} \end{array}\right) \end{aligned} tdxtdytdz = axoz+tdzox+tdxaxozoxayoz+tdzoy+tdyayozoyaz+oz+tdzbzazozbz = ax(oz+tdz)ozoz(ox+tdx)ox(oz+tdz)ay(oz+tdz)ozoz(oy+tdy)oy(oz+tdz)bz(oz+tdz)ozoz(oz+tdz) = axoz+tdztdz(dzdxozox)ayoz+tdztdz(dzdyozoy)bzoz+tdztdzoz1
我们可以分解出一个仅依赖于 t t t 来得到:
t ′ = t d z o z + t d z = 1 − o z o z + t d z d ′ = ( a x ( d x d z − o x o z ) a y ( d y d z − o y o z ) − b z 1 o z ) \begin{aligned} & t^{\prime}=\frac{t d_z}{o_z+t d_z}=1-\frac{o_z}{o_z+t d_z} \\ & \mathbf{d}^{\prime}=\left(\begin{array}{c} a_x\left(\frac{d_x}{d_z}-\frac{o_x}{o_z}\right) \\ a_y\left(\frac{d_y}{d_z}-\frac{o_y}{o_z}\right) \\ -b_z \frac{1}{o_z} \end{array}\right) \end{aligned} t=oz+tdztdz=1oz+tdzozd= ax(dzdxozox)ay(dzdyozoy)bzoz1
请注意,正如我们想要的那样, t ′ = 0 t^{\prime}=0 t=0 t = 0 t=0 t=0. 现在更进一步,我们可以得到 t ′ → 1 t^{\prime} \rightarrow 1 t1 as t → ∞ t \rightarrow \infty t

现在回到原始的投影矩阵,我们看到我们的常数是

a x = − n r a y = − n t a z = f + n f − n b z = 2 f n f − n \begin{aligned} a_x & =-\frac{n}{r} \\ a_y & =-\frac{n}{t} \\ a_z & =\frac{f+n}{f-n} \\ b_z & =\frac{2 f n}{f-n} \end{aligned} axayazbz=rn=tn=fnf+n=fn2fn
通过标准针孔相机数学,我们可以重新参数化为
a x = − f c a m W / 2 a y = − f c a m H / 2 \begin{aligned} & a_x=-\frac{f_{c a m}}{W / 2} \\ & a_y=-\frac{f_{c a m}}{H / 2} \end{aligned} ax=W/2fcamay=H/2fcam
W , H W, H W,H 是图片image的宽和高 , f c a m f_{c a m} fcam 是针孔相机的焦距.

在 NeRF 中,我们假设远场景边界是无穷大(这花费我们很少,因为 NDC 使用 z 维度来表示逆深度,即视差)。 在此限制下,z 常数简化为
a z = 1 b z = 2 n \begin{aligned} & a_z=1 \\ & b_z=2 n \end{aligned} az=1bz=2n
将所有内容组合在一起,我们得到 NeRF 代码中 ndc_rays() 函数中的表达式:
o ′ = ( − f c a m W / 2 o x o z − f c a m H / 2 o y o z 1 + 2 n o z ) d ′ = ( − f c a m W / 2 ( d x d z − o x o z ) − f c a m H / 2 ( d y d z − o y o z ) − 2 n 1 o z ) \begin{aligned} & \mathbf{o}^{\prime}=\left(\begin{array}{c} -\frac{f_{c a m}}{W / 2} \frac{o_x}{o_z} \\ -\frac{f_{c a m}}{H / 2} \frac{o_y}{o_z} \\ 1+\frac{2 n}{o_z} \end{array}\right) \\ & \mathbf{d}^{\prime}=\left(\begin{array}{c} -\frac{f_{c a m}}{W / 2}\left(\frac{d_x}{d_z}-\frac{o_x}{o_z}\right) \\ -\frac{f_{c a m}}{H / 2}\left(\frac{d_y}{d_z}-\frac{o_y}{o_z}\right) \\ -2 n \frac{1}{o_z} \end{array}\right) \end{aligned} o= W/2fcamozoxH/2fcamozoy1+oz2n d= W/2fcam(dzdxozox)H/2fcam(dzdyozoy)2noz1
我们在 NeRF 中使用的最后一个技巧是,我们将 o 移动到光线与近平面的交点处 z = − n z=-n z=n (在此 NDC 转换之前) 通过采取 o n = o + t n d \mathbf{o}_n=\mathbf{o}+t_n \mathbf{d} on=o+tnd for t n = − ( n + o z ) / d z t_n=-\left(n+o_z\right) / d_z tn=(n+oz)/dz.

一旦我们进入 NDC,我们可以简单地从 0 到 1 对 t ′ t^{\prime} t进行线性采样,以获得原始空间中从 n n n ∞ \infty 视差的线性采样!

结论

所以,将d2表达为d2 = 1. + 2. * near / rays_o[…, 2] - o2不是一个合理的表达方式
正确表达方式为

#表达方式1
d2 = -2. * near / rays_o [... , 2]
#表达方式2
o2 = 1. + 2. * near / rays_o [... , 2]
d2 = 1 - o2
#表达方式----- 只要可以和结尾数学公式d中的数学表达对应即可

补充:
数学文档补充材料
数学文档补充材料
http://www.songho.ca/opengl/gl_projectionmatrix.html

你可能感兴趣的:(nerf,ndc,空间坐标转换,数学推导)