| |
| |
| |
| |
| |
| |
|
|
| import math |
|
|
| import numpy as np |
| import torch |
|
|
|
|
| def generate_rotation_matrix_y(degrees): |
| """ |
| Generates a rotation matrix for rotation around the Y-axis by a given angle in degrees. |
| |
| Args: |
| degrees (float): The rotation angle in degrees. |
| |
| Returns: |
| numpy.ndarray: A 3x3 rotation matrix representing rotation about the Y-axis. |
| """ |
|
|
| theta = math.radians(degrees) |
| cos_theta = math.cos(theta) |
| sin_theta = math.sin(theta) |
|
|
| R = [[cos_theta, 0, sin_theta], [0, 1, 0], [-sin_theta, 0, cos_theta]] |
|
|
| return np.asarray(R, dtype=np.float32) |
|
|
|
|
| def getWorld2View2(R, t, translate=np.array([0.0, 0.0, 0.0]), scale=1.0): |
| """ |
| Computes the world-to-view (camera) transformation matrix. |
| |
| Args: |
| R (numpy.ndarray): A 3x3 rotation matrix. |
| t (numpy.ndarray): A 3-element translation vector. |
| translate (numpy.ndarray, optional): Additional translation to apply to the camera center after transformation. Defaults to np.array([0.0, 0.0, 0.0]). |
| scale (float, optional): Scaling factor for the camera center position. Defaults to 1.0. |
| |
| Returns: |
| numpy.ndarray: A 4x4 world-to-view transformation matrix suitable for use in computer graphics pipelines. |
| """ |
|
|
| Rt = np.zeros((4, 4)) |
| Rt[:3, :3] = R.transpose() |
| Rt[:3, 3] = t |
| Rt[3, 3] = 1.0 |
|
|
| C2W = np.linalg.inv(Rt) |
| cam_center = C2W[:3, 3] |
| cam_center = (cam_center + translate) * scale |
| C2W[:3, 3] = cam_center |
| Rt = np.linalg.inv(C2W) |
| return np.float32(Rt) |
|
|
|
|
| def getProjectionMatrix(znear, zfar, fovX, fovY): |
| """ |
| Constructs a perspective projection matrix. |
| |
| Args: |
| znear (float): The near clipping plane distance. |
| zfar (float): The far clipping plane distance. |
| fovX (float): The horizontal field of view in radians. |
| fovY (float): The vertical field of view in radians. |
| |
| Returns: |
| torch.Tensor: A 4x4 projection matrix suitable for 3D rendering. |
| """ |
|
|
| tanHalfFovY = math.tan((fovY / 2)) |
| tanHalfFovX = math.tan((fovX / 2)) |
|
|
| top = tanHalfFovY * znear |
| bottom = -top |
| right = tanHalfFovX * znear |
| left = -right |
|
|
| P = torch.zeros(4, 4) |
|
|
| z_sign = 1.0 |
|
|
| P[0, 0] = 2.0 * znear / (right - left) |
| P[1, 1] = 2.0 * znear / (top - bottom) |
| P[0, 2] = (right + left) / (right - left) |
| P[1, 2] = (top + bottom) / (top - bottom) |
| P[3, 2] = z_sign |
| P[2, 2] = z_sign * zfar / (zfar - znear) |
| P[2, 3] = -(zfar * znear) / (zfar - znear) |
| return P |
|
|
|
|
| def intrinsic_to_fov(intrinsic, w, h): |
| fx, fy = intrinsic[0, 0], intrinsic[1, 1] |
| fov_x = 2 * torch.arctan2(w, 2 * fx) |
| fov_y = 2 * torch.arctan2(h, 2 * fy) |
| return fov_x, fov_y |
|
|
|
|
| class Camera: |
| """ |
| Camera class for representing a pinhole or perspective camera model. |
| |
| Attributes: |
| FoVx (float): Horizontal field of view in radians. |
| FoVy (float): Vertical field of view in radians. |
| height (int): Image height in pixels. |
| width (int): Image width in pixels. |
| world_view_transform (torch.Tensor): 4x4 matrix transforming world coordinates to camera (view) coordinates. |
| zfar (float): Far clipping plane distance. |
| znear (float): Near clipping plane distance. |
| trans (np.ndarray): Camera translation vector applied after transformation. |
| scale (float): Scale factor applied to the camera center. |
| projection_matrix (torch.Tensor): 4x4 projection matrix for camera intrinsics. |
| full_proj_transform (torch.Tensor): Combined view and projection transform matrix. |
| camera_center (torch.Tensor): 3D location of the camera center in world coordinates. |
| intrinsic (torch.Tensor): Camera intrinsic matrix. |
| |
| Methods: |
| from_c2w(c2w, intrinsic, height, width): |
| Instantiates a Camera object from a camera-to-world matrix and intrinsics. |
| """ |
|
|
| def __init__( |
| self, |
| w2c, |
| intrinsic, |
| FoVx, |
| FoVy, |
| height, |
| width, |
| trans=np.array([0.0, 0.0, 0.0]), |
| scale=1.0, |
| ) -> None: |
| """ |
| Initializes the Camera object with extrinsics, intrinsics, field of view and additional parameters. |
| |
| Args: |
| w2c (torch.Tensor): 4x4 world-to-camera extrinsic transformation matrix (transposed and used as view transform). |
| intrinsic (torch.Tensor): 3x3 camera intrinsic matrix. |
| FoVx (float): Horizontal field of view in radians. |
| FoVy (float): Vertical field of view in radians. |
| height (int): Image height in pixels. |
| width (int): Image width in pixels. |
| trans (np.ndarray, optional): Camera translation vector to apply after other transforms (default: [0.0, 0.0, 0.0]). |
| scale (float, optional): Scale factor applied to the camera center (default: 1.0). |
| """ |
|
|
| self.FoVx = FoVx |
| self.FoVy = FoVy |
| self.height = height |
| self.width = width |
| self.world_view_transform = w2c.transpose(0, 1) |
|
|
| self.zfar = 100.0 |
| self.znear = 0.01 |
|
|
| self.trans = trans |
| self.scale = scale |
|
|
| self.projection_matrix = ( |
| getProjectionMatrix( |
| znear=self.znear, zfar=self.zfar, fovX=self.FoVx, fovY=self.FoVy |
| ) |
| .transpose(0, 1) |
| .to(w2c.device) |
| ) |
| self.full_proj_transform = ( |
| self.world_view_transform.unsqueeze(0).bmm( |
| self.projection_matrix.unsqueeze(0) |
| ) |
| ).squeeze(0) |
| self.camera_center = self.world_view_transform.inverse()[3, :3] |
|
|
| self.intrinsic = intrinsic |
|
|
| @staticmethod |
| def from_c2w(c2w, intrinsic, height, width): |
| """ |
| Creates a Camera object from a camera-to-world (c2w) matrix and intrinsic parameters. |
| |
| Args: |
| c2w (torch.Tensor): 4x4 camera-to-world extrinsic matrix. |
| intrinsic (torch.Tensor): 3x3 camera intrinsic matrix. |
| height (int): Image height in pixels. |
| width (int): Image width in pixels. |
| |
| Returns: |
| Camera: An instance of the Camera class constructed from the provided parameters. |
| """ |
|
|
| w2c = torch.inverse(c2w) |
| FoVx, FoVy = intrinsic_to_fov( |
| intrinsic, |
| w=torch.tensor(width, device=w2c.device), |
| h=torch.tensor(height, device=w2c.device), |
| ) |
|
|
| return Camera( |
| w2c=w2c, |
| intrinsic=intrinsic, |
| FoVx=FoVx, |
| FoVy=FoVy, |
| height=height, |
| width=width, |
| ) |
|
|
| @staticmethod |
| def from_c2w_center_modfied(c2w, intrinsic, height, width): |
| """ |
| Creates a Camera object from a camera-to-world (c2w) matrix and intrinsic parameters, |
| but modifies the intrinsic matrix so that the principal point is set to the image center. |
| |
| Args: |
| c2w (torch.Tensor): 4x4 camera-to-world extrinsic matrix. |
| intrinsic (torch.Tensor): 3x3 camera intrinsic matrix. |
| height (int): Image height in pixels. |
| width (int): Image width in pixels. |
| |
| Returns: |
| Camera: An instance of the Camera class constructed from the provided parameters, with adjusted intrinsic center. |
| """ |
|
|
| w2c = torch.inverse(c2w) |
| intrinsic = intrinsic.clone() |
|
|
| intrinsic[0, 2] = width / 2.0 |
| intrinsic[1, 2] = height / 2.0 |
|
|
| FoVx, FoVy = intrinsic_to_fov( |
| intrinsic, |
| w=torch.tensor(width, device=w2c.device), |
| h=torch.tensor(height, device=w2c.device), |
| ) |
|
|
| return Camera( |
| w2c=w2c, |
| intrinsic=intrinsic, |
| FoVx=FoVx, |
| FoVy=FoVy, |
| height=height, |
| width=width, |
| ) |
|
|