Skip to content

Commit f98ce56

Browse files
committed
Added extra visualization and fixed bug in Taubin
1 parent 3c19f8b commit f98ce56

19 files changed

+207
-31
lines changed

models/armadillo.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

models/bunny_1k.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

models/bunny_1k_2_sub.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

models/bunny_large.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

models/clean_csg.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

models/clean_teapot.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

models/dragon.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

models/torus.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

scripts/curvature.py

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import mcubes #pip install --upgrade PyMCubes
66
import json
77
import argparse
8+
from matplotlib import cm
89

910
# -- Begin primitive SDFs from
1011
# https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
@@ -81,6 +82,7 @@ def compute_curvature_directions_taubin(mesh):
8182
eig_min = np.zeros((n,), dtype=float)
8283
confidence = np.zeros((n,), dtype=float)
8384

85+
eps = 1e-8
8486
edges = [[] for i in range(n)]
8587

8688
for i in range(m):
@@ -94,26 +96,36 @@ def compute_curvature_directions_taubin(mesh):
9496

9597
for i in range(n):
9698
total_area = 0
97-
nv = normals[i][:,None]
99+
nv = -normals[i][:,None]
98100
inn = np.identity(3) - nv @ nv.T
99101
if len(edges[i]) == 0:
100102
continue
101103
for u, tri in edges[i]:
102104
uv = (vertices[u] - vertices[i])[:,None]
103-
if areas[tri] < 1e-8:
105+
if areas[tri] < eps or np.linalg.norm(uv) < eps:
104106
#print(areas[tri], np.inner(uv[:,0], uv[:,0]))
105107
continue
106108
innuv = inn @ uv
107-
t = (innuv) / np.linalg.norm(innuv)
109+
fail_count = 0
110+
while np.linalg.norm(innuv) < eps and fail_count < 10:
111+
uv += np.random.normal(0, 0.001 * np.linalg.norm(uv), uv.shape)
112+
innuv = inn @ uv
113+
fail_count += 1
114+
if fail_count >= 5:
115+
continue
116+
117+
t = innuv / np.linalg.norm(innuv)
108118
kappa = 2 * np.inner(nv[:,0], uv[:,0]) / np.inner(uv[:,0], uv[:,0])
109119

110120
total_area += areas[tri]
111121
matrices[i,:,:] += areas[tri] * kappa * (t @ t.T)
112122

113123
#not necessary for finding eigenvectors
114-
if total_area < 1e-8:
115-
total_area = 1
116-
matrices[i,:,:] /= total_area
124+
if total_area > 0:
125+
matrices[i,:,:] /= total_area
126+
127+
#print(i)
128+
#print(matrices[i,:,:])
117129

118130
eigvals, eigvecs = eig(matrices[i])
119131
eigvals = eigvals.real
@@ -236,7 +248,9 @@ def compute_curvature_directions_rusinkiewicz(mesh):
236248
LMN_local = np.array([[x[0], x[1]], [x[1], x[2]]], dtype=float)
237249

238250
rot_axis = normalize(np.cross(ay, tay))
239-
rot_theta = np.arccos(np.dot(ay, tay))
251+
if abs(np.dot(ay, tay)) > 1:
252+
print(ay, tay, np.dot(ay, tay), "???")
253+
rot_theta = np.arccos(np.clip(np.dot(ay, tay), -1, 1))
240254
rot = Rotation.from_rotvec(rot_theta * rot_axis).as_matrix()
241255

242256
rax = rot @ ax
@@ -249,6 +263,8 @@ def compute_curvature_directions_rusinkiewicz(mesh):
249263
matrices[idxA,:,:] += np.array([[L, M], [M, N]]) * area
250264

251265
for i in range(n):
266+
if vertex_areas[i] > 0:
267+
matrices[i] /= vertex_areas[i]
252268
eigvals, eigvecs = eig(matrices[i])
253269
eigvals = eigvals.real
254270
eigvecs = eigvecs.real
@@ -274,15 +290,15 @@ def get_lineset(vertices, vectors, color, l=0.01):
274290
line_set.colors = o3d.utility.Vector3dVector(colors)
275291
return line_set
276292

277-
def visualize_curvature_directions(mesh, l=0.01, show_normals=False, taubin=False):
293+
def visualize_curvature_directions(mesh, l=0.01, show_normals=False, show_curvature=True, taubin=False):
278294
mesh.compute_vertex_normals()
279295
vertices = np.array(mesh.vertices)
280296
normals = np.array(mesh.vertex_normals)
281297
n = vertices.shape[0]
282298
if taubin:
283-
curvature_min, curvature_max, _, _, _ = compute_curvature_directions_taubin(mesh)
299+
curvature_min, curvature_max, eig_min, eig_max, _ = compute_curvature_directions_taubin(mesh)
284300
else:
285-
curvature_min, curvature_max, _, _, _ = compute_curvature_directions_rusinkiewicz(mesh)
301+
curvature_min, curvature_max, eig_min, eig_max, _ = compute_curvature_directions_rusinkiewicz(mesh)
286302

287303

288304
line_set_max = get_lineset(vertices, curvature_max, [1, 0, 0])
@@ -293,6 +309,20 @@ def visualize_curvature_directions(mesh, l=0.01, show_normals=False, taubin=Fals
293309
o3d.visualization.draw_geometries([mesh, line_set_min, line_set_max, line_set_normal])
294310
else:
295311
o3d.visualization.draw_geometries([mesh, line_set_min, line_set_max])
312+
313+
if show_curvature:
314+
old_colors = np.array(mesh.vertex_colors)
315+
paint = cm.get_cmap("seismic")
316+
gaussian_curvature = eig_min * eig_max
317+
print("Gaussian curvature:\t{:.4f}\t{:.4f}".format(gaussian_curvature.min(), gaussian_curvature.max()))
318+
mesh.vertex_colors = o3d.utility.Vector3dVector(paint(gaussian_curvature/5 + 0.5)[:,:3])
319+
o3d.visualization.draw_geometries([mesh])
320+
paint = cm.get_cmap("PiYG")
321+
mean_curvature = 1/2 * (eig_min + eig_max)
322+
print("Mean curvature:\t\t{:.4f}\t{:.4f}".format(mean_curvature.min(), mean_curvature.max()))
323+
mesh.vertex_colors = o3d.utility.Vector3dVector(paint(mean_curvature/5 + 0.5)[:,:3])
324+
o3d.visualization.draw_geometries([mesh])
325+
mesh.vertex_colors = o3d.utility.Vector3dVector(old_colors)
296326

297327
def center_mesh(mesh):
298328
vertices = np.array(mesh.vertices)
@@ -354,13 +384,63 @@ def compare_taubin_rusinkiewicz(mesh):
354384
visualize_curvature_directions(mesh, taubin=True)
355385
visualize_curvature_directions(mesh, taubin=False)
356386

387+
def torus_experiment(taubin=False):
388+
torus_radius = 1.0
389+
tube_radius = 0.5
390+
mesh = o3d.geometry.TriangleMesh.create_torus(torus_radius=torus_radius, tube_radius=tube_radius, radial_resolution=90, tubular_resolution=60)
391+
mesh.compute_vertex_normals()
392+
vertices = np.array(mesh.vertices)
393+
normals = np.array(mesh.vertex_normals)
394+
n = vertices.shape[0]
395+
if taubin:
396+
curvature_min, curvature_max, eig_min, eig_max, _ = compute_curvature_directions_taubin(mesh)
397+
else:
398+
curvature_min, curvature_max, eig_min, eig_max, _ = compute_curvature_directions_rusinkiewicz(mesh)
399+
400+
401+
line_set_max = get_lineset(vertices, curvature_max, [1, 0, 0])
402+
line_set_min = get_lineset(vertices, curvature_min, [0, 1, 0])
403+
line_set_normal = get_lineset(vertices, normals, [0, 0, 1])
404+
405+
o3d.visualization.draw_geometries([mesh, line_set_min, line_set_max])
406+
407+
old_colors = np.array(mesh.vertex_colors)
408+
paint = cm.get_cmap("seismic")
409+
gaussian_curvature = eig_min * eig_max
410+
print("Gaussian curvature:\t{:.4f}\t{:.4f}".format(gaussian_curvature.min(), gaussian_curvature.max()))
411+
mesh.vertex_colors = o3d.utility.Vector3dVector(paint(gaussian_curvature/5 + 0.5)[:,:3])
412+
o3d.visualization.draw_geometries([mesh])
413+
paint = cm.get_cmap("PiYG")
414+
mean_curvature = 1/2 * (eig_min + eig_max)
415+
print("Mean curvature:\t\t{:.4f}\t{:.4f}".format(mean_curvature.min(), mean_curvature.max()))
416+
mesh.vertex_colors = o3d.utility.Vector3dVector(paint(mean_curvature/5 + 0.5)[:,:3])
417+
o3d.visualization.draw_geometries([mesh])
418+
419+
cosv = torus_radius - np.linalg.norm(vertices[:,:2], axis=1)
420+
print(cosv.shape, "cosv")
421+
true_gaussian_curvature = cosv / (tube_radius * (torus_radius + tube_radius * cosv))
422+
true_mean_curvature = (torus_radius + 2 * tube_radius * cosv) / (2 * tube_radius * (torus_radius + tube_radius * cosv))
423+
paint = cm.get_cmap("seismic")
424+
print("True Gaussian curvature:\t{:.4f}\t{:.4f}".format(true_gaussian_curvature.min(), true_gaussian_curvature.max()))
425+
mesh.vertex_colors = o3d.utility.Vector3dVector(paint(true_gaussian_curvature/5 + 0.5)[:,:3])
426+
o3d.visualization.draw_geometries([mesh])
427+
paint = cm.get_cmap("PiYG")
428+
print("True mean curvature:\t\t{:.4f}\t{:.4f}".format(true_mean_curvature.min(), true_mean_curvature.max()))
429+
mesh.vertex_colors = o3d.utility.Vector3dVector(paint(true_mean_curvature/5 + 0.5)[:,:3])
430+
o3d.visualization.draw_geometries([mesh])
431+
432+
mesh.vertex_colors = o3d.utility.Vector3dVector(old_colors)
433+
357434
if __name__ == "__main__":
358-
# #mesh = o3d.io.read_triangle_mesh("../models/bunny/reconstruction/bun_zipper.ply")
435+
mesh = o3d.io.read_triangle_mesh("../models/bunny/reconstruction/bun_zipper.ply")
359436
# #mesh = o3d.io.read_triangle_mesh("../models/csg.ply")
360437
# mesh = mesh_from_sdf(1, 160)
361-
# mesh = center_mesh(mesh)
362-
# compare_taubin_rusinkiewicz(mesh)
363-
# exit()
438+
mesh = center_mesh(mesh)
439+
#mesh = o3d.geometry.TriangleMesh.create_torus(torus_radius=1.0, tube_radius=0.5, radial_resolution=90, tubular_resolution=60)
440+
#mesh = o3d.geometry.TriangleMesh.create_sphere(radius=1.0, resolution=20)
441+
442+
compare_taubin_rusinkiewicz(mesh)
443+
exit()
364444
parser = argparse.ArgumentParser()
365445
parser.add_argument(
366446
"--source",

scripts/experiments.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import os
2+
import open3d as o3d
3+
import numpy as np
4+
from matplotlib import cm
5+
6+
from curvature import compute_curvature_directions_taubin, compute_curvature_directions_rusinkiewicz, get_lineset, visualize_curvature_directions
7+
8+
def visualize(geometries, window_name="", screenshot_path=None):
9+
vis = o3d.visualization.Visualizer()
10+
vis.create_window(window_name=window_name)
11+
for g in geometries:
12+
vis.add_geometry(g)
13+
vis.update_geometry(g)
14+
vis.poll_events()
15+
vis.update_renderer()
16+
if screenshot_path is not None:
17+
vis.capture_screen_image(screenshot_path)
18+
vis.destroy_window()
19+
20+
def torus_experiment(curvature_func, save_dir):
21+
torus_radius = 1.0
22+
tube_radius = 0.5
23+
mesh = o3d.geometry.TriangleMesh.create_torus(torus_radius=torus_radius, tube_radius=tube_radius, radial_resolution=90, tubular_resolution=60)
24+
mesh.compute_vertex_normals()
25+
vertices = np.array(mesh.vertices)
26+
normals = np.array(mesh.vertex_normals)
27+
n = vertices.shape[0]
28+
29+
curvature_min, curvature_max, eig_min, eig_max, _ = curvature_func(mesh)
30+
31+
line_set_max = get_lineset(vertices, curvature_max, [1, 0, 0])
32+
line_set_min = get_lineset(vertices, curvature_min, [0, 1, 0])
33+
line_set_normal = get_lineset(vertices, normals, [0, 0, 1])
34+
35+
visualize([mesh, line_set_min, line_set_max], "Principal curvature directions")
36+
37+
old_colors = np.array(mesh.vertex_colors)
38+
paint = cm.get_cmap("seismic")
39+
gaussian_curvature = eig_min * eig_max
40+
print("Gaussian curvature:\t{:.4f}\t{:.4f}".format(gaussian_curvature.min(), gaussian_curvature.max()))
41+
mesh.vertex_colors = o3d.utility.Vector3dVector(paint(gaussian_curvature/1 + 0.5)[:,:3])
42+
visualize([mesh], "Gaussian curvature", os.path.join(save_dir, "gaussian_curvature_approximation.png"))
43+
paint = cm.get_cmap("PiYG")
44+
mean_curvature = 1/2 * (eig_min + eig_max)
45+
print("Mean curvature:\t\t{:.4f}\t{:.4f}".format(mean_curvature.min(), mean_curvature.max()))
46+
mesh.vertex_colors = o3d.utility.Vector3dVector(paint(mean_curvature/1 + 0.5)[:,:3])
47+
visualize([mesh], "Mean curvature", os.path.join(save_dir, "mean_curvature_approximation.png"))
48+
49+
cosv = np.linalg.norm(vertices[:,:2], axis=1) - torus_radius
50+
true_gaussian_curvature = cosv / (tube_radius * (torus_radius + tube_radius * cosv))
51+
true_mean_curvature = (torus_radius + 2 * tube_radius * cosv) / (2 * tube_radius * (torus_radius + tube_radius * cosv))
52+
paint = cm.get_cmap("seismic")
53+
print("True Gaussian curvature:\t{:.4f}\t{:.4f}".format(true_gaussian_curvature.min(), true_gaussian_curvature.max()))
54+
mesh.vertex_colors = o3d.utility.Vector3dVector(paint(true_gaussian_curvature/1 + 0.5)[:,:3])
55+
visualize([mesh], "Gaussian curvature", os.path.join(save_dir, "gaussian_curvature_exact.png"))
56+
paint = cm.get_cmap("PiYG")
57+
print("True mean curvature:\t\t{:.4f}\t{:.4f}".format(true_mean_curvature.min(), true_mean_curvature.max()))
58+
mesh.vertex_colors = o3d.utility.Vector3dVector(paint(true_mean_curvature/1 + 0.5)[:,:3])
59+
visualize([mesh], "Mean curvature", os.path.join(save_dir, "mean_curvature_exact.png"))
60+
61+
mesh.vertex_colors = o3d.utility.Vector3dVector(old_colors)
62+
63+
def simple_check():
64+
mesh = o3d.geometry.TriangleMesh()
65+
h = 1.0
66+
mesh.vertices = o3d.utility.Vector3dVector(
67+
np.array(
68+
[
69+
[0.0, 0.0, h],
70+
[1.0, 0.0, 0.0],
71+
[0.0, 1.0, 0.0],
72+
[-1.0, 0.0, 0.0],
73+
[0.0, -1.0, 0.0]
74+
]
75+
)
76+
)
77+
mesh.triangles = o3d.utility.Vector3iVector(
78+
np.array(
79+
[
80+
[0, 1, 2],
81+
[0, 2, 3],
82+
[0, 3, 4],
83+
[0, 4, 1],
84+
[1, 3, 2],
85+
[1, 4, 3]
86+
]
87+
)
88+
)
89+
90+
curvature_min, curvature_max, eig_min, eig_max, _ = compute_curvature_directions_taubin(mesh)
91+
print(np.array(mesh.vertex_normals))
92+
93+
94+
simple_check()
95+
#torus_experiment(compute_curvature_directions_taubin, "taubin")
96+
#torus_experiment(compute_curvature_directions_rusinkiewicz, "rusinkiewicz")

0 commit comments

Comments
 (0)