This is my programmatic note 19 on Ch. 3, p. 13-20, in "Ripples in Mathematics"
by A. Jensen & A. la Cour-Harbo. These notes document my
programmatic journey, sometimes easy, sometimes
painful, but always joyful, through this great text. My notes
are for those who want to use Java as a programming
investigation tool of various wavelet algorithms and Octave for plotting
the
results. You can find my previous notes by searching my blog on "ripples
in mathematics." If you are on the same journey, let me know of any
bugs in my code or improvements you have found that let us all, fellow
travelers on the wavelet road, get better and more beautiful results.
This programmatic note is the programmatic continuation of my conceptual note 5 on Sections
3.2 - 3.4 of Chapter 3 in "Ripples in Mathematics." That note described how one could conceptualize forward and inverse DWT via lifting
networks.This note describes my programmatic implementation of that conceptualization.
Let us start again with the definition of basic forward DWT via lifting is given in Fig. 1. You can read the explanation of symbols in conceptual note 5. This network implements the two forward
DWT equations given in the bottom of Fig. 1. In equation 1, the
difference vector, i.e., the wavelets, are computed by subtracting the
predicted events from the odds. In equation 2, the evens and the updated
odds are added to obtain the coarser signal.
Figure 1. Basic forward DWT network |
To implement this network I conceptualized it in terms of the following classes: Node.java, Wire.java, Splitter.java, Predictor.java, Updater.java, Plus.java, and Storage.java. The node is the top of the class hierarchy. Every other class, except Wire.java, extends it. The Wire class connects two Nodes and has the public method transfer() that transmits the signal from one Node to the other.
The forward DTW lifting network in Fig. 1 is implemented in ForwardDWTNetwork.java. The class declares the member variables and then, in the constructor, instantiates the Node objects and nine Wire objects that connect them exactly as they are connected in Fig. 1. The network is defined as an ArrayList of Wire objects. The processSignal() method sets the input and output of the Storage object on the left (i.e., S1) and then loops through the ArrayList of Wires calling the transfer() method on each Wire object. The output of the network is the output of the S0 and D0 Storage objects.
public class ForwardDWTNetwork {
Storage D1; Storage S0; Storage D0;
Splitter SPLITTER1; Predictor PREDICTOR1;
Updater UPDATER1; Minus MINUS1; Plus PLUS1;
Wire WIRE1; Wire WIRE2; Wire WIRE3;
Wire WIRE4; Wire WIRE5; Wire WIRE6;
Wire WIRE7; Wire WIRE8; Wire WIRE9;
ArrayList<Wire> NETWORK;
public ForwardDWTNetwork() {
D1 = new Storage(); S0 = new Storage(); D0 = new Storage();
SPLITTER1 = new Splitter(); PREDICTOR1 = new Predictor(); UPDATER1 = new Updater();
MINUS1 = new Minus(); PLUS1 = new Plus();
WIRE1 = new Wire(D1, SPLITTER1);
WIRE2 = new Wire(SPLITTER1, PREDICTOR1);
WIRE3 = new Wire(SPLITTER1, MINUS1);
WIRE4 = new Wire(PREDICTOR1, MINUS1);
WIRE5 = new Wire(MINUS1, UPDATER1);
WIRE6 = new Wire(MINUS1, D0);
WIRE7 = new Wire(SPLITTER1, PLUS1);
WIRE8 = new Wire(UPDATER1, PLUS1);
WIRE9 = new Wire(PLUS1, S0);
NETWORK = new ArrayList<>();
NETWORK.add(WIRE1); NETWORK.add(WIRE2); NETWORK.add(WIRE3);
NETWORK.add(WIRE4); NETWORK.add(WIRE5); NETWORK.add(WIRE6);
NETWORK.add(WIRE7); NETWORK.add(WIRE8); NETWORK.add(WIRE9);
}
public Storage processSignal(Storage storage) {
D1.setInput(storage.getInput()); D1.setOutput(storage.getOutput());
for(Wire w: NETWORK) { w.transfer(); }
System.out.print("Samples:\t");
for(double d: S0.getOutput()) { System.out.print(d + " "); }
System.out.println();
System.out.print("Wavelets:\t");
for(double d: D0.getOutput()) { System.out.print(d + " "); }
System.out.println();
return S0;
}
}
Fig. 2 gives an example of how the basic forward DWT network works on (11, 3).
Figure 2. Basic forward DWT network processing (11, 3) |
We can test our forward DWT network in (11, 3), as shown in Fig. 2, and also on (5, 1, 2, 8) as follows:
public static void test1() {
final double[] signal = {11, 3};
final double[] signal2 = {5, 1, 2, 8};
ForwardDWTNetwork net1 = new ForwardDWTNetwork();
Storage storage1 = new Storage();
storage1.setInput(signal); storage1.setOutput(signal);
Storage output1 = net1.processSignal(storage1);
double[] output_signal = output1.getOutput();
for(int i = 0; i < output_signal.length; i++) {
System.out.print(output_signal[i] + " ");
}
System.out.println();
storage1.setInput(signal2); storage1.setOutput(signal2);
output1 = net1.processSignal(storage1);
output_signal = output1.getOutput();
for(int i = 0; i < output_signal.length; i++) {
System.out.print(output_signal[i] + " ");
}
System.out.println();
}
The output of running test1() in the main() is as follows:
Samples: 7.0
Wavelets: -8.0
7.0
Samples: 3.0 5.0
Wavelets: -4.0 6.0
3.0 5.0
As discussed in in conceptual note 5, basic forward DWT networks can be layered, as shown in Fig. 3 where two basic forward DWT networks are layered to implement two scales of forward DWT. Fig. 4 gives a concrete example of the layered network in Fig. 3 on the signal (5, 1, 2, 8). The network results in one coarser signal (4) and two wavelet vectors (2) and (-4, 6). Note that the coarser signal (4) gives the mean of the original signal (5, 1, 2, 8).
public static void test1() {
final double[] signal = {11, 3};
final double[] signal2 = {5, 1, 2, 8};
ForwardDWTNetwork net1 = new ForwardDWTNetwork();
Storage storage1 = new Storage();
storage1.setInput(signal); storage1.setOutput(signal);
Storage output1 = net1.processSignal(storage1);
double[] output_signal = output1.getOutput();
for(int i = 0; i < output_signal.length; i++) {
System.out.print(output_signal[i] + " ");
}
System.out.println();
storage1.setInput(signal2); storage1.setOutput(signal2);
output1 = net1.processSignal(storage1);
output_signal = output1.getOutput();
for(int i = 0; i < output_signal.length; i++) {
System.out.print(output_signal[i] + " ");
}
System.out.println();
}
The output of running test1() in the main() is as follows:
Samples: 7.0
Wavelets: -8.0
7.0
Samples: 3.0 5.0
Wavelets: -4.0 6.0
3.0 5.0
As discussed in in conceptual note 5, basic forward DWT networks can be layered, as shown in Fig. 3 where two basic forward DWT networks are layered to implement two scales of forward DWT. Fig. 4 gives a concrete example of the layered network in Fig. 3 on the signal (5, 1, 2, 8). The network results in one coarser signal (4) and two wavelet vectors (2) and (-4, 6). Note that the coarser signal (4) gives the mean of the original signal (5, 1, 2, 8).
Figure 3. Two layered forward basic DWT networks |
We can construct and test a two-layer forward DWT on (5, 1, 2, 8) as follows:
public static void test2() {
final double[] signal2 = {5, 1, 2, 8};
ForwardDWTNetwork net1 = new ForwardDWTNetwork();
ForwardDWTNetwork net2 = new ForwardDWTNetwork();
Storage storage2 = new Storage();
storage2.setInput(signal2);
storage2.setOutput(signal2);
Storage output2 = net1.processSignal(storage2);
double[] output2_signal = output2.getOutput();
for(int i = 0; i < output2_signal.length; i++) {
System.out.print(output2_signal[i] + " ");
}
System.out.println();
Storage output3 = net2.processSignal(output2);
double[] output3_signal = output3.getOutput();
for(int i = 0; i < output3_signal.length; i++) {
System.out.print(output3_signal[i] + " ");
}
System.out.println();
}
Running test2() in the main() gives us the output below, which is the same as the output in Fig. 4.
Samples: 3.0 5.0
Wavelets: -4.0 6.0
3.0 5.0
Samples: 4.0
Wavelets: 2.0
4.0
Figure 4. Layered forward DWT network processing (5, 1, 2, 8) |
I implemented the
basic inverse DWT in InverseDWTNetwork.java. This network takes the coarser signal and the corresponding
wavelets and synthesizes from them the more refined signal at the
previous scale, as shown in Fig. 5 to the right of the red dash line in the middle.
Figure 5. Basic forward DWT and its inverse |
public static void main(String[] args) {
final double[] s0 = {7};
final double[] d0 = {-8};
InverseDWTNetwork net1 = new InverseDWTNetwork();
Storage ss0 = new Storage();
ss0.setInput(s0);
ss0.setOutput(s0);
Storage dd0 = new Storage();
dd0.setInput(d0);
dd0.setOutput(d0);
Storage output2 = net1.processSignal(ss0, dd0);
double[] output2_signal = output2.getOutput();
for(int i = 0; i < output2_signal.length; i++) {
System.out.print(output2_signal[i] + " ");
}
System.out.println();
}
The output is the signal (11, 3), which is the input signal to the forward DWT network.
Inversed samples: 11.0 3.0
11.0 3.0
The inverse DWT networks can also be layered like their forward counterparts.