File size: 3,363 Bytes
9e15541
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import torch

EPS = 1e-3


def normalize_calib(K: torch.Tensor, img_sizes: torch.Tensor) -> torch.Tensor:
    """Normalize the calibration matrices for fisheye cameras based on the image size

    Args:
        calib (torch.Tensor): B, n_views, 3, 3
        img_sizes (torch.Tensor): B, n_views, 2

    Returns:
        torch.Tensor: B, n_views 7
    """

    K[..., :2, :] = K[..., :2, :] / img_sizes.unsqueeze(-1) * 2.0
    K[..., :2, 2] = K[..., :2, 2] - 1.0

    return K


def unnormalize_calib(K: torch.Tensor, img_sizes: torch.Tensor) -> torch.Tensor:
    """Unnormalize the calibration matrices for fisheye cameras based on the image size

    Args:
        calib (torch.Tensor): B, n_views, 3, 3
        img_sizes (torch.Tensor): B, n_views, 2

    Returns:
        torch.Tensor: B, n_views 7
    """

    K[..., :2, 2] = K[..., :2, 2] + 1.0
    K[..., :2, :] = K[..., :2, :] * img_sizes.unsqueeze(-1) / 2.0

    return K


def pts_into_camera(pts: torch.Tensor, poses_w2c: torch.Tensor) -> torch.Tensor:
    """Project points from world coordinates into camera coordinate

    Args:
        pts (torch.Tensor): B, n_pts, 3
        poses_w2c (torch.Tensor): B, n_view, 4, 4

    Returns:
        torch.Tensor: B, n_views, n_pts, 3
    """

    # Add a singleton dimension to the input point cloud to match grid_f_poses_w2c shape
    pts = pts.unsqueeze(1)  # [B, 1, n_pts, 3]
    ones = torch.ones_like(
        pts[..., :1]
    )  ## Create a tensor of ones to add a fourth dimension to the point cloud for homogeneous coordinates
    pts = torch.cat(
        (pts, ones), dim=-1
    )  ## Concatenate the tensor of ones with the point cloud to create homogeneous coordinates
    return (poses_w2c[:, :, :3, :]) @ pts.permute(0, 1, 3, 2)


def project_to_image(
    pts: torch.Tensor, Ks: torch.Tensor
) -> tuple[torch.Tensor, torch.Tensor]:
    """Project pts in camera coordinates into image coordinates.

    Args:
        pts (torch.Tensor): B, n_views, n_pts, 3
        Ks (torch.Tensor): B, n_views, 3, 3

    Returns:
        tuple[torch.Tensor, torch.Tensor]: (B, n_views, n_pts, 2), (B, n_views, n_pts, 1)
    """
    pts = (Ks @ pts).permute(
        0, 1, 3, 2
    )  ## Apply the intrinsic camera parameters to the projected points to get pixel coordinates
    xy = pts[
        :, :, :, :2
    ]  ## Extract the x,y coordinates and depth value from the projected points
    z_ = pts[:, :, :, 2:3]

    xy = xy / z_.clamp_min(EPS)

    return xy, z_


def outside_frustum(
    xy: torch.Tensor,
    z: torch.Tensor,
    limits_x: tuple[float, float] | tuple[int, int] = (-1.0, 1.0),
    limits_y: tuple[float, float] | tuple[int, int] = (-1.0, 1.0),
    limit_z: float = EPS,
) -> torch.Tensor:
    """_summary_

    Args:
        xy (torch.Tensor): _description_
        z (torch.Tensor): _description_
        limits_x (tuple[float, float] | tuple[int, int], optional): _description_. Defaults to (-1.0, 1.0).
        limits_y (tuple[float, float] | tuple[int, int], optional): _description_. Defaults to (-1.0, 1.0).
        limit_z (float, optional): _description_. Defaults to EPS.

    Returns:
        torch.Tensor: _description_
    """
    return (
        (z <= limit_z)
        | (xy[..., :1] < limits_x[0])
        | (xy[..., :1] > limits_x[1])
        | (xy[..., 1:2] < limits_y[0])
        | (xy[..., 1:2] > limits_y[1])
    )