Skip to content

Commit 3f20351

Browse files
committed
Run without GUI and cross reenactment example
1 parent 84d54d7 commit 3f20351

File tree

14 files changed

+233
-10
lines changed

14 files changed

+233
-10
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ The viewer options are the same as in the case of [instant-ngp](https://github.c
5656
Usage example
5757

5858
```shell
59+
# Run without GUI examples script
60+
./run.sh
61+
62+
# Run cross reenactment based on deformation gradient transfer
63+
./run_transfer.sh
64+
5965
# Training
6066
./build/rta --config insta.json --scene data/obama --height 512 --width 512
6167

configs/nerf/insta.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"parent": "main.json",
3-
"max_steps": 33000,
3+
"max_steps": 30000,
44
"max_cached_bvh": 4000,
55
"max_images_gpu": 1700,
66
"use_dataset_cache": true,
7-
"render_novel_trajectory": false,
7+
"render_novel_trajectory": true,
88
"render_from_snapshot": false
99
}

configs/nerf/main.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"decay": 0.95,
2424
"nested": {
2525
"otype": "Adam",
26-
"learning_rate": 0.0025,
26+
"learning_rate": 0.002,
2727
"beta1": 0.9,
2828
"beta2": 0.99,
2929
"epsilon": 1e-15,

configs/nerf/transfer.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"parent": "main.json",
3+
"max_steps": 1,
4+
"max_cached_bvh": 4000,
5+
"max_images_gpu": 1700,
6+
"use_dataset_cache": true,
7+
"render_novel_trajectory": true,
8+
"render_from_snapshot": true,
9+
"render_config": "transfer"
10+
}

include/rta/core.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ RTA_NAMESPACE_BEGIN
6666
virtual tcnn::GPUMatrix<float> surface_closest_point(const tcnn::GPUMatrix<float> &coords, cudaStream_t stream);
6767
virtual tcnn::GPUMatrix<tcnn::network_precision_t, tcnn::RM> point_density_flame_closest_point(const tcnn::GPUMatrix<float> &points, const tcnn::GPUMatrix<tcnn::network_precision_t, tcnn::RM> &density, float radius, cudaStream_t stream);
6868
virtual void raycast_flame(ngp::CudaRenderBuffer &render_buffer, const Eigen::Vector2i &max_res, const Eigen::Vector2f &focal_length, const Eigen::Matrix<float, 3, 4> &camera_matrix, const Eigen::Vector2f &screen_center, cudaStream_t stream);
69-
69+
std::shared_ptr<Recorder> m_recorder;
7070
private:
7171
tcnn::GPUMatrix<float> surface_closest_point_cpu(uint32_t n_elements, float *coords, cudaStream_t stream);
7272
tcnn::GPUMatrix<float> surface_closest_point_gpu(uint32_t n_elements, float *coords, cudaStream_t stream);
@@ -79,7 +79,6 @@ RTA_NAMESPACE_BEGIN
7979
void test_raycasting();
8080

8181
ngp::ThreadPool m_pool;
82-
std::shared_ptr<Recorder> m_recorder;
8382
SphereTracer m_tracer;
8483
std::vector<std::shared_ptr<TinyMesh>> m_meshes;
8584
std::shared_ptr<TinyMesh> m_canonical_shape = nullptr;

include/rta/recorder.h

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class Recorder {
8686
void set_neutral_camera();
8787
void save_depth(float *depth_cpu, const char *path, const char *name, Eigen::Vector2i res3d);
8888

89+
std::string m_render_config = "";
8990
bool m_is_recording = false;
9091
bool m_record_all = false;
9192
bool m_render_from_snapshot = false;

instant-ngp/include/neural-graphics-primitives/testbed.h

+1
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,7 @@ class Testbed {
853853
bool m_iso_surface_occ_grid = false;
854854

855855
uint32_t m_training_step = 0;
856+
bool early_stop = false;
856857
uint32_t m_training_batch_size = 1 << 18;
857858
Ema m_loss_scalar = {EEmaType::Time, 100};
858859
std::vector<float> m_loss_graph = std::vector<float>(256, 0.0f);

run.sh

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
# Simple script showing how to run the RTA for multiple actors without GUI.
4+
5+
actors=("bala" "biden")
6+
7+
for actor in "${actors[@]}"; do
8+
echo "Running for actor = $actor"
9+
./build/rta --config insta.json --scene "data/$actor" --height 512 --width 512 --no-gui
10+
done

run_transfer.sh

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/bash
2+
3+
# This script runs the transfer.py script for all combinations of target and source
4+
# It will automatically generate the necessary meshes
5+
set -e # Stop the script if any command fails
6+
7+
targets=("justin" "malte_1")
8+
sources=("biden" "obama" "bala")
9+
exp_names=("insta")
10+
11+
for exp_name in "${exp_names[@]}"; do
12+
for target in "${targets[@]}"; do
13+
for source in "${sources[@]}"; do
14+
if [ "$target" == "$source" ]; then
15+
echo "Skipping: target and source are the same ($target)"
16+
continue
17+
fi
18+
19+
echo "Processing target: $target, source: $source"
20+
21+
python transfer.py --target "$target" --source "$source"
22+
23+
./build/rta --config transfer.json --scene data/"$target"/transforms_transfer.json --snapshot data/"$target"/experiments/$exp_name/debug/snapshot.msgpack --no-gui --width 512 --height 512
24+
25+
dst=data/"$target"/experiments/$exp_name/debug/transfer_"$source"_to_"$target"
26+
27+
rm -rf "$dst"
28+
mv data/"$target"/experiments/transfer "$dst"
29+
30+
done
31+
done
32+
done

scripts/environment.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@ dependencies:
211211
- tifffile==2022.7.28
212212
- tinycss2==1.1.1
213213
- tomlkit==0.11.1
214-
- torch==1.12.1
215-
- torchvision==0.13.1
214+
- torch
215+
- torchvision
216216
- tqdm==4.64.0
217217
- traitlets==5.3.0
218218
- trimesh==3.11.2

scripts/generate.sh

+2
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ python postprocess.py -i ${OUTPUT}
3838
rm -rf ${OUTPUT}/background/
3939

4040
echo "End!"
41+
42+
# ./generate.sh /home/wojciech/projects/metrical-tracker/output/berna/ /home/wojciech/projects/INSTA/data/berna/ 600

src/core.cu

+1
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ void rta::Core::post_loading() {
226226
if (m_network_config.contains("horizontal_normals")) m_recorder->m_horizontal_normals = m_network_config["horizontal_normals"];
227227
if (m_network_config.contains("render_novel_trajectory")) m_recorder->m_record_all = m_network_config["render_novel_trajectory"];
228228
if (m_network_config.contains("render_from_snapshot")) m_recorder->m_render_from_snapshot = m_network_config["render_from_snapshot"];
229+
if (m_network_config.contains("render_config")) m_recorder->m_render_config = m_network_config["render_config"];
229230
if (m_recorder->m_record_all)
230231
m_recorder->m_video_mode = VideoType::Floating;
231232
}

src/recorder.cu

+7-3
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ void rta::Recorder::imgui() {
124124
ImGui::Separator();
125125
ImGui::Text("Record video");
126126
// if (ImGui::Button("Snapshot")) snapshot();
127-
if (m_record_all && !m_is_recording && !m_single_step) m_video_mode = VideoType::Floating;
127+
if (m_record_all && !m_is_recording && !m_single_step) m_video_mode = VideoType::Horizontal;
128128
if (ImGui::Button("Start")) start();
129129
ImGui::SameLine();
130130
if (ImGui::Button("Stop")) stop();
@@ -172,6 +172,10 @@ void rta::Recorder::start() {
172172
core->m_dataset_settings.is_training = false;
173173
core->m_dataset_settings.shuffle = false;
174174
std::string mode = "test";
175+
if (!m_render_config.empty())
176+
mode = m_render_config;
177+
if(m_video_mode == VideoType::Horizontal)
178+
mode = "horizontal";
175179
core->m_background_color.w() = 0.f;
176180
core->reload_training_data(true, mode);
177181
core->m_offscreen_rendering = false;
@@ -384,13 +388,13 @@ void rta::Recorder::set_floating_camera(size_t index) {
384388

385389
// m_ngp->first_training_view();
386390
m_ngp->reset_camera();
387-
391+
m_ngp->set_fov(17);
388392
m_ngp->m_camera = rt;
389393
m_ngp->m_smoothed_camera = rt;
390394
}
391395

392396
void rta::Recorder::step() {
393-
if ((m_ngp->m_training_step == m_training_steps_wait || m_render_from_snapshot) && !m_is_recording) {
397+
if ((m_ngp->m_training_step == m_training_steps_wait || m_render_from_snapshot || m_ngp->early_stop) && !m_is_recording) {
394398
start();
395399
}
396400

transfer.py

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import json
2+
import argparse
3+
import os
4+
import shutil
5+
from pathlib import Path
6+
import time
7+
import numpy as np
8+
import trimesh
9+
from tqdm import tqdm
10+
11+
12+
import numpy as np
13+
14+
15+
def tri2tet(tris):
16+
v1 = tris[:, 0, :]
17+
v2 = tris[:, 1, :]
18+
v3 = tris[:, 2, :]
19+
20+
e21 = v2 - v1
21+
e31 = v3 - v1
22+
n = np.cross(e21, e31)
23+
n = n / np.sqrt(np.linalg.norm(n, axis=1, keepdims=True))
24+
25+
v4 = v1 + n
26+
27+
return v1, v2, v3, v4
28+
29+
30+
def gradient(v1, v2, v3, v4):
31+
n = v1.shape[0]
32+
33+
R = np.stack([v2 - v1, v3 - v1, v4 - v1], axis=2)
34+
T = v1
35+
36+
RT = np.eye(4)[None].repeat(n, axis=0).astype(np.float32)
37+
38+
RT[:, :3, :3] = R
39+
RT[:, :3, 3] = T
40+
41+
return RT
42+
43+
def calculate_tbn(mesh):
44+
tris = mesh[0][mesh[1]]
45+
tets = tri2tet(tris)
46+
R = gradient(*tets)[:, :3, :3]
47+
return R
48+
49+
50+
def deformation_gradient(canon_tris, deform_tris):
51+
tet_canon = tri2tet(canon_tris)
52+
tet_def = tri2tet(deform_tris)
53+
54+
RT_canon = gradient(*tet_canon)
55+
RT_def = gradient(*tet_def)
56+
57+
to_local = np.linalg.inv(RT_canon)
58+
to_deform = RT_def
59+
60+
return to_local, to_deform
61+
62+
63+
def transform_vertices(vertices, faces, to_local, to_deform):
64+
tris = vertices[faces] # Shape: (num_faces, 3, 3)
65+
homogeneous_tris = np.concatenate([tris, np.ones((*tris.shape[:2], 1))], axis=2) # Shape: (num_faces, 3, 4)
66+
local_tris = np.einsum('fij,fkj->fki', to_local, homogeneous_tris) # Shape: (num_faces, 3, 4)
67+
deformed_tris = np.einsum('fij,fkj->fki', to_deform, local_tris) # Shape: (num_faces, 3, 4)
68+
transformed_tris = deformed_tris[:, :, :3] / deformed_tris[:, :, 3:4]
69+
70+
transformed_vertices = np.zeros_like(vertices)
71+
counts = np.zeros((vertices.shape[0], 1))
72+
73+
np.add.at(transformed_vertices, faces[:, 0], transformed_tris[:, 0])
74+
np.add.at(transformed_vertices, faces[:, 1], transformed_tris[:, 1])
75+
np.add.at(transformed_vertices, faces[:, 2], transformed_tris[:, 2])
76+
77+
np.add.at(counts, faces[:, 0], 1)
78+
np.add.at(counts, faces[:, 1], 1)
79+
np.add.at(counts, faces[:, 2], 1)
80+
81+
transformed_vertices = np.divide(transformed_vertices, counts, where=counts != 0)
82+
83+
return transformed_vertices
84+
85+
86+
def process(canon_target, canon_source, source_data, target_data, source, target):
87+
source_meshes = []
88+
source_frames = source_data["frames"]
89+
for frame in tqdm(source_frames):
90+
mesh_path = frame["mesh_path"]
91+
mesh = trimesh.load(f"./data/{source}/{mesh_path}", process=False)
92+
source_meshes.append(mesh)
93+
94+
canon_tris = canon_source.vertices[canon_source.faces]
95+
target_canon = canon_target.vertices[canon_target.faces]
96+
97+
mesh_dst = f"./data/{target}/transfer/meshes"
98+
os.system(f"rm -rf {mesh_dst}")
99+
Path(mesh_dst).mkdir(parents=True, exist_ok=True)
100+
101+
expr_dst = f"./data/{target}/transfer/expr"
102+
os.system(f"rm -rf {expr_dst}")
103+
Path(expr_dst).mkdir(parents=True, exist_ok=True)
104+
105+
target_frames = target_data["frames"]
106+
for i, src_mesh in tqdm(enumerate(source_meshes)):
107+
vertices = src_mesh.vertices
108+
faces = src_mesh.faces
109+
deform_tris = vertices[faces]
110+
to_local, to_deform = deformation_gradient(canon_tris, deform_tris)
111+
new_vertices = transform_vertices(canon_target.vertices, faces, to_local, to_deform)
112+
mesh_name = f"{str(i).zfill(5)}.obj"
113+
expr_name = f"{str(i).zfill(5)}.txt"
114+
trimesh.Trimesh(new_vertices, faces).export(f"{mesh_dst}/{mesh_name}")
115+
116+
src_expr = source_frames[i]["exp_path"]
117+
os.system(f"cp ./data/{source}/{src_expr} {expr_dst}/{expr_name}")
118+
119+
source_frames[i]["mesh_path"] = f"transfer/meshes/{mesh_name}"
120+
source_frames[i]["exp_path"] = f"transfer/expr/{expr_name}"
121+
# Copy rest
122+
source_frames[i]["depth_path"] = target_frames[i]["depth_path"]
123+
source_frames[i]["file_path"] = target_frames[i]["depth_path"]
124+
source_frames[i]["seg_mask_path"] = target_frames[i]["seg_mask_path"]
125+
126+
return source_frames
127+
128+
129+
def main():
130+
# Set up argument parser
131+
parser = argparse.ArgumentParser(description="Update JSON file copy with n_sample.")
132+
parser.add_argument("--target", type=str, required=True, help="The target actor name used to locate the JSON file.")
133+
parser.add_argument("--source", type=str, required=True, help="The source actor name used to locate the JSON file.")
134+
args = parser.parse_args()
135+
136+
print(f"Processing {args.source} to {args.target}")
137+
138+
# Define JSON file paths
139+
source_test_path = f"./data/{args.source}/transforms_test.json"
140+
target_test_path = f"./data/{args.target}/transforms_test.json"
141+
142+
canonical_target = trimesh.load(f"./data/{args.target}/canonical.obj", process=False)
143+
canonical_soruce = trimesh.load(f"./data/{args.source}/canonical.obj", process=False)
144+
145+
with open(source_test_path, "r") as file:
146+
source_data = json.load(file)
147+
148+
with open(target_test_path, "r") as file:
149+
target_data = json.load(file)
150+
151+
source_data["frames"] = process(canonical_target, canonical_soruce, source_data, target_data, args.source, args.target)
152+
with open(f"./data/{args.target}/transforms_transfer.json", "w") as file:
153+
json.dump(source_data, file, indent=4)
154+
155+
156+
if __name__ == "__main__":
157+
main()

0 commit comments

Comments
 (0)