小波变换

傅里叶变换的不足:

  • 对非平稳过程,傅里叶变换有局限性
    它只能获取一段信号总体上包含哪些频率的成分,但是对各成分出现的时刻并无所知。因此时域相差很大的两个信号,可能频谱图一样。
import numpy as np
import matplotlib.pyplot as plt
from scipy.fftpack import fft

t = np.linspace(0, 1, 400, endpoint=False)
cond = [t<0.25, (t>=0.25)&(t<0.5), t>=0.5]
f1 = lambda t: np.cos(2*np.pi*10*t)
f2 = lambda t: np.cos(2*np.pi*50*t)
f3 = lambda t: np.cos(2*np.pi*100*t)

y1 = np.piecewise(t, cond, [f1, f2, f3])
y2 = np.piecewise(t, cond, [f2, f1, f3])

Y1 = abs(fft(y1))
Y2 = abs(fft(y2))

plt.figure(figsize=(12, 9))
plt.subplot(221)
plt.plot(t, y1)
plt.title('signal_1 in time domain')
plt.xlabel('Time/second')

plt.subplot(222)
plt.plot(range(400), Y1)
plt.title('signal_1 in frequency domain')
plt.xlabel('Frequency/Hz')

plt.subplot(223)
plt.plot(t, y2)
plt.title('signal_2 in time domain')
plt.xlabel('Time/second')

plt.subplot(224)
plt.plot(range(400), Y2)
plt.title('signal_2 in frequency domain')
plt.xlabel('Frequency/Hz')

plt.tight_layout()
plt.show()

想知道各个成分出现的时间。知道信号频率随时间变化的情况,各个时刻的瞬时频率及其幅值——这也就是时频分析。

短时傅里叶变换(Short-time Fourier Transform, STFT)

一个简单可行的方法就是——加窗。把整个时域过程分解成无数个等长的小过程,每个小过程近似平稳,再傅里叶变换,就知道在哪个时间点上出现了什么频率了。”这就是短时傅里叶变换。


使用STFT存在一个问题,我们应该用多宽的窗函数?


窗太窄,窗内的信号太短,会导致频率分析不够精准,频率分辨率差。窗太宽,时域上又不够精细,时间分辨率低。

用窄窗,时频图在时间轴上分辨率很高,几个峰基本成矩形,而用宽窗则变成了绵延的矮山。但是频率轴上,窄窗明显不如下边两个宽窗精确。

高频适合小窗口,低频适合大窗口。然而STFT的窗口是固定的,在一次STFT中宽度不会变化,所以STFT还是无法满足非稳态信号变化的频率的需求。

频域中提取的特征主要有:FFT系数、熵、能谱密度、功率下降率(Power Decline Rate,PDR)等。

小波变换

小波函数定义为


其中 是缩放因子,控制小波函数的伸缩;是平移参数,控制小波函数的平移。缩放因子对应频率,平移参数对应时间。
在缩放因子为的子空间的投影为

其中小波系数为

代表复共轭。
为了更直观地理解小波变换,先引入 Parseval 定理:

从上式不难看出,只有当小波中心频率与原始信号固有频率接近的时候,小波系数才会取得极大值。因此,小波可以看作是一个只允许频率和小波中心频率相近的信号通过的带通滤波器。通过缩放因子可以得到一系列不同的中心频率,通过平移系数则可以检测时域上不同位置的信号。这样就得到了原信号在各个时间点包含的频率信息。

import numpy as np
import matplotlib.pyplot as plt
import pywt

t = np.linspace(0, 1, 400, endpoint=False)
cond = [t<0.25, (t>=0.25)&(t<0.5), t>=0.5]
f1 = lambda t: np.cos(2*np.pi*10*t)
f2 = lambda t: np.cos(2*np.pi*50*t)
f3 = lambda t: np.cos(2*np.pi*100*t)

y1 = np.piecewise(t, cond, [f1, f2, f3])  # (400,)
y2 = np.piecewise(t, cond, [f2, f1, f3])

cwtmatr1, freqs1 = pywt.cwt(y1, np.arange(1, 200), 'cgau8', 1/400)  # (199, 400) (199,)
cwtmatr2, freqs2 = pywt.cwt(y2, np.arange(1, 200), 'cgau8', 1/400)

plt.figure(figsize=(12, 9))
plt.subplot(221)
plt.plot(t, y1)
plt.title('signal_1 in time domain')
plt.xlabel('Time/second')

plt.subplot(222)
plt.contourf(t, freqs1, abs(cwtmatr1))
plt.title('time-frequency relationship of signal_1')
plt.xlabel('Time/second')
plt.ylabel('Frequency/Hz')

plt.subplot(223)
plt.plot(t, y2)
plt.title('signal_2 in time domain')
plt.xlabel('Time/second')

plt.subplot(224)
plt.contourf(t, freqs2, abs(cwtmatr2))
plt.title('time-frequency relationship of signal_2')
plt.xlabel('Time/second')
plt.ylabel('Frequency/Hz')

plt.tight_layout()
plt.show()

STFT是给信号加窗,分段做FFT;而小波直接把傅里叶变换的基给换了——将无限长的三角函数基换成了有限长的会衰减的小波基。这样不仅能够获取频率,还可以定位到时间了

傅里叶变换把无限长的三角函数作为基函数:


image.png

基函数会伸缩、会平移(其实本质并非平移,而是两个正交基的分解)。缩得窄,对应高频;伸得宽,对应低频。然后这个基函数不断和信号做相乘。


某一个尺度(宽窄)下乘出来的结果,就可以理解成信号所包含的当前尺度对应频率成分有多少。于是,基函数会在某些尺度下,与信号相乘得到一个很大的值,因为此时二者有一种重合关系。那么我们就知道信号包含该频率的成分的多少。这两种尺度能乘出一个大的值(相关度高),所以信号包含较多的这两个频率成分,在频谱上这两个频率会出现两个峰。

小波做的改变就在于,将无限长的三角函数基换成了有限长的会衰减的小波基。


不同于傅里叶变换,变量只有频率,小波变换有两个变量:尺度(scale)和平移量 (translation)。尺度控制小波函数的伸缩,平移量 控制小波函数的平移。尺度就对应于频率(反比),平移量就对应于时间。

当伸缩、平移到这么一种重合情况时,也会相乘得到一个大的值。这时候和傅里叶变换不同的是,这不仅可以知道信号有这样频率的成分,而且知道它在时域上存在的具体位置。在每个尺度下都平移着和信号乘过一遍后,我们就知道信号在每个位置都包含哪些频率成分。做傅里叶变换只能得到一个频谱,做小波变换却可以得到一个时频谱!

对于突变信号,傅里叶变换存在吉布斯效应,我们用无限长的三角函数怎么也拟合不好突变信号:



Pywalvets

尺度函数 : scaling function (在一些文档中又称为父函数 father wavelet )
小波函数 : wavelet function(在一些文档中又称为母函数 mother wavelet)
连续的小波变换 :CWT
离散的小波变换 :DWT

不同的小波基函数,是由同一个基本小波函数经缩放和平移生成的。
小波变换是将原始图像与小波基函数以及尺度函数进行内积运算,所以一个尺度函数和一个小波基函数就可以确定一个小波变换

  • 查看小波族pywt.families

['haar', 'db', 'sym', 'coif', 'bior', 'rbio', 'dmey', 'gaus', 'mexh', 'morl', 'cgau', 'shan', 'fbsp', 'cmor']

  • 查看每个小波族中提供的系数 pywt.wavelist(family=None, kind=’all’)
  • haar family: haar
  • db family: db1,db2,db3,db4,db5,db6,db7,db8,db9,db10,db11,db12,db13,db14,db15,db16,db17,db18,db19,db20,db21,db22,db23,db24,db25,db26,db27,db28,db29,db30,db31,db32,db33,db34,db35,db36,db37,db38
  • sym family: sym2,sym3,sym4,sym5,sym6,sym7,sym8,sym9,sym10,sym11,sym12,sym13,sym14,sym15,sym16,sym17,sym18,sym19,sym20
  • coif family: coif1,coif2,coif3,coif4,coif5,coif6,coif7,coif8,coif9,coif10,coif11,coif12,coif13,coif14,coif15,coif16,coif17
  • bior family: bior1.1,bior1.3,bior1.5,bior2.2,bior2.4,bior2.6,bior2.8,bior3.1,bior3.3,bior3.5,bior3.7,bior3.9,bior4.4,bior5.5,bior6.8
  • bio family: rbio1.1,rbio1.3,rbio1.5,rbio2.2,rbio2.4,rbio2.6,rbio2.8,rbio3.1,rbio3.3,rbio3.5,rbio3.7,rbio3.9,rbio4.4,rbio5.5,rbio6.8
  • dmey family: dmey
  • gaus family: gaus1,gaus2,gaus3,gaus4,gaus5,gaus6,gaus7,gaus8
  • mexh family: mexh
  • morl family: morl
  • cgau family: cgau1,cgau2,cgau3,cgau4,cgau5,cgau6,cgau7,cgau8
  • shan family: shan
  • fbsp family: fbsp
  • cmor family: cmor

一维小波变换

连续小波变换
cwt(data, scales, wavelet, sampling_period=1.)

scales小波尺度
wavelet小波基
sampling_period采样间隔
return:frequencies
coefs连续小波变换

import matplotlib.pyplot as plt
import numpy as np
import pywt
# 显示中文
plt.rcParams['font.sans-serif'] = [u'SimHei']
plt.rcParams['axes.unicode_minus'] = False
sampling_rate = 1024
t = np.arange(0, 1.0, 1.0 / sampling_rate)
f1 = 100
f2 = 200
f3 = 300
data = np.piecewise(t,
    [t < 1, t < 0.8, t < 0.3],
    [lambda t: np.sin(2 * np.pi * f1 * t), lambda t: np.sin(2 * np.pi * f2 * t),
     lambda t: np.sin(2 * np.pi * f3 * t)])
wavename = 'cgau8'
totalscal = 256
fc = pywt.central_frequency(wavename)
# print(fc)  0.7
cparam = 2 * fc * totalscal  # 358.4
scales = cparam / np.arange(totalscal, 1, -1)  #(255,) The wavelet scales to use   np.arange(totalscal, 1, -1) 256-2

[cwtmatr, frequencies] = pywt.cwt(data, scales, wavename, 1.0 / sampling_rate)
plt.figure(figsize=(8, 4))
plt.subplot(211)
plt.plot(t, data)
plt.xlabel(u"时间(秒)")
plt.title(u"300Hz和200Hz和100Hz的分段波形和时频谱",fontsize=20)
plt.subplot(212)
plt.contourf(t, frequencies, abs(cwtmatr))
plt.ylabel(u"频率(Hz)")
plt.xlabel(u"时间(秒)")
plt.subplots_adjust(hspace=0.4)
plt.show()

离散小波变换
pywt.dwt(data, wavelet, mode=’symmetric’, axes=-1)

import pywt
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt


 # 平稳化
 ts_log = np.log(ts)
 ts_diff = ts_log.diff(1)
import numpy as np
import matplotlib.pyplot as plt
import pywt

y = pywt.data.ecg()
x = range(len(y))

ca, cd = pywt.dwt(y, 'db4')  # (515,) (515,)
ya = pywt.idwt(ca, None, 'db4') # approximated component
yd = pywt.idwt(None, cd, 'db4') # 噪音detailed component
# (1024,) (1024,)


plt.figure(figsize=(12,9))
plt.subplot(311)
plt.plot(x, y)
plt.title('original signal')
plt.subplot(312)
plt.plot(x, ya)
plt.title('approximated component')
plt.subplot(313)
plt.plot(x, yd)
plt.title('detailed component')
plt.tight_layout()
plt.show()
  • 多级一维离散小波变换
    wavedec(data, wavelet, mode='symmetric', level=None, axis=-1)

return
[cA_n, cD_n, cD_n-1, ..., cD2, cD1]系数数组的有序列表

  • 多级一维逆离散小波变换
    waverec(coeffs, wavelet, mode='symmetric', axis=-1)
import numpy as np
import matplotlib.pyplot as plt
import pywt

y = pywt.data.ecg()
x = range(len(y))  # x = range(len(y))

coeffs = pywt.wavedec(y, 'db4', level=4)  # 4阶小波分解
# (70,)(70,)(134,)(261,)(515,)
ya4 = pywt.waverec(np.multiply(coeffs, [1, 0, 0, 0, 0]).tolist(), 'db4')
yd4 = pywt.waverec(np.multiply(coeffs, [0, 1, 0, 0, 0]).tolist(), 'db4')
yd3 = pywt.waverec(np.multiply(coeffs, [0, 0, 1, 0, 0]).tolist(), 'db4')
yd2 = pywt.waverec(np.multiply(coeffs, [0, 0, 0, 1, 0]).tolist(), 'db4')
yd1 = pywt.waverec(np.multiply(coeffs, [0, 0, 0, 0, 1]).tolist(), 'db4')
# (1024,) (1024,) (1024,) (1024,)

plt.figure(figsize=(12, 12))
plt.subplot(611)
plt.plot(x, y)
plt.title('original signal')
plt.subplot(612)
plt.plot(x, ya4)
plt.title('approximated component in level 4')
plt.subplot(613)
plt.plot(x, yd4)
plt.title('detailed component in level 4')
plt.subplot(614)
plt.plot(x, yd3)
plt.title('detailed component in level 3')
plt.subplot(615)
plt.plot(x, yd2)
plt.title('detailed component in level 2')
plt.subplot(616)
plt.plot(x, yd1)
plt.title('detailed component in level 1')
plt.tight_layout()
plt.show()

二维小波变换(一维和n维类似):

  • 单层变换pywt.dwt2(data, wavelet, mode=’symmetric’, axes=(-2, -1))

data: 输入的数据
wavelet:小波基
mode: 默认是对称的
return: (cA, (cH, cV, cD))要注意返回的值,分别为低频分量,水平高频、垂直高频、对角线高频。高频的值包含在一个tuple中。返回值:cA:Approximation(近似), cD:Detail(细节),其中近似cA是周期性有规律的部分,可以被模拟和预测,而cD可看做是噪声。

近似系数表征了信号小波分解重构的低频部分信息,细节系数则表征了信号的高频部分信息

经过小波变换后图像会生成低频信息和高频信息。低频信息对应于求均值,高频信息对应于求差值。
均值是局部的平均值,变化缓慢,属于低频信息,存储图片的轮廓信息,近似信息
差值是局部的波动值,变化较快,属于高频信息,存储图片的细节信息,局部信息,另外含有噪音

是高通滤波器,允许高频信息通过是低通滤波器,允许低频信息通过


import numpy as np
import pywt
import cv2
import matplotlib.pyplot as plt

img = cv2.imread("cat.jpg")
img = cv2.resize(img, (448, 448))
# 将多通道图像变为单通道图像
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).astype(np.float32)

plt.figure('二维小波一级变换')
coeffs = pywt.dwt2(img, 'haar')
cA, (cH, cV, cD) = coeffs

# 将各个子图进行拼接,最后得到一张图
AH = np.concatenate([cA, cH], axis=1)
VD = np.concatenate([cV, cD], axis=1)
img = np.concatenate([AH, VD], axis=0)

# 显示为灰度图
plt.imshow(img,'gray')
plt.title('result')
plt.show()

在拼接子图之前,应该先对各个子图进行处理。未处理的情况下,因为高频部分的像素值极小甚至小于0,所以高频区域呈黑色。最简单的处理方式为:将高频信息均加255,得到如下结果:


  • 单层逆变换pywt.idwt2(coeffs, wavelet, mode, axes)

coeffs: 经小波变换后得到的各层的系数
wavelet:小波基

  • 多尺度变换 pywt.wavedec2(data, wavelet, mode=’symmetric’, level=None, axes=(-2, -1))

data: 输入的数据
wavelet:小波基
level: 尺度(要变换多少层)
return: 返回的值要注意,每一层的高频都是包含在一个tuple中,例如三层的话返回为 [cA3, (cH3, cV3, cD3), (cH2, cV2, cD2), (cH1, cV1, cD1)]

import numpy as np
import pywt
import cv2
import matplotlib.pyplot as plt

img = cv2.imread("cat.jpg")
img = cv2.resize(img, (448, 448))
# img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).astype(np.float32)

plt.figure('二维图像多级分解')
coeffs = pywt.wavedec2(img, 'haar', level=2)
cA2, (cH2, cV2, cD2), (cH1, cV1, cD1) = coeffs

# 将每个子图的像素范围都归一化到与CA2一致  CA2 [0,255* 2**level]
AH2 = np.concatenate([cA2, cH2+510], axis=1)
VD2 = np.concatenate([cV2+510, cD2+510], axis=1)
cA1 = np.concatenate([AH2, VD2], axis=0)

AH = np.concatenate([cA1, (cH1+255)*2], axis=1)
VD = np.concatenate([(cV1+255)*2, (cD1+255)*2], axis=1)
img = np.concatenate([AH, VD], axis=0)

plt.imshow(img,'gray')
plt.title('2D WT')
plt.show()

阈值函数 pywt.threshold (data, value, mode=, substitute=)

data: 输入的数据
value:阈值
mode:阈值函数的类型 ,模式有4种,soft, hard, greater, less; substitute是替换值
substitute:要替换的值(经阈值函数处理后的值)

信号产生的小波系数含有信号的重要信息,将信号经小波分解后小波系数较大,噪声的小波系数较小,并且噪声的小波系数要小于信号的小波系数,通过选取一个合适的阀值,大于阀值的小波系数被认为是有信号产生的,应予以保留,小于阀值的则认为是噪声产生的,置为零从而达到去噪的目的。

小波阀值去噪的基本问题包括三个方面:小波基的选择,阀值的选择,阀值函数的选择。
(1) 小波基的选择:通常我们希望所选取的小波满足以下条件:正交性、高消失矩、紧支性、对称性或反对称性。但事实上具有上述性质的小波是不可能存在的,因为小波是对称或反对称的只有Haar小波,并且高消失矩与紧支性是一对矛盾,所以在应用的时候一般选取具有紧支的小波以及根据信号的特征来选取较为合适的小波。
(2) 阀值的选择:直接影响去噪效果的一个重要因素就是阀值的选取,不同的阀值选取将有不同的去噪效果。目前主要有通用阀值(VisuShrink)、SureShrink阀值、Minimax阀值、BayesShrink阀值等。
(3) 阀值函数的选择:阀值函数是修正小波系数的规则,不同的反之函数体现了不同的处理小波系数的策略。最常用的阀值函数有两种:一种是硬阀值函数,另一种是软阀值函数。还有一种介于软、硬阀值函数之间的Garrote函数。
另外,对于去噪效果好坏的评价,常用信号的信噪比(SNR)与估计信号同原始信号的均方根误差(RMSE)来判断。

data = np.linspace(1, 10, 10)
# [ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]

data_soft = pywt.threshold(data=data, value=6, mode='soft', substitute=12)
# [12. 12. 12. 12. 12. 0. 1. 2. 3. 4.] 将小于6 的值设置为12, 大于等于6 的值全部减去6
data_hard = pywt.threshold(data=data, value=6, mode='hard', substitute=12)
# [12. 12. 12. 12. 12. 6. 7. 8. 9. 10.] 将小于6 的值设置为12, 其余的值不变
data_greater = pywt.threshold(data, 6, 'greater', 12)
# [12. 12. 12. 12. 12. 6. 7. 8. 9. 10.] 将小于6的值设置为12,大于等于阈值的值不变化
data_less = pywt.threshold(data, 6, 'less', 12)
# [ 1. 2. 3. 4. 5. 6. 12. 12. 12. 12.] 将大于6 的值设置为12, 小于等于阈值的值不变
ecg = pywt.data.ecg()  # 生成心电信号
# Create wavelet object and define parameters
w = pywt.Wavelet('db8')  # 选用Daubechies8小波
# dwt_max_level(data_len, filter_len):Compute the maximum useful level of decomposition.
maxlev = pywt.dwt_max_level(len(ecg), w.dec_len)
print("maximum level is " + str(maxlev))
threshold = 0.04  # Threshold for filtering
# Decompose into wavelet components, to the level selected:
coeffs = pywt.wavedec(ecg, 'db8', level=maxlev)  # 将信号进行小波分解
for i in range(1, len(coeffs)):
    coeffs[i] = pywt.threshold(coeffs[i], threshold*max(coeffs[i]))  # 将噪声滤波
datarec = pywt.waverec(coeffs, 'db8')  # 将信号进行小波重构
plt.figure()
plt.subplot(2, 1, 1)
plt.plot(ecg)
plt.xlabel('time (s)')
plt.ylabel('microvolts (uV)')
plt.title("Raw signal")
plt.subplot(2, 1, 2)
plt.plot(datarec)
plt.xlabel('time (s)')
plt.ylabel('microvolts (uV)')
plt.title("De-noised signal using wavelet techniques")
plt.tight_layout()
plt.show()

注意

可以看到API给出了很多小波族,每个小波族又有很多系数可供我们去选择,“相同类的统计特征相近,不同类的统计特征相差很大”,来挑选小波基函数。

多尺度小波变换一般是3~4层,但是要注意的是,如果实践中所用的图片太小,或者纹理并不丰富,其实用单层的小波变换就足够了。如果你用多层的小波变换,Pywalvets 仍只会返回给你一层变换的结果,因为信息量过小导致不能采样来进一步进行变换。

你可能感兴趣的:(小波变换)