An Introduction to Wavelets and the Haar Transform

by Musawir Ali

 

In this article, I will present an introduction to “wavelets” and the 1D Haar Transform. Then I will show how the 1D Haar Transform can easily be extended to 2D. This article assumes that the reader has general knowledge about basis functions. If not, please look at my introductory article on basis functions before reading this one.

 

What are Wavelets?

 

Wavelets are a set of non-linear bases. When projecting (or approximating) a function in terms of wavelets, the wavelet basis functions are chosen according to the function being approximated. Hence, unlike families of linear bases where the same, static set of basis functions are used for every input function, wavelets employ a dynamic set of basis functions that represents the input function in the most efficient way. Thus wavelets are able to provide a great deal of compression and are therefore very popular in the fields of image and signal processing.

 

Let’s get in to the details of how this dynamic set of basis functions is chosen and how the input function is transformed into wavelets.

 

Lets suppose that we have a string of numbers: ( 2, 2, 2, 2 ) and we want to transmit this over a network. Of course, we would like to do this in the fastest possible way, which implies that we want to send the least amount of data possible. So let’s consider our options. Trivially, one of our options is to just send all the four numbers, i.e., send the first '2', then the second '2', then the third, and lastly the fourth. In doing so, we are implicitly choosing the following basis:
 

<1, 0, 0, 0>

<0, 1, 0, 0>

<0, 0, 1, 0>

<0, 0, 0, 1>


But as you would suspect, this is not the best way of doing things. Can we do better? The trick is to choose a basis that represents our data efficiently and in a very compact fashion. Notice that our data is pretty uniform; in fact it is just a constant signal of 2. We would like to exploit this uniformity. If we choose the basis vector <1, 1, 1, 1>, we can represent our data by just one number! We would only have to send the number 2 over the network, and our entire data string could be reconstructed by just multiplying (or weighting) with the basis vector <1, 1, 1, 1>. This is great, but we still need three more basis vectors to complete our basis since the space in our example is 4 dimensional. Remember, that all basis vectors have to be orthogonal (or perpendicular). This means that if you take the dot (or scalar) product of any two basis vectors, the result should be zero. So our task is to find a vector that is orthogonal to <1, 1, 1, 1>. One such vector is <1, 1, -1, -1>. If you take the dot product of these two vectors, the result is indeed zero. Graphically, these two vectors look like this:

 

<1,1,1,1>

<1,1,-1,-1>


Notice that graphically these basis vectors look like “waves”, hence the name wavelets. Now that we have two basis vectors, we need two more. Haar constructed the remaining basis vectors by a process of dilation and shifting. Dilation basically means squeezing; therefore the remaining basis vectors were constructed by squeezing and shifting. If we squeeze the vector <1, 1, -1, -1>, we get <1, -1, 0, 0>. The 1, 1 pair gets squeezed in to a single 1, and similarly the -1, -1 pair becomes a single -1. Next, we perform a shift on the resultant basis vector and get: <0, 0, 1, -1> which is our final basis vector. Graphically, these two vectors look like this:

 

<1,-1,0,0>

<0,0,1,-1>

 

We now have a complete basis for our four dimensional space, comprised of the following basis vectors or wavelets.
 

<1, 1, 1, 1>

<1, 1, -1, -1>

<1, -1, 0, 0>

<0, 0, 1, -1>

 

Take time to convince yourself that all four of these vectors are perpendicular to each other (take the dot product and see if it is zero). Even though these basis vectors are orthogonal, they are not orthonormal. However, we can easily normalize them by calculating the magnitude of each of these vectors and then dividing their components by that magnitude.

 

<1, 1, 1, 1>

--> magnitude = sqrt((1)2 + (1)2 + (1)2 + (1)2) = 2 --> <1/2, 1/2, 1/2, 1/2>
<1, 1, -1, -1> --> magnitude = sqrt((1)2 + (1)2 + (-1)2 + (-1)2) = 2 --> <1/2, 1/2, -1/2, -1/2>
<1, -1, 0, 0> --> magnitude = sqrt((1)2 + (-1)2 + (0)2 + (0)2) = √2 --> <1/√2, -1/√2, 0, 0>
<0, 0, 1, -1> --> magnitude = sqrt((0)2 + (0)2 + (1)2 + (-1)2) = √2 --> <0, 0, 1/√2, -1/√2>


Now that we have our basis, let us look at an example of how we can project an input vector in to wavelets. This is also known as the 1D Haar Transform.


 

1D Haar Transform

 

Suppose our input vector is: <4, 2, 5, 5>. To project this in to wavelets, we simply take a dot product of the input vector with each of the basis vectors.


 

dot ( <4, 2, 5, 5> , <1/2, 1/2, 1/2, 1/2> ) = 8

dot ( <4, 2, 5, 5> , <1/2, 1/2, -1/2, -1/2> ) = -2
dot ( <4, 2, 5, 5> , <1/√2, -1/√2, 0, 0> ) = 2/√2
dot ( <4, 2, 5, 5> , <0, 0, 1/√2, -1/√2> ) = 0

Thus the input vector got transformed in to <8, -2, 2/√2, 0>. Notice the 4th component is 0! This means that we do not need the 4th basis vector, we can reconstruct our original input vector with just the first  three basis vectors. In other words, we dynamically chose 3 basis vectors from a possible 4 according to our input.

8 * <1/2, 1/2, 1/2, 1/2>

= <4, 4, 4, 4>

-2 * <1/2, 1/2, -1/2, -1/2> = <-1, -1, 1, 1>
2/√2 * <1/√2, -1/√2, 0, 0> = <1, -1, 0, 0>

add the vectors

= <4, 2, 5, 5>

Until now, we had a 4 component input vector, and a corresponding set of 4 component basis vectors. But what if we have a larger input vector, say with 8 components? Would we need to find the new basis vectors? The answer is no. We can use the smaller basis that we already have. In fact, we can use the simplest wavelet basis which consists of: <1/√2, 1/√2> and <1/√2, -1/√2>. These are the smallest wavelets, notice you cannot squeeze them any further. However in choosing these smaller basis vectors for larger input, we can no longer do the Haar wavelet transform in one pass as we did earlier. We will have to recursively transform the input vector until we get to our final result. As an example, let us use the simple, 2 component basis to transform the 4 component input vector that we had in our previous example. The algorithm is outlined below, and our example is traced along side.
 
#  

Example

  Input Vector: <4,2,5,5>
  Transformed Vector (basis coefficients): <?,?,?,?> (we are calculating this)
  Basis Vectors (wavelets): <1/√2, 1/√2>, <1/√2, -1/√2>
1. If size of input vector is less than size of basis vector, place input in transformed vector and exit Size of input vector = 4
Size of basis vector = 2
4 > 2, so continue
2. Break input vector in to parts of size of the wavelet: <4,2> , <5,5>
3. Take dot product of first basis with every part: dot(<4,2> , <1/√2, 1/√2>) = 6/√2
dot(<5,5> , <1/√2, 1/√2>) = 10/√2
4. Take dot product of second basis with every part: dot(<4,2> , <1/√2, -1/√2>) = 2/√2
dot(<5,5> , <1/√2, -1/√2>) = 0
  Result of 3. is the new Input Vector: <6/√2, 10/√2>
  Result of 4. fills part of the Transformed Vector: < ?, ?, 2/√2, 0>
5. Go to 1. Size of input vector = 2
Size of basis vector = 2
2 = 2, so continue
    dot(<6/√2, 10/√2> , <1/√2, 1/√2>) = 8
    dot(<6/√2, 10/√2> , <1/√2, -1/√2>) = -2
  Input Vector: <8>
  Updated Transformed Vector: < ?, -2, 2/√2, 0>
    Size of input vector = 1
Size of basis vector = 2
1 < 2, so place input in transformed vector and exit
  Transformed Vector (final result): <8, -2, 2/√2, 0>

This algorithm is very simple. If you think about it, all it does is take the sums and differences of every pair of numbers in the input vector and divides them by square root of 2. Then, the process is repeated on the resultant vector of the summed terms. Following is an implementation of the 1D Haar Transform in C++


/* Inputs: vec = input vector, n = size of input vector */

void haar1d(float *vec, int n)
{
     int i=0;
    
int w=n;
    
float *vecp = new float[n];
    
for(i=0;i<n;i++)
          vecp[i] =
0;

    
while(w>1)
     {
          w/=
2;
     
    for(i=0;i<w;i++)
          {
               vecp[i] = (vec[
2*i] + vec[2*i+1])/sqrt(2.0);
               vecp[i+w] = (vec[
2*i] - vec[2*i+1])/sqrt(2.0);
          }

     
    for(i=0;i<(w*2);i++)
               vec[i] = vecp[i];
     }

     delete [] vecp;
}
 


2D Haar Transform

The 1D Haar Transform can be easily extended to 2D. In the 2D case, we operate on an input matrix instead of an input vector. To transform the input matrix, we first apply the 1D Haar transform on each row. We take the resultant matrix, and then apply the 1D Haar transform on each column. This gives us the final transformed matrix. The source code for both the 1D and 2D Haar transform can be downloaded here. The 2D Haar transform is used extensively in image compression.