I am Charmie

メモとログ

PyWavelets

PyWaveletsに関するメモ (公式資料の低質の和訳).

PyWaveletsとは

  • Wavelet変換を実現するためのオープンソースPythonパッケージ
  • 以下の機能を提供
    • 1次元,2次元,多次元の離散Wavelet変換(DWT)と逆離散Wavelet変換(IDWT)
    • 1次元,2次元,多次元の多重解像度DWTとIDWT
    • 1次元,2次元,多次元の定常Wavelet変換 (非間引きWavelet変換?)
    • 1次元,2次元のWaveletパケット分解・再構成
    • 1次元連続Wavelet変換
    • waveletとscaling関数の近似計算
    • 100個以上のbuilt-in waveletフィルタと自作waveletのサポート
    • 単精度・倍精度計算
    • 実数・虚数計算
    • Matlab Wavelet Toolbox (TM)との整合性

インストール

  1. バイナリ(pip, anaconda)
  2. ソースからビルド

API

Wavelet ファミリー

  • Waveletファミリーは 以下のファミリー(mother wavelet(基底信号?)の大きな分類?)がbuilt-inで実装されている.
  • 各ファミリーがどのような信号か知りたい人は,Matlab Wavelet Toolboxのこのページを見ると良い.
    • Haar (haar)
    • Daubechies (db)
    • Symlets (sym)
    • Coiflets (coif)
    • Biorthogonal (bior)
    • Reverse biorthogonal (rbio)
    • “Discrete” FIR approximation of Meyer wavelet (dmey)
    • Gaussian wavelets (gaus)
    • Mexican hat wavelet (mexh)
    • Morlet wavelet (morl)
    • Complex Gaussian wavelets (cgau)
    • Shannon wavelets (shan)
    • Frequency B-Spline wavelets (fbsp)
    • Complex Morlet wavelets (cmor)
  • pywt.families()を使うと,built-inのファミリーをstrのlistとして取得できる.
>>> families = pywt.families()
>>> print(families)
['haar', 'db', 'sym', 'coif', 'bior', 'rbio', 'dmey', 'gaus', 'mexh', 'morl', 'cgau', 'shan', 'fbsp', 'cmor']
>>> families_not_short = pywt.families(short=False)
>>>  print(families_not_short)
['Haar', 'Daubechies', 'Symlets', 'Coiflets', 'Biorthogonal', 'Reverse biorthogonal', 'Discrete Meyer (FIR Approximation)', 'Gaussian', 'Mexican hat wavelet', 'Morlet wavelet', 'Complex Gaussian wavelets', 'Shannon wavelets', 'Frequency B-Spline wavelets', 'Complex Morlet wavelets']

使用可能なWavelet

  • pywt.wavelist()を使うと,指定したファミリーのwaveletをstrのlistとして取得できる.
  • kind引数を'continuous', 'discrete'に指定すると,それぞれ連続,離散waveletの一覧を取得できる.
>>> wavelets_coif = pywt.wavelist('coif')
>>> print(wavelets_coif)
['coif1', 'coif2', 'coif3', 'coif4', 'coif5', 'coif6', 'coif7', 'coif8', 'coif9', 'coif10', 'coif11', 'coif12', 'coif13', 'coif14', 'coif15', 'coif16', 'coif17']
>>> wavelets_continuous = pywt.wavelist(kind='continuous')
>>> print(wavelets_continuous)
['cgau1', 'cgau2', 'cgau3', 'cgau4', 'cgau5', 'cgau6', 'cgau7', 'cgau8', 'cmor', 'fbsp', 'gaus1', 'gaus2', 'gaus3', 'gaus4', 'gaus5', 'gaus6', 'gaus7', 'gaus8', 'mexh', 'morl', 'shan']

Waveletオブジェクト

  • pywt.Wavelet()を使うと,離散Waveletの属性(特徴)を表すオブジェクトを取得できる.

  • 連続waveletについてはpywt.ContinuousWaveletを使う.
  • wavelet名の指定はpywt.wavelist()で得られる名前を使う.
  • 自作waveletのWaveletオブジェクトを作るためには自身が定義したフィルタセットを filter_bank パラメータと一緒に与える必要がある.
  • Waveletオブジェクトの各パラメータの意味は以下の通り
  • フィルタは全てfloatのlist
    • name: Wavelet名 (pywt.wavelist()で得られる名前)
    • orthogonal: 直交性 (直交性を持つならTrue)
    • biorthogonal: 双直交性 (双直交性を持つならTrue)
    • symmetry: 対称性 (asymmetric, near symmetric, symmetricのどれか)
    • 分解 (decomposition)
      • dec_len: 分解フィルタ長
      • dec_lo: 分解フィルタ (低周波)
      • dec_hi: 分解フィルタ (高周波)
    • 再構成 (reconstruction)
      • rec_len: 再構成フィルタ長
      • rec_lo: 再構成フィルタ (低周波)
      • rec_hi: 再構成フィルタ (高周波)
    • filter_bank: 全フィルタ
      • listのtuple
      • tupleの順番はdec_lo, dec_hi, rec_lo, rec_hi
    • inverse_filter_bank: reverse wavelet filters係数のリスト(と書いてある)
      • listのtuple
      • tupleの順番はrec_lo[::-1], rec_hi[::-1], dec_lo[::-1], dec_hi[::-1]
    • vanishing_moments_psi: wavelet関数の消失モーメントの数
    • vanishing_moments_phi: scaling関数の消失モーメントの数
    • 以下のDWTとCWTの取得方法が分からなかった...
>>> wavelet = pywt.Wavelet('haar')
>>> print(wavelet)
Wavelet haar
  Family name:    Haar
  Short name:     haar
  Filters length: 2
  Orthogonal:     True
  Biorthogonal:   True
  Symmetry:       asymmetric
  DWT:            True
  CWT:            False

DWT

  • PyWaveletsはWavelet変換の解像度と対象信号の次元という2つの多様性を持つ
    • 解像度
      • 単解像度 (dwt, idwt)
      • 多重解像度 (wavedec, waverec)
    • 対象信号の次元
      • 1次元:
      • 2次元
      • n次元
  • 上記の4個の関数名は1次元信号用
    • 2次元,n次元用の関数名は末尾に2,nを付ける (例: 2次元信号の多重解像度分解は wavedec2)

1次元単解像度Wavelet変換

  • dwtの返戻値は要素数が2のtuple.
    • 要素0はapproximationの係数 (float64,1次元のnumpy.ndarray型)
    • 要素1はdetailの係数 (float64,1次元のnumpy.ndarray型)
  • idwtの引数はapproximation, detail係数とmother wavelet
>>> signal = [1, 2, 3, 4, 5, 6]
>>> mother_wavelet = 'db1'
>>> (cA, cD) = pywt.dwt(signal, mother_wavelet)
>>> print(type(cA), type(cD))
  <class 'numpy.ndarray'> <class 'numpy.ndarray'>
>>> print(cA.shape, cD.shape)
  (3,) (3,)
>>> print(cA.dtype, cD.dtype)
  float64 float64

>>> signal_recon = pywt.idwt(cA, cD, mother_wavelet)
>>> print(signal_recon)
  [1. 2. 3. 4. 5. 6.]
>>> print(type(signal_recon))
  <class 'numpy.ndarray'>
>>> print(signal_recon.shape)
  (6,)
>>> print(signal_recon.dtype)
  float64

2次元単解像度Wavelet変換

  • dwt2の返戻値は要素数が2のtuple.
    • 要素0はapproximationの係数
      • float64,2次元のnumpy.ndarray型
      • 低周波成分に該当
    • 要素1はdetailの係数をまとめた要素3のtuple
      • tupleの要素は横方向,縦方向,斜め方向の高周波成分
      • 各要素はfloat64,2次元のnumpy.ndarray型
  • idwt2の引数はwavelet係数とmother wavelet
    • wavelet係数はdwt2の返戻値と同じ形式でまとめる
>>> signal = np.random.rand(8, 8)
>>> mother_wavelet = 'db1'
>>> c = pywt.dwt2(signal, mother_wavelet)
>>> print('c:', type(c), len(c))
  c: <class 'tuple'> 2
>>> print('  c[0]:', type(c[0]), c[0].shape)
    c[0]: <class 'numpy.ndarray'> (4, 4)
>>> print('  c[1]:', type(c[1]), len(c[1]))
    c[1]: <class 'tuple'> 3
>>> [print('    c[1][%d]:' % n, type(c[1][n]), c[1][n].shape, c[1][n].dtype) for n in range(len(c[1]))]
      c[1][0]: <class 'numpy.ndarray'> (4, 4) float64
      c[1][1]: <class 'numpy.ndarray'> (4, 4) float64
      c[1][2]: <class 'numpy.ndarray'> (4, 4) float64

>>> cA, (cH, cV, cD) = c
>>> signal_recon = pywt.idwt2((cA, (cH, cV, cD)), mother_wavelet)
>>> print(np.testing.assert_almost_equal(signal, signal_recon))
  None
>>> print(type(signal_recon))
  <class 'numpy.ndarray'>
>>> print(signal_recon.shape)
  (8, 8)
>>> print(signal_recon.dtype)
  float64

多重解像度分解

  • wavedec2の返戻値は要素数が引数levelで指定した解像度と同数のlist.
    • 要素0はapproximationの係数
      • float64,2次元のnumpy.ndarray型
      • 低周波成分に該当
    • 要素1〜level-1はdetailの係数をまとめた要素3のtuple
      • tupleの要素は横方向,縦方向,斜め方向の高周波成分
      • 各要素はfloat64,2次元のnumpy.ndarray型
  • waverec2は調べてないけど,idwt2と似た感じだと思う

level=2とした時の画像とwavelet係数の対応関係を図にしてみた(分かりにくい).

[c[0], (c[1][0], c[1][1], c[1][2]), (c[2][0], c[2][1], c[2][2])]  <---> 
------------------------------------------
|         |         |                    |
|  c[0]   | c[1][0] |                    |
|         |         |                    |
---------------------      c[2][0]       |
|         |         |                    |
| c[1][1] | c[1][2] |                    |
|         |         |                    |
------------------------------------------
|                   |                    |
|                   |                    |
|                   |                    |
|      c[2][1]      |      c[2][2]       |
|                   |                    |
|                   |                    |
|                   |                    |
------------------------------------------
>>> image = pywt.data.camera().astype(np.float32)
>>> print('  ', type(image), image.shape)
  <class 'numpy.ndarray'> (512, 512)

>>> mother_wavelet = 'db1'
>>> for level in range(5):
>>>     c = pywt.wavedec2(image, mother_wavelet, mode='periodization', level=level)
>>>     print('Wavelet info with level=%d' % level)
>>>     print('  level=%d' % level, type(c), len(c))
  level=0 <class 'list'> 1
  level=1 <class 'list'> 2
  level=2 <class 'list'> 3
  level=3 <class 'list'> 4
  level=4 <class 'list'> 5

>>> level = 2
>>> c = pywt.wavedec2(image, mother_wavelet, mode='periodization', level=level)
>>> print('level=%d' % level)
>>> print('  c[0]:', type(c[0]), c[0].shape)
  level=2
    c[0]: <class 'numpy.ndarray'> (128, 128)
>>> for l in range(level):
>>>     print('  c[%d]' % (l+1), type(c[l+1]), len(c[l+1]))
>>>     for elem in range(len(c[l+1])):
>>>         print('    c[%d][%d]' % (l+1, elem), type(c[l+1][elem]), c[l+1][elem].shape)
    c[1] <class 'tuple'> 3
      c[1][0] <class 'numpy.ndarray'> (128, 128)
      c[1][1] <class 'numpy.ndarray'> (128, 128)
      c[1][2] <class 'numpy.ndarray'> (128, 128)
    c[2] <class 'tuple'> 3
      c[2][0] <class 'numpy.ndarray'> (256, 256)
      c[2][1] <class 'numpy.ndarray'> (256, 256)
      c[2][2] <class 'numpy.ndarray'> (256, 256)

Wavelet係数の扱い方

  • Wavelet係数と単一の(元信号と同じ次元の)numpy.ndarrayの相互変換
  • 多重解像度のWavelet係数を扱いやすくする方法
    • pywt.coeffs_to_array: Wavelet係数からarrayへ変換
      • coeff_arr (第1返戻値): 原信号と同じ次元数のnumpy.ndarray型オブジェクト.
      • coeff_slices (第2返戻値): 要素数がlevel引数で指定した多重解像度と同じlist型オブジェクト.
    • pywt.array_to_coeffs: arrayからWavelet係数へ変換
  • pywt.coeffs_to_arrayによってwavelet係数を1枚の画像に変換できる
    • levelを変えてもpywt.coeffs_to_arrayによって得られる画像の解像度は変わらない (画像サイズが2の階乗でない場合は違うかも)
    • levelの数が多重解像度の数であることが分かる
>>> image = pywt.data.camera().astype(np.float32)
>>> print('Image info: , type(image), image.shape)
  Image info: <class 'numpy.ndarray'> (512, 512)

>>> mother_wavelet = 'db1'
>>> levels = 4
>>> fig, axes = plt.subplots(1, levels+1, figsize=[12, 10])
>>> axes[0].imshow(image, cmap=plt.cm.gray)
>>> axes[0].set_axis_off()
>>> axes[0].set_title('Original image')
>>> for level in range(4):
>>>     print('level=%d' % level)
>>>     coeff = pywt.wavedec2(image, mother_wavelet, mode='periodization', level=level)
>>>     arr, coeff_slices = pywt.coeffs_to_array(coeff)
>>>     print('  coeff:', type(arr), arr.shape)
>>>     print('  array:', type(coeff_slices), len(coeff_slices))
>>>     axes[level+1].imshow(arr, cmap=plt.cm.gray)
>>>     axes[level+1].set_axis_off()
>>>     axes[level+1].set_title('Wavelet coeff. at level %d' % level)
  level=0
    coeff: <class 'numpy.ndarray'> (512, 512)
    array: <class 'list'> 1
  level=1
    coeff: <class 'numpy.ndarray'> (512, 512)
    array: <class 'list'> 2
  level=2
    coeff: <class 'numpy.ndarray'> (512, 512)
    array: <class 'list'> 3
  level=3
    coeff: <class 'numpy.ndarray'> (512, 512)
    array: <class 'list'> 4

f:id:charmie11:20200327110105p:plain
Image and its wavelet coefficients with different levels

  • Wavelet係数をまとめた画像(信号)から各解像度,方向の成分を取り出すには,pywt.coeffs_to_arrayの第2返戻値を使う
  • coeff_slices[level][key]
    • 解像度level,信号の方向keyのWavelet係数をcoeffから取得するためのslice型オブジェクトを要素に持つtuple型オブジェクト.
    • keyは'da', 'ad', 'dd'の3種類でそれぞれ横,縦,斜め方向の係数を表す
    • coeff_slices[level][key]
>>> image = pywt.data.camera().astype(np.float32)
>>> mother_wavelet = 'db1'
>>> level = 2
>>> coeff = pywt.wavedec2(image, mother_wavelet, mode='periodization', level=level)
>>> arr, coeff_slices = pywt.coeffs_to_array(coeff)

>>> fig, axes = plt.subplots(level+2, 3, figsize=[12, 10])
>>> axes[0, 0].imshow(image, cmap=plt.cm.gray)
>>> axes[0, 0].set_axis_off()
>>> axes[0, 0].set_title('Original image')
>>> axes[0, 1].imshow(arr, cmap=plt.cm.gray)
>>> axes[0, 1].set_axis_off()
>>> axes[0, 1].set_title('Wavelet coefficients')
>>> axes[0, 2].set_axis_off()
>>> print('  coeff:', type(arr), arr.shape)
>>> print('  array:', type(coeff_slices), len(coeff_slices))
  coeff: <class 'numpy.ndarray'> (512, 512)
  array: <class 'list'> 3
>>> for l in range(level):
>>>     print('    array for level=%d info' % (l + 1), type(coeff_slices[l + 1]), len(coeff_slices[l + 1]))
>>>     if l == 0:
>>>         axes[1, 0].imshow(arr[coeff_slices[l]], cmap=plt.cm.gray)
>>>         axes[1, 0].set_title('Low frequency')
>>>         axes[1, 0].set_axis_off()
>>>         axes[1, 1].set_axis_off()
>>>         axes[1, 2].set_axis_off()
>>>     for n, key in enumerate(coeff_slices[l+1].keys()):
>>>         axes[l+2, n].imshow(arr[coeff_slices[l+1][key]], cmap=plt.cm.gray)
>>>         axes[l+2, n].set_title('%s at level %d' % (key, l))
>>>         axes[l+2, n].set_axis_off()
>>>         print('      ', key, type(coeff_slices[l+1][key]), coeff_slices[l+1][key])
>>> plt.show()
    array for level=1 info <class 'dict'> 3
       ad <class 'tuple'> (slice(None, 128, None), slice(128, 256, None))
       da <class 'tuple'> (slice(128, 256, None), slice(None, 128, None))
       dd <class 'tuple'> (slice(128, 256, None), slice(128, 256, None))
    array for level=2 info <class 'dict'> 3
       ad <class 'tuple'> (slice(None, 256, None), slice(256, 512, None))
       da <class 'tuple'> (slice(256, 512, None), slice(None, 256, None))
       dd <class 'tuple'> (slice(256, 512, None), slice(256, 512, None))

f:id:charmie11:20200327110739p:plain
Wavelet coefficients w.r.t. direction