An Overview of The Implementation of Level Set Methods, Including The Use of The Narrow Band Method
An Overview of The Implementation of Level Set Methods, Including The Use of The Narrow Band Method
1
1 Introduction
Level set methods are mathematical tools for transforming surfaces. These
surfaces are described by a signed-distance-function, that given a point returns
the distance to the surface. The surface separates the inside and the outside of
some object, it is therefore often referred to as the interface. On a computer,
one stores an implicit representation of the interface. That is, we store a value
for each pixel, representing the distance from that pixel to the surface. Inside
the object, this distance is negative, and outside it is positive, see figure 1 for a
small example, with a circle with unit radius, centred in a 3 × 3 grid.
0.41 0 0.41
0 −1 0
0.41 0 0.41
2
Picking a surface distance at time t0 and solving equations thus allows for
the calculation of the surface at a later timestep t1 . Every time we calculate
this step forward in time, we risk corrupting our φ matrix, so that it is no longer
descriping a signed distance function. It is therefore vital to re-normalize it to
a signed distance function. How this is done in practice is covered later on.
The first part of this paper will cover how we solve a level set equation, how
we reinitialize our signed distance function representation and what the Narrow
band method is all about.
The second part of the paper will consider itself with our basic framework
for working with level sets. This can be seen as a guide to how to write a level
set solver, or at least how we have done it.
The third part descripes applications of our framework. In this part we will
descripe how to generete exciting φ matrixes and how to perform the binary
operations union, intersection and minus. We will look at different speed func-
tions, including a speed function for morphing between different φ. Finally we
will look at the detection of edges in images.
1.1 Solving
1.1.1 Advection by vector field
∂φ
A possible solution to the equation ∂t = H is
H = −V · ∇φ (1)
where ∇φ = ( ∂φ ∂φ
∂x , ∂y ) and V is a vector field.
Solving the level set equation thus amounts to moving the interface in the
direction of a vector field. This field can depend on a set of variables.
3
The idea is to pick a vector field and for each coordinate calculate ∇φ, dot
this with V and see where it takes you. The main problem is, of course, to
calculate ∇φ = ( ∂φ ∂φ
∂x , ∂y ), from our grid representation.
This is where magical calculus kicks in. We can approximate this value in
a point, using the upwind scheme. Given a point (x, y) ∈ R2 and a vector
v = (vx , vy ) = V (x, y), the upwind scheme dictates that
−
∂φ φx = φ(x + 1, y) − φ(x, y) if vx < 0
≈ − (2)
∂x φ x = φ(x, y) − φ(x − 1, y) if vx > 0
∇φ |∇φ|2
H = −v · ∇φ = −a · n · ∇φ = −a · · ∇φ = −a · = −a · |∇φ| (3)
|∇φ| |∇φ|
As you see, we now only need to find the length of the gradient. One way of
doing this, is using equation 2, but this is not as precise as Godunovs method.
This method dictates how to find ( ∂φ 2 ∂φ 2
∂x ) and ( ∂y ) , but it can not be used to
find ∂φ ∂φ
∂x or ∂y , which is obviously the reason why not to use it in the general
vector field case. . .
When ( ∂φ 2 ∂φ 2
∂x ) and ( ∂y ) are calculated, they can be used to find |∇φ|. With
this we can find ∂φ
∂t ≈ −a · |∇φ| which then again can be used to find φnew , by
using the Euler method.
So how do we calculate ( ∂φ 2 ∂φ 2
∂x ) and ( ∂y ) ? Godunov dictates that
max(max(φ− 2 + 2
∂φ 2 x , 0) , min(φx , 0) ) if a < 0
( ) ≈ (4)
∂x max(min(φx , 0) , max(φx , 0)2 )
− 2 +
if a > 0
4
where φ− +
x and φx are defined as in (2).
That’s all there’s to it!
1.2 Re-normalisation
A signed distance function φ has the property, that its length |φ| is close to
one at all time. This, however, is not always the case when we solve level set
equations. There is nothing in the methods described above that guarantees
this.
What is needed is a so-called re-normalisation. In our implementation, this
is done by solving the Eikonal Equation ∂φ
∂t = Sign(φ)(1 − |∇φ|) where Sign is
a continuous sign function . In our implementation Sign(φ) = √ 2 φ
1
.
φ +|∇φ|
If you examine this equation, you will notice that it is similar to the equation
for advection in the normal direction. Therefore, the same method can be used
for solving it! We simply use −Sign(φ) as the speed a, and solve it using
Godunov and Euler.
This equation should be solved enough times to ensure that the system is
reasonable stabile around the interface. The idea is, that if this is done for all
1 If it was not, we would have no method for solving it
5
points, it will give the points just beside the interface a reasonable value. In the
next iteration the next layer will have a reasonable value and so on, and so on.
−3 3
All values outside this band are assumed to have a value of ±γ, until they
are close enough for the gamma-band to suck them in. To ensure this we use
a so-called safe-tube. The safe-tube is defined as all the points that are not in
the gamma-band, but who do have a neighbour that is. When a point enters
this magical safe-tube, its value is set to ±γ, depending on the sign of its lucky
neighbour, who is small enough to get into the gamma-band. The gamma-band
together with the safe tube is called the narrow band.
Please notice, that this ensures that the value in the point can be used by
e.g. the Godunov scheme to calculate the length of the gradient. It might not
2 Hence the name
6
be precise, but it is precise enough3 , and after the re-normalisation it will be
even more precise.
As you might have guessed, or already know, we only need to re-normalise
as many times as this gamma-band is wide, divided by the maximum step size
in our CFL condition. In our case, we have chosen a γ of three.
If we start out with such a gamma-band and a safe-tube, we first solve our
level set equation in the gamma-band. When we are done, we re-normalise in
the gamma-band. We can now estimate the values in the safe-band better, and
the values in it is therefore updated. How this is done is described in the next
section.
We now have to rebuild our gamma-band and safe-tube. This can be done
in a naive way, and in a smart way. We have obviously chosen the latter, and
how it is done, is described in section 2.3.n
7
2 Framework
Level Set
2.1 Phi
φ holds distances to the interface, recall that we only sample in a finite number
of grid points. φ give access to manipulate the value in the individual grid
points.
A φ can be asked to renormalise itself, using the same methods as the narrow
band.
The specialised versions of φ is described in 3.1.
Implementation The distances is held in a 2D array. The only class that uses
renormalize is the ImagePhi. Details about the implementation of renormalize
can be found in the description of the narrow band.
8
time step4 , this is done to propagate the changes through the entire γ-band.
Since the re-normalisation moves pixels in the γ-band closer to the interface,
it is necessary to rebuild the safe tube. This is done by using Manhattan city
distance.
Finally the narrow band is asked to rebuild itself.
The level set is responsible for doing morphologic operations, opening and
closing, as well as different ways of moving the level set forward in time.
2.2.1 Implementation
The level set has a pointer to the narrow band and φ.
Safe translation Since we cannot make time steps larger than our CFL con-
dition, we have to split a long steps into several smaller steps.
Opening The idea of opening is to first contract the interface for a certain
amount of time, and afterwards expand the interface for the same amount of
time. Opening removes bumps and spikes on the interface.
Solver oldSolver = nb.getSolver();
Solver normalDirectionSolver =
new NormalDirectionSolver(new ContractingSpeedFunction());
nb.setSolver(normalDirectionSolver);
safeTranslationInTime(d);
normalDirectionSolver =
new NormalDirectionSolver(new ExpandingSpeedFunction());
nb.setSolver(normalDirectionSolver);
safeTranslationInTime(d);
nb.setSolver(oldSolver);
step. However, it works! This is probably because small time steps are followed by small time
steps, and thus the interface is not moving enough for the inaccurate values to be a problem.
Instead we should use another CFL condition.
9
Since the narrow band has information about all the interesting pixels in
the level set, computations that involve all pixels are send through the narrow
band.
2.3.1 Solve
Asks the solver to transform all the pixels in the γ band, one by one.
Implementation We iterate through all the pixels in the narrow band and
ask the solver for the new distance of those pixel with mask 2. All these new
distances are stored in a new φ.
Implementation The solver iterates over all pixel in the narrow band and
ask the solver for the advection of those with mask = 2. The maximum time
step is calculated as:
1
(8)
advectionmax · 2
2.3.3 Re-normalisation
2.3.4 Rebuild bands
Rebuilding the bands is done in two stages: First the γ-band is rebuild, and
then a new safe tube is added.
2.3.5 Implementation
We have chosen to implement the rebuilding of the narrow band in what we
thought was the most intuitive way. By construction of the narrow band, all
pixels in the new γ-band are already in the old narrow band. To construct
the new γ-band we simply run through the pixels in the old narrow band and
add those whose updated distance is smaller than γ to the γ-band. The new
10
members of the γ-band are all prior members of the safe tube that was moved
within γ-distance when updating the city distance. To make a new safe tube,
we run through the new γ-band and remember all the neighbour pixels that are
not already in the γ-band. Notice that all new pixels in the safe tube are either
neighbours of pixels from the old safe tube, that entered the γ-band, or pixels
that just moved out of the γ-band.
2.4 Solver
The solver is responsible for doing operations on a single given pixel. These
operations include finding the length of the gradient, finding the advection and
solving the level set equation.
We have done two types of solvers; a normal direction solver and a vector
field solver. They have the same functionality, but the computations differ quite
a bit as described in 1.1. The vector field solver depend on actually finding the
the gradient, whereas the normal direction solver only uses the length of the
gradient.
2.4.3 Advection
The solver can tell us how far a a pixel will move if t = 1 is used.
11
it by t. Updating a pixel is therefore done in the following way:
−−−−−−−→ −−−−→
φnew (x, y) = φold (x, y) − t · ∇φold (x, y) • V (x, y) (10)
Implementation The following three lines are all it takes to find the new
distance in a pixel (x, y).
float[] grad = gradient(phi, x, y);
float[] v = V.getVector(x, y, phi);
return (phi.get(x, y) - t*(grad[0]*v[0] + grad[1]*v[1]));
12
3 Applications
There are many different applications of the level set method. In this part of
the paper, we will briefly describe some of the more usable methods and our
implementation of them in our framework.
3.1.1 Circle
The simplest of forms to represent
p in a level set. As you very well know, a point
(x1 , y1 ) has the distance (x0 − x1 )2 + (y0 − y1 )2 to the point (x0 , y0 ), and if
we define a circle with centre p in (x0 , y0 ) and with radius r, the distance from a
point (x1 , y1 ) to the circle is (x0 − x1 )2 + (y0 − y1 )2 − r. Please notice, that
this means, that the points inside the circle have a negative distance, as we
desire.
13
c
−c
14
}
The code for reinitialisation is stored in Phi, as it might come in handy in
other situations. The most important thing to notice is, that the reinitialisation
uses the sign in the grid to calculate the norm using Godunov.
It is worth noticing, that one should rebuild the narrow band after combining
the functions. It is also worth noticing, that the φs do not have to be accurate
everywhere. It is a sufficient condition, that they are accurate around their
narrow bands. Remember, that all the values outside the band are assumed
to have numerical values greater than γ, and they are clamped to such a value
upon entering the safe band anyway.
3.2.1 Implementation
The implementations are made as methods in our Phi super class. These meth-
ods take a Phi object, which it uses to change the object on which the method
is invoked.
As an example, consider the following code implementing the union of two
Phi objects.
public void union(Phi phi)
{
for(int x = 0; x < getWidth(); x++)
for(int y = 0; y < getHeight(); y++)
if(get(x, y) > phi.get(x, y))
grid[x][y] = phi.get(x, y);
}
Whenever these methods are called, one should always remember to rebuild
the narrow band!
15
3.3 Simple speed functions
As previous mentioned, speed functions, used to advect an interface in the nor-
mal direction, can depend on any number of parameters. There are, however,
two extremely simple speed functions. These are the speed function for ex-
panding any given interface, and the speed function for collapsing any given
interface.
The expanding speed function is simply 1 at all time, in any point. This will
give us a constant expansion along the normals.
Similarly the collapsing speed function is simply −1 at all time, in all points.
3.3.1 Implementation
The implementation is very simple. Here is the code for getting the speed in a
point when we want to expand the interface
public float getSpeed(int x, int y, Phi phi) { return 1; }
3.4 Morphing
A very cool effect in computer graphics is the morphing of objects from one
shape to another. This can very easy be achieved in level sets, using advection
in the normal direction. If we have two overlapping Phi objects, we can define
a speed function from the primer to the latter by
∂φ
= (φ − φtarget · |∇φ|) (12)
∂t
It is worth noticing we have changed the sign, as compared to [KMJ], the
reason being we have a negative sign inside the interface, and a positive sign
outside.
16
3.4.1 Implementation
Morphing is just implemented as a fancy speed function. This speed function
needs to know φtarget at all points. Please notice that φtarget has to stay a
signed distance function in all points at all time.
The speed depends on φtarget , and the φ we are moving. target is therefore
given at the construction time of the object.
3.5.1 Implementation
The idea is to translate the interface forward in time using one of the two simple
speed functions and advection in the normal direction. How far into time we
are going to translate it, differs from data set to data set, but we can not always
make the translations in big steps. Since we are restricted by a CFL condition,
we must take many small steps.
This is easily implemented. We have chosen to implement it in our Level Set
class.
17
public void safeTranslationInTime(float time)
{
while(time > 0)
{
float maxTimeStep = nb.maxTimeStep(phi);
step(Math.min(time, maxTimeStep));
time -= maxTimeStep;
}
}
This translation method can then be used to perform a morphological open-
ing on the surface. This is also implemented in the Level Set class.
18
This image can then be used to find the direction to the nearest edge,
just by finding the derivatives in the image. These derivatives will point from
the nearest edge to the point we are standing in. If we negate such a derivative,
we have a vector field pointing towards the edges. We can use this vector field in
our advection by vector field scheme. We simply create a figure that is smaller
than the shape in the image, and place the shape inside the shape. We then
advect the figure by the vector field scheme, thus growing outwards until it hit
the edges, where the vector field is zero. The ingenious thing is, that around
the holes in the shape, the derivatives will point in opposite directions, and pull
the interface out into a straight line. Please consult figure 8 for a clarification.
This has the effect, that the derivatives will be very close to zero vectors,
and the interface stops moving, filling the gaps with straight lines. Brilliant, is
it not?
Another strategy is creating a large interface, that you are absolutely sure
covers the entire shape, and collapse it. The result is the same, unless of course,
there a several disjoint shapes in you image.
3.6.1 Implementation
To get rid of the noise, one would typically use an image filter. In this project
we have used a Gaussian filter as described in ([Ima02]) to clear out the noise.
This filter simply makes a weighted approximation of the value in each pixel,
by looking at their neighbourhood. The pixel value in each point is weighted
using a kernel of size 2 · k + 1 × 2 · k + 1 where k is chosen in advance.
1 (i−k−1)2 +(j−k−1)2
Gi,j = 2
·e 2·σ2 (13)
2·σ ·π
and the new value in each point (x, y) is simply
X
Gi,j · Ix−i+k,y−j+k (14)
19
where Ix,y is the value in the original image.
As mentioned, the next step is to find the edges of the shape(s) in the image.
This is done in a series of steps, as described by Michael Bang Nielsen in a
mail5 :
• Compute an image, A, where every pixel is the norm of the gradient in
the smoothed image. The derivative of x in a pixel is defined as the value
in the pixel to the left minus the value in the pixel to the right, divided
by 2. The derivative of y is found in a similar manner.
• Compute another image, B, where every pixel is the gradient at the cor-
responding position in image A dotted with the normal from the corre-
sponding position in the image.
• Compute an image, C, where every pixel is the numerical value of the
gradient in the image dotted with the normal.
• Now you have to look for zero-crossings in image B. Basically a pixel is a
zero crossing if any of its neighbouring pixels have a different sign, or if
the value of the pixel itself is zero. A pixel is classified as an edge if it is
a zero-crossing and if the value at the corresponding pixel in image C is
higher than some threshold. We have chosen 107 as our threshold.
Please notice that we need the integer value in the pixels.
The only question that remains is, how do we compute the distance to the
nearest edge in every point? We have chosen to do it the easy way, using city
distance. We create a new image where all points on edges gets the value 0,
and all other points get the highest possible value, that is, the distance from
the top left corner to the bottom right corner. We then update all the distances
using the Manhattan city distance scheme. We first update the values from the
top left corner down to the bottom left. We then update them the other way.
This guarantees, that all the distances are a correct approximation, in the city
distance scheme.
Next we just use these distances to compute a vector field. The implemen-
tation is straight forward. Please remember, that one has to negate the vector
field, so that is does not point away from the edges, but onto them.
public float[] getVector(int x, int y, Phi phi) {
float dxG = (D[x + 1][y] - D[x - 1][y]) / 2;
float dyG = (D[x][y + 1] - D[x][y - 1]) / 2;
float[] v = new float[2];
v[0] = -dxG;
v[1] = -dyG;
return v;
}
. . . easy and simple.
5 The following is taken directly from that mail, and edited to explain the tricky parts
20
References
[ea99] Peng et. al. A pde-based fast local level set method. Journal of Com-
putational Physics 155, 1999.
[Ima02] Computer Vision - A modern Approach. Prentice Hall, 2002.
[KMJ] Ross T. Whitaker Sean Mauch Ken Museth, David E. Breen and David
Johnson. Algorithms for interactive editing of level set models.
21