本文介绍的scipy 版本信息如下
Name: scipy
Version: 1.4.1
Summary: SciPy: Scientific Library for Python
Home-page: https://www.scipy.org
Author:
Author-email:
License: BSD
anaconda3/envs/torch1.7.1/lib/python3.7/site-packages
Requires: numpy
Required-by: scikit-learn, resampy, librosa
如图,给出了spectrogram
中核心调用函数, 除此之外,spectrogram
函数也调用了其他函数;
def spectrogram(x, fs=1.0, window=('tukey', .25), nperseg=None, noverlap=None,
nfft=None, detrend='constant', return_onesided=True,
scaling='density', axis=-1, mode='psd'):
...
pass
return freqs, time, Sxx
data: 音频数据,
fs: 采样率
mode: 复数模式
**kwargs: { nperseg: 1000, noverlap: 500, nfft: 5000 }
nperseg: number of per segment;
代表每一段(帧)的点数长度, 默认值 None, 如果给出nperseg, 则使用给出值;
如果 window 是 字符或者元组, 则设置成256,
如果 window 是 类似数组, 则设置成 window 长度的大小;
noverlap: 相邻两帧之间的重叠度;
nfft; 对每一帧进行 FFT变换的点数;
Returns
-------
freqs : ndarray
Array of sample frequencies.
t : ndarray
Array of times corresponding to each data segment
result : ndarray
Array of output data, contents dependent on *mode* kwarg.
freq: 频域信息 ; 样本的频率
t: 时域信息; 多帧的时间信息
spec: 时频谱, 基于 mode 选择输出;
当输入参数:
data= 10001 个数值; fs = 100; mode = "complex";
**kwargs: { nperseg: 1000, noverlap: 500, nfft: 5000 }
输出参数如下:
freq = 2501 个数值, 代表频率;
t = 19 个数值, 代表19 帧;
spect = (2501, 19) 输出的是一个复数矩阵, 由于mode = "complex"
spectrogram()
函数中的运作机制根据 modelist = [‘psd’, ‘complex’, ‘magnitude’, ‘angle’, ‘phase’]
选择对应的模式
_triage_segments()
window, nperseg = _triage_segments(window, nperseg, input_length=x.shape[axis])
输入: window, nperseg, input_length()
返回参数: win, nperseg()
设置 window, nperseg参数, 用于 spectrogram 和 _spectral_helper()
参数解释:
window: string, tuple, or ndarray 可选的有三种类型;
如果window 窗口是通过 string ,or tuple 指定了, 并且 nperseg 没有指定,
则nperseg 设置成256,并且返回指定窗口的长度;
如果window 是个 arrary_like()类似数组的, 并且nperseg 没有指定,
则nperseg 设置成窗口长度的大小;
错误情况, 如果用户同时提供了类似数组的窗口, 又提供 nperseg的数值, 但是nperseg != 数组窗口的长度;
则会报错;
当window 是string 或者 tuple 类型时:
比如, window = (“tukey”, 0.25), nperseg = 1000;
执行以下流程
判断, nperseg 是否大于 input_length,
if nperseg > input_length , 则发出警告, 说明设定一帧的长度大于输入的总长度,
重新给nperseg 赋值, 赋值为 input_length;
然后调用 get_window(window, nperseg)
函数,获取 win 长度;
返回 三个参数,
最终 triage_segments() 返回的是,
if noverlap is None:
noverlap = nperseg // 8
_spectral_helper()
根据模式选择, mode == stft
:
当模式是STFT 或者 complex 时, 两者等价;
freqs, time, Sxx = _spectral_helper(x, x, fs, window, nperseg,
noverlap, nfft, detrend,
return_onesided, scaling, axis,
mode='stft')
输出结果 freqs,time 和 Sxx,下面结合具体实现流程分析:
判断模式是 psd, stft
boundary_func : 是否对输入信号在两边进行边界延拓,
延拓的效果是在数据X的基础上, 向两侧分别延长 (nperseg//2)的长度,
延拓的方式有 (const_ext, even_ext, odd_ext, zero_ext);
判断x, y 两个数据类型相等
使得输出类型为 np.complex64 复数;
调用 win, nperseg = _triage_segments(window, nperseg, input_length= x.shape[-1] )
此时win = 之前的window ,为相同的一千个数;
nperseg 仍代表 一个整形数 1000 ;
判断 nfft 的点数必须 >= nperseg, 一个段(帧)中的点数;
nstep = nperseg - noverlap
根据scaling 是 density
还是 spectrum
, 求出对应的scale,
这里采用 density
求出的 scale 为0.00344;
freqs = sp_fft.rfftfreq(nfft, 1/fs)
该函数的作用是取 nfft 中的, 单边fft 取实数部分, 所以 rfft 求出来的个数,是nfft 中的一半;
freqs = 2501 ( nfft/2); 执行结束后, freqs= ( 2501,) 是包含 2501 个数的数组;
_fft_helper()
主要实现 FFT 变换的计算, 在这一步骤中;
对每一个 nperseg 窗口序列的长度, 进行nfft 长度的fft 变换;
10.1. 求出 step = nperseg - noverlap = 500
10.2. 求出 shape = (19, 1000) tuple;
10.3. 求出 strides = (4000, 8)
10.4. result = fun1(x, shape, strides), ndarray(19, 1000)
detrend(result), 对每一帧数据进行去 趋势效应;
10.5. result = win * result;
result = (19, 1000)
10.6. result = result. real , 取出 result 中的实部, 本来就是实数;
10.7. 令 func = sp_fft. rfft;
10.8. result = func(result, n= nfft),
result = (19, 1000) , nfft = 5000;
result = (19, 2501) 是一个复数矩阵, 每一个数都是复数;
result *= scale 进行尺度缩放;
time = {ndarray: 19}
result = 维度置换, resutl: (19, 2501) --> (2501, 19)
scipy中 spectrogram 函数中的核心实现,通过 _spectral_helper()
函数;
而 _spectral_helper()
中的核心功能是通过 _fft_helper()
实现;
最终 spectrogram 函数返回的参数, 都是通过_spectral_helper()
函数获得的,spectrogram
返回的是三项参数:
freqs: 2501 个数
time: 19 段帧,
result: (2501, 19) 一个复数矩阵, 代表了时频谱图;
spectral.py
文件介绍该文件中, 主要实现了 14 个功能函数,
其中 10 个核心函数:
stft, istft, csd, welch, coherence, periodogram,
spectrogram, check_COLA, check_NOLA, lombscargle
4 个辅助函数;
_triage_segments()
_spectral_helper()
_fft_helper()
_median_bias()
其余的为其他模块中的函数调用;
具体的运作机理, 请阅读这里参考spectral.py