Skip to content

Commit 624461b

Browse files
committed
Improve reproducibility
1 parent 1a61b65 commit 624461b

File tree

3 files changed

+71
-34
lines changed

3 files changed

+71
-34
lines changed

README.md

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ install our package:
4646
pip install -e .
4747

4848
Next, download the [dataset](https://neural-gaussian-scale-space-fields.mpi-inf.mpg.de/data.zip) and extract it into the
49-
repository such that, e.g., the folder `/data/picture` exists.
49+
repository such that the new `data/` folder and the `scripts/` folder are siblings.
50+
51+
Steps 1 and 2 take a long time. To skip them, download the
52+
[pretrained models](https://neural-gaussian-scale-space-fields.mpi-inf.mpg.de/models.zip) and extract them into the
53+
repository such that the new `results/` folder and the `scripts/` folder are siblings.
54+
55+
The outputs of any experiment or visualizer will be written to the `results/` folder.
5056

5157
### Step 1: Train
5258

@@ -64,6 +70,12 @@ third argument. Look in the `data/` folder for all available names. Notice that
6470
python scripts/train.py neural picture bbq
6571
python scripts/train.py neural mesh armadillo
6672

73+
Training a field takes about 4 hours because it does not halt upon convergence, but stoically continues for the maximum
74+
number of iterations. To speed up training, you can significantly lower the `n_iters` variables in
75+
[`train.py`](scripts/train.py) without notably sacrificing quality.
76+
77+
Each field takes 24MB of space, summing to 1.3GB for all fields.
78+
6779
### Step 2: Calibrate
6880

6981
Use the [`calibrate.py`](scripts/calibrate.py) script to run our post-training calibration:
@@ -85,6 +97,9 @@ Also use this script to generate smoothed ground truths via Monte Carlo convolut
8597
python scripts/benchmark.py gauss picture [name]
8698
python scripts/benchmark.py gauss mesh [name]
8799

100+
Benchmarking a picture/mesh field takes about 5min/15min. Generating picture/mesh ground truths takes about 30min/4h.
101+
Per picture/mesh, the smoothed versions take 5GB/6.5GB, summing to about 1TB for all results.
102+
88103
### Step 4: Metrics
89104

90105
Use the [`metrics.py`](scripts/metrics.py) script to compare the output of our field with the ground truth:
@@ -105,20 +120,20 @@ Numbers very similar to those found in Tables 1-4 in our paper should now be ava
105120

106121
Use the [`visualize.py`](scripts/visualize.py) script to get the images, videos, and meshes from our paper and website:
107122

108-
python scripts/visualize.py picture_isotropic
109-
python scripts/visualize.py picture_anisotropic
110-
python scripts/visualize.py picture_foveation
111-
python scripts/visualize.py mesh_isotropic
112-
python scripts/visualize.py mesh_anisotropic
123+
python scripts/visualize.py picture_isotropic [name]
124+
python scripts/visualize.py picture_anisotropic [name]
125+
python scripts/visualize.py picture_foveation [name]
126+
python scripts/visualize.py mesh_isotropic [name]
127+
python scripts/visualize.py mesh_anisotropic [name]
113128
python scripts/visualize.py lightstage
114-
python scripts/visualize.py picture_video
115-
python scripts/visualize.py mesh_video_objects
116-
python scripts/visualize.py mesh_video_ellipsoids
129+
python scripts/visualize.py picture_video [name]
130+
python scripts/visualize.py mesh_video_objects [name]
131+
python scripts/visualize.py mesh_video_ellipsoids [name]
117132
python scripts/visualize.py lightstage_video
118133

119134
### Ablations
120135

121-
To reproduce our ablations, perform the above four above steps with `neural` replaced by one of the following:
136+
To reproduce our ablations, perform steps 1-4 with `neural` replaced by one of the following:
122137

123138
| Configuration as in the paper | Script equivalent |
124139
|-------------------------------|-----------------------------------------|
@@ -136,6 +151,11 @@ First install the required additional dependencies:
136151

137152
pip install -e .[neural-texture]
138153

154+
If you neither downloaded the pretrained models nor ran steps 1-2 yet, run these commands to prepare the neural texture:
155+
156+
python scripts/train.py neural textured
157+
python scripts/calibrate.py neural textured
158+
139159
Open the 3D renderer window using the [`textured_render_uv.py`](scripts/textured_render_uv.py) script. Press ESC to
140160
quit, W/A/S/D/SPACE/SHIFT to move, and use the mouse to look around. You will see the mesh of a fish, but instead of a
141161
texture, it is covered in UV coordinates. If you feel dizzy, set `gizmo_and_grid` in the script to true to render a
@@ -149,3 +169,12 @@ factor here and increased the window size in `textured_render_uv.py` to still ge
149169
demonstrate our method, you can replace `16` with `1`:
150170

151171
python scripts/textured_apply_neural_texture.py moving 16
172+
173+
### Performance
174+
175+
Use the [`performance.py`](scripts/performance.py) script to reproduce our timing experiment:
176+
177+
python scripts/performance.py vanilla picture 30
178+
python scripts/performance.py vanilla mesh 0.004
179+
python scripts/performance.py neural picture 30
180+
python scripts/performance.py neural mesh 0.004

scripts/performance.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@
1313
def main(field_type, category, stop_metric):
1414
stop_metric = float(stop_metric)
1515

16-
out_dir = ngssf.results.performance_dir()
17-
pred_dir = out_dir / f"predictions_{field_type}_{category}_{stop_metric}"
18-
pred_dir.mkdir(parents=True, exist_ok=True)
19-
2016
# Warm-up
2117
time_method(field_type, category, perf_names(category)[0], 0, stop_metric)
2218

@@ -33,7 +29,7 @@ def main(field_type, category, stop_metric):
3329
its.append(it)
3430
rows.append((f"{var_bench_idx}", field_type, f"{np.mean(tts):.4f}", f"{np.mean(its):.4f}"))
3531

36-
with open(out_dir / f"timings_{field_type}_{category}_{stop_metric}.csv", "w") as f:
32+
with open(ngssf.results.performance_dir() / f"timings_{field_type}_{category}_{stop_metric}.csv", "w") as f:
3733
f.write("\n".join(",".join(row) for row in rows))
3834

3935

@@ -70,8 +66,7 @@ def start_fn():
7066
start_time = cur_time()
7167

7268
def loop_fn(itr, pred_fn):
73-
# Bacon produces extremely complex meshes initially, which slow down our Chamfer code significantly.
74-
if (itr + 1) % 50 != 0 or field_type == "bacon" and category == "mesh" and itr < 15_000:
69+
if (itr + 1) % 50 != 0:
7570
return False
7671
inference_start_time = cur_time()
7772
with torch.no_grad():
@@ -117,7 +112,7 @@ def train(field_type, category, true_grid, true_mesh, res, start_fn, loop_fn):
117112
sampler = ngssf.MinibatchSampler(2 ** 24, ngssf.SDFSampler(true_mesh)).cuda()
118113
enc_kw["length_distribution_param"] = 100
119114
n_samples = 200_000
120-
if field_type.startswith("neural"):
115+
if field_type == "neural":
121116
scaler = ngssf.MinibatchScaler(10_000_000, ngssf.RandomScaler(True, sig.coords)).cuda()
122117
field = ngssf.nn.prefab.Smoothable4x1024NeuralField(sig.coords, sig.channels, True, None, enc_kw)
123118
elif field_type == "vanilla":

scripts/visualize.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ def picture_anisotropic():
3131

3232

3333
def _picture(base_dir, scale_set, indices):
34-
for name in tqdm([
34+
for name in tqdm(args_or([
3535
"bbq", "cliffs", "colosseo", "crystals", "firenze", "firewood", "mutter", "peak", "portal", "rue",
3636
"schaumbrunnen", "steepshore", "toomuchbleach", "tunnelrampe", "zebras"
37-
], desc="name", leave=False):
37+
]), desc="name", leave=False):
3838
for index in tqdm(indices, desc="scale", leave=False):
3939
gauss_img = _prepare_picture_image(ngssf.results.load_benchmark("gauss", "picture", name, scale_set, index))
4040
pred_img = _prepare_picture_image(ngssf.results.load_benchmark("neural", "picture", name, scale_set, index))
@@ -45,12 +45,14 @@ def _picture(base_dir, scale_set, indices):
4545

4646

4747
def picture_foveation():
48-
field = ngssf.results.load_neural_field("neural", "picture", "squirrel").cuda()
48+
name = arg_or("squirrel")
49+
field = ngssf.results.load_neural_field("neural", "picture", name).cuda()
4950
res = 512
5051
X = ngssf.util.grid_coords(res, 2, device="cuda")
5152
with torch.no_grad():
5253
pred_img = field(X, (X.norm(dim=1) - 0.35).clamp(0) ** 3 / 200).T.reshape(3, res, res)
53-
_write_image(ngssf.results.visualizations_dir() / "picture_foveation.jpg", _prepare_picture_image(pred_img))
54+
img = _prepare_picture_image(pred_img)
55+
_write_image(ngssf.results.visualizations_dir() / "picture_foveation" / f"{name}.jpg", img)
5456

5557

5658
def _prepare_picture_image(img):
@@ -59,7 +61,7 @@ def _prepare_picture_image(img):
5961

6062
def mesh_isotropic():
6163
scale_set = "variance_benchmark"
62-
for name in tqdm(ngssf.data.names("mesh"), desc="name", leave=False):
64+
for name in tqdm(args_or(ngssf.data.names("mesh")), desc="name", leave=False):
6365
d = ngssf.results.visualizations_dir() / "mesh_isotropic"
6466
dg = d / name / "gauss"
6567
dn = d / name / "neural"
@@ -73,9 +75,10 @@ def mesh_isotropic():
7375

7476

7577
def mesh_anisotropic():
76-
d = ngssf.results.visualizations_dir() / "mesh_anisotropic"
78+
name = arg_or("thai")
79+
d = ngssf.results.visualizations_dir() / "mesh_anisotropic" / name
7780
d.mkdir(parents=True, exist_ok=True)
78-
field = ngssf.results.load_neural_field("neural", "mesh", "thai").cuda()
81+
field = ngssf.results.load_neural_field("neural", "mesh", name).cuda()
7982
for label, variances in [
8083
("isotropic", [1e-2, 1e-2, 1e-2]),
8184
("anisotropic_horizontal", [1e-2, 1e-8, 1e-2]),
@@ -84,12 +87,13 @@ def mesh_anisotropic():
8487
scale = torch.diag(torch.tensor(variances))
8588
with torch.no_grad():
8689
grid = ngssf.util.eval_grid(256, field, scale.cuda(), batch_size=2 ** 18).cpu()
87-
ngssf.util.mesh_from_grid(grid).export(d / f"thai_{label}.ply")
90+
ngssf.util.mesh_from_grid(grid).export(d / f"{label}.ply")
8891

8992

9093
def lightstage():
94+
name = arg_or("cute")
9195
light_positions = ngssf.data.lightstage_light_positions()
92-
field = ngssf.results.load_neural_field("neural", "lightstage", "cute").cuda()
96+
field = ngssf.results.load_neural_field("neural", "lightstage", name).cuda()
9397
w, h = 512, 384
9498
X = torch.cat([
9599
torch.cartesian_prod(torch.linspace(-0.75, 0.75, h), torch.linspace(-1, 1, w)).flip(1),
@@ -99,7 +103,7 @@ def lightstage():
99103
with torch.no_grad():
100104
Y = field(X.cuda(), scale)
101105
img = _prepare_image(Y.T.reshape(3, h, w))
102-
_write_image(ngssf.results.visualizations_dir() / "lightstage" / f"{i}.jpg", img)
106+
_write_image(ngssf.results.visualizations_dir() / "lightstage" / name / f"{i}.jpg", img)
103107

104108

105109
def picture_video():
@@ -114,7 +118,7 @@ def picture_video():
114118
spectrum_label = _label("Spectrum", 1300, (128, 32), overlay=True)
115119
cov_label = _label("Covariance", 1300, (128, 32))
116120

117-
for name in tqdm(["bbq", "firewood", "schaumbrunnen", "tunnelrampe"], leave=False):
121+
for name in tqdm(args_or(["bbq", "firewood", "schaumbrunnen", "tunnelrampe"]), leave=False):
118122
orig_picture = torch.as_tensor(resize(ngssf.data.load("picture", name).numpy(), (3, 512, 512)), device="cuda")
119123
neural_field = ngssf.results.load_neural_field("neural", "picture", name).cuda()
120124
gauss_field = ngssf.GaussianMonteCarloSmoothableField(ngssf.GridField(orig_picture, padding_mode="reflection"))
@@ -146,7 +150,7 @@ def _spectrum(picture):
146150
def mesh_video_objects():
147151
cov_mats = torch.tensor(_mesh_video_covariance_matrices(), dtype=torch.float32, device="cuda")
148152

149-
for name in tqdm(ngssf.data.names("mesh"), leave=False):
153+
for name in tqdm(args_or(ngssf.data.names("mesh")), leave=False):
150154
orig_mesh = ngssf.data.load("mesh", name)
151155
neural_field = ngssf.results.load_neural_field("neural", "mesh", name).cuda()
152156
gauss_field = ngssf.GaussianMonteCarloSmoothableField(
@@ -191,12 +195,13 @@ def _mesh_video_covariance_matrices():
191195

192196

193197
def lightstage_video():
194-
d = ngssf.results.visualizations_dir()
198+
name = arg_or("cute")
199+
d = ngssf.results.visualizations_dir() / "lightstage_video"
195200
d.mkdir(parents=True, exist_ok=True)
196201

197-
light_shots = ngssf.data.load("lightstage", "cute")
202+
light_shots = ngssf.data.load("lightstage", name)
198203
light_pos = ngssf.data.lightstage_light_positions()
199-
neural_field = ngssf.results.load_neural_field("neural", "lightstage", "cute").cuda()
204+
neural_field = ngssf.results.load_neural_field("neural", "lightstage", name).cuda()
200205
neural_field.calibration_factors[1] = 500
201206
gauss_field = ngssf.GaussianMonteCarloSmoothableField(ngssf.LightStageField(light_shots, light_pos), {2, 3}).cuda()
202207

@@ -218,7 +223,7 @@ def lightstage_video():
218223
cov_label = _label("Covariance", 1300, (128, 32))
219224
plotted_pos = light_pos[(light_pos - (light_pos[2] + light_pos[22]) / 2).norm(dim=1) < 0.15]
220225

221-
video = VideoWriter(str(d / "lightstage_video.mp4"), VideoWriter.fourcc('a', 'v', 'c', '1'), 60, (1152, 384))
226+
video = VideoWriter(str(d / f"{name}.mp4"), VideoWriter.fourcc('a', 'v', 'c', '1'), 60, (1152, 384))
222227
for x_light, cov_mat in tqdm(list(zip(xs_light, cov_mats)), leave=False):
223228
X = torch.cat([X_pixel, x_light.tile(w * h, 1)], dim=1).cuda()
224229
with torch.no_grad():
@@ -304,5 +309,13 @@ def _write_image(file, img):
304309
iio.imwrite(file, (img * 255).astype(np.uint8), quality=90)
305310

306311

312+
def arg_or(default):
313+
return sys.argv[2] if len(sys.argv) > 2 else default
314+
315+
316+
def args_or(default):
317+
return sys.argv[2:] if len(sys.argv) > 2 else default
318+
319+
307320
if __name__ == "__main__":
308321
globals()[sys.argv[1]]()

0 commit comments

Comments
 (0)