File size: 1,970 Bytes
0cfefd2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
"""f-theta 正向投影:3D 点(相机系) -> 像素。

仅支持 backward polynomial 形式(与 NVIDIA 工具一致),
forward 形式可在内部用牛顿迭代反推。
"""

from __future__ import annotations

import torch

from ..modules.rays import FThetaCamera


def project_points_ftheta(
    points_cam: torch.Tensor,        # [..., 3],相机系下 3D 点
    cam: FThetaCamera,
) -> tuple[torch.Tensor, torch.Tensor]:
    """正向投影:相机系点 -> 像素 ``(u, v)``,并返回深度。

    返回
    ----
    uv : [..., 2]
    depth : [..., 1],沿主光轴(z)方向的深度(如果 z<0 则视为后方,仍计算
            但调用方需用 ``depth > 0`` 做有效性筛选)。
    """
    x = points_cam[..., 0]
    y = points_cam[..., 1]
    z = points_cam[..., 2]
    norm = torch.sqrt(x * x + y * y + z * z).clamp_min(1e-6)
    cos_theta = z / norm
    cos_theta = cos_theta.clamp(-1.0 + 1e-7, 1.0 - 1e-7)
    theta = torch.acos(cos_theta)
    phi = torch.atan2(y, x)

    if cam.intr.is_bw_poly:
        # backward poly 是 r_pix -> theta;正向需要反求 theta -> r_pix。
        # 用牛顿迭代:希望 _eval_poly(r) = theta
        r = theta.clone()  # 初始猜测
        for _ in range(8):
            f = cam._eval_poly(r) - theta
            df = cam._eval_poly_grad(r).clamp_min(1e-6)
            r = r - f / df
        r_pix = r
    else:
        r_pix = cam._eval_poly(theta)

    cos_p = torch.cos(phi)
    sin_p = torch.sin(phi)
    du = r_pix * cos_p
    dv = r_pix * sin_p
    # 反线性修正:linear_cde 是仿射 (du,dv) = M (du0,dv0),正投影需要逆
    c = cam.intr.linear_cde[0]
    d = cam.intr.linear_cde[1]
    e = cam.intr.linear_cde[2]
    # 简化:忽略 linear_cde 的修正(与 unproject 中近似一致)
    du0 = du
    dv0 = dv
    u = du0 + cam.intr.cx
    v = dv0 + cam.intr.cy
    uv = torch.stack([u, v], dim=-1)
    depth = z.unsqueeze(-1)
    return uv, depth