Spaces:
Running
Running
import numpy as np | |
import torch | |
import torch.nn as nn | |
import trimesh | |
from sklearn.neighbors import KDTree | |
import tqdm | |
import cv2 as cv | |
import os, glob | |
from .lib.networks.faceverse_torch import FaceVerseModel | |
from .lib.networks.smpl_torch import SmplTorch | |
from .lib.utils.gaussian_np_utils import GaussianAttributes, load_gaussians_from_ply, save_gaussians_as_ply, \ | |
apply_transformation_to_gaussians, combine_gaussians, select_gaussians, update_gaussian_attributes | |
from .lib.utils.geometry import search_nearest_correspondence, estimate_rigid_transformation | |
from .lib.utils.sh_utils import SH2RGB | |
def process_smpl_head(): | |
smpl = SmplTorch(model_file='./AnimatableGaussians/smpl_files/smplx/SMPLX_NEUTRAL.npz') | |
smpl_v_template = smpl.v_template.detach().cpu().numpy() | |
smpl_faces = smpl.faces.detach().cpu().numpy() | |
# head_skinning_weights = smpl.weights[:, 15] + smpl.weights[:, 22] + smpl.weights[:, 23] + smpl.weights[:, 24] | |
# | |
# blend_weight = np.clip(head_skinning_weights* 1.2 - 0.2, 0, 1) | |
# head_ids = np.where(blend_weight > 0)[0] | |
# | |
# np.savez('./data/smplx_head_vidx_and_blendweight.npz', blend_weight=blend_weight, head_ids=head_ids) | |
if not os.path.exists('./data/smpl_models/smplx_head_3.obj'): | |
trimesh.Trimesh(vertices=smpl_v_template, faces=smpl_faces).export('./data/smpl_models/smplx_head_3.obj') | |
print('Please cut out SMPL head!!!') | |
import pdb; pdb.set_trace() | |
smplx_head = trimesh.load('./data/smpl_models/smplx_head_3.obj') | |
smplx_to_head_dist = np.zeros([smpl_v_template.shape[0]]) | |
for vi, v in enumerate(smpl_v_template): | |
nndist = np.min(np.linalg.norm(v.reshape([1, 3]) - smplx_head.vertices, axis=1)) | |
smplx_to_head_dist[vi] = nndist | |
head_ids = np.where(smplx_to_head_dist < 0.001)[0] | |
blend_weight = np.exp(-smplx_to_head_dist*smplx_to_head_dist * 2000) | |
np.savez('./data/smpl_models/smplx_head_vidx_and_blendweight.npz', blend_weight=blend_weight, head_ids=head_ids) | |
return | |
def load_body_params(path): | |
param = dict(np.load(path)) | |
global_orient = param['global_orient'] | |
transl = param['transl'] | |
body_pose = param['body_pose'] | |
betas = param['betas'] | |
return global_orient, transl, body_pose, betas | |
def load_face_params(path): | |
param = dict(np.load(path)) | |
pose = param['pose'] | |
scale = param['scale'] | |
id_coeff = param['id_coeff'] | |
exp_coeff = param['exp_coeff'] | |
return pose, scale, id_coeff, exp_coeff | |
def get_smpl_verts_and_head_transformation(smpl, global_orient, body_pose, transl, betas): | |
pose = torch.cat([ | |
torch.from_numpy(global_orient.astype(np.float32)), | |
torch.from_numpy(body_pose.astype(np.float32)), | |
torch.zeros([(3+15+15)*3], dtype=torch.float32)], dim=-1) | |
beta = torch.from_numpy(betas.astype(np.float32)) | |
verts, skinning_dict = smpl.forward(pose.reshape(1, -1), beta.reshape(1, -1)) | |
verts = verts[0].detach().cpu().numpy() | |
head_joint_transfmat = skinning_dict['G'][0, 15].detach().cpu().numpy() | |
verts += transl.reshape([1, 3]) | |
head_joint_transfmat[:3, 3] += transl.reshape([3]) | |
return verts, head_joint_transfmat | |
def crop_facial_area(faceverse_verts, points, dist_thres=0.025): | |
min_x, min_y, min_z = np.min(faceverse_verts, axis=0) | |
max_x, max_y, max_z = np.max(faceverse_verts, axis=0) | |
pad = dist_thres*2 | |
in_bbox_mask = (points[:, 0] > min_x - pad) * (points[:, 0] < max_x + pad) * \ | |
(points[:, 1] > min_y - pad) * (points[:, 1] < max_y + pad) * \ | |
(points[:, 2] > min_z - pad) * (points[:, 2] < max_z + pad) | |
in_bbox_idx = np.where(in_bbox_mask)[0] | |
facial_points = points[in_bbox_mask] | |
nndist = np.ones([len(facial_points)]) * 1e10 | |
for i in tqdm.trange(len(facial_points), desc='calculating facial area'): | |
nndist[i] = np.min(np.linalg.norm(faceverse_verts - facial_points[i:(i+1)], axis=1, keepdims=False)) | |
close_to_face_mask = nndist < dist_thres | |
facial_points = facial_points[close_to_face_mask] | |
facial_idx = in_bbox_idx[close_to_face_mask] | |
return facial_points, facial_idx | |
def crop_facial_area2(smpl_verts, smpl_head_vids, points): | |
min_x, min_y, min_z = np.min(smpl_verts[smpl_head_vids], axis=0) | |
max_x, max_y, max_z = np.max(smpl_verts[smpl_head_vids], axis=0) | |
pad = 0.05 | |
in_bbox_mask = (points[:, 0] > min_x - pad) * (points[:, 0] < max_x + pad) * \ | |
(points[:, 1] > min_y - pad) * (points[:, 1] < max_y + pad) * \ | |
(points[:, 2] > min_z - pad) * (points[:, 2] < max_z + pad) | |
in_bbox_idx = np.where(in_bbox_mask)[0] | |
facial_points = points[in_bbox_mask] | |
smpl_head_mask = np.zeros([len(smpl_verts)], dtype=np.bool_) | |
smpl_head_mask[smpl_head_vids] = True | |
close_to_face_mask = np.zeros([len(facial_points)], dtype=np.bool_) | |
for i in tqdm.trange(len(facial_points)): | |
nnid = np.argmin(np.linalg.norm(smpl_verts - facial_points[i:(i+1)], axis=1, keepdims=False)) | |
close_to_face_mask[i] = smpl_head_mask[nnid] | |
facial_points = facial_points[close_to_face_mask] | |
facial_idx = in_bbox_idx[close_to_face_mask] | |
return facial_points, facial_idx | |
def transform_faceverse_to_live_body_space(faceverse_verts, faceverse_to_smplx, head_joint_transfmat): | |
faceverse_verts = np.matmul(faceverse_verts, faceverse_to_smplx[:3, :3].transpose()) + faceverse_to_smplx[:3, 3].reshape(1, 3) | |
faceverse_verts = np.matmul(faceverse_verts, head_joint_transfmat[:3, :3].transpose()) + head_joint_transfmat[:3, 3].reshape(1, 3) | |
return faceverse_verts | |
def calc_livehead2livebody(head_pose, smplx_to_faceverse, head_joint_transfmat): | |
head_cano2live = np.eye(4, dtype=np.float32) | |
head_cano2live[:3, :3] = cv.Rodrigues(head_pose[:3])[0] | |
head_cano2live[:3, 3] = head_pose[3:] | |
head_live2cano = np.linalg.inv(head_cano2live) | |
faceverse_to_smplx = np.linalg.inv(smplx_to_faceverse) | |
total_transf = np.eye(4, dtype=np.float32) | |
for t in [head_live2cano, np.diag([1, -1, -1, 1]), faceverse_to_smplx, head_joint_transfmat]: | |
total_transf = np.matmul(t, total_transf) | |
return total_transf | |
def get_face_blend_weight(head_facial_points, smpl_verts, sigma=0.015): | |
# dists = np.load('./data/faceverse/smplx_verts_to_faceverse_dist.npy').astype(np.float32) | |
# face_nerf_blend_weight = np.exp(-dists**2/(2*sigma**2)) | |
# face_nerf_blend_weight = np.clip(face_nerf_blend_weight*1.2 - 0.1, 0, 1) | |
smpl_blend_weight = dict(np.load('./data/smpl_models/smplx_head_vidx_and_blendweight.npz'))['blend_weight'] | |
corr_idx_, _ = search_nearest_correspondence(head_facial_points, smpl_verts) | |
corr_bw = smpl_blend_weight[corr_idx_] | |
for _ in tqdm.trange(10): | |
corr_bw_ = np.zeros_like(corr_bw) | |
tree = KDTree(head_facial_points, leaf_size=2) | |
for i in range(len(head_facial_points)): | |
_, idx = tree.query(head_facial_points[i:(i+1)], k=4) | |
corr_bw_[i] = np.mean(corr_bw[idx]) | |
corr_bw = np.copy(corr_bw_) | |
# corr_bw = np.clip(corr_bw*1.2 - 0.15, 0, 1) | |
# with open('./debug/debug_head_facial_bw.obj', 'w') as fp: | |
# for p, w in zip(head_facial_points, corr_bw): | |
# fp.write('v %f %f %f %f %f %f\n' % (p[0], p[1], p[2], w, w, w)) | |
# import pdb; pdb.set_trace() | |
return corr_bw | |
def get_face_blend_weight2(head_facial_points, body_points, body_facial_idx): | |
body_facial_bbox_min = np.min(body_points[body_facial_idx], axis=0) | |
body_facial_bbox_max = np.max(body_points[body_facial_idx], axis=0) | |
body_facial_bbox_min = body_facial_bbox_min - 0.1 | |
body_facial_bbox_max = body_facial_bbox_max + 0.1 | |
inside_bbox_flag = \ | |
np.int32(body_points[:, 0] > body_facial_bbox_min[0]) * \ | |
np.int32(body_points[:, 0] < body_facial_bbox_max[0]) * \ | |
np.int32(body_points[:, 1] > body_facial_bbox_min[1]) * \ | |
np.int32(body_points[:, 1] < body_facial_bbox_max[1]) * \ | |
np.int32(body_points[:, 2] > body_facial_bbox_min[2]) * \ | |
np.int32(body_points[:, 2] < body_facial_bbox_max[2]) | |
point_idx_inside_bbox = np.nonzero(inside_bbox_flag >0)[0] | |
body_blend_weight = np.zeros([len(body_points)], dtype=np.float32) | |
body_blend_weight[body_facial_idx] = 1 | |
body_points_in_bbox = body_points[point_idx_inside_bbox] | |
body_blend_weight_in_bbox = body_blend_weight[point_idx_inside_bbox] | |
for _ in tqdm.trange(1, desc='Calculating body facial blend weight'): | |
corr_bw_ = np.zeros_like(body_blend_weight_in_bbox) | |
tree = KDTree(body_points_in_bbox, leaf_size=2) | |
for i in tqdm.trange(len(body_points_in_bbox)): | |
ind = tree.query_radius(body_points_in_bbox[i:(i+1)], r=0.035) | |
corr_bw_[i] = np.mean(body_blend_weight_in_bbox[ind[0]]) | |
body_blend_weight_in_bbox = np.copy(corr_bw_) | |
body_blend_weight[point_idx_inside_bbox] = body_blend_weight_in_bbox | |
with open('./debug/debug_body_facial_bw.obj', 'w') as fp: | |
for p, w in zip(body_points, body_blend_weight): | |
fp.write('v %f %f %f %f %f %f\n' % (p[0], p[1], p[2], w, w, w)) | |
tree = KDTree(body_points, leaf_size=2) | |
corr_bw = np.zeros([len(head_facial_points)], dtype=np.float32) | |
for i in range(len(head_facial_points)): | |
_, idx = tree.query(head_facial_points[i:(i+1)], k=4) | |
corr_bw[i] = np.mean(body_blend_weight[idx]) | |
# corr_bw = np.clip(corr_bw*1.2 - 0.15, 0, 1) | |
corr_bw_bmin, corr_bw_bmax = np.percentile(corr_bw, 5), np.percentile(corr_bw, 95) | |
corr_bw = np.clip((corr_bw-corr_bw_bmin)/(corr_bw_bmax-corr_bw_bmin), 0, 1) | |
with open('./debug/debug_head_facial_bw.obj', 'w') as fp: | |
for p, w in zip(head_facial_points, corr_bw): | |
fp.write('v %f %f %f %f %f %f\n' % (p[0], p[1], p[2], w, w, w)) | |
return corr_bw | |
def estimate_color_transfer(head_facial_points, body_facial_points, head_facial_color, body_facial_color, head_facial_opacity): | |
head_facial_color = head_facial_color * 0.28209479177387814 + 0.5 | |
body_facial_color = body_facial_color * 0.28209479177387814 + 0.5 | |
corr_idx, _ = search_nearest_correspondence(head_facial_points, body_facial_points) | |
corr_color = body_facial_color[corr_idx] | |
opacity = 1/(1+np.exp(-head_facial_opacity)) | |
weight = np.float32(opacity > 0.35) | |
head_facial_color = head_facial_color.reshape(len(head_facial_color), 3) * weight.reshape([-1, 1]) | |
corr_color = corr_color.reshape(len(corr_color), 3) * weight.reshape([-1, 1]) | |
head_facial_color = np.concatenate([head_facial_color, np.zeros_like(head_facial_color[:, :1])], axis=1) | |
corr_color = np.concatenate([corr_color, np.zeros_like(corr_color[:, :1])], axis=1) | |
transfer = nn.Parameter(torch.eye(4, dtype=torch.float32)) | |
head_facial_color_th = torch.from_numpy(head_facial_color).float() | |
corr_color_th = torch.from_numpy(corr_color).float() | |
weight_th = torch.from_numpy(weight).float() | |
optim = torch.optim.Adam([transfer], lr=1e-2) | |
for i in range(500): | |
optim.zero_grad() | |
loss = torch.mean(torch.abs(corr_color_th - torch.matmul(head_facial_color_th, transfer.permute(1, 0)))*weight_th) | |
loss = loss + torch.sum(torch.square(transfer - torch.eye(4, dtype=torch.float32))) * 5e-2 | |
if i % 25 == 0: | |
print(loss.item()) | |
loss.backward() | |
optim.step() | |
transfer = transfer.detach().cpu().numpy() | |
print(transfer) | |
# with open('./debug/debug_body_facial_color_updated.obj', 'w') as fp: | |
# for p, c in zip(body_facial_points, body_facial_color): | |
# # c = c * 0.28209479177387814 + 0.5 | |
# c = np.clip(c, 0, 1) | |
# fp.write('v %f %f %f %f %f %f\n' % (p[0], p[1], p[2], c[0], c[1], c[2])) | |
# with open('./debug/debug_head_facial_color_updated.obj', 'w') as fp: | |
# head_facial_color = np.matmul(head_facial_color, transfer) | |
# for p, c, w in zip(head_facial_points, head_facial_color, weight): | |
# if w < 0.1: | |
# continue | |
# # c = c * 0.28209479177387814 + 0.5 | |
# c = np.clip(c, 0, 1) | |
# fp.write('v %f %f %f %f %f %f\n' % (p[0], p[1], p[2], c[0], c[1], c[2])) | |
# import pdb; pdb.set_trace() | |
return transfer | |
def blend_color(head_facial_color, body_facial_color, blend_weight): | |
blend_weight = blend_weight.reshape([len(blend_weight)] + [1]*(len(head_facial_color.shape)-1)) | |
result = head_facial_color * blend_weight + body_facial_color * (1-blend_weight) | |
return result | |
def save_body_face_stitching_data( | |
result_path, smplx_to_faceverse, residual_transf, body_nonface_mask, head_nonface_mask, | |
head_facial_idx, body_facial_idx, corr_idx, face_color_bw, color_transfer): | |
# os.makedirs('./data/%s' % result_suffix, exist_ok=True) | |
# np.savez('./data/%s/body_face_blending_param.npz' % result_suffix, | |
# smplx_to_faceverse=smplx_to_faceverse.astype(np.float32), | |
# residual_transf=residual_transf.astype(np.float32), | |
# body_nonface_mask=body_nonface_mask.astype(np.int32), | |
# head_facial_idx=head_facial_idx.astype(np.int32), | |
# body_facial_idx=body_facial_idx.astype(np.int32), | |
# head_body_facial_corr_idx=corr_idx.astype(np.int32), | |
# face_color_bw=face_color_bw.astype(np.float32), | |
# color_transfer=color_transfer.astype(np.float32)) | |
head_color_bw = np.zeros([len(head_nonface_mask)]) | |
head_color_bw[head_facial_idx] = face_color_bw | |
head_corr_idx = np.zeros([len(head_nonface_mask)]) | |
head_corr_idx[head_facial_idx] = body_facial_idx[corr_idx] | |
np.savez(result_path, | |
smplx_to_faceverse=smplx_to_faceverse.astype(np.float32), | |
residual_transf=residual_transf.astype(np.float32), | |
body_nonface_mask=body_nonface_mask.astype(np.int32), | |
head_nonface_mask=head_nonface_mask.astype(np.int32), | |
head_facial_idx=head_facial_idx.astype(np.int32), | |
body_facial_idx=body_facial_idx.astype(np.int32), | |
head_body_corr_idx=head_corr_idx.astype(np.int32), | |
head_color_bw=head_color_bw.astype(np.float32), | |
color_transfer=color_transfer.astype(np.float32)) | |
return | |
def manual_refine_facial_cropping(head_facial_points, head_facial_idx, head_facial_colors, body_facial_points, body_facial_idx, body_facial_colors): | |
def _save_points_as_obj(fpath, points, points_color): | |
points_color = np.clip(points_color, 0, 1) | |
with open(fpath, 'w') as fp: | |
for p, c in zip(points, points_color): | |
fp.write('v %f %f %f %f %f %f\n' % (p[0], p[1], p[2], c[0], c[1], c[2])) | |
return | |
_save_points_as_obj('./debug/head_facial_points.obj', head_facial_points, head_facial_colors) | |
_save_points_as_obj('./debug/body_facial_points.obj', body_facial_points, body_facial_colors) | |
# trimesh.Trimesh(vertices=head_facial_points, vertex_colors=head_facial_colors).export('./debug/head_facial_points.obj') | |
# trimesh.Trimesh(vertices=body_facial_points, vertex_colors=body_facial_colors).export('./debug/body_facial_points.obj') | |
if True: | |
print('Saving facial points cropped by algorithms. Please remove unnecessary points manually!') | |
import pdb; pdb.set_trace() | |
head_facial_points_ = trimesh.load('./debug/head_facial_points.obj').vertices | |
body_facial_points_ = trimesh.load('./debug/body_facial_points.obj').vertices | |
_, head_nndist = search_nearest_correspondence(head_facial_points, head_facial_points_) | |
_, body_nndist = search_nearest_correspondence(body_facial_points, body_facial_points_) | |
head_flag = head_nndist < 1e-4 | |
body_flag = body_nndist < 1e-4 | |
return head_facial_points[head_flag], head_facial_idx[head_flag], body_facial_points[body_flag], body_facial_idx[body_flag] | |
def stitch_body_and_head(ref_body_gaussian_path, ref_head_gaussian_path, | |
ref_body_param_path, ref_head_param_path, | |
smplx2faceverse_path, result_folder): | |
device = torch.device("cuda") | |
body_gaussians = load_gaussians_from_ply(ref_body_gaussian_path) | |
head_gaussians = load_gaussians_from_ply(ref_head_gaussian_path) | |
global_orient, transl, body_pose, betas = load_body_params(ref_body_param_path) | |
head_pose, head_scale, id_coeff, exp_coeff = load_face_params(ref_head_param_path) | |
smplx_to_faceverse = np.load(smplx2faceverse_path) | |
faceverse_to_smplx = np.linalg.inv(smplx_to_faceverse) | |
smpl = SmplTorch(model_file='./AnimatableGaussians/smpl_files/smplx/SMPLX_NEUTRAL.npz') | |
smpl_verts, head_joint_transfmat = get_smpl_verts_and_head_transformation( | |
smpl, global_orient, body_pose, transl, betas) | |
smpl_head_vids = dict(np.load('./data/smpl_models/smplx_head_vidx_and_blendweight.npz'))['head_ids'] | |
smpl_head_verts = smpl_verts[smpl_head_vids] | |
model_dict = np.load('./data/faceverse_models/faceverse_simple_v2.npy', allow_pickle=True).item() | |
faceverse_model = FaceVerseModel(model_dict, batch_size=1) | |
faceverse_model.init_coeff_tensors( | |
id_coeff=torch.from_numpy(id_coeff).reshape([1, -1]).to(device), | |
scale_coeff=torch.from_numpy(head_scale).reshape([1, 1]).to(device), | |
) | |
faceverse_verts = faceverse_model.forward()['v'][0].detach().cpu().numpy() | |
faceverse_verts = transform_faceverse_to_live_body_space(faceverse_verts, faceverse_to_smplx, head_joint_transfmat) | |
livehead2livebody = calc_livehead2livebody( | |
head_pose, smplx_to_faceverse, head_joint_transfmat) | |
head_gaussians_xyz = np.matmul(head_gaussians.xyz, livehead2livebody[:3, :3].transpose()) \ | |
+ livehead2livebody[:3, 3].reshape(1, 3) | |
# head_facial_points, head_facial_idx = crop_facial_area(smpl_head_verts, head_gaussians_xyz) | |
# body_facial_points, body_facial_idx = crop_facial_area(smpl_head_verts, body_gaussians.xyz) | |
head_facial_points, head_facial_idx = crop_facial_area2(smpl_verts, smpl_head_vids, head_gaussians_xyz) | |
body_facial_points, body_facial_idx = crop_facial_area2(smpl_verts, smpl_head_vids, body_gaussians.xyz) | |
residual_transf = np.eye(4) | |
head_facial_points, head_facial_idx, body_facial_points, body_facial_idx = manual_refine_facial_cropping( | |
head_facial_points, head_facial_idx, SH2RGB(head_gaussians.features_dc[head_facial_idx]), | |
body_facial_points, body_facial_idx, SH2RGB(body_gaussians.features_dc[body_facial_idx])) | |
while True: | |
for _ in tqdm.trange(4, desc='Fitting residual transformation'): | |
corr_idx, _ = search_nearest_correspondence(head_facial_points, body_facial_points) | |
corr = body_facial_points[corr_idx] | |
transf = estimate_rigid_transformation(head_facial_points, corr) | |
residual_transf = np.matmul(transf, residual_transf) | |
head_facial_points = np.matmul(head_facial_points, transf[:3, :3].transpose()) + transf[:3, 3].reshape(1, 3) | |
if_crop_well = input('If the facial cropping is good enough? (y/n): ') | |
if if_crop_well == 'y': | |
break | |
else: | |
head_facial_points, head_facial_idx, body_facial_points, body_facial_idx = manual_refine_facial_cropping( | |
head_facial_points, head_facial_idx, SH2RGB(head_gaussians.features_dc[head_facial_idx]), | |
body_facial_points, body_facial_idx, SH2RGB(body_gaussians.features_dc[body_facial_idx])) | |
# head_facial_points, head_facial_idx, body_facial_points, body_facial_idx = manual_refine_facial_cropping( | |
# head_facial_points, head_facial_idx, SH2RGB(head_gaussians.features_dc[head_facial_idx]), | |
# body_facial_points, body_facial_idx, SH2RGB(body_gaussians.features_dc[body_facial_idx])) | |
# 更改一下逻辑,改成直到对齐为止。 | |
print(np.matmul(residual_transf, livehead2livebody)) | |
residual_transf = np.matmul(np.linalg.inv(livehead2livebody), np.matmul(residual_transf, livehead2livebody)) | |
corr_idx, _ = search_nearest_correspondence(head_facial_points, body_facial_points) | |
# head_gaussians_xyz = np.matmul(head_gaussians_xyz, residual_transf[:3, :3].transpose()) + residual_transf[:3, 3].reshape(1, 3) | |
# faceverse_verts = np.matmul(faceverse_verts, residual_transf[:3, :3].transpose()) + residual_transf[:3, 3].reshape(1, 3) | |
# total_transf = np.matmul(residual_transf, livehead2livebody) | |
total_transf = np.matmul(livehead2livebody, residual_transf) | |
print(total_transf) | |
color_transfer = estimate_color_transfer( | |
head_facial_points, body_facial_points, | |
head_gaussians.features_dc[head_facial_idx], body_gaussians.features_dc[body_facial_idx], | |
head_gaussians.opacities[head_facial_idx] | |
) | |
# face_color_bw = get_face_blend_weight(head_facial_points, smpl_verts, sigma=0.015) | |
face_color_bw = get_face_blend_weight2(head_facial_points, body_gaussians.xyz, body_facial_idx) | |
body_nonface_mask = np.ones([len(body_gaussians.xyz)], dtype=np.bool_) | |
body_nonface_mask[body_facial_idx] = 0 | |
head_nonface_mask = np.ones([len(head_gaussians.xyz)], dtype=np.bool_) | |
head_nonface_mask[head_facial_idx] = 0 | |
save_body_face_stitching_data( | |
os.path.join(result_folder, 'body_head_blending_param.npz'), | |
smplx_to_faceverse, residual_transf, body_nonface_mask, head_nonface_mask, | |
head_facial_idx, body_facial_idx, corr_idx, face_color_bw, color_transfer) | |
body_gaussians = apply_transformation_to_gaussians(body_gaussians, np.eye(4)) | |
head_gaussians = apply_transformation_to_gaussians(head_gaussians, total_transf, np.eye(3)) | |
body_gaussians_wo_face = select_gaussians(body_gaussians, body_nonface_mask) | |
head_gaussians_face_only = select_gaussians(head_gaussians, head_facial_idx) | |
head_gaussians_face_only_new_color = blend_color( | |
head_gaussians_face_only.features_dc, body_gaussians.features_dc[body_facial_idx][corr_idx], face_color_bw) | |
head_gaussians_face_only_new_xyz = blend_color( | |
head_gaussians_face_only.xyz, body_gaussians.xyz[body_facial_idx][corr_idx], face_color_bw) | |
head_gaussians_face_only_new_opacities = blend_color( | |
head_gaussians_face_only.opacities, body_gaussians.opacities[body_facial_idx][corr_idx], face_color_bw) | |
head_gaussians_face_only_new_scales = blend_color( | |
head_gaussians_face_only.scales, body_gaussians.scales[body_facial_idx][corr_idx], face_color_bw) | |
head_gaussians_face_only = update_gaussian_attributes( | |
head_gaussians_face_only, new_rgb=head_gaussians_face_only_new_color, | |
new_xyz=head_gaussians_face_only_new_xyz, new_opacity=head_gaussians_face_only_new_opacities, | |
new_scale=head_gaussians_face_only_new_scales) | |
full_gaussians = combine_gaussians([body_gaussians_wo_face, head_gaussians_face_only]) | |
save_gaussians_as_ply(os.path.join(result_folder, 'full_gaussians.ply'), full_gaussians) | |