音频 scipy 中 spectrogram 的运作机制

本文介绍的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 函数也调用了其他函数;

在这里插入图片描述

1. 输入输出参数

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

1.1 输入参数

 data: 音频数据,
 fs:   采样率
 mode: 复数模式

 **kwargs: {
    
      nperseg: 1000, noverlap: 500, nfft: 5000 }

nperseg: number of per segment;
代表每一段(帧)的点数长度, 默认值 None, 如果给出nperseg, 则使用给出值;
如果 window 是 字符或者元组, 则设置成256,
如果 window 是 类似数组, 则设置成 window 长度的大小;

noverlap: 相邻两帧之间的重叠度;
nfft; 对每一帧进行 FFT变换的点数;

1.2 输出参数


    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  选择输出;

1.3 输入,输出计算

当输入参数:

 data= 10001 个数值;  fs = 100;  mode = "complex";
 **kwargs: {
    
     nperseg: 1000, noverlap: 500, nfft: 5000 }

输出参数如下:

freq =  2501 个数值, 代表频率;
t = 19 个数值,  代表19 帧;
spect = (2501, 19)   输出的是一个复数矩阵, 由于mode = "complex"

2. spectrogram() 函数中的运作机制

2.1 选择模式

根据 modelist = [‘psd’, ‘complex’, ‘magnitude’, ‘angle’, ‘phase’]

选择对应的模式

2.2 调用 _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 长度;
    返回 三个参数,

    1. 一个段(帧)中的样本点 (长度为nperseg),
    2. 窗口的类型;
    3. 是否采样fftbins,

最终 triage_segments() 返回的是,

  1. window() 一帧的样本点数, 这里1000个数;
  2. nperseg() 一帧样本的长度, 这里是指一个 整型数 ,数值为1000;

2.3 设置 noverlap

if noverlap is None:
    noverlap = nperseg // 8

2.4 核心调用 _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,下面结合具体实现流程分析:

  1. 判断模式是 psd, stft

  2. boundary_func : 是否对输入信号在两边进行边界延拓,
    延拓的效果是在数据X的基础上, 向两侧分别延长 (nperseg//2)的长度,
    延拓的方式有 (const_ext, even_ext, odd_ext, zero_ext);

  3. 判断x, y 两个数据类型相等

  4. 使得输出类型为 np.complex64 复数;

  5. 调用 win, nperseg = _triage_segments(window, nperseg, input_length= x.shape[-1] )
    此时win = 之前的window ,为相同的一千个数;
    nperseg 仍代表 一个整形数 1000 ;

  6. 判断 nfft 的点数必须 >= nperseg, 一个段(帧)中的点数;

  7. nstep = nperseg - noverlap

  8. 根据scaling 是 density 还是 spectrum, 求出对应的scale,
    这里采用 density 求出的 scale 为0.00344;

  9. freqs = sp_fft.rfftfreq(nfft, 1/fs)  
    

    该函数的作用是取 nfft 中的, 单边fft 取实数部分, 所以 rfft 求出来的个数,是nfft 中的一半;

    freqs = 2501 ( nfft/2); 执行结束后, freqs= ( 2501,) 是包含 2501 个数的数组;

  10. _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)  是一个复数矩阵, 每一个数都是复数;

  1. result *= scale 进行尺度缩放;

  2. time = {ndarray: 19}

  3. result = 维度置换, resutl: (19, 2501) --> (2501, 19)

2.5 小结

scipy中 spectrogram 函数中的核心实现,通过 _spectral_helper() 函数;

_spectral_helper() 中的核心功能是通过 _fft_helper() 实现;
最终 spectrogram 函数返回的参数, 都是通过_spectral_helper() 函数获得的,spectrogram 返回的是三项参数:

freqs: 2501 个数
time: 19 段帧,
result: (2501, 19) 一个复数矩阵,  代表了时频谱图;

3. scipy 中的 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()

其余的为其他模块中的函数调用;

3.1 调用关系

在这里插入图片描述

3.2 运作机制

具体的运作机理, 请阅读这里参考spectral.py

猜你喜欢

转载自blog.csdn.net/chumingqian/article/details/125487161