File size: 4,075 Bytes
03da825
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import torch

import math

from rendering import math_utils


def create_cam2world_matrix(forward_vector, origin):
    """
    Takes in the direction the camera is pointing and the camera origin and returns a cam2world matrix.
    Works on batches of forward_vectors, origins. Assumes y-axis is up and that there is no camera roll.
    """

    forward_vector = math_utils.normalize_vecs(forward_vector)
    up_vector = torch.tensor([0, 1, 0], dtype=torch.float, device=origin.device).expand_as(forward_vector)

    right_vector = -math_utils.normalize_vecs(torch.cross(up_vector, forward_vector, dim=-1))
    up_vector = math_utils.normalize_vecs(torch.cross(forward_vector, right_vector, dim=-1))

    rotation_matrix = torch.eye(4, device=origin.device).unsqueeze(0).repeat(forward_vector.shape[0], 1, 1)
    rotation_matrix[:, :3, :3] = torch.stack((right_vector, up_vector, forward_vector), axis=-1)

    translation_matrix = torch.eye(4, device=origin.device).unsqueeze(0).repeat(forward_vector.shape[0], 1, 1)
    translation_matrix[:, :3, 3] = origin
    cam2world = (translation_matrix @ rotation_matrix)[:, :, :]
    assert(cam2world.shape[1:] == (4, 4))
    return cam2world


def FOV_to_intrinsics(fov_degrees, principal_x, principal_y):
    """
    Creates a 3x3 camera intrinsics matrix from the camera field of view, specified in degrees.
    Note the intrinsics are returned as normalized by image size, rather than in pixel units.
    Assumes principal point is at image center.
    """

    focal_length = (1 / (torch.tan(fov_degrees * 3.14159 / 360) * 1.414)).float()
    intrinsics = torch.eye(3, device=fov_degrees.device).unsqueeze(0).repeat(fov_degrees.shape[0], 1, 1)
    intrinsics[:, 0, 0] = focal_length
    intrinsics[:, 0, 2] = principal_x
    intrinsics[:, 1, 1] = focal_length
    intrinsics[:, 1, 2] = principal_y
    return intrinsics


class LookAtPoseSampler:
    """
    Same as GaussianCameraPoseSampler, except the
    camera is specified as looking at 'lookat_position', a 3-vector.

    Example:
    For a camera pose looking at the origin with the camera at position [0, 0, 1]:
    cam2world = LookAtPoseSampler.sample(math.pi/2, math.pi/2, torch.tensor([0, 0, 0]), radius=1)
    """

    @staticmethod
    def sample(
        horizontal_min, vertical_min, radius_mean,
        lookat_position,
        horizontal_max, vertical_max, radius_stddev=0,
        batch_size=1, device='cpu'
    ):
        if horizontal_max == -1:
            horizontal_max = horizontal_min
        h = torch.rand((batch_size, 1), device=device) * (horizontal_max - horizontal_min) + horizontal_min
        v = torch.rand((batch_size, 1), device=device) * (vertical_max - vertical_min) + vertical_min
        radius = torch.randn((batch_size, 1), device=device) * radius_stddev + radius_mean
        v = torch.clamp(v, 1e-5, math.pi - 1e-5)

        theta = h
        v = v / math.pi
        phi = torch.arccos(1 - 2*v)

        camera_origins = torch.zeros((batch_size, 3), device=device)

        camera_origins[:, 0: 1] = radius*torch.sin(phi) * torch.cos(math.pi-theta)
        camera_origins[:, 2: 3] = radius*torch.sin(phi) * torch.sin(math.pi-theta)
        camera_origins[:, 1: 2] = radius*torch.cos(phi)

        # forward_vectors = math_utils.normalize_vecs(-camera_origins)
        forward_vectors = math_utils.normalize_vecs(lookat_position - camera_origins)
        cam2world = create_cam2world_matrix(forward_vectors, camera_origins)

        return cam2world


class IntrinsicsSampler:
    @staticmethod
    def sample(
        focal_degrees_mean, principal_mean,
        focal_degrees_stddev=0, principal_stddev=0,
        batch_size=1, device='cpu'
    ):
        focal_degrees = torch.randn((batch_size), device=device) * focal_degrees_stddev + focal_degrees_mean
        principal_x = torch.randn((batch_size), device=device) * principal_stddev + principal_mean
        principal_y = torch.randn((batch_size), device=device) * principal_stddev + principal_mean

        return FOV_to_intrinsics(focal_degrees, principal_x, principal_y)