import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from pygsp import graphs, filters
def plot_cloud(graph):
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
graph.plot(ax=ax)
ax.axis('off')
graphs.Bunny()
.graph = graphs.Bunny()
plot_cloud(graph)
np.random.normal()
, to the 3D position of the vertices (stored in graph.coords
).noise = np.random.normal(0, 0.05, size=(graph.N, 3))
coords_noisy = graph.coords + noise
graph.coords = coords_noisy
plot_cloud(graph)
graph.estimate_lmax()
g = filters.Heat(graph, 2)
g.plot()
graph.coords = g.filter(coords_noisy)
plot_cloud(graph)
Why does it work so well? Find the trick, and use a more realistic approach.
graph = graphs.NNGraph(coords_noisy, NNtype='radius', epsilon=0.2, plotting=graph.plotting)
graph.estimate_lmax()
g = filters.Heat(graph, 2)
graph.coords = g.filter(coords_noisy)
plot_cloud(graph)
graph = graphs.Bunny()
graph.estimate_lmax()
Create a filterbank of 6 mexican hat wavelets. Visualize the filterbank in the spectral domain.
g = filters.MexicanHat(graph, Nf=6)
g.plot()
Visualize the filterbank in the vertex domain. Look at some of the localized wavelets.
DELTA = 20
signal = g.localize(DELTA)
fig = plt.figure(figsize=(12, 3))
for i in range(3):
ax = fig.add_subplot(1, 3, i+1, projection='3d')
graph.plot_signal(signal[:, i], ax=ax)
_ = ax.set_title('Wavelet {}'.format(i+1))
ax.set_axis_off()
Let's now try to estimate the curvature of the underlying 3D model by only using spectral filtering on the nearest-neighbor graph formed by its point cloud. A simple, but not theoretically grounded, way to accomplish that is to use the coordinates map $[x, y, z]$ and filter it using the above defined wavelets. Doing so gives us a set of $N_f$ 3-dimensional signals $[g_i(L)x, g_i(L)y, g_i(L)z], \ i \in [0, \ldots, N_f]$ that describe variation along the 3 coordinates.
signal = graph.coords
signal = g.filter(signal)
The curvature is then estimated by taking the $\ell_1$ or $\ell_2$ norm across the 3D position.
signal = np.linalg.norm(signal, ord=2, axis=1)
Plot the result to observe that we indeed have a measure of the curvature at different scales.
fig = plt.figure(figsize=(10, 7))
for i in range(4):
ax = fig.add_subplot(2, 2, i+1, projection='3d')
graph.plot_signal(signal[:, i], ax=ax)
title = 'Curvature estimation (scale {})'.format(i+1)
_ = ax.set_title(title)
ax.set_axis_off()