You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Many scanning technologies output a set of $n$ point samples $\mathbf{P}$ on the
surface of the object in question. From these points and perhaps the location
of the camera, one can also estimate normals $\mathbf{N}$ to the surface for each point
$\mathbf{p} \in \mathbf{P}$. This image shows the data/elephant.pwn input
data with a white dot for each point and a red line segment pointing outward for
each corresponding normal vector.
For shape analysis, visualization and other downstream geometry processing
phases, we would like to convert this finitely sampled point cloud data into
an explicit continuous surface representation: i.e., a triangle
mesh (a special case
of a polygon mesh). This image
shows the corresponding output mesh for data/elephant.pwn input data above:
Voxel-based Implicit Surface
Converting the point cloud directly to a triangle mesh makes it very difficult
to ensure that the mesh meets certain topological postconditions: i.e., that
it is manifold,
closed,
and has a small number of
holes.
Instead we will first convert the point cloud sampling representation into a
an implicit surface
representation: where the
unknown surface is defined as the
level-set of some function $g: \mathbf{R}^3 \Rightarrow \mathbf{R}$
mapping all points in space to a scalar value. For example, we may define
the surface $\partial \mathbf{S}$ of some solid,
volumetric shape $\mathbf{S}$ to be all points $\mathbf{x} \in \mathbf{R}^3$ such that $g(x) = {\sigma}$, where we may arbitrarily set ${\sigma}=\frac12 $.
On the computer, it is straightforward
discretize an implicit
function. We define a regular 3D grid of
voxels containing at least the bounding
box of $\mathbf{S}$. At each
node in the grid $\mathbf{x}_{i,j,k}$ we store the value of
the implicit function $g(\mathbf{x}_{i,j,k})$. This defines $g$everywhere in the grid via trilinear
interpolation.
For example, consider a point $\mathbf{x} = (\frac13 ,\frac14 ,\frac12)$ lying in the middle of the
bottom-most, front-most, left-most cell. We know the values at the eight
corners. Trilinear interpolation can be understood as linear
interpolation in the
$x$-direction by $\frac13$ on each $x$-axis-aligned edge, resulting in four values
living on the same plane. These can then be linearly interpolated in the $y$
direction by $\frac14$ resulting in two points on the same line, and finally in the
$z$ direction by $\frac12$ to get to our evaluation point $(\frac13 ,\frac14 ,\frac12 )$.
An implicit surface stored as the level-set of a trilinearly interpolated grid
can be contoured into a triangle mesh via the Marching Cubes
Algorithm.
For the purposes of this assignment, we will treat this as a black
box. Instead, we focus on
determining what values for $g$ to store on the grid.
Characteristic functions of solids
We assume that our set of points $\mathbf{P}$ lie on the surface $\partial \mathbf{S}$ of some physical
solid object $\mathbf{S}$. This solid object
must have some non-trivial volume that we can calculate abstractly as the
integral of unit density over the solid:
$$
\int \limits_\mathbf{S} 1 ;dA. %_
$$
We can rewrite this definite integral as an indefinite integral over all of
$\mathbf{R}^3$:
Compared to typical implicit surface
functions, this function
represents the surface $\partial \mathbf{S}$ of the shape $\mathbf{S}$ as the discontinuity between
the one values and the zero values. Awkwardly, the gradient of the
characteristic function ${\nabla}{\chi}_\mathbf{S}$ is not defined along $\partial \mathbf{S}$.
One of the key observations made in [Kazhdan et al. 2006] is that the gradient
of a infinitesimally mollified
(smoothed) characteristic function:
points in the direction of the normal near the surface $\partial \mathbf{S}$, and
is zero everywhere else.
Our goal will be to use our points $\mathbf{P}$ and normals $\mathbf{N}$ to optimize an
implicit function $g$ over a regular grid, so that its gradient ${\nabla}g$ meets
these two criteria. In that way, our $g$ will be an approximation of the
mollified characteristic function.
Poisson surface reconstruction
Or: how I learned to stop worrying and minimize squared Gradients
Let us start by making two assumptions:
we know how to compute ${\nabla}g$ at each node location $\mathbf{x}_{i,j,k}$, and
our input points $\mathbf{P}$ all lie perfectly at grid nodes:
$\exists\ \mathbf{x}_{i,j,k}$ = $\mathbf{p}_\ell$.
We will find out these assumptions are not realistic and we will have to relax
them (i.e., we will not make these assumptions in the completion of the
tasks). However, it will make the following algorithmic description easier on
the first pass.
If our points $\mathbf{P}$ lie at grid points, then our corresponding normals $\mathbf{N}$ also
live at grid points. This leads to a very simple set of linear equations to
define a function $g$ with a gradient equal to the surface normal at the
surface and zero gradient away from the surface:
This is a vector-valued equation. The gradients, normals and zero-vectors are
three-dimensional (e.g., ${\nabla}g \in \mathbf{R}^3 $). In effect, this is three equations for
every grid node.
Since we only need a single number at each grid node (the value of $g$), we
have too many equations.
Like many geometry processing algorithms confronted with such an over
determined, we will
optimize for the solution that best minimizes the error of equation:
where $\mathbf{g}$ (written in boldface) is a vector of unknown grid-nodes values,
where $g_{i,j,k} = g(\mathbf{x}_{i,j,k})$.
Part of the convenience of working on a regular grid is that we can use the
finite difference
method to approximate
the gradient ${\nabla}g$ on the grid.
After revisiting our assumptions, we will be able to compute
approximations of
the $x$-, $y$- and $z$-components of ${\nabla}g$ via a sparse
matrix multiplication of
a "gradient matrix" $\mathbf{G}$ and our vector of unknown grid values $\mathbf{g}$. We will be
able to write the
minimization problem above in matrix form:
This is a quadratic "energy" function of the variables of $\mathbf{g}$, its minimum occurs when
an infinitesimal change in $\mathbf{g}$ produces no change in the energy:
We will approximate each partial derivative individually. Let's consider the
partial derivative in the $x$ direction, $\partial g(\mathbf{x})/\partial x$, and we will assume
without loss of generality that what we derive applies symmetrically for $y$
and $z$.
The partial derivative in the $x$-direction is a one-dimensional derivative.
This couldn't be easier to do with finite differences. We approximate the
derivative of the function $g$ with respect to the $x$ direction is the
difference between the function evaluated at one grid node and at the grid node
before it in the $x$-direction then divided by the spatial distance between
adjacent nodes $h$ (i.e., the grid step size):
where we use the index $i-\frac12$ to indicate that this derivative in the
$x$-direction lives on a staggered
gridin between the grid
nodes where the function values for $g$.
The following pictures show a 2D example, where $g$ lives on the nodes of a
$5\times 5$ blue grid:
The partial derivatives of $g$ with respect to the $x$-direction $\partial g(\mathbf{x})/\partial x$
live on a $4 \times 5$ green, staggered grid:
The partial derivatives of $g$ with respect to the $y$-direction $\partial g(\mathbf{x})/\partial y$
live on a $5\times 4$ yellow, staggered grid:
Letting $\mathbf{g} \in \mathbf{R}^{n_xn_yn_z \times 1}$ be column vector of function values on the
primary grid (blue in the example pictures), we can construct a sparse matrix
$\mathbf{D}^x \in \mathbf{R}^{(n_x-1)n_yn_z \times n_xn_yn_z}$ so that each row $\mathbf{D}^x_{i-\frac12 ,j,k} \in \mathbf{R}^{1 \times n_xn_yn_z}$ computes the partial derivative at the corresponding
staggered grid location $\mathbf{x}_{i-\frac12 ,j,k}$. The ℓth entry in that row receives a
value only for neighboring primary grid nodes:
Now, obviously in our code we cannot index the column vector $\mathbf{g}$ by a
triplet of numbers ${i,j,k}$ or the rows of $\mathbf{D}^x$ by the triplet
${i-\frac12 ,j,k}$. We will assume that $\mathbf{g}_{i,j,k}$ refers to
g(i+j*n_x+k*n_y*n_x). Similarly, for the staggered grid subscripts
${i-\frac12 ,j,k}$ we will assume that $\mathbf{D}^x_{i-\frac12 ,j,k}(\ell)$ refers to the matrix
entry Dx(i+j*n_x+k*n_y*n_x,l), where the $i-\frac12$ has been rounded down.
We can similarly build matrices $\mathbf{D}^y$ and $\mathbf{D}^z$ and stack these matrices
vertically to create a gradient matrix $\mathbf{G}$:
This implies that our vector $\mathbf{v}$ of zeros and normals in our minimization
problem should not live on the primary, but rather it, too, should be broken
into $x$-, $y$- and $z$-components that live of their resepctive staggered
grids:
B-b-b-b-but the input normals might not be at grid node locations?
At this point, we would actually liked to have had that our input normals
were given component-wise on the staggered grid. Then we could immediate stick
them into $\mathbf{v}$. But this doesn't make much sense as each normal $\mathbf{n}_\ell$lives
at its associated point $\mathbf{p}_\ell$, regardless of any grids.
To remedy this, we will distribute each component of each input normal $\mathbf{n}_\ell$
to $\mathbf{v}$ at the corresponding staggered grid node location.
For example, consider the normal $\mathbf{n}$ at some point $\mathbf{x}_{1,\frac14 ,\frac12 }$. Conceptually,
we'll think of the $x$-component of the normal $n_x$ as floating in the
staggered grid corresponding to $\mathbf{D}^x$, in between the eight staggered grid
locations:
where $w_{iِ+\frac12 ,j,k}(\mathbf{p})$ is the trilinear interpolation weight associate with
staggered grid node $\mathbf{x}_{iِ+\frac12 ,j,k}$ to interpolate a value at the point $\mathbf{p}$.
The trilinear interpolation weights so that:
Since we need to do these for the $x$-component of each input normal, we will
assemble a sparse matrix $\mathbf{W}^x \in n \times (n_x-1)n_yn_z$ that interpolates$\mathbf{v}^x$ at each point $\mathbf{p}$:
the transpose of $\mathbf{W}^x$ is not quite its
inverse, but instead can
be interpreted as distributing values onto staggered grid locations where
$\mathbf{v}^x$ lives:
Using this definition of $\mathbf{v}^x$ and analogously for $\mathbf{v}^y$ and $\mathbf{v}^z$ we can
construct the vector $\mathbf{v}$ in our energy minimization problem above.
The discrete energy minimization problem we've written looks like the squared
norm of some gradients. An analogous energy in the smooth world is the
Dirichlet energy:
$$
E(g) = \int _{\Omega} | {\nabla}g| ^2 dA
$$
to minimize this energy with respect to $g$ as an unknown function, we
need to invoke Calculus of
Variations and
Green's First
Identity.
In doing so we find that minimizers will satisfy:
$$
{\nabla}\cdot {\nabla} g = 0 \text{ on ${\Omega}$},
$$
Notice that if we interpret the transpose of our gradient matrix
$\mathbf{G}^{\mathsf T}$ as a divergence matrix (we can and we should), then the
structure of these smooth energies and equations are directly preserved in
our discrete energies and equations.
This kind of structure preservation is a major criterion for judging
discrete methods.
Choosing a good iso-value
Constant functions have no gradient. This means that we can add a constant
function to our implicit function $g$ without changing its gradient:
The same is true for our discrete gradient matrix $\mathbf{G}$: if the vector of grid
values $\mathbf{g}$ is constant then $\mathbf{G} \mathbf{g}$ will be a vector zeros.
This is potentially problematic for our least squares solve: there are many
solutions, since we can just add a constant. Fortunately, we don't really
care. It's elegant to say that our surface is defined at $g=0$, but we'd be
just as happy declaring that our surface is defined at $g=c$.
To this end we just need to find a solution$\mathbf{g}$, and then to pick a good
iso-value ${\sigma}$.
As suggested in [Kazhdan et al. 2006], we can pick a good iso-value by
interpolating our solution $\mathbf{g}$ at each of the input points (since we know
they're on the surface) and averaging their values. For an appropriate
interpolation matrix $\mathbf{W}$ on the primary (non-staggered) grid this can be
written as:
where $\mathbf{1} \in \mathbf{R}^{n\times 1}$ is a vector of ones.
Just how much does this assignment simplify [Kazhdan et al. 2006]?
Besides the insights above, a major contribution of [Kazhdan et al. 2006] was
to setup and solve this problem on an adaptive
grid rather than a
regular grid. They also track "confidence" of their input data effecting how
they smooth and interpolate values. As a result, their method is one of the
most highly used surface reconstruction techniques to this day.
Consider adding your own insights to the wikipedia entry for this
method.
Tasks
Read [Kazhdan et al. 2006]
This reading task is not directly graded, but it's expected that you read and
understand this paper before moving on to the other tasks.
src/fd_interpolate.cpp
Given a regular finite-difference grid described by the number of nodes on each
side (nx, ny and nz), the grid spacing (h), and the location of the
bottom-left-front-most corner node (corner), and a list of points (P),
construct a sparse matrix W of trilinear interpolation weights so that
P = W * x.
src/fd_partial_derivative.cpp
Given a regular finite-difference grid described by the number of nodes on each
side (nx, ny and nz), the grid spacing (h), and a desired direction,
construct a sparse matrix D to compute first partial derivatives in the given
direction onto the staggered grid in that direction.
src/fd_grad.cpp
Given a regular finite-difference grid described by the number of nodes on each
side (nx, ny and nz), and the grid spacing (h), construct a sparse
matrix G to compute gradients with each component on its respective staggered
grid.
Hint
Use fd_partial_derivative to compute Dx, Dy, and Dz and
then simply concatenate these together to make G.
src/poisson_surface_reconstruction.cpp
Given a list of points P and the list of corresponding normals N, construct
a implicit function g over a regular grid (built for you) using approach
described above.
You will need to distribute the given normals N onto the staggered grid
values in v via sparse trilinear interpolation matrices Wx, Wy and Wz
for each staggered grid.
Then you will need to construct and solve the linear system $\mathbf{G}^{\mathsf T} \mathbf{G} \mathbf{g} = \mathbf{G}^{\mathsf T} \mathbf{v}$.
Determine the iso-level sigma to extract from the g.
Feed this implicit function g to igl::copyleft::marching_cubes to contour
this function into a triangle mesh V and F.