Scipy之傅里叶和DCT

SciPy提供fftpack模块,可让用户计算快速傅立叶变换。

一维离散傅立叶变换

长度为N的序列x [n]FFT y [k]fft()计算,逆变换使用ifft()计算。

import numpy as np
from scipy.fftpack import fft

#快速傅里叶变换
def test01():
    x = np.array([1.0, 2.0, 1.0, -1.0, 1.5])

    y = fft((x))

    print(y)

E:\开发工具\python3\python.exe E:/开发工具/pythonProject/lucky/test/fftp.py
[ 4.5       -0.j          2.08155948-1.65109876j -1.83155948+1.60822041j
 -1.83155948-1.60822041j  2.08155948+1.65109876j]

 

import numpy as np
from scipy.fftpack import fft
from scipy.fftpack import ifft




#傅里叶逆变换
def test02():
    x = np.array([1.0, 2.0, 1.0, -1.0, 1.5])

    y = fft((x))

    y1 = ifft(y)

    print(y1)

[ 1. +0.j  2. +0.j  1. +0.j -1. +0.j  1.5+0.j]

我们不知道信号频率; 只知道信号sig的采样时间步长。 信号应该来自实际函数,所以傅里叶变换将是对称的。 scipy.fftpack.fftfreq()函数将生成采样频率,scipy.fftpack.fft()将计算快速

def test03():
    #步长
    time_step = 0.02
    #采样时长
    period = 5.
    #步长是0.02,在0-20之间 20/0.02=1000个数据
    time_vec = np.arange(0, 20, time_step)
    #构建一个正弦信号和一个噪音信号叠加
    sig = np.sin(2 * np.pi / period * time_vec) + 0.5 * np.random.randn(time_vec.size)
    print(sig.size)
    #使用fftfreq的好处就是可以自动生成频率范围,而不用去考虑信号长度是奇数还是偶数的问题,fftfreq(信号长度,1/采样率)
    #注意这里的信号长度是信号做傅里叶变换之后的原始长度(画频谱图的时候则取一半长度),得到频率范围后发现频率有负值,这时同样对频率取一半即可。
    sample_freq = fftpack.fftfreq(sig.size, d=time_step) #离散傅里叶变换采样频率,给定采样窗口长度是sig.size,采样间隔是time_step
    #然后进行傅里叶变换
    sig_fft = fftpack.fft(sig)
    print(sig_fft)
    #通常一个波形由振幅,相位,频率三个变量确定

关于fft变换后负数解读:

fft返回值是一个复数数组,每个复数表示一个正弦波。通常一个波形由振幅,相位,频率三个变量确定。

假设a是时域中的周期信号,采样频率为Fs,采样点数为N。如果A[N] = fft(a[N]),返回值A[N]是一个复数数组,其中:

  • A[0]表示频率为0hz的信号,即直流分量。
  • A[1:N/2]包含正频率项,A[N/2:]包含负频率项。正频率项就是转化后的频域信号,通常我们只需要正频率项,即前面的n/2项,负频率项是计算的中间结果(正频率项的镜像值)。
  • 每一项的频率计算:假设A[i]为数组中的元素,表示一个波形,该波形的频率 = i * Fs / N
  • A[i] = real + j * imag,是一个复数,相位就是复数的辐角,相位 = arg(real/imag)
  • 类似的,振幅就是复数的模,振幅 = sqrt(real^2+imag^2)。但是fft的返回值的模是放大值,直流分量的振幅放大了N倍,弦波分量的振幅放大了N/2倍。

频率分辨率
频率分辨率是离散傅里叶变换(DFT)频域相邻刻度之间的实际频率之差。采样时,数据采样了T秒(T = 采样点数N / 采样频率Fs),信号的成分中周期最大也就是T秒,最低频率即“基频”就等于1 / T,也就是Fs / N,这就是频率分辨率。基频 = Fs / N,各个谐波的频率就是 i * Fs / N,这个公式用于计算各个波形的频率。

 

import numpy as np
from scipy.fftpack import fft

# 采样点数
N = 4000

# 采样频率 (根据采样定理,采样频率必须大于信号最高频率的2倍,信号才不会失真)
Fs = 8000
x = np.linspace(0.0, N/Fs, N)

# 时域信号,包含:直流分量振幅1.0,正弦波分量频率100hz/振幅2.0, 正弦波分量频率150Hz/振幅0.5/相位np.pi
y = 1.0 + 2.0 * np.sin(100.0 * 2.0*np.pi*x) + 0.5*np.sin(150.0 * 2.0*np.pi*x + np.pi)

# 进行fft变换
yf = fft(y)

# 获取振幅,取复数的绝对值,即复数的模
abs_yf = np.abs(yf)

# 获取相位,取复数的角度
angle_y=np.angle(yf)

# 直流信号
print('\n直流信号')
print('振幅:', abs_yf[0]/N) # 直流分量的振幅放大了N倍

# 100hz信号
index_100hz = 100 * N // Fs # 波形的频率 = i * Fs / N,倒推计算索引:i = 波形频率 * N / Fs
print('\n100hz波形')
print('振幅:', abs_yf[index_100hz] * 2.0/N) # 弦波分量的振幅放大了N/2倍
print('相位:', angle_y[index_100hz])

# 150hz信号
index_150hz = 150 * N // Fs # 波形的频率 = i * Fs / N,倒推计算索引:i = 波形频率 * N / Fs
print('\n150hz波形')
print('振幅:', abs_yf[index_150hz] * 2.0/N) # 弦波分量的振幅放大了N/2倍
print('相位:', angle_y[index_150hz])
print('100hz与150hz相位差:',  angle_y[index_150hz] - angle_y[index_100hz])
print('\n')

 

直流信号
振幅: 1.0

100hz波形
振幅: 1.9989359813189005
相位: -1.5315264186250062

150hz波形
振幅: 0.5008489983048182
相位: 1.6297011890497097
100hz与150hz相位差: 3.161227607674716

离散余弦变换DCT:

由于许多要处理的信号都是实信号,在使用FFT时,对于实信号,傅立叶变换的共轭对称性导致在频域中有一半的数据冗余。

离散余弦变换(DCT)是对实信号定义的一种变换,变换后在频域中得到的也是一个实信号,相比离散傅里叶变换DFT而言, DCT可以减少一半以上的计算。DCT还有一个很重要的性质(能量集中特性):大多书自然信号(声音、图像)的能量都集中在离散余弦变换后的低频部分,因而DCT在(声音、图像)数据压缩中得到了广泛的使用。由于DCT是从DFT推导出来的另一种变换,因此许多DFT的属性在DCT中仍然是保留下来的。

 SciPy.fftpack中,提供了离散余弦变换(DCT)与离散余弦逆变换(IDCT)的实现。

 离散余弦正变换:

from scipy.fftpack import dct
mydict = dct(np.array([4., 3., 5., 10., 5., 3.]))
print(mydict)


 [ 60. -3.48476592 -13.85640646 11.3137085 6. -6.31319305]

idct函数是dct函数的反函数:

from scipy.fftpack import dct
from scipy.fftpack import idct
d = idct(np.array([4., 3., 5., 10., 5., 3.]))
print(d)


 [ 39.15085889 -20.14213562 -6.45392043 7.13341236 8.14213562 -3.83035081]

 

你可能感兴趣的:(【信号分析】,【人工智能AI】,scipy,python,开发语言)