quasi-physical-sims / models /renderer_def.py
meow
a
710e818
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import logging
import mcubes
from icecream import ic
import os
import trimesh
from pysdf import SDF
from uni_rep.rep_3d.dmtet import marching_tets_tetmesh, create_tetmesh_variables
def create_mt_variable(device):
triangle_table = torch.tensor(
[
[-1, -1, -1, -1, -1, -1],
[1, 0, 2, -1, -1, -1],
[4, 0, 3, -1, -1, -1],
[1, 4, 2, 1, 3, 4],
[3, 1, 5, -1, -1, -1],
[2, 3, 0, 2, 5, 3],
[1, 4, 0, 1, 5, 4],
[4, 2, 5, -1, -1, -1],
[4, 5, 2, -1, -1, -1],
[4, 1, 0, 4, 5, 1],
[3, 2, 0, 3, 5, 2],
[1, 3, 5, -1, -1, -1],
[4, 1, 2, 4, 3, 1],
[3, 0, 4, -1, -1, -1],
[2, 0, 1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1]
], dtype=torch.long, device=device)
num_triangles_table = torch.tensor([0, 1, 1, 2, 1, 2, 2, 1, 1, 2, 2, 1, 2, 1, 1, 0], dtype=torch.long, device=device)
base_tet_edges = torch.tensor([0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3], dtype=torch.long, device=device)
v_id = torch.pow(2, torch.arange(4, dtype=torch.long, device=device))
return triangle_table, num_triangles_table, base_tet_edges, v_id
def extract_fields_from_tets(bound_min, bound_max, resolution, query_func, def_func=None):
# load tet via resolution #
# scale them via bounds #
# extract the geometry #
# /home/xueyi/gen/DeepMetaHandles/data/tets/100_compress.npz # strange #
device = bound_min.device
# if resolution in [64, 70, 80, 90, 100]:
# tet_fn = f"/home/xueyi/gen/DeepMetaHandles/data/tets/{resolution}_compress.npz"
# else:
tet_fn = f"/home/xueyi/gen/DeepMetaHandles/data/tets/{100}_compress.npz"
tets = np.load(tet_fn)
verts = torch.from_numpy(tets['vertices']).float().to(device) # verts positions
indices = torch.from_numpy(tets['tets']).long().to(device) # .to(self.device)
# split #
# verts; verts; #
minn_verts, _ = torch.min(verts, dim=0)
maxx_verts, _ = torch.max(verts, dim=0) # (3, ) # exporting the
# scale_verts = maxx_verts - minn_verts
scale_bounds = bound_max - bound_min # scale bounds #
### scale the vertices ###
scaled_verts = (verts - minn_verts.unsqueeze(0)) / (maxx_verts - minn_verts).unsqueeze(0) ### the maxx and minn verts scales ###
# scaled_verts = (verts - minn_verts.unsqueeze(0)) / (maxx_verts - minn_verts).unsqueeze(0) ### the maxx and minn verts scales ###
scaled_verts = scaled_verts * 2. - 1. # init the sdf filed viathe tet mesh vertices and the sdf values ##
# scaled_verts = (scaled_verts * scale_bounds.unsqueeze(0)) + bound_min.unsqueeze(0) ## the scaled verts ###
# scaled_verts = scaled_verts - scale_bounds.unsqueeze(0) / 2. #
# scaled_verts = scaled_verts - bound_min.unsqueeze(0) - scale_bounds.unsqueeze(0) / 2.
sdf_values = []
N = 64
query_bundles = N ** 3 ### N^3
query_NNs = scaled_verts.size(0) // query_bundles
if query_NNs * query_bundles < scaled_verts.size(0):
query_NNs += 1
for i_query in range(query_NNs):
cur_bundle_st = i_query * query_bundles
cur_bundle_ed = (i_query + 1) * query_bundles
cur_bundle_ed = min(cur_bundle_ed, scaled_verts.size(0))
cur_query_pts = scaled_verts[cur_bundle_st: cur_bundle_ed]
if def_func is not None:
cur_query_pts = def_func(cur_query_pts)
cur_query_vals = query_func(cur_query_pts)
sdf_values.append(cur_query_vals)
sdf_values = torch.cat(sdf_values, dim=0)
# print(f"queryed sdf values: {sdf_values.size()}") #
GT_sdf_values = np.load("/home/xueyi/diffsim/DiffHand/assets/hand/100_sdf_values.npy", allow_pickle=True)
GT_sdf_values = torch.from_numpy(GT_sdf_values).float().to(device)
# intrinsic, tet values, pts values, sdf network #
triangle_table, num_triangles_table, base_tet_edges, v_id = create_mt_variable(device)
tet_table, num_tets_table = create_tetmesh_variables(device)
sdf_values = sdf_values.squeeze(-1) # how the rendering #
# print(f"GT_sdf_values: {GT_sdf_values.size()}, sdf_values: {sdf_values.size()}, scaled_verts: {scaled_verts.size()}")
# print(f"scaled_verts: {scaled_verts.size()}, ")
# pos_nx3, sdf_n, tet_fx4, triangle_table, num_triangles_table, base_tet_edges, v_id,
# return_tet_mesh=False, ori_v=None, num_tets_table=None, tet_table=None):
# marching_tets_tetmesh ##
verts, faces, tet_verts, tets = marching_tets_tetmesh(scaled_verts, sdf_values, indices, triangle_table, num_triangles_table, base_tet_edges, v_id, return_tet_mesh=True, ori_v=scaled_verts, num_tets_table=num_tets_table, tet_table=tet_table)
### use the GT sdf values for the marching tets ###
GT_verts, GT_faces, GT_tet_verts, GT_tets = marching_tets_tetmesh(scaled_verts, GT_sdf_values, indices, triangle_table, num_triangles_table, base_tet_edges, v_id, return_tet_mesh=True, ori_v=scaled_verts, num_tets_table=num_tets_table, tet_table=tet_table)
# print(f"After tet marching with verts: {verts.size()}, faces: {faces.size()}")
return verts, faces, sdf_values, GT_verts, GT_faces # verts, faces #
def extract_fields(bound_min, bound_max, resolution, query_func):
N = 64
X = torch.linspace(bound_min[0], bound_max[0], resolution).split(N)
Y = torch.linspace(bound_min[1], bound_max[1], resolution).split(N)
Z = torch.linspace(bound_min[2], bound_max[2], resolution).split(N)
u = np.zeros([resolution, resolution, resolution], dtype=np.float32)
with torch.no_grad():
for xi, xs in enumerate(X):
for yi, ys in enumerate(Y):
for zi, zs in enumerate(Z):
xx, yy, zz = torch.meshgrid(xs, ys, zs)
pts = torch.cat([xx.reshape(-1, 1), yy.reshape(-1, 1), zz.reshape(-1, 1)], dim=-1)
val = query_func(pts).reshape(len(xs), len(ys), len(zs)).detach().cpu().numpy()
u[xi * N: xi * N + len(xs), yi * N: yi * N + len(ys), zi * N: zi * N + len(zs)] = val
# should save u here #
# save_u_path = os.path.join("/data2/datasets/diffsim/neus/exp/hand_test/womask_sphere_reverse_value/other_saved", "sdf_values.npy")
# np.save(save_u_path, u)
# print(f"u saved to {save_u_path}")
return u
def extract_geometry(bound_min, bound_max, resolution, threshold, query_func):
print('threshold: {}'.format(threshold))
## using maching cubes ###
u = extract_fields(bound_min, bound_max, resolution, query_func)
vertices, triangles = mcubes.marching_cubes(u, threshold) # grid sdf and marching cubes #
b_max_np = bound_max.detach().cpu().numpy()
b_min_np = bound_min.detach().cpu().numpy()
vertices = vertices / (resolution - 1.0) * (b_max_np - b_min_np)[None, :] + b_min_np[None, :]
### using maching cubes ###
### using marching tets ###
# vertices, triangles = extract_fields_from_tets(bound_min, bound_max, resolution, query_func)
# vertices = vertices.detach().cpu().numpy()
# triangles = triangles.detach().cpu().numpy()
### using marching tets ###
# b_max_np = bound_max.detach().cpu().numpy()
# b_min_np = bound_min.detach().cpu().numpy()
# vertices = vertices / (resolution - 1.0) * (b_max_np - b_min_np)[None, :] + b_min_np[None, :]
return vertices, triangles
def extract_geometry_tets(bound_min, bound_max, resolution, threshold, query_func, def_func=None):
# print('threshold: {}'.format(threshold))
### using maching cubes ###
# u = extract_fields(bound_min, bound_max, resolution, query_func)
# vertices, triangles = mcubes.marching_cubes(u, threshold) # grid sdf and marching cubes #
# b_max_np = bound_max.detach().cpu().numpy()
# b_min_np = bound_min.detach().cpu().numpy()
# vertices = vertices / (resolution - 1.0) * (b_max_np - b_min_np)[None, :] + b_min_np[None, :]
### using maching cubes ###
##
### using marching tets ### fiels from tets ##
vertices, triangles, tet_sdf_values, GT_verts, GT_faces = extract_fields_from_tets(bound_min, bound_max, resolution, query_func, def_func=def_func)
# vertices = vertices.detach().cpu().numpy()
# triangles = triangles.detach().cpu().numpy()
### using marching tets ###
# b_max_np = bound_max.detach().cpu().numpy()
# b_min_np = bound_min.detach().cpu().numpy()
#
# vertices = vertices / (resolution - 1.0) * (b_max_np - b_min_np)[None, :] + b_min_np[None, :]
return vertices, triangles, tet_sdf_values, GT_verts, GT_faces
def sample_pdf(bins, weights, n_samples, det=False):
# This implementation is from NeRF
# Get pdf
weights = weights + 1e-5 # prevent nans
pdf = weights / torch.sum(weights, -1, keepdim=True)
cdf = torch.cumsum(pdf, -1)
cdf = torch.cat([torch.zeros_like(cdf[..., :1]), cdf], -1)
# Take uniform samples
if det:
u = torch.linspace(0. + 0.5 / n_samples, 1. - 0.5 / n_samples, steps=n_samples)
u = u.expand(list(cdf.shape[:-1]) + [n_samples])
else:
u = torch.rand(list(cdf.shape[:-1]) + [n_samples])
# Invert CDF # invert cdf #
u = u.contiguous()
inds = torch.searchsorted(cdf, u, right=True)
below = torch.max(torch.zeros_like(inds - 1), inds - 1)
above = torch.min((cdf.shape[-1] - 1) * torch.ones_like(inds), inds)
inds_g = torch.stack([below, above], -1) # (batch, N_samples, 2)
matched_shape = [inds_g.shape[0], inds_g.shape[1], cdf.shape[-1]]
cdf_g = torch.gather(cdf.unsqueeze(1).expand(matched_shape), 2, inds_g)
bins_g = torch.gather(bins.unsqueeze(1).expand(matched_shape), 2, inds_g)
denom = (cdf_g[..., 1] - cdf_g[..., 0])
denom = torch.where(denom < 1e-5, torch.ones_like(denom), denom)
t = (u - cdf_g[..., 0]) / denom
samples = bins_g[..., 0] + t * (bins_g[..., 1] - bins_g[..., 0])
return samples
def load_GT_vertices(GT_meshes_folder):
tot_meshes_fns = os.listdir(GT_meshes_folder)
tot_meshes_fns = [fn for fn in tot_meshes_fns if fn.endswith(".obj")]
tot_mesh_verts = []
tot_mesh_faces = []
n_tot_verts = 0
for fn in tot_meshes_fns:
cur_mesh_fn = os.path.join(GT_meshes_folder, fn)
obj_mesh = trimesh.load(cur_mesh_fn, process=False)
# obj_mesh.remove_degenerate_faces(height=1e-06)
verts_obj = np.array(obj_mesh.vertices)
faces_obj = np.array(obj_mesh.faces)
tot_mesh_verts.append(verts_obj)
tot_mesh_faces.append(faces_obj + n_tot_verts)
n_tot_verts += verts_obj.shape[0]
# tot_mesh_faces.append(faces_obj)
tot_mesh_verts = np.concatenate(tot_mesh_verts, axis=0)
tot_mesh_faces = np.concatenate(tot_mesh_faces, axis=0)
return tot_mesh_verts, tot_mesh_faces
class NeuSRenderer:
def __init__(self,
nerf,
sdf_network,
deviation_network,
color_network,
n_samples,
n_importance,
n_outside,
up_sample_steps,
perturb):
self.nerf = nerf
self.sdf_network = sdf_network
self.deviation_network = deviation_network
self.color_network = color_network
self.n_samples = n_samples
self.n_importance = n_importance
self.n_outside = n_outside
self.up_sample_steps = up_sample_steps
self.perturb = perturb
GT_meshes_folder = "/home/xueyi/diffsim/DiffHand/assets/hand"
self.mesh_vertices, self.mesh_faces = load_GT_vertices(GT_meshes_folder=GT_meshes_folder)
maxx_pts = 25.
minn_pts = -15.
self.mesh_vertices = (self.mesh_vertices - minn_pts) / (maxx_pts - minn_pts)
f = SDF(self.mesh_vertices, self.mesh_faces)
self.gt_sdf = f ## a unite sphere or box
self.minn_pts = 0
self.maxx_pts = 1.
# self.minn_pts = -1.5 # gorudn-truth states with the deformation -> update the sdf value fiedl
# self.maxx_pts = 1.5
self.bkg_pts = ... # TODO: the bkg pts # bkg_pts; # bkg_pts_defs #
self.cur_fr_bkg_pts_defs = ... # TODO: set the cur_bkg_pts_defs for each frame #
self.dist_interp_thres = ... # TODO: set the cur_bkg_pts_defs #
self.bending_network = ... # TODO: add the bending network #
self.use_bending_network = ... # TODO: set the property #
self.use_delta_bending = ... # TODO
# use bending network #
# get the pts and render the pts #
# pts and the rendering pts #
def deform_pts(self, pts, pts_ts=0):
if self.use_bending_network:
if len(pts.size()) == 3:
nnb, nns = pts.size(0), pts.size(1)
pts_exp = pts.contiguous().view(nnb * nns, -1).contiguous()
else:
pts_exp = pts
# pts_ts #
if self.use_delta_bending:
# if pts_ts >= 5:
# pts_exp = self.bending_network(pts_exp, input_pts_ts=pts_ts)
# for cur_pts_ts in range(4, -1, -1):
# # print(f"using delta bending with pts_ts: {cur_pts_ts}")
# pts_exp = self.bending_network(pts_exp, input_pts_ts=cur_pts_ts)
# else:
# for cur_pts_ts in range(pts_ts, -1, -1):
# # print(f"using delta bending with pts_ts: {cur_pts_ts}")
# pts_exp = self.bending_network(pts_exp, input_pts_ts=cur_pts_ts)
for cur_pts_ts in range(pts_ts, -1, -1):
# print(f"using delta bending with pts_ts: {cur_pts_ts}")
pts_exp = self.bending_network(pts_exp, input_pts_ts=cur_pts_ts)
else:
pts_exp = self.bending_network(pts_exp, input_pts_ts=pts_ts)
if len(pts.size()) == 3:
pts = pts_exp.contiguous().view(nnb, nns, -1).contiguous()
else:
pts = pts_exp
return pts
# pts: nn_batch x nn_samples x 3
if len(pts.size()) == 3:
nnb, nns = pts.size(0), pts.size(1)
pts_exp = pts.contiguous().view(nnb * nns, -1).contiguous()
else:
pts_exp = pts
# print(f"prior to deforming: {pts.size()}")
dist_pts_to_bkg_pts = torch.sum(
(pts_exp.unsqueeze(1) - self.bkg_pts.unsqueeze(0)) ** 2, dim=-1 ## nn_pts_exp x nn_bkg_pts
)
dist_mask = dist_pts_to_bkg_pts <= self.dist_interp_thres #
dist_mask_float = dist_mask.float()
# dist_mask_float #
cur_fr_bkg_def_exp = self.cur_fr_bkg_pts_defs.unsqueeze(0).repeat(pts_exp.size(0), 1, 1).contiguous()
cur_fr_pts_def = torch.sum(
cur_fr_bkg_def_exp * dist_mask_float.unsqueeze(-1), dim=1
)
dist_mask_float_summ = torch.sum(
dist_mask_float, dim=1
)
dist_mask_float_summ = torch.clamp(dist_mask_float_summ, min=1)
cur_fr_pts_def = cur_fr_pts_def / dist_mask_float_summ.unsqueeze(-1) # bkg pts deformation #
pts_exp = pts_exp - cur_fr_pts_def
if len(pts.size()) == 3:
pts = pts_exp.contiguous().view(nnb, nns, -1).contiguous()
else:
pts = pts_exp
return pts #
def render_core_outside(self, rays_o, rays_d, z_vals, sample_dist, nerf, background_rgb=None, pts_ts=0):
"""
Render background
"""
batch_size, n_samples = z_vals.shape
# Section length
dists = z_vals[..., 1:] - z_vals[..., :-1]
dists = torch.cat([dists, torch.Tensor([sample_dist]).expand(dists[..., :1].shape)], -1)
mid_z_vals = z_vals + dists * 0.5
# Section midpoints #
pts = rays_o[:, None, :] + rays_d[:, None, :] * mid_z_vals[..., :, None] # batch_size, n_samples, 3 #
# pts = pts.flip((-1,)) * 2 - 1
pts = pts * 2 - 1
pts = self.deform_pts(pts=pts, pts_ts=pts_ts)
dis_to_center = torch.linalg.norm(pts, ord=2, dim=-1, keepdim=True).clip(1.0, 1e10)
pts = torch.cat([pts / dis_to_center, 1.0 / dis_to_center], dim=-1) # batch_size, n_samples, 4 #
dirs = rays_d[:, None, :].expand(batch_size, n_samples, 3)
pts = pts.reshape(-1, 3 + int(self.n_outside > 0))
dirs = dirs.reshape(-1, 3)
density, sampled_color = nerf(pts, dirs)
sampled_color = torch.sigmoid(sampled_color)
alpha = 1.0 - torch.exp(-F.softplus(density.reshape(batch_size, n_samples)) * dists)
alpha = alpha.reshape(batch_size, n_samples)
weights = alpha * torch.cumprod(torch.cat([torch.ones([batch_size, 1]), 1. - alpha + 1e-7], -1), -1)[:, :-1]
sampled_color = sampled_color.reshape(batch_size, n_samples, 3)
color = (weights[:, :, None] * sampled_color).sum(dim=1)
if background_rgb is not None:
color = color + background_rgb * (1.0 - weights.sum(dim=-1, keepdim=True))
return {
'color': color,
'sampled_color': sampled_color,
'alpha': alpha,
'weights': weights,
}
def up_sample(self, rays_o, rays_d, z_vals, sdf, n_importance, inv_s, pts_ts=0):
"""
Up sampling give a fixed inv_s
"""
batch_size, n_samples = z_vals.shape
pts = rays_o[:, None, :] + rays_d[:, None, :] * z_vals[..., :, None] # n_rays, n_samples, 3
# pts = pts.flip((-1,)) * 2 - 1
pts = pts * 2 - 1
pts = self.deform_pts(pts=pts, pts_ts=pts_ts)
radius = torch.linalg.norm(pts, ord=2, dim=-1, keepdim=False)
inside_sphere = (radius[:, :-1] < 1.0) | (radius[:, 1:] < 1.0)
sdf = sdf.reshape(batch_size, n_samples)
prev_sdf, next_sdf = sdf[:, :-1], sdf[:, 1:]
prev_z_vals, next_z_vals = z_vals[:, :-1], z_vals[:, 1:]
mid_sdf = (prev_sdf + next_sdf) * 0.5
cos_val = (next_sdf - prev_sdf) / (next_z_vals - prev_z_vals + 1e-5)
# ----------------------------------------------------------------------------------------------------------
# Use min value of [ cos, prev_cos ]
# Though it makes the sampling (not rendering) a little bit biased, this strategy can make the sampling more
# robust when meeting situations like below:
#
# SDF
# ^
# |\ -----x----...
# | \ /
# | x x
# |---\----/-------------> 0 level
# | \ /
# | \/
# |
# ----------------------------------------------------------------------------------------------------------
prev_cos_val = torch.cat([torch.zeros([batch_size, 1]), cos_val[:, :-1]], dim=-1)
cos_val = torch.stack([prev_cos_val, cos_val], dim=-1)
cos_val, _ = torch.min(cos_val, dim=-1, keepdim=False)
cos_val = cos_val.clip(-1e3, 0.0) * inside_sphere
dist = (next_z_vals - prev_z_vals)
prev_esti_sdf = mid_sdf - cos_val * dist * 0.5
next_esti_sdf = mid_sdf + cos_val * dist * 0.5
prev_cdf = torch.sigmoid(prev_esti_sdf * inv_s)
next_cdf = torch.sigmoid(next_esti_sdf * inv_s)
alpha = (prev_cdf - next_cdf + 1e-5) / (prev_cdf + 1e-5)
weights = alpha * torch.cumprod(
torch.cat([torch.ones([batch_size, 1]), 1. - alpha + 1e-7], -1), -1)[:, :-1]
z_samples = sample_pdf(z_vals, weights, n_importance, det=True).detach()
return z_samples
def cat_z_vals(self, rays_o, rays_d, z_vals, new_z_vals, sdf, last=False, pts_ts=0):
batch_size, n_samples = z_vals.shape
_, n_importance = new_z_vals.shape
pts = rays_o[:, None, :] + rays_d[:, None, :] * new_z_vals[..., :, None]
# pts = pts.flip((-1,)) * 2 - 1
pts = pts * 2 - 1
pts = self.deform_pts(pts=pts, pts_ts=pts_ts)
z_vals = torch.cat([z_vals, new_z_vals], dim=-1)
z_vals, index = torch.sort(z_vals, dim=-1)
if not last:
new_sdf = self.sdf_network.sdf(pts.reshape(-1, 3)).reshape(batch_size, n_importance)
sdf = torch.cat([sdf, new_sdf], dim=-1)
xx = torch.arange(batch_size)[:, None].expand(batch_size, n_samples + n_importance).reshape(-1)
index = index.reshape(-1)
sdf = sdf[(xx, index)].reshape(batch_size, n_samples + n_importance)
return z_vals, sdf
def render_core(self,
rays_o,
rays_d,
z_vals,
sample_dist,
sdf_network,
deviation_network,
color_network,
background_alpha=None,
background_sampled_color=None,
background_rgb=None,
cos_anneal_ratio=0.0,
pts_ts=0):
batch_size, n_samples = z_vals.shape
# Section length
dists = z_vals[..., 1:] - z_vals[..., :-1]
dists = torch.cat([dists, torch.Tensor([sample_dist]).expand(dists[..., :1].shape)], -1)
mid_z_vals = z_vals + dists * 0.5 # z_vals and dists * 0.5 #
# Section midpoints
pts = rays_o[:, None, :] + rays_d[:, None, :] * mid_z_vals[..., :, None] # n_rays, n_samples, 3
dirs = rays_d[:, None, :].expand(pts.shape)
pts = pts.reshape(-1, 3) # pts, nn_ou
dirs = dirs.reshape(-1, 3)
pts = (pts - self.minn_pts) / (self.maxx_pts - self.minn_pts)
# pts = pts.flip((-1,)) * 2 - 1
pts = pts * 2 - 1
pts = self.deform_pts(pts=pts, pts_ts=pts_ts)
sdf_nn_output = sdf_network(pts)
sdf = sdf_nn_output[:, :1]
feature_vector = sdf_nn_output[:, 1:]
gradients = sdf_network.gradient(pts).squeeze()
sampled_color = color_network(pts, gradients, dirs, feature_vector).reshape(batch_size, n_samples, 3)
# deviation network #
inv_s = deviation_network(torch.zeros([1, 3]))[:, :1].clip(1e-6, 1e6) # Single parameter
inv_s = inv_s.expand(batch_size * n_samples, 1)
true_cos = (dirs * gradients).sum(-1, keepdim=True)
# "cos_anneal_ratio" grows from 0 to 1 in the beginning training iterations. The anneal strategy below makes
# the cos value "not dead" at the beginning training iterations, for better convergence.
iter_cos = -(F.relu(-true_cos * 0.5 + 0.5) * (1.0 - cos_anneal_ratio) +
F.relu(-true_cos) * cos_anneal_ratio) # always non-positive
# Estimate signed distances at section points
estimated_next_sdf = sdf + iter_cos * dists.reshape(-1, 1) * 0.5
estimated_prev_sdf = sdf - iter_cos * dists.reshape(-1, 1) * 0.5
prev_cdf = torch.sigmoid(estimated_prev_sdf * inv_s)
next_cdf = torch.sigmoid(estimated_next_sdf * inv_s)
p = prev_cdf - next_cdf
c = prev_cdf
alpha = ((p + 1e-5) / (c + 1e-5)).reshape(batch_size, n_samples).clip(0.0, 1.0)
pts_norm = torch.linalg.norm(pts, ord=2, dim=-1, keepdim=True).reshape(batch_size, n_samples)
inside_sphere = (pts_norm < 1.0).float().detach()
relax_inside_sphere = (pts_norm < 1.2).float().detach()
# Render with background
if background_alpha is not None:
alpha = alpha * inside_sphere + background_alpha[:, :n_samples] * (1.0 - inside_sphere)
alpha = torch.cat([alpha, background_alpha[:, n_samples:]], dim=-1)
sampled_color = sampled_color * inside_sphere[:, :, None] +\
background_sampled_color[:, :n_samples] * (1.0 - inside_sphere)[:, :, None]
sampled_color = torch.cat([sampled_color, background_sampled_color[:, n_samples:]], dim=1)
weights = alpha * torch.cumprod(torch.cat([torch.ones([batch_size, 1]), 1. - alpha + 1e-7], -1), -1)[:, :-1]
weights_sum = weights.sum(dim=-1, keepdim=True)
color = (sampled_color * weights[:, :, None]).sum(dim=1)
if background_rgb is not None: # Fixed background, usually black
color = color + background_rgb * (1.0 - weights_sum)
# Eikonal loss
gradient_error = (torch.linalg.norm(gradients.reshape(batch_size, n_samples, 3), ord=2,
dim=-1) - 1.0) ** 2
gradient_error = (relax_inside_sphere * gradient_error).sum() / (relax_inside_sphere.sum() + 1e-5)
return {
'color': color,
'sdf': sdf,
'dists': dists,
'gradients': gradients.reshape(batch_size, n_samples, 3),
's_val': 1.0 / inv_s,
'mid_z_vals': mid_z_vals,
'weights': weights,
'cdf': c.reshape(batch_size, n_samples),
'gradient_error': gradient_error,
'inside_sphere': inside_sphere
}
def render(self, rays_o, rays_d, near, far, pts_ts=0, perturb_overwrite=-1, background_rgb=None, cos_anneal_ratio=0.0, use_gt_sdf=False):
batch_size = len(rays_o)
sample_dist = 2.0 / self.n_samples # Assuming the region of interest is a unit sphere
z_vals = torch.linspace(0.0, 1.0, self.n_samples)
z_vals = near + (far - near) * z_vals[None, :]
z_vals_outside = None
if self.n_outside > 0:
z_vals_outside = torch.linspace(1e-3, 1.0 - 1.0 / (self.n_outside + 1.0), self.n_outside)
n_samples = self.n_samples
perturb = self.perturb
if perturb_overwrite >= 0:
perturb = perturb_overwrite
if perturb > 0:
t_rand = (torch.rand([batch_size, 1]) - 0.5)
z_vals = z_vals + t_rand * 2.0 / self.n_samples
if self.n_outside > 0:
mids = .5 * (z_vals_outside[..., 1:] + z_vals_outside[..., :-1])
upper = torch.cat([mids, z_vals_outside[..., -1:]], -1)
lower = torch.cat([z_vals_outside[..., :1], mids], -1)
t_rand = torch.rand([batch_size, z_vals_outside.shape[-1]])
z_vals_outside = lower[None, :] + (upper - lower)[None, :] * t_rand
if self.n_outside > 0:
z_vals_outside = far / torch.flip(z_vals_outside, dims=[-1]) + 1.0 / self.n_samples
background_alpha = None
background_sampled_color = None
# Up sample
if self.n_importance > 0:
with torch.no_grad():
pts = rays_o[:, None, :] + rays_d[:, None, :] * z_vals[..., :, None]
pts = (pts - self.minn_pts) / (self.maxx_pts - self.minn_pts)
# sdf = self.sdf_network.sdf(pts.reshape(-1, 3)).reshape(batch_size, self.n_samples)
# gt_sdf #
#
# pts = ((pts - xyz_min) / (xyz_max - xyz_min)).flip((-1,)) * 2 - 1
# pts = pts.flip((-1,)) * 2 - 1
pts = pts * 2 - 1
pts = self.deform_pts(pts=pts, pts_ts=pts_ts)
pts_exp = pts.reshape(-1, 3)
# minn_pts, _ = torch.min(pts_exp, dim=0)
# maxx_pts, _ = torch.max(pts_exp, dim=0) # deformation field (not a rigid one) -> the meshes #
# print(f"minn_pts: {minn_pts}, maxx_pts: {maxx_pts}")
# pts_to_near = pts - near.unsqueeze(1)
# maxx_pts = 1.5; minn_pts = -1.5
# # maxx_pts = 3; minn_pts = -3
# # maxx_pts = 1; minn_pts = -1
# pts_exp = (pts_exp - minn_pts) / (maxx_pts - minn_pts)
## render and iamges ####
if use_gt_sdf:
### use the GT sdf field ####
# print(f"Using gt sdf :")
sdf = self.gt_sdf(pts_exp.reshape(-1, 3).detach().cpu().numpy())
sdf = torch.from_numpy(sdf).float().cuda()
sdf = sdf.reshape(batch_size, self.n_samples)
### use the GT sdf field ####
else:
#### use the optimized sdf field ####
sdf = self.sdf_network.sdf(pts_exp).reshape(batch_size, self.n_samples)
#### use the optimized sdf field ####
for i in range(self.up_sample_steps):
new_z_vals = self.up_sample(rays_o,
rays_d,
z_vals,
sdf,
self.n_importance // self.up_sample_steps,
64 * 2**i,
pts_ts=pts_ts)
z_vals, sdf = self.cat_z_vals(rays_o,
rays_d,
z_vals,
new_z_vals,
sdf,
last=(i + 1 == self.up_sample_steps),
pts_ts=pts_ts)
n_samples = self.n_samples + self.n_importance
# Background model
if self.n_outside > 0:
z_vals_feed = torch.cat([z_vals, z_vals_outside], dim=-1)
z_vals_feed, _ = torch.sort(z_vals_feed, dim=-1)
ret_outside = self.render_core_outside(rays_o, rays_d, z_vals_feed, sample_dist, self.nerf, pts_ts=pts_ts)
background_sampled_color = ret_outside['sampled_color']
background_alpha = ret_outside['alpha']
# Render core
ret_fine = self.render_core(rays_o, #
rays_d,
z_vals,
sample_dist,
self.sdf_network,
self.deviation_network,
self.color_network,
background_rgb=background_rgb,
background_alpha=background_alpha,
background_sampled_color=background_sampled_color,
cos_anneal_ratio=cos_anneal_ratio,
pts_ts=pts_ts)
color_fine = ret_fine['color']
weights = ret_fine['weights']
weights_sum = weights.sum(dim=-1, keepdim=True)
gradients = ret_fine['gradients']
s_val = ret_fine['s_val'].reshape(batch_size, n_samples).mean(dim=-1, keepdim=True)
return {
'color_fine': color_fine,
's_val': s_val,
'cdf_fine': ret_fine['cdf'],
'weight_sum': weights_sum,
'weight_max': torch.max(weights, dim=-1, keepdim=True)[0],
'gradients': gradients,
'weights': weights,
'gradient_error': ret_fine['gradient_error'],
'inside_sphere': ret_fine['inside_sphere']
}
def extract_geometry(self, bound_min, bound_max, resolution, threshold=0.0):
return extract_geometry(bound_min, # extract geometry #
bound_max,
resolution=resolution,
threshold=threshold,
query_func=lambda pts: -self.sdf_network.sdf(pts))
def extract_geometry_tets(self, bound_min, bound_max, resolution, pts_ts=0, threshold=0.0, wdef=False):
if wdef:
return extract_geometry_tets(bound_min, # extract geometry #
bound_max,
resolution=resolution,
threshold=threshold,
query_func=lambda pts: -self.sdf_network.sdf(pts),
def_func=lambda pts: self.deform_pts(pts, pts_ts=pts_ts))
else:
return extract_geometry_tets(bound_min, # extract geometry #
bound_max,
resolution=resolution,
threshold=threshold,
query_func=lambda pts: -self.sdf_network.sdf(pts))