• 音频 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如图,给出了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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.1 输入参数

     data: 音频数据,
     fs:   采样率
     mode: 复数模式
    
     **kwargs: {  nperseg: 1000, noverlap: 500, nfft: 5000 }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1.3 输入,输出计算

    当输入参数:

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

    输出参数如下:

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

    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])
    
    • 1
    输入: window,  nperseg,  input_length()
    返回参数:  win,  nperseg()
    
    • 1
    • 2

    设置 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
    
    • 1
    • 2

    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')                                                                                               
    
    • 1
    • 2
    • 3
    • 4

    输出结果 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)  
      
      • 1

      该函数的作用是取 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    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) 一个复数矩阵,  代表了时频谱图;
    
    • 1
    • 2
    • 3

    3. scipy 中的 spectral.py 文件介绍

    该文件中, 主要实现了 14 个功能函数,

    其中 10 个核心函数:

    stft, istft, csd, welch, coherence, periodogram, 
    spectrogram, check_COLA, check_NOLA, lombscargle
    
    • 1
    • 2

    4 个辅助函数;

    _triage_segments()
    _spectral_helper()
    _fft_helper()
    _median_bias()
    
    • 1
    • 2
    • 3
    • 4

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

    3.1 调用关系

    在这里插入图片描述

    3.2 运作机制

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

  • 相关阅读:
    【IEEE2017】RL:机器人库:一种面向对象的机器人应用程序的方法
    Java调用Kotlin特性
    算法通过村第十四关-堆|黄金笔记|中位数
    CAS:122567-66-2_DSPE-生物素_DSPE-Biotin
    Python爬虫详解(一看就懂)
    SDS-redis动态字符串
    vscode搭建springboot开发环境
    SAR雷达系统反设计及典型目标建模与仿真实现研究——目标生成与检测(Matlab代码实现)
    tomcat总结笔记
    从后端开发转大数据开发怎么样?
  • 原文地址:https://blog.csdn.net/chumingqian/article/details/125487161