Spaces:
Runtime error
Runtime error
import os | |
import torch | |
from einops import rearrange | |
import numpy as np | |
from plyfile import PlyData, PlyElement | |
from os import makedirs, path | |
from errno import EEXIST | |
def mkdir_p(folder_path): | |
# Creates a directory. equivalent to using mkdir -p on the command line | |
try: | |
makedirs(folder_path) | |
except OSError as exc: # Python >2.5 | |
if exc.errno == EEXIST and path.isdir(folder_path): | |
pass | |
else: | |
raise | |
def RGB2SH(rgb): | |
return (rgb - 0.5) / C0 | |
C0 = 0.28209479177387814 | |
# https://github.com/facebookresearch/pytorch3d/blob/main/pytorch3d/transforms/rotation_conversions.py | |
def quaternion_to_matrix( | |
quaternions, | |
eps=1e-8, | |
) : | |
# Order changed to match scipy format! | |
i, j, k, r = torch.unbind(quaternions, dim=-1) | |
two_s = 2 / ((quaternions * quaternions).sum(dim=-1) + eps) | |
o = torch.stack( | |
( | |
1 - two_s * (j * j + k * k), | |
two_s * (i * j - k * r), | |
two_s * (i * k + j * r), | |
two_s * (i * j + k * r), | |
1 - two_s * (i * i + k * k), | |
two_s * (j * k - i * r), | |
two_s * (i * k - j * r), | |
two_s * (j * k + i * r), | |
1 - two_s * (i * i + j * j), | |
), | |
-1, | |
) | |
return rearrange(o, "... (i j) -> ... i j", i=3, j=3) | |
def build_covariance( | |
scale, | |
rotation_xyzw, | |
): | |
scale = scale.diag_embed() | |
rotation = quaternion_to_matrix(rotation_xyzw) | |
return ( | |
rotation | |
) | |
def inverse_sigmoid(x): | |
return torch.log(x/(1-x)) | |
class GaussianModel: | |
def __init__(self, sh_degree : int): | |
self.active_sh_degree = 0 | |
self.max_sh_degree = sh_degree | |
self._xyz = torch.empty(0) | |
self._features_dc = torch.empty(0) | |
self._features_rest = torch.empty(0) | |
self._scaling = torch.empty(0) | |
self._rotation = torch.empty(0) | |
self._opacity = torch.empty(0) | |
self.max_radii2D = torch.empty(0) | |
self.xyz_gradient_accum = torch.empty(0) | |
self.denom = torch.empty(0) | |
self.optimizer = None | |
self.percent_dense = 0 | |
self.spatial_lr_scale = 0 | |
self._semantic_feature = torch.empty(0) | |
def get_scaling(self): | |
return self._scaling | |
def get_rotation(self): | |
return self._rotation | |
def get_xyz(self): | |
return self._xyz | |
def get_features(self): | |
features_dc = self._features_dc | |
features_rest = self._features_rest | |
return torch.cat((features_dc, features_rest), dim=1) | |
def get_opacity(self): | |
return self._opacity | |
def get_semantic_feature(self): | |
return self._semantic_feature | |
def construct_list_of_attributes(self): | |
l = ['x', 'y', 'z', 'nx', 'ny', 'nz'] | |
# All channels except the 3 DC | |
for i in range(self._features_dc.shape[1]*self._features_dc.shape[2]): | |
l.append('f_dc_{}'.format(i)) | |
for i in range(self._features_rest.shape[1]*self._features_rest.shape[2]): | |
l.append('f_rest_{}'.format(i)) | |
l.append('opacity') | |
for i in range(self._scaling.shape[1]): | |
l.append('scale_{}'.format(i)) | |
for i in range(self._rotation.shape[1]): | |
l.append('rot_{}'.format(i)) | |
# Add semantic features | |
for i in range(self._semantic_feature.shape[1]*self._semantic_feature.shape[2]): | |
l.append('semantic_{}'.format(i)) | |
return l | |
def from_predictions(pred, sh_degree): | |
gaussians = GaussianModel(sh_degree=sh_degree) | |
gaussians._xyz = pred['means'] | |
gaussians._features_dc = pred['sh_coeffs'][:, :1] # N, 1, d_sh | |
gaussians._features_rest = pred['sh_coeffs'][:, 1:] # N, d_sh-1, d_sh | |
gaussians._opacity = pred['opacities'] # N, 1 | |
gaussians._scaling = pred['scales'] # N, 3, 3 | |
gaussians._rotation = pred['rotations'] # N, 4 | |
gaussians._semantic_feature = pred['gs_feats'][:, None, :] # N, 1, d_feats | |
return gaussians | |
def save_ply(self, path): | |
mkdir_p(os.path.dirname(path)) | |
xyz = self._xyz.detach().cpu().numpy() | |
normals = np.zeros_like(xyz) | |
f_dc = self._features_dc.detach().transpose(1, 2).flatten(start_dim=1).contiguous().cpu().numpy() | |
f_rest = self._features_rest.detach().transpose(1, 2).flatten(start_dim=1).contiguous().cpu().numpy() | |
opacities = inverse_sigmoid(self._opacity).detach().cpu().numpy() | |
scale = torch.log(self._scaling).detach().cpu().numpy() | |
rotation = self._rotation.detach().cpu().numpy() | |
semantic_feature = self._semantic_feature.detach().transpose(1, 2).flatten(start_dim=1).contiguous().cpu().numpy() | |
dtype_full = [(attribute, 'f4') for attribute in self.construct_list_of_attributes()] | |
elements = np.empty(xyz.shape[0], dtype=dtype_full) | |
attributes = np.concatenate((xyz, normals, f_dc, f_rest, opacities, scale, rotation, semantic_feature), axis=1) | |
# attributes = np.concatenate((xyz, normals, f_dc, f_rest, opacities, scale, rotation), axis=1) | |
elements[:] = list(map(tuple, attributes)) | |
el = PlyElement.describe(elements, 'vertex') | |
PlyData([el]).write(path) |