# # Copyright (C) 2023, Inria # GRAPHDECO research group, https://team.inria.fr/graphdeco # All rights reserved. # # This software is free for non-commercial, research and evaluation use # under the terms of the LICENSE.md file. # # For inquiries contact george.drettakis@inria.fr # import torch import math import numpy as np from typing import NamedTuple class BasicPointCloud(NamedTuple): points : np.array colors : np.array normals : np.array def geom_transform_points(points, transf_matrix): P, _ = points.shape ones = torch.ones(P, 1, dtype=points.dtype, device=points.device) points_hom = torch.cat([points, ones], dim=1) points_out = torch.matmul(points_hom, transf_matrix.unsqueeze(0)) denom = points_out[..., 3:] + 0.0000001 return (points_out[..., :3] / denom).squeeze(dim=0) def getWorld2View(R, t): Rt = np.zeros((4, 4)) Rt[:3, :3] = R.transpose() Rt[:3, 3] = t Rt[3, 3] = 1.0 return np.float32(Rt) def getWorld2View2(R, t, translate=np.array([.0, .0, .0]), scale=1.0): 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, K = None, img_h = None, img_w = None): if K is None: tanHalfFovY = math.tan((fovY / 2)) tanHalfFovX = math.tan((fovX / 2)) top = tanHalfFovY * znear bottom = -top right = tanHalfFovX * znear left = -right else: near_fx = znear / K[0, 0] near_fy = znear / K[1, 1] left = - (img_w - K[0, 2]) * near_fx right = K[0, 2] * near_fx bottom = (K[1, 2] - img_h) * near_fy top = K[1, 2] * near_fy 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 fov2focal(fov, pixels): return pixels / (2 * math.tan(fov / 2)) def focal2fov(focal, pixels): return 2*math.atan(pixels/(2*focal)) def _so3_exp_map( log_rot: torch.Tensor, eps: float = 0.0001 ): """ A helper function that computes the so3 exponential map and, apart from the rotation matrix, also returns intermediate variables that can be re-used in other functions. """ def hat(v: torch.Tensor) -> torch.Tensor: """ Compute the Hat operator [1] of a batch of 3D vectors. Args: v: Batch of vectors of shape `(minibatch , 3)`. Returns: Batch of skew-symmetric matrices of shape `(minibatch, 3 , 3)` where each matrix is of the form: `[ 0 -v_z v_y ] [ v_z 0 -v_x ] [ -v_y v_x 0 ]` Raises: ValueError if `v` is of incorrect shape. [1] https://en.wikipedia.org/wiki/Hat_operator """ N, dim = v.shape if dim != 3: raise ValueError("Input vectors have to be 3-dimensional.") h = torch.zeros((N, 3, 3), dtype=v.dtype, device=v.device) x, y, z = v.unbind(1) h[:, 0, 1] = -z h[:, 0, 2] = y h[:, 1, 0] = z h[:, 1, 2] = -x h[:, 2, 0] = -y h[:, 2, 1] = x return h _, dim = log_rot.shape if dim != 3: raise ValueError("Input tensor shape has to be Nx3.") nrms = (log_rot * log_rot).sum(1) # phis ... rotation angles rot_angles = torch.clamp(nrms, eps).sqrt() rot_angles_inv = 1.0 / rot_angles fac1 = rot_angles_inv * rot_angles.sin() fac2 = rot_angles_inv * rot_angles_inv * (1.0 - rot_angles.cos()) skews = hat(log_rot) skews_square = torch.bmm(skews, skews) R = ( # pyre-fixme[16]: `float` has no attribute `__getitem__`. fac1[:, None, None] * skews + fac2[:, None, None] * skews_square + torch.eye(3, dtype=log_rot.dtype, device=log_rot.device)[None] ) return R, rot_angles, skews, skews_square def so3_exp_map(log_rot: torch.Tensor, eps: float = 0.0001) -> torch.Tensor: """ Convert a batch of logarithmic representations of rotation matrices `log_rot` to a batch of 3x3 rotation matrices using Rodrigues formula [1]. In the logarithmic representation, each rotation matrix is represented as a 3-dimensional vector (`log_rot`) who's l2-norm and direction correspond to the magnitude of the rotation angle and the axis of rotation respectively. The conversion has a singularity around `log(R) = 0` which is handled by clamping controlled with the `eps` argument. Args: log_rot: Batch of vectors of shape `(minibatch, 3)`. eps: A float constant handling the conversion singularity. Returns: Batch of rotation matrices of shape `(minibatch, 3, 3)`. Raises: ValueError if `log_rot` is of incorrect shape. [1] https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula """ return _so3_exp_map(log_rot, eps=eps)[0]