Exercises From Linear Algebra, Signal Processing, and Wavelets. A Unified Approach. Python Version
Exercises From Linear Algebra, Signal Processing, and Wavelets. A Unified Approach. Python Version
Øyvind Ryan
8 Digital images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
3
Chapter 1
Sound and Fourier series
Removing noise from recorded sound can be very challenging, but adding noise is simple.
There are many kinds of noise, but one kind is easily obtained by adding random
numbers to the samples of a sound. For this we can use the function random.random as
follows.
z = x + c*(2*random.random(shape(x))-1)
This adds noise to all channels. The function for returning random numbers returns
numbers between 0 and 1, and above we have adjusted these so that they are between −1
and 1 instead, as for other sound which can be played by the computer. c is a constant
(usually smaller than 1) that dampens the noise.
Write code which adds noise to the audio sample file, and listen to the result for
damping constants c=0.4 and c=0.1. Remember to scale the sound values after you
have added noise, since they may be outside [−1, 1].
Solution.
z = x + c*(2*random.random(shape(x))-1)
z /= abs(z).max()
play(z, fs)
With c = 0.4 the resulting sound can be found in the file castanetsnoisehigh.wav, while
with c = 0.1 it can be found in the file castanetsnoiselow.wav.
In music theory, an octave is a set of pure tones at frequencies f0 , ..., f11 , f12 so that the
ratio of neighboring tones are the same, and so that f12 is double the frequency of f0 ,
i.e. so that
5
6 1 Sound and Fourier series
f1 f2 f12
= = ··· = = 21/12 .
f0 f1 f11
Make a program which plays all the pure tones in an octave, and listen to it with
f0 = 440Hz.
Solution.
f_s = 44100
num_sec = 1
k = 2**(1/12)
f = 440
t = linspace(0, num_sec, f_s*num_sec)
for s in range(13):
play(sin(2*pi*f*t), f_s)
time.sleep(num_sec)
f *= k
Solution.
t = linspace(0, T, 100)
x = zeros(len(t))
for k in range(1, 20, 2):
x -= (8/(k*pi)**2)*cos(2*pi*k*t/T)
plt.figure()
plt.plot(t, x, ’k-’)
b) Write code so that you can listen to the Fourier series of the triangle wave. How
high must you choose N for the Fourier series to be indistinguishable from the triangle
wave itself?
Solution.
x = tile(x, antsec/T)
play(x, fs)
1 Sound and Fourier series 7
Write down difference equations for finding the Fourier coefficients of f (t) = tk from
those of f (t) = tk−1 , and write a program which uses this recursion to compute the
Fourier coefficients of f (t) = tk . Use the program to verify what you computed in
Exercise 1.22 in the book.
Solution.
Let us define an,k , bn,k as the Fourier coefficients of tk . When k > 0 and n > 0, integration
by parts gives us the following difference equations:
Z T
2
an,k = tk cos(2πnt/T )dt
T 0
T Z T !
2 T k kT k−1
= t sin(2πnt/T ) − t sin(2πnt/T )dt
T 2πn 0 2πn 0
kT
=− bn,k−1
2πn
Z T
2
bn,k = tk sin(2πnt/T )dt
T 0
T Z T !
2 T k kT k−1
= − t cos(2πnt/T ) + t cos(2πnt/T )dt
T 2πn 0 2πn 0
Tk kT
=− + an,k−1 .
πn 2πn
When n > 0, these can be used to express an,k , bn,k in terms of an,0 , bn,0 , for which we
Tk
clearly have an,0 = bn,0 = 0. For n = 0 we have that a0,k = k+1 for all k.
The following program computes an,k , bn,k recursively when n > 0.
Use the previous exercise to find the Fourier series for f (x) = − 13 x3 + 12 x2 − 16
3
x + 1 on
the interval [0, 1]. Plot the 9th order Fourier series for this function. You should obtain
the plots from Figure 1.5 in the book.
8 1 Sound and Fourier series
Solution.
The following code generates the left plot of Figure 1.5 in the book.
plt.figure()
polycoeffs = matrix([[1, -3/16, 1/2, -1/3]])
num_terms = 9
ank = matrix(zeros(( shape(polycoeffs)[1], num_terms + 1)))
bnk = ank.copy()
for k in range(shape(polycoeffs)[1]):
for n in range(0, num_terms + 1):
ank[k, n], bnk[k, n] = find_fourier_coeffs(n, k, 1)
t = arange(0, 1, 0.01); t = t.reshape((1,len(t))); t = matrix(t)
n = arange(num_terms + 1); n = n.reshape((len(n), 1)); n = matrix(n)
vals = polycoeffs*(ank*cos(2*pi*n*t) + bnk*sin(2*pi*n*t))
t_plot = array( t.reshape((1, shape(t)[1])) )
powervals = matrix(zeros(( shape(polycoeffs)[1], shape(t)[1] )))
for k in range(shape(polycoeffs)[1]):
powervals[k,:] = t_plot**k
t_plot = array( t.reshape(( shape(t)[1], 1 )) )
polyvals = polycoeffs*powervals
plt.plot(t_plot, polyvals.reshape((shape(polyvals)[1],1)),’r’)
plt.hold(’on’)
plt.axis([0, 1, 0.975, 1])
plt.plot(t.reshape((shape(t)[1],1)), vals.reshape((shape(vals)[1],1)), ’b’)
The polynomial coefficients are stored in the row vector polycoeffs at the top. The
code is written so that it will work for any polynomial: You just need to replace the
vector of polynomial coefficients. The Fourier coefficients are stored in the matrices
ank, bnk, using the function implemented in the previous exercise. Each row in those
matrices correspond to Fourier coefficients of a fixed polynomial. We also multiplied
with matrices containing cosine and sine values at all instances in time.
Compute the complex Fourier series of the function f (t) = sin2 (2πt/T ).
Solution.
We have that
2
1 2πit/T
f (t) = sin2 (2πt/T ) = (e − e−2πit/T
2i
1 1 1 1
= − (e2πi2t/T − 2 + e−2πi2t/T ) = − e2πi2t/T + − e−2πi2t/T .
4 4 2 4
This gives the Fourier series of the function (with y2 = y−2 = −1/4, y0 = 1/2). This
could also have been shown by using the trigonometric identity sin2 x = 12 (1 − cos(2x))
RT
first, or by computing the integral T1 0 f (t)e−2πint/T dt (but this is rather cumbersome).
1 Sound and Fourier series 9
Compute the complex Fourier coefficients of the square wave using equation (1.15) in
the book, i.e. repeat the calculations from Example 1.13 in the book for the complex
case. Use Theorem 15 in the book to verify your result.
Solution.
We obtain that
Z T /2 Z T
1 −2πint/T 1
yn = e dt − e−2πint/T dt
T 0 T T /2
T /2 T
1 T −2πint/T T −2πint/T 1
=− e e +
T 2πin 0 2πin T T /2
1
−e−πin + 1 + 1 − e−πin +
=
2πin (
1 −πin
0, if n is even;
= 1−e = .
πin 2/(πin), if n is odd.
2(1−cos(nπ)
Instead using Theorem 15 in the book together with the coefficients bn = nπ we
computed in Example 1.13 in the book, we obtain
( (
1 1 0, if n is even; 0, if n is even;
yn = (an − ibn ) = − i =
2 2 4/(nπ), if n is odd. 2/(πin), if n is odd.
Solution.
We obtain that
10 1 Sound and Fourier series
T /4
1 −T /4 −2πint/T 1 T /2 −2πint/T
Z Z Z
1 −2πint/T
yn = e dt − e dt − e dt
T −T /4 T −T /2 T T /4
T /4 −T /4 T /2
1 −2πint/T 1 −2πint/T 1 −2πint/T
=− e + e + e
2πin −T /4 2πin −T /2 2πin T /4
1 −πin/2
= −e + eπin/2 + eπin/2 − eπin + e−πin − e−πin/2
2πin
1 2
= (2 sin(πn/2) − sin(πn)) = sin(πn/2).
πn πn
The square wave defined in this exercise can be obtained by delaying our original square
wave with −T /4. Using Property 3 in Theorem 17 in the book with d = −T /4 on the
complex Fourier coefficients
(
0, if n is even;
yn =
2/(πin), if n is odd,
which we obtained for the square wave in Exercise 1.32, we obtain the Fourier coefficients
( (
2πin(T /4)/T 0, if n is even; 0, if n is even;
e = 2i sin(πn/2)
2/(πin), if n is odd. πin , if n is odd.
(
0, if n is even;
= 2 .
πn sin(πn/2), if n is odd.
Solution.
We have that
1 2πint/T
s(cos(2πnt/T )) = s (e + e−2πint/T )
2
1 1
= λs (n/T )e2πint/T + λs (−n/T )e−2πint/T
2 2
1 2πint/T
= λs (n/T ) (e + e−2πint/T ) = λs (n/T ) cos(2πnt/T ),
2
Solution.
We have that
Z a Z a
−2πiνs
λs (ν) = g(s)e ds = g(s)e2πiνs ds = λs (−ν),
−a −a
so that s is symmetric.
Hint.
2
Use that π |u| ≤ | sin u| when u ∈ [−π/2, π/2].
Solution.
We compute
N
2
X |n|
sin (πt/T ) 1− e2πint/N
N +1
n=−N
N
1 X |n|
= (1 − cos(2πt/T )) 1− e2πint/N
2 N +1
n=−N
N
X
1 1 1 |n|
= − e−2πint/T + − e2πint/T 1− e2πint/N .
4 2 4 N +1
n=−N
When one here multiplies out, one sees that most terms cancel (since −1/4 + 1/2 − 1/4 =
0), and we are left with
12 1 Sound and Fourier series
1 1 1 1 1
− e−2πi(N +1)t/T + − e2πi(N +1)t/T = sin2 (π(N + 1)t/T ).
N +1 4 2 4 N +1
2
This proves the claim. Finally, using that π |u| ≤ | sin u| when u ∈ [−π/2, π/2], we obtain
2
1 π2 T2
1 sin(π(N + 1)t/T ) 1
≤ = .
N +1 sin(πt/T ) N + 1 4 π 2 t2 /T 2 4(N + 1)t2
Solution.
The first property of summability is easily seen to hold. Since from a) FN (t) ≥ 0, the
the second property follows from the first. To prove the third, due to a) we only need to
RT
compare with the integral δ t−2 dt, which exists for any δ.
RT PN
c) Show that T1 0 FN (u)f (t − u)du = N1+1 n=0 fn (t).
Hint.
1
PN
Show that FN (t) = N +1 n=0 Dn (t), and use Exercise 1.43 b).
Solution.
We have that
N N
!
1 X 1 X
−2πint/T 2πint/T
Dn (t) = N +1+ (N − n + 1)(e +e )
N + 1 n=0 N +1 n=0
N
X N + 1 − n −2πint/T
=1− (e + e2πint/T )
n=0
N +1
N
X n
=1− 1− (e−2πint/T + e2πint/T )
n=0
N + 1
N
X
|n|
= 1− e2πint/T = FN (t).
N +1
n=−N
T N N
1 X 1 T
Z Z
1 1 X
FN (u)f (t − u)du = DN (u)f (t − u)du = fn (t).
T 0 N + 1 n=0 T 0 N + 1 n=0
Chapter 2
Digital sound and Discrete Fourier analysis
Solution.
The idea is to express x as a linear combination of the Fourier basis vectors φn , and use
that FN φn = en . We have that
1 2πik/N 2
cos2 (2πk/N ) = e + e−2πikn/N
2
1 2πi2k/N 1 1 −2πi2k/N 1 1 1
= e + + e = e2πi2k/N + + e2πi(N −2)k/N
4 2 4 4 2 4
√ 1 1 1
= N φ2 + φ0 + φN −2 .
4 2 4
We here used the periodicity of e2πikn/N , i.e. that e−2πi2k/N = e2πi(N −2)k/N . Since FN
is linear and FN (φn ) = en , we have that
√ √
1 1 1
FN (x) = N e2 + e0 + eN −2 = N (1/2, 0, 1/4, 0, . . . , 0, 1/4, 0) .
4 2 4
Extend the code for the function dft_impl in Example 2.4 in the book so that
1. The function also takes a second parameter called forward. If this is true the DFT is
applied. If it is false, the IDFT is applied. If this parameter is not present, then the
forward transform should be assumed.
2. If the input x is two-dimensional (i.e. a matrix), the DFT/IDFT should be applied
to each column of x. This ensures that, in the case of sound, the FFT is applied to
each channel in the sound when the entire sound is used as input.
13
14 2 Digital sound and Discrete Fourier analysis
Solution.
Let x1 , x2 be real vectors, and set x = x1 + ix2 . Use Theorem 7 in the book to show
that
1
(FN (x1 ))k = (FN (x))k + (FN (x))N −k
2
1
(FN (x2 ))k = (FN (x))k − (FN (x))N −k
2i
This shows that we can compute two DFT’s on real data from one DFT on complex
data, and 2N extra additions.
Solution.
We have that
2 Digital sound and Discrete Fourier analysis 15
(FN (x))k = (FN (x1 + ix2 ))k = (FN (x1 ))k + i(FN (x2 ))k
(FN (x))N −k = (FN (x1 ))N −k + i(FN (x2 ))N −k = (FN (x1 ))k + i(FN (x2 ))k ,
where we have used Property 1 of Theorem 7 in the book. If we take the complex
conjugate in the last equation, we are left with the two equations
• at the top you define the function f (x) = cos6 (x), and M = 3,
• compute the unique interpolant from VM,T (i.e. by taking N = 2M + 1 samples over
one period), as guaranteed by Proposition 9 in the book,
• plot the interpolant against f over one period.
Finally run the code also for M = 4, M = 5, and M = 6. Explain why the plots coincide
for M = 6, but not for M < 6. Does increasing M above M = 6 have any effect on the
plots?
Solution.
Let us compare execution times for the different methods for computing the DFT.
a) Write code which compares the execution times for an N -point DFT for the following
three cases: Direct implementation of the DFT (as in Example 2.4 in the book), the FFT
implementation used in this chapter, and the built-in fft-function. Your code should
use the sample audio file castanets.wav, apply the different DFT implementations to
the first N = 2r samples of the file for r = 3 to r = 15, store the execution times in a
vector, and plot these. You can use the function time() in the time module to measure
the execution time.
Solution.
x0, fs = audioread(’sounds/castanets.wav’)
kvals = arange(3,16)
slowtime = zeros(len(kvals))
fasttime = zeros(len(kvals))
fastesttime = zeros(len(kvals))
N = 2**kvals
for k in kvals:
x = x0[0:2**k].astype(complex)
start = time()
dft_impl(x)
slowtime[k - kvals[0]] = time() - start
start = time()
fft_impl(x, fft_kernel_standard)
fasttime[k - kvals[0]] = time() - start
start = time()
fft.fft(x, axis=0)
fastesttime[k - kvals[0]] = time() - start
# a.
plt.plot(kvals, slowtime, ’ro-’, \
kvals, fasttime, ’go-’, \
kvals, fastesttime, ’bo-’)
plt.grid(’on’)
plt.title(’time usage of the DFT methods’)
plt.legend([’DFT’, ’Standard FFT’, ’Built-in FFT’])
plt.xlabel(’log_2 N’)
plt.ylabel(’time used [s]’)
plt.show()
plt.figure()
# b.
plt.loglog(N, slowtime, ’ro-’, N, fasttime, ’go-’, N, fastesttime, ’bo-’)
plt.axis(’equal’)
plt.legend([’DFT’, ’Standard FFT’, ’Built-in FFT’])
b) A problem for large N is that there is such a big difference in the execution times
between the two implementations. We can address this by using a loglog-plot. Plot N
against execution times using the function loglog. How should the fact that the number
of arithmetic operations are 8N 2 and 5N log2 N be reflected in the plot?
2 Digital sound and Discrete Fourier analysis 17
Solution.
The two different curves you see should have a derivative approximately equal to one
and two, respectively.
c) It seems that the built-in FFT is much faster than our own FFT implementation,
even though they may use similar algorithms. Try to explain what can be the cause of
this.
Solution.
There may be several reasons for this. One is that Python code runs slowly when
compared to native code, which is much used in the built-in FFT. Also, the built-in fft
has been subject to much more optimization than we have covered here.
It is possible to adapt an FFT algorithm to real input. Since yN −n = yn for real input,
there is no additional complexity in computing the second half of the DFT coefficients,
once the first half has been computed. We will now rewrite equation (2.11) in the book
for indices n and N/2 − n as
We see here that, if we already have computed DFTN/2 x(e) and DFTN/2 x(o) , we need
one additional complex multiplication for each yn with 0 ≤ n < N/4 (since e−2πin/N
and (DFTN/2 x(o) )n are complex). No further multiplications are needed in order to
compute yN/2−n , since we simply conjugate terms before adding them. Again yN/2 must
be handled explicitly with this approach. For this we can use the formula
Solution.
The formulas at the top show that we need only compute the e−2πin/N (DFTN/2 x(o) )n
for 0 ≤ n < N/4, i.e. N/4 complex additions. Since N/2 formulas are listed, N/2 complex
18 2 Digital sound and Discrete Fourier analysis
additions are needed. This translates to N real multiplications, and N/2 + N = 3N/2
real additions. These are exactly half the numbers we obtained for the complex version of
the algorithm, for which we obtained MN = O(2N log2 (N )) and AN = O(3N log2 (N )).
version will require MN = O(N log2 N ) real
It is then not difficult to see that the real
multiplications and AN = O 32 N log2 N real additions.
b) Find an IFFT algorithm adapted to vectors with conjugate symmetry, which has
the same operation count as this FFT algorithm adapted to real data.
Hint.
Consider the vectors z, w with entries zn = yn + yN/2−n ∈ CN/2 and wn = e2πin/N (yn −
yN/2−n ) ∈ CN/2 . From the equations above, how can these be used in an IFFT?
Solution.
Note that
showing that the vectors z and w also have conjugate symmetry. Note also that, if we
add and subtract the equations at the top, we obtain
so that
1 1
x(e) = IDFTN/2 z x(o) = IDFTN/2 w.
2 2
The point is that the number of arithmetic operations in computing the vectors z and
w is the same as the FFT in a), so that the overall complexity is the same.
Use the factorization in (2.16) in the book to write a kernel function for a non-recursive
FFT implementation. In your code, perform the matrix multiplications in equation
(2.16) in the book from right to left in an (outer) for-loop. For each matrix loop through
the different blocks on the diagonal in an (inner) for-loop. Make sure you have the right
number of blocks on the diagonal, each block being on the form
2 Digital sound and Discrete Fourier analysis 19
I DN/2k
.
I −DN/2k
It may be a good idea to start by implementing multiplication with such a simple matrix
first as these are the building blocks in the algorithm. Do this so that everything is
computed in-place. Also compare the execution times with our original FFT algorithm,
as we did in Exercise 2.25, and try to explain what you see in this comparison.
Solution.
If you add the non-recursive algorithm to the code from Exercise 2.25, you will see
that the non-recursive algorithm performs much better. There may be several reasons
for this. First of all, there are no recursive function calls. Secondly, the values in the
matrices DN/2 are constructed once and for all with the non-recursive algorithm. Code
which compares execution times for the original FFT algorithm, our non-recursive
implementation, and the split-radix algorithm of the next exercise, can look as follows:
x0, fs = audioread(’sounds/castanets.wav’)
kvals = arange(3,16)
slowtime = zeros(len(kvals))
fasttime = zeros(len(kvals))
fastesttime = zeros(len(kvals))
N = 2**kvals
for k in kvals:
x = x0[0:2**k].astype(complex)
start = time()
fft_impl(x, fft_kernel_standard)
slowtime[k - kvals[0]] = time() - start
start = time()
fft_impl(x, fft_kernel_nonrec)
fasttime[k - kvals[0]] = time() - start
start = time()
fft_impl(x, fft_kernel_splitradix)
fastesttime[k - kvals[0]] = time() - start
20 2 Digital sound and Discrete Fourier analysis
N −1
Show that the vectors {cos(2π(n + 1/2)(k + 1/2)/(2N ))}n=0qin RN are orthogonal,
p
with lengths N/2. This means that the matrix with entries N2 cos(2π(n + 1/2)(k +
1/2)/(2N )) is orthogonal. Since this matrix also is symmetric, it is its own inverse. This
(IV)
is the matrix DCTN , which can be used for type IV cosine matrices.
Hint.
Solution.
We can write
n + 21
1 2n + 1 1
cos 2π k+ = cos 2π k+ .
2N 2 4N 2
If we consider these as vectors of length 2N , we recognize thesep as the unit
p vectors
d2n+1 in the 2N -dimensional DCT,
√ divided by the factor dn = 2/(2N ) = 1/N , so
that these vectors have length N . To see that these vectors are orthogonal when we
restrict to the first N elements using the property
2N −1
n1 + 21 n2 + 21
X 1 1
N δn1 ,n2 = cos 2π k+ cos 2π k+
2N 2 2N 2
k=0
N −1
n1 + 21 n2 + 21
X 1 1
= cos 2π k+ cos 2π k+
2N 2 2N 2
k=0
2N −1
n1 + 21 n2 + 21
X 1 1
+ cos 2π k+ cos 2π k+
2N 2 2N 2
k=N
N −1
n1 + 12 n2 + 21
X 1 1
= cos 2π k+ cos 2π k+
2N 2 2N 2
k=0
N −1
n1 + 21 n2 + 21
X 1 1
+ cos 2π k+ cos 2π k+
2N 2 2N 2
k=0
N −1
n1 + 21 n2 + 21
X 1 1
=2 cos 2π k+ cos 2π k+ .
2N 2 2N 2
k=0
Solution.
Solution.
The matrix for the operation which keeps every second component is
1 0 ··· 0 0
0 0 · · · 0 0
.. .. .. .. ..
. . . . . ,
0 0 · · · 1 0
0 0 ··· 0 0
23
24 3 Discrete time filters
where 1 and 0 are repeated in alternating order along the main diagonal. Since the
matrix is not constant on the main diagonal, it is not a circulant Toeplitz matrix, and
hence not a filter.
in RM (in s̃, M − (2N − 1) zeros have been inserted in the middle). Show that the first
N elements of s̃ ~ x̃ equal s ~ x, so that a circular convolution can be computed as part
of a longer circular convolution. A good strategy may now be to choose M as a power
of two, since circular convolution can be computed with the help of the DFT, and since
there are efficient DFT algorithms when the number of points is a power of two.
Solution.
Let s, x ∈ RN , and assume that s has at most k nonzero entries, gathered in one
segment.
a) Show that there exists a s̃ ∈ RM +k−1 and a number a so that, for any r,
Solution.
Denote by S the N × N circulant Toeplitz matrix with first column equal to s. Assume
that the nonzero entries in the first row of S occur in the interval [a, a + k − 1]. Then
the nonzero entries in rows r, ..., r + M − 1 of S are in the columns from a + r to
a + r + M − 1 + k − 1 = a + r + M + k − 2, i.e. the interval a + [r, r + M + k − 2] (there
are M + k − 1 such columns). With
Solution.
Recall that an N -point split-radix FFT requires 4N log2 N −6N +8 arithmetic operations.
With M + k − 1 = 2r for N this gives (4r − 6)2r + 8 operations for each of the shorter
convolutions. A 2r -point convolution implemented in terms of the FFT (or rather FFT,
multiplication in frequency, and IFFT) thus requires
Although this code does not plot the optimal value for r, this value is very much needed
as a guideline for implementations on how to split convolutions into shorter convolutions.
The reason that we iterate r from log2 k in the code is that k ≤ 2r secures that there is
room for all the filter coefficients inside the vector s̃ used in the smaller convolution.
From the resulting plot we see that when the number of filter coefficients gets above 5,
the FFT-based approach works better. When the number of filter coefficients is below
this, a direct implementation of the filter should be chosen.
When the number of points in an FFT gets high, one usually not cares about the
terms −6N + 8 from the exact operation count of the Split radix FFT, since they
are not dominant terms. In this exercise these terms really mattered, however, since
short FFT’s are in focus. If you had dropped the terms −6N + 8, you can verify that
((8r − 6)2r + 16)/(2r − k + 1) should be replaced by (8r + 6)2r /(2r − k + 1), and that a
new plot shows that at least 8 filter coefficients are needed in order for the FFT-based
approach to outperform the direct implementation.
Show that
Note that there is a factor in front of the last two equations, so that one must be a bit
careful here.
Solution.
Solution.
Here we have that t−1 = 1/4, t0 = 1/4, t1 = 1/4, and t2 = 1/4. We now get that
s0 = t0 = 1/4, s1 = t1 = 1/4, and s2 = t2 = 1/4 (first formula), and sN −1 = s7 =
t−1 = 1/4 (second formula). This means that S = {= 1/4, 1/4, 1/4, 1/4}, and that the
corresponding circulant Toeplitz matrix is
11000011
1 1 1 0 0 0 0 1
1 1 1 1 0 0 0 0
1 0 1 1 1 1 0 0 0
S= .
4 0 0 1 1 1 1 0 0
0 0 0 1 1 1 1 0
0 0 0 0 1 1 1 1
10000111
b) Given the circulant Toeplitz matrix
1 2 0 0
0 1 2 0
S=
0
.
0 1 2
2 0 0 1
Write down the corresponding compact filter notation.
Solution.
Assume that S = {1, c, c2 , . . . , ck }. Compute and plot λS (ω) when k = 4 and k = 8, for
c = 0.5. How do the choices of k and c influence the frequency response?
Solution.
k
X 1 − ck+1 e−i(k+1)ω
cs e−isω = .
s=0
1 − ce−iω
Solution.
x, fs = audioread(’sounds/castanets.wav’)
x = x[:,0]
N= len(x)
kmax=100
vals1 = zeros(int(kmax/10))
vals2 = zeros(int(kmax/10))
vals3 = zeros(int(kmax/10))
ind = 0
for k in range(10, kmax+1,10):
t = random.random(k)
start = time()
convolve(t, x)
vals1[ind] = time() - start
z = zeros(N)
start = time()
for s in range(k):
z[(k-1):N] += t[s]*x[(k-1-s):(N-s)]
vals2[ind] = time() - start
z = zeros(N)
start = time()
for n in range(k-1, N):
for s in range(k):
z[n] += t[s]*x[n-s]
vals3[ind] = time() - start
ind += 1
plt.plot( range(10, kmax+1,10),log(vals1), ’r-’, \
range(10, kmax+1,10), log(vals2), ’g-’, \
range(10, kmax+1,10), log(vals3), ’b-’, )
plt.legend([’conv’,’simple for’,’double for’])
3 Discrete time filters 29
Filters are often implemented in hardware, and hardware implementations have strong
limitations on the number of local variables. Assume that a filter t has n nonzero
coefficients, all known to the hardware, and that the vector x is input. Show that t ∗ x
can be implemented in-place (i.e. that t ∗ x can be computed and stored directly in x)
by using n − 1 local variables. You can assume that two local variables can be swapped
without using an additional local variable. It follows that, since any real convolution
can be split into real convolutions with three filter coefficients, only 2 local variables are
needed in order to compute any convolution in-place.
Solution.
Assume for simplicity that the nonzero filter coefficients are t0 , ..., tn−1 . We have that
x0 is needed in order to compute z0 , ..., zn−1 . Therefore, z0 can’t be stored into the
location of x0 until zn−1 has been computed. When we start the computation of zn−1 ,
z0 , ..., zn−2 must be stored in n − 1 local variables. The final contribution of x0 is with
x0 tn−1 in zn−1 . Now, multiply x0 in-place with tn−1 , and compute zn−1 in the same
memory location. Finally, swap the result with the local variable holding z0 . As a result,
z0 has replaced x0 in the input memory, and the local variables hold z1 , ..., zn−1 instead.
This is continued incrementally.
When the first nonzero filter coefficient is not t0 , it is clear that this procedure will
still work, but that the output will be a delayed version.
a) Write code where you apply the low-pass and high-pass filters in examples 3.34
and 3.35 in the book to the audio sample file, and verify that the sounds you get are
the same as in these examples. How high must k be in order for you to hear difference
from the actual sound? How high can you choose k and still recognize the sound at all?
If you solved Exercise 3.28 you can also use the function filter_impl to perform the
filtering, rather than using convolution (which, as mentioned, discards circularity).
Solution.
The code can look like this, when we apply low-pass filters.
z = x[:, 0]
for kval in range(k):
z = convolve(z, [1/2., 1/2.])
play(z, fs)
If we apply high-pass filters instead, the code can look like this.
30 3 Discrete time filters
z = x[:, 0]
for kval in range(k):
z = convolve(z, [1/2., -1/2.])
play(z, fs)
b) In your code, it is not necessary to scale the values after applying the low-pass or
high-pass filters so that values fit inside [−1, 1]. Explain why this is the case.
Solution.
The low-pass filters compute weighted averages of the input samples. Therefore, as long
as the input values are inside the legal range[−1, 1], the output values will as well. Since
the filter coefficients sum to one it is easy to see that also the high-pass filters produce
values in the legal range.
Assume that tk are the filter coefficients of a filter S1 , and that S2 is the filter with filter
coefficients cos(kωc )tk , where ωc ∈ [0, π). Show that
1
λS2 (ω) = (λS1 (ω − ωc ) + λS1 (ω + ωc )).
2
In other words, when we multiply (modulate) the filter coefficients with a cosine, the
new frequency response can be obtained by shifting the old frequency response with ωc
in both directions, and taking the average of the two. We saw that the filters in the MP3
standard were constructed in this way. The collection of these filters are also called a
cosine modulated filter bank. It is also possible to construct DFT-modulated filter banks
where the shift in frequency is obtained by multiplying with a complex exponential
instead, but this produces filters with complex coefficients.
Solution.
We have that
X 1 X ikωc
λS2 (ω) = cos(kωc )tk e−ikω = (e + e−ikωc )tk e−ikω
2
k k
!
1 X
−ik(ω−ωc )
X
−ik(ω+ωc )
= tk e + tk e
2
k k
1
= (λS1 (ω − ωc ) + λS1 (ω + ωc )).
2
Chapter 4
Motivation for wavelets and some simple
examples
Let f (t) ∈ V1 , and let fn,1 be the value f attains on [n, n + 1/2), and fn,2 the value f
attains on [n + 1/2, n + 1). Show that
• projV0 (f ) is the function in V0 which equals (fn,1 + fn,2 )/2 on the interval [n, n + 1).
• projW0 (f ) is the function in W0 which is (fn,1 − fn,2 )/2 on [n, n + 1/2), and −(fn,1 −
fn,2 )/2 on [n + 1/2, n + 1).
Hint.
Solution.
Since f is constant and equal to f (n) on [n, n+1/2), and constant and equal to f (n+1/2)
on [n + 1/2, n + 1), we get that
Z N Z n+1
hf, φ0,n i = f (t)φ0,n (t)dt = f (t)dt
0 n
Z n+1/2 Z n+1
= f (t)dt + f (t)dt
n n+1/2
Z n+1/2 Z n+1
= f (n)dt + f (n + 1/2)dt
n n+1/2
31
32 4 Motivation for wavelets and some simple examples
Since φ0,n is 1 on [n, n + 1) and 0 elsewhere, projV0 f is the piecewise constant function
which is equal to (f (n) + f (n + 1/2))/2 on [n, n + 1).
Solution.
Assume that λ is an eigenvalue common to both A and B. Then there exists a vector v1
so that Av1 = λv1 , and a vector v2 so that Bv2 = λv2 . We now have that
v A 0 v1 Av1 λv1 v
diag(A, B) 1 = = = =λ 1 .
v2 0 B v2 Bv2 λv2 v2
v1
This shows that λ is an eigenvalue for diag(A, B) also, with an eigenvector.
v2
b) Assume that A and B also are non-singular. Show that diag(A, B) is non-singular,
and that (diag(A, B))−1 = diag(A−1 , B −1 ).
Solution.
We have that
−1
−1 −1 A 0 A 0
diag(A, B)diag(A ,B )=
0 B 0 B −1
AA−1 0
I0
= = =I
0 BB −1 0I
where we have multiplied as block matrices. This proves that diag(A, B) is non-singular,
with inverse diag(A−1 , B −1 ).
c) Show that diag(A, B)diag(C, D) = diag(AC, BD).
Solution.
We have that
A 0 C 0 AC 0
diag(A, B)diag(C, D) = = = diag(AC, BD)
0 B 0 D 0 BD
4 Motivation for wavelets and some simple examples 33
Suppose that we have the vector x with length 210 = 1024, defined by xn = 1 for n
even, xn = −1 for n odd. What will be the result if you run a 10-level DWT on x? Use
the function dwt_impl to verify what you have found.
Hint.
√
We defined ψ by ψ(t) = (φ √1,0 (t) − φ1,1 (t))/ 2. From this connection
√ it follows that
ψ9,n = (φ10,2n − φ10,2n+1 )/ 2, and thus φ10,2n − φ10,2n+1 = 2ψ9,n . Try to couple this
identity with the alternating sign you see in x.
Solution.
P1023
The vector x is the coordinate vector of the function f (t) = n=0 (−1)n φ10,n in the
√ P511 √
basis φ10 for V10 . Since φ10,2n − φ10,2n+1 = 2ψ9,n , we can write f (t) = n=0 2ψ9,n .
Since a 10-level-DWT gives as a result the coordinate vector of f in
(φ0 , ψ0 , ψ1 , ψ2 , ψ3 , ψ4 , ψ5 , ψ6 , ψ7 , ψ8 , ψ9 ),
(the DWT is √ nothing but the change of coordinates from φ10 to this basis), and√since
P511
f (t) = n=0 2ψ9,n , it is clear that the coordinate vector of f in this basis has 2 in
the second part (the ψ9 -coordinates), and 0 elsewhere. The 10-level DWT of x√therefore
gives the vector of length 1024 which is 0 on the first half, and equal to 2 on the
second half. m = 10 is here arbitrarily chosen: The result would have been the same for
m = 1, m = 2, and so on. The following code verifies the result:
x = tile([1.,-1.], 512)
dwt_impl(x, ’Haar’, m=10)
print(x)
Show that, for f ∈ V0 we have that [f ]φ0 = (f (0), f (1), . . . , f (N − 1)). This shows that,
also for the piecewise linear wavelet, there is no loss in working with the samples of f
rather than f itself.
Solution.
PN −1
Let us write f (t) = n=0 cn φ0,n (t). If k is an integer we have that
34 4 Motivation for wavelets and some simple examples
N
X −1 N
X −1
f (k) = cn φ0,n (k) = cn φ(k − n).
n=0 n=0
Clearly the only integer for which φ(s) = 6 0 is s = 0 (since φ(0) = 1), so that the
only n which contributes in the sum is n = k. This means that f (k) = ck , so that
[f ]φ0 = (f (0), f (1), . . . , f (N − 1)).
Write a function
lifting_odd_symm(lambda, x, bd_mode)
which applies an elementary lifting matrix of odd type (equation (4.28) in the book)
to x. Assume that N is even. The parameter bd_mode should do nothing, as we will
return to this parameter later. The function should not allocate the full matrix, and
apply as few multiplications as possible. How many local variables are needed in your
code? Compare with the corresponding number you found for filters in Exercise 3.26.
Solution.
Since λ appears twice in each row, note that we have added entries before multiplication,
to avoid extra multiplication. We also see that there is no need for local variables in the
code.
In this exercise we will show that there is a unique function on the form given by
equation (4.30) in the book which has two vanishing moments.
a) Show that, when ψ̂ is defined by equation (4.30) in the book, we have that
4 Motivation for wavelets and some simple examples 35
−αt − α for − 1 ≤ t < 0
(2 + α − β)t − α for 0 ≤ t < 1/2
ψ̂(t) = (α − β − 2)t − α + 2 for 1/2 ≤ t < 1
βt − 2β for 1 ≤ t < 2
0 for all other t
Solution.
The function ψ̂ is a sum of the functions ψ = √12 φ1,1 , φ, and φ0,1 (i.e. we have set n = 0
in equation (4.30) in the book). All these are continuous and piecewise linear, and we
can write
2t
0 ≤ t < 1/2
ψ(t) = 2 − 2t 1/2 ≤ t < 1
0 elsewhere
1 + t
−1 ≤ t < 0
φ0,0 (t) = 1 − t 0≤t<1
0 elsewhere
t
0≤t<1
φ0,1 (t) = 2 − t 1≤t<2 .
0 elsewhere
It follows that ψ̂(t) = ψ(t) − αφ(t) − βφ1,1 is piecewise linear, and linear on the segments
[−1, 0], [0, 1/2], [1/2, 1], [1, 2].
On the segment [−1, 0] only the function φ is seen to be nonzero, and since φ(t) = 1+t
here, we have that ψ̂(t) = −α(1 + t) = −α − αt here.
On the segment [0, 1/2] all three functions are nonzero, and
φ1,1 (t) = 2t
φ0,0 (t) = 1 − t
φ0,1 (t) = t
on this interval. This means that ψ̂(t) = 2t − α(1 − t) − βt = (2 + α − β)t − α on [0, 1/2].
On the segment [0, 1/2] all three functions are nonzero, and
φ1,1 (t) = 2 − 2t
φ0,0 (t) = 1 − t
φ0,1 (t) = t
On the segment [1, 2] only the function φ0,1 is seen to be nonzero, and since φ0,1 (t) =
2 − t here, we have that ψ̂(t) = −β(2 − t) = βt − 2β here. For all other values of t, ψ̂ is
zero. This proves the formulas for ψ̂ on the different intervals.
b) Show that
Z N Z N
1 1
ψ̂(t)dt = − α − β, tψ̂(t)dt = − β.
0 2 0 4
Solution.
We can write
Z N Z 2 Z 0 Z 1/2 Z 1 Z 2
ψ̂(t)dt = ψ̂(t)dt = ψ̂(t)dt + ψ̂(t)dt + ψ̂(t)dt + ψ̂(t)dt
0 −1 −1 0 1/2 1
Z 0 Z 1/2
= (−α − αt)dt + (2 + α − β)t − α)dt
−1 0
Z 1 Z 2
+ ((α − β − 2)t − α + 2)dt + (βt − 2β)dt
1/2 1
0 1/2
1 2 1 2
= −αt − αt + (2 + α − β)t − αt
2 −1 2 0
1 2
1 2 1 2
+ (α − β − 2)t + (2 − α)t + βt − 2βt
2 1/2 2 1
1 1 1 3 1 3
= −α + α + (2 + α − β) − α + (α − β − 2) + (2 − α) + β − 2β
2 8 2 8 2 2
1
= − α − β,
2
RN 1
0
tψ̂(t)dt is computed similarly, so that we in the end arrive at 4 − β.
c) Explain why there is a unique function on the form given by equation (4.30) in the
book which has two vanishing moments, and that this function is given by equation
(4.32) in the book.
Solution.
1
−α−β =0
2
1
−β =0
4
has the unique solution α = β = 14 , which we already have found.
Chapter 5
The filter representation of wavelets
Solution.
We have that H0 = 15 {1, 1, 1, 1, 1}, and H1 = 13 {−1, 1, −1}. The frequency responses are
37
38 5 The filter representation of wavelets
1/2 −1/4 0 0 ···
1/4 3/8
1/4 1/16 · · ·
0 −1/4 1/2 −1/4 · · ·
0 1/16
1/4 3/8 · · ·
0 0 0 −1/4 · · ·
G= 0
0 0 1/16 · · ·
0
0 0 0 · · ·
.. .. .. .. ..
. . . . .
0 0 0 0 · · ·
1/4 1/16 0 0 ···
Write down the filters G0 , G1 , and compute and plot the frequency responses. Are the
filters symmetric?
Solution.
We have that G0 = {1/4, 1/2, 1/4}, and G1 = {1/16, −1/4, 3/8, −1/4, 1/16}. The
frequency responses are
1 iω 1 1 −iω
λG0 (ω) = e + + e
4 2 4
1 1
= cos(ω) +
2 2
1 2iω 1 iω 3 1 −iω 1 −2iω
λG1 (ω) = e − e + − e e
16 4 8 4 16
1 1 3
= cos(2ω) − cos ω + .
8 2 8
Both filters are symmetric.
c) Assume that H0 = {1/16, 1/4, 3/8, 1/4, 1/16}, and H1 = {−1/4, 1/2, −1/4}. Plot
the frequency responses of H0 and H1 , and verify that H0 is a low-pass filter, and that
H1 is a high-pass filter. Also write down the corresponding forward filter bank transform
H.
Solution.
3/8 1/4 1/16 0 · · · 1/16 1/4
−1/4 1/2 −1/4 0 · · · 0 0
The remaining rows are obtained by translating these in alternating order.
d) Assume that G0 = 13 {1, 1, 1}, and G1 = 15 {1, −1, 1, −1, 1}. Plot the frequency
responses of G0 and G1 , and verify that G0 is a low-pass filter, and that G1 is a
high-pass filter. Also write down the corresponding reverse filter bank transform G.
Solution.
1 iω 1 1 −iω 2 1
λG0 (ω) = e + + e = cos ω +
3 3 3 3 3
1 2iω 1 iω 1 1 −iω 1 −2iω
λG1 (ω) = e − e + − e e
5 5 5 5 5
2 2 1
= cos(2ω) − cos ω +
5 5 5
The two first columns in G are
1/3 −1/5
1/3 1/5
0
−1/5
0 1/5
0 0
.. ..
. .
0 0
1/3 1/5
The remaining columns are obtained by translating these in alternating order.
Combine equations (5.4) and (5.5) in Theorem 3 in the book to show that
1
GHφr = (λH0 ,r λG0 ,r + λH1 ,r λG1 ,r )φr
2
1
+ (λH0 ,r λG0 ,r+N/2 − λH1 ,r λG1 ,r+N/2 )φr+N/2 .
2
Conclude that, in terms of continuous frequency responses, we have alias cancellation if
and only if
Solution.
We have that
GHφr
1 1
= (λH0 ,r + λH1 ,r )Gφr + (λH0 ,r − λH1 ,r )Gφr+N/2
2 2
1 1 1
= (λH0 ,r + λH1 ,r ) (λG0 ,r + λG1 ,r )φr + (λG0 ,r+N/2 − λG1 ,r+N/2 )φr+N/2
2 2 2
1 1 1
+ (λH0 ,r − λH1 ,r ) (λG0 ,r+N/2 + λG1 ,r+N/2 )φr+N/2 + (λG0 ,r − λG1 ,r )φr )
2 2 2
1
= ((λH0 ,r + λH1 ,r )(λG0 ,r + λG1 ,r ) + (λH0 ,r − λH1 ,r )(λG0 ,r − λG1 ,r )) φr
4
1
+ (λH0 ,r + λH1 ,r )(λG0 ,r+N/2 − λG1 ,r+N/2 ) + (λH0 ,r − λH1 ,r )(λG0 ,r+N/2 + λG1 ,r+N/2 ) φr+N/2
4
1 1
= (λH0 ,r λG0 ,r + λH1 ,r λG1 ,r )φr + (λH0 ,r λG0 ,r+N/2 − λH1 ,r λG1 ,r+N/2 )φr+N/2 .
2 2
An alternative QMF filter bank is one where G0 = (H0 )T and G1 = (H1 )T (i.e. the filter
coefficients in the forward and reverse transforms are reverse of one-another), and
Solution.
If we set α = 1, d = 0, we get equality here. A similar computation follows for the second
alias cancellation condition.
5 The filter representation of wavelets 41
b) Show that the perfect reconstruction condition for alternative QMF filter banks can
be written as
Solution.
We have that
2 = λH0 (ω)λG0 (ω) + λH1 (ω)λG1 (ω) = λH0 (ω)λH0 (ω) + λH0 (ω + π)λH0 (ω + π)
= |λH0 (ω)|2 + |λH0 (ω + π)|2 .
We see that the perfect reconstruction conditions of the two definitions of QMF filter
banks only differ in that the latter takes absolute values. It turns out that the latter
also has many interesting solutions, as we will see in Chapter 6 in the book.
The scaling functions and mother wavelets we encounter will always turn out to be
functions with compact support (for a general proof of this, see [1]). An interesting
consequence of equations (5.2) and (5.3) in the book is that the corresponding filters
then are FIR, and that one can find a connection between the supports of the scaling
function and mother wavelet, and the number of filter coefficients. In the following
we will say that the support of a filter is [E, F ] if tE , ..., tF are the only nonzero filter
coefficients.
a) Assume that G0 has support [M0 , M1 ], G1 has support [N0 , N1 ]. Show that Supp(φ) =
[M0 , M1 ], and Supp(ψ) = [(M0 + N0 + 1)/2, (M1 + N1 + 1)/2].
Solution.
Let [m0 , m1 ] be the support of φ. Then φ1,n clearly has support [(m0 + n)/2, (m1 + n)/2].
In the equation
M1
X
φ(t) = (G0 )n φ1,n ,
n=M0
the function with the leftmost support is φ1,M0 , while the one with the rightmost one is
φ1,M1 . This gives the support [(m0 + M0 )/2, (m1 + M1 )/2] for the sum. In order for the
supports of the two sides to match we clearly must have
Solution.
Supp(φ) is on the stated form due to a), and since the support of a symmetric G0 has
the form [−M1 , M1 ]. Since N0 = −N1 as well when G1 is symmetric, we have that
For both piecewise linear wavelets M1 = 1 so that Supp(φ) = [−1, 1]. For the first
piecewise linear wavelet we had N0 = N1 = 0 so that
Supp(ψ) = 1/2 + [−(M1 + N1 )/2, (M1 + N1 )/2] = 1/2 + [−1/2, 1/2] = [0, 1].
Supp(ψ) = 1/2 + [−(M1 + N1 )/2, (M1 + N1 )/2] = 1/2 + [−3/2, 3/2] = [−1, 2].
c) The wavelet with symmetric filters with most filter coefficients we will consider has
7 and 9 filter coefficients, respectively. Explain why plotting over [−4, 4] captures the
entire support of both the scaling function and the mother wavelet when there are at
most this number of filter coefficients. This explains why [−4, 4] has been used as the
plotting interval in many of the plots.
5 The filter representation of wavelets 43
Solution.
With 7 and 9 filter coefficients it is easily seen that the support of φ is [−3, 3], and the
support of ψ is [−3, 4]. Both these are contained in [−4, 4].
d) Verify that, for the Haar wavelet, G0 has filter coefficients evenly distributed around
1/2, G1 has equally many, and evenly distributed around −1/2, and the supports of
both φ and ψ are symmetric around 1/2. The Haar wavelet is the simplest orthonormal
wavelet, and these properties will be used as a template for the other orthonormal
wavelets.
Solution.
We know that both φ and ψ for the Haar wavelet are supported on [0, 1), so that 1/2
is the midpoint of the support. The symmetries around 1/2 and −1/2 follows from
G0 = √12 {1, 1} and G1 = √12 {1, −1}.
e) We will only consider orthonormal wavelets with at most 8 filter coefficients. Explain
again why [−4, 4] as plotting interval is desirable.
Solution.
This number of filter coefficients is easily seen to give the support [−3, 4], which again is
contained within [−4, 4].
a) The function you implemented in Exercise 3.28 computed circular convolution, i.e. it
assumed a periodic extension of the input. Reimplement that function so that it also
takes a third parameter bd_mode. If this equals "per" a periodic extension should be
assumed. If it equals "symm" a symmetric extension should be assumed, as described
above. Again, the function filter_impl in the library can serve as a template for your
code.
Solution.
The implementation of filter_impl in the library looks as follows, and handles other
boundary modes which we do not cover here as well:
which return DWT and IDWT kernels that apply the function filter_impl from a)
together with Theorem 2 in the book. The parameter wav_props should be an object
which gives access to the filters by means of writing wav_props.h0, wav_props.h1,
wav_props.g0, and wav_props.g1.
Solution.
Using these functions one can define standard DWT and IDWT kernels in the following
way, assuming that the wav_props has been computed:
Show that
5 The filter representation of wavelets 45
1 2λ 0 0 · · · 0 0 0
0 1 0 0 · · · 0 0 0
0 λ 1 λ · · · 0 0 0
(Aλ )r = . . . . . . . .
.. .. .. .. .. .. .. ..
0 0 0 0 · · · λ 1 λ
0 0 0 0 ··· 0 0 1
1 0 0 0 ··· 0 0 0
λ 1 λ 0 · · · 0 0 0
0 0 1 0 · · · 0 0 0
(Bλ )r = . . . . . . . . .
.. .. .. .. .. .. .. ..
0 0 0 0 · · · 0 1 0
0 0 0 0 · · · 0 2λ 1
Solution.
Elementary lifting matrices can be viewed as wavelet transforms where the filters are
{λ, 1, λ} and {1}. Since these are symmetric, Theorem 11 in the book guarantees that
they preserve symmetric extensions.
It is not too difficult to implement the encoding and decoding steps as explained in the
MP3 standard. Write a function which implements the encoding steps, and one which
implements the decoding steps. In your code, assume for simplicity that the input and
output vectors to your methods all have lengths which are multiples of 32, and use the
functions mp3_ctable, mp3_dtable.
In the library the functions mp3_forward_fbt and mp3_reverse_fbt implement these
encoding- and decoding steps. Use these as a template for your code.
Solution.
def mp3_reverse_fbt(z):
N = len(z)
z = z. reshape((N,1))
z = mat(z)
Ns = int(N/32)
x = zeros(32*Ns)
D = mp3_dtable() # The reconstruction window.
V = mat(zeros((1024,1)))
# The 64x32 matrix N
yvec = arange(16, 80, 1, float)
yvec = yvec.reshape((64, 1))
xvec = arange(0, 32, 1, float)
xvec = xvec.reshape((1, 32))
Nmatr = mat(cos(yvec*(2*xvec + 1)*pi/64))
U = zeros(512)
for n in range(1, Ns+1):
V[64:1024, 0] = V[0:(1024-64), 0]
V[0:64, 0] = Nmatr*z[((n-1)*32):(n*32), 0]
for i in range(8):
U[(i*64):(i*64 + 32)] = \
array(V[(i*128):(i*128 + 32), 0]).flatten()
U[(i*64 + 32):((i + 1)*64)] = \
array(V[(i*128 + 96):((i+1)*128), 0]).flatten()
W = U*D
for i in range(16):
x[((n-1)*32):(n*32)] += W[32*i:(32*(i + 1))]
return x
Chapter 6
Constructing interesting wavelets
Let f (t) = k xk h(t − nTs ). Show that fˆ(ν) = x̂(νTs )ĥ(ν). This can be considered a
P
mixed continuous-discrete expression for that convolution in time to corresponds to
multiplication in frequency.
Solution.
We have that
Z ∞X Z ∞
ˆ 1 −iνt
X 1
f (ν) = √ xk h(t − nTs )e dt = xk √ h(t − nTs )e−iνt dt
2π −∞ k k
2π −∞
Z ∞
−iνnTs 1
X
−iνt
= xk e √ h(t)e dt = x̂(νTs )ĥ(ν).
k
2π −∞
Assume that U U T is non-singular, so that {un }n is a frame. From the previous exercise
it follows from this that σm > 0. From Appendix A in the book it also follows that
the rank of U is m, i.e. U has full column rank. Show that Ũ = (U T )† satisfies the
requirements of a dual frame, where (U T )† is the generalized inverse of U T , defined in
the appendix. The appendix then says that Ũ = (U T )† = (U U T )−1 U . It follows that
(U U T )−1 maps the frame vectors uk to the dual frame vectors ũk , and that the frame
operator U U T maps the dual frame vectors to the frame vectors.
Solution.
Ũ U T = (U T )† U T = Im ,
47
48 6 Constructing interesting wavelets
where we used Appendix A in the book. Also, if σi are the singular values of U T , 1/σi
are the singular values of (U T )† . Its smallest singular value is thus 1/σ1 , and its largest
singular value is 1/σm . Again with U T = U0 Σ(V0 )H a singular value decomposition of
U T , we have that Ũ T = U0 Σ −1 (V0 )H . Following the deductions in the previous exercises
we get that
1 1
2 kf k2 ≤ kŨ T f k2 =≤ 2 kf k2 .
σ1 σm
Since 1/σ1 = 1/B and 1/σm = 1/A the result follows.
Solution.
We have that
N1 /2
1 1
λH0 (ω) = (1 + cos ω) Q1 (1 − cos ω)
2 2
2
1 1
= (1 + cos ω) Q(4) (1 − cos ω)
2 2
N2 /2 2
1 1 1
λG0 (ω) = (1 + cos ω) Q2 (1 − cos ω) = (1 + cos ω)
2 2 2
2
1 1 1 1 2iω
= 1 + eiω + e−iω = (e + 4eiω + 6 + 4e−iω + e−2iω ).
4 2 2 16
1
Therefore G0 = 16 {1, 4, 6, 4, 1}. We do not recommend to compute H0 by hand. With
the package sympy in Python you can do as follows to compute H0 .
1
H1 = {1, −4, 6, −4, 1}
16
1
G1 = {5, 20, 1, −96, −70, 280, −70, −96, 1, 20, 5}
128
The library represents a Spline wavelet with x and y vanishing moments (i.e. N1 and
N2 ) with the name "splinex.y".
a) Listen to the low-resolution approximations and detail components in sound for the
"spline4.4" wavelet.
Solution.
The following code can be used for listening to the low-resolution approximations for a
given value of m.
m = 1
x, fs = forw_comp_rev_dwt1(m, ’spline4.4’)
play(x, fs)
b) Plot all scaling functions and mother wavelets (using the cascade algorithm), and
frequency responses for the "spline4.4" wavelet.
Solution.
m=10;
cascade_alg(m, -4, 4, ’spline4.4’, True, False)
cascade_alg(m, -4, 4, ’spline4.4’, False, False)
Let H and G be 2-channel forward and reverse filter bank transforms, with filters H0 ,
H1 , G0 , G1 , and polyphase components H (i,j) , G(i,j) .
a) Show that
Solution.
51
52 7 The polyphase representation of filter bank transforms
follow from
Solution.
The first column in the matrix on the left hand side gives the filter G0 . On the right hand
side, a) states that the even-indexed columns are taken from the filter with frequency
response
This shows that λG0 (ω) = αe2idω λH1 (ω+π). We obtain the first equation easily from this.
Now, the second column in the matrix on the left hand side gives the filter coefficients
of G1 . On the right hand side, a) states that the odd-indexed columns are taken from
the filter with frequency response
This shows that λG1 (ω) = αe2idω λH0 (ω + π), which is the second equation.
7 The polyphase representation of filter bank transforms 53
How we can spot alias cancellation in terms of the polyphase representation will be
useful to us in the next section when we explain why the filter bank transform of
the MP3 standard provides such cancellation. We will there end up with a polyphase
representation on the form given in a) of this exercise.
a) Assume that the polyphase representation of S is a k × k-block diagonal matrix
where the diagonal elements are M × M -filters which are all equal. Show that S is
a (kM ) × (kM )-filter. Thus, if GH has this form for forward and reverse filter bank
transforms H and G, we have alias cancellation.
Solution.
Since S has a k × k-block diagonal polyphase representation with equal diagonal blocks,
the only nonzero elements in the filter representation are at indices (i + M r1 , i + M r2 )
with 0 ≤ i < M , 0 ≤ r1 , r2 < k. Increasing i with 1 just switches to the next block
on the diagonal, and we get the same value since the blocks are assumed equal. Also,
increasing both r1 and r2 with one clearly gives a new element on the same diagonal in
the same block, which has the same value by the assumption that each block entry is
a filter. It follows that S is constant on diagonals 0, ±M , ±2M , and so on, and zero
elsewhere. It follows that S is a (kM ) × (kM )-filter.
b) If S is a general (kM ) × (kM )-filter, give a general description of its polyphase
representation.
Solution.
Each block in the polyphase representation is a k × k-filter. Also, the filters S (i,j) and
S (i+k,j+k) must be equal for any i, j, k.
Let us use the different lifting factorizations obtained in this chapter to implement the
corresponding wavelet kernels. Your functions should call the functions from Exercise 4.24
and 4.29 in the book. You will need equations (7.8)-(7.11) here, in order to complete
the kernels.
a) Write functions
dwt_kernel_53(x, bd_mode)
idwt_kernel_53(x, bd_mode)
which implement the DWT and IDWT kernels for the Spline 5/3 wavelet. Use the lifting
factorization obtained in Example 7.2.3 in the book.
54 7 The polyphase representation of filter bank transforms
Solution.
b) Write functions
dwt_kernel_97(x, bd_mode)
idwt_kernel_97(x, bd_mode)
which implement the DWT and IDWT kernels for the CDF 9/7 wavelet. Use the lifting
factorization obtained in Example 7.2.4 in the book.
Solution.
Solution.
The following code can be used for listening to the low-resolution approximations for a
given value of m.
m = 1
x, fs = forw_comp_rev_dwt1(m, ’cdf53’)
play(x, fs)
m = 1
x, fs = forw_comp_rev_dwt1(m, ’cdf97’)
play(x, fs)
d) Plot all scaling functions and mother wavelets for the Spline 5/3 and the CDF 9/7
wavelets, using the cascade algorithm and the kernels you have implemented.
Solution.
In the plot for the CDF 9/7 wavelet, it is seen that the functions and their dual
counterparts are close to being equal. This reflects the fact that this wavelet is close to
being orthogonal.
1
H0 = {−5, 20, −1, −96, 70, 280, 70, −96, −1, 20, −5}
128
1
H1 = {1, −4, 6, −4, 1}
16
1
G0 = {1, 4, 6, 4, 1}
16
1
G1 = {5, 20, 1, −96, −70, 280, −70, −96, 1, 20, 5}.
128
56 7 The polyphase representation of filter bank transforms
Show that
1
I − 41 {1, 1}
1
I − 128 {5, −29, −29, 5} I 0 0
G= 4 ,
0 I −{1, 1} I 0 I 04
and derive a lifting factorization of G from this.
Solution.
1 1
I − 14 {1, 1}
16 {1, 6, 1} 128 {5, 1, −70, −70, 1, 5}
1 1
0 I 16 {4, 4} 128 {20, −96, 280, −96, 20}
1 1
16 {4} 128 {20, −116, −116, 20}
= 1 1 .
16 {4, 4} 128 {20, −96, 280, −96, 20}
1 1
1 1
I 0 16 {4} 128 {20, −116, −116, 20} = 4 128 {20, −116, −116, 20}
1 1
−{1, 1} I 16 {4, 4} 128 {20, −96, 280, −96, 20} 0 4
Since
1
1 1
1
I − 512 {20, −116, −116, 20} 4 128 {20, −116, −116, 20} 0
= 4 ,
0 I 0 4 04
it follows that
1
I − 41 {1, 1}
1
I − 128 {5, −29, −29, 5} I 0 0
G= 4 .
0 I −{1, 1} I 0 I 04
Run the forward and then the reverse transform from Exercise 5.30 on the vector
(1, 2, 3, . . . , 8192). Verify that there seems to be a delay on 481 elements, as promised in
Section 7.3.4 in the book. Do you get the exact same result back?
Solution.
x = arange(1,8193)
mp3_reverse_fbt(mp3_forward_fbt(x))
plt.plot(x)
7 The polyphase representation of filter bank transforms 57
There are some small errors from the original vector in the resulting vector, when one
compensates for the delay of 481 elements.
Use your computer to verify the following symmetries for the prototype filters:
(
−C512−i i 6= 64, 128, . . . , 448
Ci =
C512−i i = 64, 128, . . . , 448.
Explain also that this implies that hi = h512−i for i = 1, . . . , 511. Recall that the
modified version had the symmetry hi = h511−i . When the filters V (m) are defined as in
this section, explain why (7.26) in the book should be replaced by
Solution.
ctable = mp3_ctable()
ctable = ctable[1::]
ctable[63:255:64] = -ctable[63:255:64]
res = ctable + ctable[-1::-1]
res[255]=0
print(abs(res).max())
Note that the middle element of the table is set to 0 in this verification. The reason is
that, for i = 256, there is nothing to check. So, since the verification works by producing
zero when there is symmetry, the element corresponding to i = 256 is set to zero. Also,
every 64’th element in one half of the table changed sign, in order to establish symmetry
for the corresponding entries, rather than anti-symmetry as for the other entries.
Chapter 8
Digital images
hn (x) = xn ,
maps the interval [0, 1] → [0, 1] for any n, and that h0n (1) → ∞ as n → ∞.
Solution.
59
60 8 Digital images
Hint.
Solution.
n = 100
X = double(imread(’images/secret.jpg’, ’jpg’))
X = (X[:,:,0] + X[:,:,1]+ X[:,:,2])/3.
map_to_01(X)
X **= n
X *= 255
X = uint8(X)
imshow(X)
Write code which calls the function tensor2_impl with appropriate filters to produce
the following images:
a) The right image in Figure 8.10 in the book.
Solution.
X3 = excerpt.copy()
S2 = convolve((1/8.)*array([1, 3, 3, 1]),(1/8.)*array([1, 3, 3, 1]))
def longmolecule(x, bd_mode):
filter_impl(S2, x, bd_mode)
tensor2_impl(X3, longmolecule, longmolecule, ’symm’)
imshow(uint8(X3))
Solution.
X3 = excerpt.copy()
tensor2_impl(X3, donothing, diffxmolecule, ’per’)
map_to_01(X3)
X3 *= 255
contrast_adjust0(X3,50)
imshow(uint8(X3))
Solution.
excerpt=create_excerpt()
X1 = excerpt.copy()
tensor2_impl(X1, donothing, diffxmolecule, ’per’)
X2 = excerpt.copy()
tensor2_impl(X2, diffymolecule, donothing, ’per’)
X1 = X1**2 + X2**2
imshow(uint8(X1))
X2[:] = X1
map_to_01(X2)
X2 *= 255
imshow(uint8(X2))
X3[:] = X2
contrast_adjust0(X3, 50)
imshow(uint8(X3))
Solution.
excerpt=create_excerpt()
X1 = excerpt.copy()
tensor2_impl(X1, donothing, diffxmolecule, ’per’)
map_to_01(X1)
X1 *= 255
contrast_adjust0(X1, 50)
imshow(uint8(X1))
Solution.
excerpt=create_excerpt()
X1 = excerpt.copy()
tensor2_impl(X1, donothing, diffxmolecule, ’per’)
tensor2_impl(X1, donothing, diffxmolecule, ’per’)
map_to_01(X1)
X1 *= 255
contrast_adjust0(X1, 100)
imshow(uint8(X1))
X2 = excerpt.copy()
tensor2_impl(X2, diffymolecule, diffxmolecule, ’per’)
map_to_01(X2)
X2 *= 255
contrast_adjust0(X2, 100)
imshow(uint8(X2))
X3 = excerpt.copy()
tensor2_impl(X3, diffymolecule, donothing, ’per’)
tensor2_impl(X3, diffymolecule, donothing, ’per’)
map_to_01(X3)
X3 *= 255
contrast_adjust0(X3, 100)
imshow(uint8(X3))
Solution.
1 121
(1, 2, 1) ⊗ (1, 2, 1) = (1, 2, 1) ⊗ (1, 2, 1) = 2 1 2 1 = 2 4 2 .
1 121
b) Let us define x = (1, 2, 3), y = (3, 2, 1), z = (2, 2, 2), and w = (1, 4, 2). Compute the
matrix A = x ⊗ y + z ⊗ w.
Solution.
We get that
1 2
A = 2 3 2 1 + 2 1 4 2
3 2
321 284 5 10 5
= 6 4 2 + 2 8 4 = 8 12 6 .
963 284 11 14 7
c) Compute (S ⊗ S)A by applying the filter S to every row and column in the matrix
the way we have learned. If the matrix A was more generally an image, what can you
say about how the new image will look?
Solution.
Solution.
We have that
In this section we have used functions which apply the DCT and the DFT either to
subblocks of size 8 × 8, or to the full image.
a) Implement functions dft_impl8, idft_impl8, dct_impl8, and idct_impl8 which
apply the DFT, IDFT, DCT, and IDCT, to consecutive blocks of length 8.
Solution.
b) Implement the two-dimensional FFT, IFFT, DCT, and IDCT on images, with the
help of their one-dimensional counterparts, as well as the function tensor2_impl.
8 Digital images 65
Solution.
c) The function forw_comp_rev_2d in the library applies given transforms to the rows
and columns of an input image, sets the coefficients below a certain threshold to zero,
and transforms back. Run forw_comp_rev_2d for different threshold parameters on the
sample image, and with the functions dct_impl8, idct_impl8, as well as DCT and
IDCT applied to the entire input, as parameters. Check that this reproduces the DCT
test images of this section, and that the correct numbers of values which have been
discarded (i.e. which are below the threshold) are printed on screen.
Solution.
Once
X1 = X.copy()
forw_comp_rev_2d(X1, dct_impl8, idct_impl8, 30)
imshow(uint8(X1))
X2 = X.copy()
forw_comp_rev_2d(X2, dct_impl8, idct_impl8, 50)
imshow(uint8(X2))
X3 = X.copy()
forw_comp_rev_2d(X3, dct_impl8, idct_impl8, 100)
imshow(uint8(X3))
X1 = X.copy()
forw_comp_rev_2d(X1, dct_impl_full, idct_impl_full, 30)
imshow(uint8(X1))
X2 = X.copy()
forw_comp_rev_2d(X2, dct_impl_full, idct_impl_full, 50)
imshow(uint8(X2))
X3 = X.copy()
forw_comp_rev_2d(X3, dct_impl_full, idct_impl_full, 100)
imshow(uint8(X3))
Chapter 9
Using tensor products to apply wavelets to
images
Tensor products can easily be generalized to higher dimensions. For 3D, and for x ∈ RM ,
y ∈ RN , z ∈ RK , we define x ⊗ y ⊗ z as the "3D matrix" with entries
(x ⊗ y ⊗ z)i,j,k = xi yj zk ,
and if S1 , S2 and S3 are linear transformations on RM , RN , and RK , respectively, their
tensor product is the unique linear transformation S1 ⊗ S2 ⊗ S3 defined on 3D matrices
by
Solution.
67
68 9 Using tensor products to apply wavelets to images
X
(S1 x0:(M −1),j,k ) ⊗ ej ⊗ ek .
i,j
This proves the claim that we can apply S1 to the different “(:, j, k)-segments”. The
cases for I ⊗ S2 ⊗ I and I ⊗ I ⊗ S3 follow similarly.
b) In particular we can extend the tensor product to wavelets in 3D. How many detail
components will there be at each level in a 3D DWT, following the setup we did for the
2D DWT? How would you label the different components?
Solution.
Duplicating the theory for changes of coordinates in 2D, we see that the target basis for
a 1-level 3D DWT consists of 23 = 8 different types of basis functions:
Using as before L for φ, H for ψ, these would be labeled LLL, HLL, LHL, LLH, HHL,
HLH, LHH, and HHH, respectively.
dwt3_impl_internal and idwt3_impl_internal are the functions in the library
which compute the 3D DWT and the 3D IDWT. You are in particular encouraged to
review the function tensor3_impl, which is used in the functions, and is a straightforward
generalization of tensor2_impl. Also in the 3D case there is a simpler alternative
definition for the DWT, following the setup from the previous exercise.
The following code produces a chess pattern type image almost identical to that from
Example 9.5 in the book.
img=zeros((128,128))
for x in range(128):
for y in range(128):
img[x,y]=255*( (mod(x,64)>=32) == (mod(y,64)>=32) )
imshow(uint8(img))
Let us now apply a 2D DWT to this image as well with the Haar wavelet:
img2 = img.copy()
dwt_impl(img2, ’Haar’, bd_mode=’per’, dims=2)
map_to_01(img2)
img2 *= 255
imshow(uint8(img2))
Fig. 9.1 A simple image before and after one level of the 2D DWT. The Haar wavelet was used.
There seem to be no detail components here, which is very different from what you
saw in Example 9.5 in the book, even though the images are very similar. Attempt to
explain what causes this to happen.
Hint.
Solution.
In Example 9.5 in the book, the borders in the chess pattern was chosen so that they
occur at odd numbers. This means that the image can not be represented exactly in
Vm−1 ⊗ Vm−1 , so that there is detail present in the image at all the borders in the
chess pattern. In the new image, the borders in the chess pattern was chosen so that
they occur at even numbers. This means that the image can be represented exactly in
Vm−1 ⊗ Vm−1 , so that there is no detail components present in the image.
References
71