File: wp2d.rst

package info (click to toggle)
pywavelets 1.4.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 13,680 kB
  • sloc: python: 8,849; ansic: 5,134; makefile: 93
file content (429 lines) | stat: -rw-r--r-- 12,996 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
.. _reg-wp2d:

.. currentmodule:: pywt


2D Wavelet Packets
==================

Import pywt
-----------

>>> from __future__ import print_function
>>> import pywt
>>> import numpy


Create 2D Wavelet Packet structure
----------------------------------

Start with preparing test data:

    >>> x = numpy.array([[1, 2, 3, 4, 5, 6, 7, 8]] * 8, 'd')
    >>> print(x)
    [[ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]]

Now create a :class:`2D Wavelet Packet <WaveletPacket2D>` object:

    >>> wp = pywt.WaveletPacket2D(data=x, wavelet='db1', mode='symmetric')

The input ``data`` and decomposition coefficients are stored in the
:attr:`WaveletPacket2D.data` attribute:

    >>> print(wp.data)
    [[ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]]

:class:`Nodes <Node2D>` are identified by paths. For the root node the path is
``''`` and the decomposition level is ``0``.

    >>> print(repr(wp.path))
    ''
    >>> print(wp.level)
    0

The :attr:`WaveletPacket2D.maxlevel`, if not given in the constructor, is
automatically computed based on the data size:

    >>> print(wp.maxlevel)
    3


Traversing WP tree:
-------------------

Wavelet Packet :class:`nodes <Node2D>` are arranged in a tree. Each node in a WP
tree is uniquely identified and addressed by a :attr:`~Node2D.path` string.

In the 1D :class:`WaveletPacket` case nodes were accessed using ``'a'``
(approximation) and ``'d'`` (details) path names (each node has two 1D
children).

Because now we deal with a bit more complex structure (each node has four
children), we have four basic path names based on the dwt 2D output convention
to address the WP2D structure:

    * ``a`` - LL, low-low coefficients
    * ``h`` - LH, low-high coefficients
    * ``v`` - HL, high-low coefficients
    * ``d`` - HH, high-high coefficients

In other words, subnode naming corresponds to the :func:`dwt2` function output
naming convention (as wavelet packet transform is based on the dwt2 transform)::

                                -------------------
                                |        |        |
                                | cA(LL) | cH(LH) |
                                |        |        |
    (cA, (cH, cV, cD))  <--->   -------------------
                                |        |        |
                                | cV(HL) | cD(HH) |
                                |        |        |
                                -------------------

       (fig.1: DWT 2D output and interpretation)


Knowing what the nodes names are, we can now access them using the indexing
operator ``obj[x]`` (:meth:`WaveletPacket2D.__getitem__`):

    >>> print(wp['a'].data)
    [[  3.   7.  11.  15.]
     [  3.   7.  11.  15.]
     [  3.   7.  11.  15.]
     [  3.   7.  11.  15.]]
    >>> print(wp['h'].data)
    [[ 0.  0.  0.  0.]
     [ 0.  0.  0.  0.]
     [ 0.  0.  0.  0.]
     [ 0.  0.  0.  0.]]
    >>> print(wp['v'].data)
    [[-1. -1. -1. -1.]
     [-1. -1. -1. -1.]
     [-1. -1. -1. -1.]
     [-1. -1. -1. -1.]]
    >>> print(wp['d'].data)
    [[ 0.  0.  0.  0.]
     [ 0.  0.  0.  0.]
     [ 0.  0.  0.  0.]
     [ 0.  0.  0.  0.]]

Similarly, a subnode of a subnode can be accessed by:

    >>> print(wp['aa'].data)
    [[ 10.  26.]
     [ 10.  26.]]

Indexing base :class:`WaveletPacket2D` (as well as 1D :class:`WaveletPacket`)
using compound path is just the same as indexing WP subnode:

    >>> node = wp['a']
    >>> print(node['a'].data)
    [[ 10.  26.]
     [ 10.  26.]]
    >>> print(wp['a']['a'].data is wp['aa'].data)
    True

Following down the decomposition path:

    >>> print(wp['aaa'].data)
    [[ 36.]]
    >>> print(wp['aaaa'].data)
    Traceback (most recent call last):
    ...
    IndexError: Path length is out of range.

Ups, we have reached the maximum level of decomposition for the ``'aaaa'`` path,
which btw. was:

    >>> print(wp.maxlevel)
    3


Now try some invalid path:

    >>> print(wp['f'])
    Traceback (most recent call last):
    ...
    ValueError: Subnode name must be in ['a', 'h', 'v', 'd'], not 'f'.


Accessing Node2D's attributes:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:class:`WaveletPacket2D` is a tree data structure, which evaluates to a set
of :class:`Node2D` objects. :class:`WaveletPacket2D` is just a special subclass
of the :class:`Node2D` class (which in turn inherits from a :class:`BaseNode`,
just like with :class:`Node` and :class:`WaveletPacket` for the 1D case.).

    >>> print(wp['av'].data)
    [[-4. -4.]
     [-4. -4.]]

    >>> print(wp['av'].path)
    av

    >>> print(wp['av'].node_name)
    v

    >>> print(wp['av'].parent.path)
    a

    >>> print(wp['av'].parent.data)
    [[  3.   7.  11.  15.]
     [  3.   7.  11.  15.]
     [  3.   7.  11.  15.]
     [  3.   7.  11.  15.]]

    >>> print(wp['av'].level)
    2

    >>> print(wp['av'].maxlevel)
    3

    >>> print(wp['av'].mode)
    symmetric


Collecting nodes
~~~~~~~~~~~~~~~~

We can get all nodes on the particular level using the
:meth:`WaveletPacket2D.get_level` method:

    * 0 level - the root `wp` node:

        >>> len(wp.get_level(0))
        1
        >>> print([node.path for node in wp.get_level(0)])
        ['']

    * 1st level of decomposition:

        >>> len(wp.get_level(1))
        4
        >>> print([node.path for node in wp.get_level(1)])
        ['a', 'h', 'v', 'd']

    * 2nd level of decomposition:

        >>> len(wp.get_level(2))
        16
        >>> paths = [node.path for node in wp.get_level(2)]
        >>> for i, path in enumerate(paths):
        ...     if (i+1) % 4 == 0:
        ...         print(path)
        ...     else:
        ...         print(path, end=' ')
        aa ah av ad
        ha hh hv hd
        va vh vv vd
        da dh dv dd

    * 3rd level of decomposition:

        >>> print(len(wp.get_level(3)))
        64
        >>> paths = [node.path for node in wp.get_level(3)]
        >>> for i, path in enumerate(paths):
        ...     if (i+1) % 8 == 0:
        ...         print(path)
        ...     else:
        ...         print(path, end=' ')
        aaa aah aav aad aha ahh ahv ahd
        ava avh avv avd ada adh adv add
        haa hah hav had hha hhh hhv hhd
        hva hvh hvv hvd hda hdh hdv hdd
        vaa vah vav vad vha vhh vhv vhd
        vva vvh vvv vvd vda vdh vdv vdd
        daa dah dav dad dha dhh dhv dhd
        dva dvh dvv dvd dda ddh ddv ddd

Note that :meth:`WaveletPacket2D.get_level` performs automatic decomposition
until it reaches the given level.


Reconstructing data from Wavelet Packets:
-----------------------------------------

Let's create a new empty 2D Wavelet Packet structure and set its nodes
values with known data from the previous examples:

    >>> new_wp = pywt.WaveletPacket2D(data=None, wavelet='db1', mode='symmetric')

    >>> new_wp['vh'] = wp['vh'].data # [[0.0, 0.0], [0.0, 0.0]]
    >>> new_wp['vv'] = wp['vh'].data # [[0.0, 0.0], [0.0, 0.0]]
    >>> new_wp['vd'] = [[0.0, 0.0], [0.0, 0.0]]

    >>> new_wp['a'] = [[3.0, 7.0, 11.0, 15.0], [3.0, 7.0, 11.0, 15.0],
    ...                [3.0, 7.0, 11.0, 15.0], [3.0, 7.0, 11.0, 15.0]]
    >>> new_wp['d'] = [[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0],
    ...                [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]]

    For convenience, :attr:`Node2D.data` gets automatically extracted from the
    base :class:`Node2D` object:

    >>> new_wp['h'] = wp['h'] # all zeros

    Note: just remember to not assign to the node.data parameter directly (todo).

And reconstruct the data from the ``a``, ``d``, ``vh``, ``vv``, ``vd`` and ``h``
packets (Note that ``va`` node was not set and the WP tree is "not complete"
- the ``va`` branch will be treated as *zero-array*):

    >>> print(new_wp.reconstruct(update=False))
    [[ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]]

Now set the ``va`` node with the known values and do the reconstruction again:

    >>> new_wp['va'] = wp['va'].data # [[-2.0, -2.0], [-2.0, -2.0]]
    >>> print(new_wp.reconstruct(update=False))
    [[ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]]

which is just the same as the base sample data ``x``.

Of course we can go the other way and remove nodes from the tree. If we delete
the ``va`` node, again, we get the "not complete" tree from one of the previous
examples:

    >>> del new_wp['va']
    >>> print(new_wp.reconstruct(update=False))
    [[ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]
     [ 1.5  1.5  3.5  3.5  5.5  5.5  7.5  7.5]]

Just restore the node before next examples.

    >>> new_wp['va'] = wp['va'].data

If the ``update`` param in the :meth:`WaveletPacket2D.reconstruct` method is set
to ``False``, the node's :attr:`Node2D.data` attribute will not be updated.

    >>> print(new_wp.data)
    None

Otherwise, the :attr:`WaveletPacket2D.data` attribute will be set to the
reconstructed value.

    >>> print(new_wp.reconstruct(update=True))
    [[ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]]
    >>> print(new_wp.data)
    [[ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]
     [ 1.  2.  3.  4.  5.  6.  7.  8.]]

Since we have an interesting WP structure built, it is a good occasion to
present the :meth:`WaveletPacket2D.get_leaf_nodes` method, which collects
non-zero leaf nodes from the WP tree:

    >>> print([n.path for n in new_wp.get_leaf_nodes()])
    ['a', 'h', 'va', 'vh', 'vv', 'vd', 'd']

Passing the ``decompose = True`` parameter to the method will force the WP
object to do a full decomposition up to the *maximum level* of decomposition:

    >>> paths = [n.path for n in new_wp.get_leaf_nodes(decompose=True)]
    >>> len(paths)
    64
    >>> for i, path in enumerate(paths):
    ...     if (i+1) % 8 == 0:
    ...         print(path)
    ...     else:
    ...         try:
    ...             print(path, end=' ')
    ...         except:
    ...             print(path, end=' ')
    aaa aah aav aad aha ahh ahv ahd
    ava avh avv avd ada adh adv add
    haa hah hav had hha hhh hhv hhd
    hva hvh hvv hvd hda hdh hdv hdd
    vaa vah vav vad vha vhh vhv vhd
    vva vvh vvv vvd vda vdh vdv vdd
    daa dah dav dad dha dhh dhv dhd
    dva dvh dvv dvd dda ddh ddv ddd

Lazy evaluation:
----------------

.. note:: This section is for demonstration of pywt internals purposes
          only. Do not rely on the attribute access to nodes as presented in
          this example.

>>> x = numpy.array([[1, 2, 3, 4, 5, 6, 7, 8]] * 8)
>>> wp = pywt.WaveletPacket2D(data=x, wavelet='db1', mode='symmetric')

1) At first the wp's attribute ``a`` is ``None``

   >>> print(wp.a)
   None

   **Remember that you should not rely on the attribute access.**

2) During the first attempt to access the node it is computed
   via decomposition of its parent node (the wp object itself).

   >>> print(wp['a'])
   a: [[  3.   7.  11.  15.]
    [  3.   7.  11.  15.]
    [  3.   7.  11.  15.]
    [  3.   7.  11.  15.]]

3) Now the ``a`` is set to the newly created node:

    >>> print(wp.a)
    a: [[  3.   7.  11.  15.]
     [  3.   7.  11.  15.]
     [  3.   7.  11.  15.]
     [  3.   7.  11.  15.]]

   And so is `wp.d`:

    >>> print(wp.d)
    d: [[ 0.  0.  0.  0.]
     [ 0.  0.  0.  0.]
     [ 0.  0.  0.  0.]
     [ 0.  0.  0.  0.]]