librosa に基づく LFCC および CQCC の特徴抽出

このホワイト ペーパーでは、librosa ベースの LFCC および CQCC 特徴抽出を実装し、主に librosa での MFCC 特徴抽出のプロセスを参照し、torchaudio を使用して LFCC の正確性を検証し、matlab を使用して CQCC の正確性を検証します。

LFCC

原理

LFCCとMFCCの違いはフィルターバンクの違いで、MFCCはメル周波数のフィルターバンクを使うのに対し、LFCCは周波数が線形に分布するフィルターバンクを使うので、MFCCのフィルターバンクの取得方法を変えるだけで他のコードを保持するには、変更しないでください。

達成

librosa ライブラリに基づいて実装された LFCC は次のとおりです。

import warnings
import numpy as np
import librosa
import scipy


def linear(sr, n_fft, n_filters=128, fmin=0.0, fmax=None, dtype=np.float32):

    if fmax is None:
        fmax = float(sr) / 2
    # Initialize the weights
    n_filters = int(n_filters)
    weights = np.zeros((n_filters, int(1 + n_fft // 2)), dtype=dtype)

    # Center freqs of each FFT bin
    fftfreqs = librosa.fft_frequencies(sr=sr, n_fft=n_fft)

    # 'Center freqs' of liner bands - uniformly spaced between limits
    # * Need to validate
    linear_f = np.linspace(fmin, fmax, n_filters + 2)

    fdiff = np.diff(linear_f)
    ramps = np.subtract.outer(linear_f, fftfreqs)

    for i in range(n_filters):
        # lower and upper slopes for all bins
        lower = -ramps[i] / fdiff[i]
        upper = ramps[i + 2] / fdiff[i + 1]

        # .. then intersect them with each other and zero
        weights[i] = np.maximum(0, np.minimum(lower, upper))

    # Only check weights if f_mel[0] is positive
    if not np.all((linear_f[:-2] == 0) | (weights.max(axis=1) > 0)):
        # This means we have an empty channel somewhere
        warnings.warn(
            "Empty filters detected in linear frequency basis. "
            "Some channels will produce empty responses. "
            "Try increasing your sampling rate (and fmax) or "
            "reducing n_filters.",
            stacklevel=2,
        )

    return weights


def linear_spec(
    y=None,
        sr=22050,
        S=None,
        n_fft=2048,
        hop_length=512,
        win_length=None,
        window='hann',
        center=True,
        pad_mode='constant',
        power=2.0,
        **kwargs
):
    if S is not None:
        # Infer n_fft from spectrogram shape, but only if it mismatches
        if n_fft // 2 + 1 != S.shape[-2]:
            n_fft = 2 * (S.shape[-2] - 1)
    else:
        S = (
            np.abs(
                librosa.stft(
                    y,
                    n_fft=n_fft,
                    hop_length=hop_length,
                    win_length=win_length,
                    center=center,
                    window=window,
                    pad_mode=pad_mode,
                )
            )
            ** power
        )
    # Build a linear filter
    linear_basis = linear(sr=sr, n_fft=n_fft, **kwargs)

    return np.einsum("...ft,mf->...mt", S, linear_basis, optimize=True)


def expand_to(x, *, ndim, axes):
    try:
        axes = tuple(axes)
    except TypeError:
        axes = tuple([axes])

    if len(axes) != x.ndim:
        raise Exception(
            "Shape mismatch between axes={} and input x.shape={}".format(
                axes, x.shape)
        )

    if ndim < x.ndim:
        raise Exception(
            "Cannot expand x.shape={} to fewer dimensions ndim={}".format(
                x.shape, ndim)
        )

    shape = [1] * ndim
    for i, axi in enumerate(axes):
        shape[axi] = x.shape[i]

    return x.reshape(shape)


def lfcc(y=None,
         sr=22050,
         S=None,
         n_lfcc=20,
         dct_type=2,
         norm='ortho',
         lifter=0,
         **kwargs):
    if S is None:
        S = librosa.power_to_db(linear_spec(y=y, sr=sr, **kwargs))

    M = scipy.fftpack.dct(S, axis=-2, type=dct_type,
                          norm=norm)[..., :n_lfcc, :]

    if lifter > 0:
        # shape lifter for broadcasting
        LI = np.sin(np.pi * np.arange(1, 1 + n_lfcc, dtype=M.dtype) / lifter)
        LI = expand_to(LI, ndim=S.ndim, axes=-2)

        M *= 1 + (lifter / 2) * LI
        return M
    elif lifter == 0:
        return M
    else:
        raise Exception(
            "LFCC lifter={} must be a non-negative number".format(lifter)
        )

torchaudio の既存の関数で検証します。

import torchaudio
import librosa
from torchaudio.transforms import LFCC
path = ''

# torchaudio
waveform, sample_rate = torchaudio.load(full_path)
print(waveform.shape)
lfcc_transform = LFCC(
    sample_rate=sample_rate,
    n_lfcc=13,
    n_filter=128,
    speckwargs={
    
    "n_fft": 512, "hop_length": 160, },
)
torch_lfcc = lfcc_transform(waveform)
print(torch_lfcc[0, :, 0])
# librosa
wave, sr = librosa.load(full_path, sr=16000)
print(wave.shape)
librosa_lfcc = lfcc(
    y=wave, sr=sr, n_lfcc=13, n_fft=512, hop_length=240, n_filters=128, pad_mode='reflect')
print(librosa_lfcc[:, 0])

操作の結果は次のとおりです。2
ここに画像の説明を挿入
つによって得られる結果は基本的に同じです。

CQCC

原理

STFT と CQT は、時間信号の周波数ドメイン解析のための 2 つの兄弟メソッドと見なすことができます。

STFT では、長いシーケンスから固定長のフラグメントを抽出し、ウィンドウ関数を掛けて FFT を実行し、スライディング ウィンドウを繰り返し適用して最終的なスペクトルを取得します。

STFT は実際にはフィルター バンクであり、Q ファクターはフィルター帯域幅に対するフィルターの中心周波数の比率として定義されます。Q = fk δ f Q=\frac{f_k}{\delta f}Q=f _kSTFT では、各フィルターの帯域幅は一定であり、周波数が低周波数から高周波数になるにつれて Q ファクターが増加します。

ただし、人間の知覚システムの Q ファクターは 500Hz ~ 20KHz の間でほぼ一定であるため、少なくとも知覚の観点からは、STFT は音声信号の時間周波数分析には理想的ではない可能性があります。

そこで CQT 変換が提案され、最初のアルゴリズムは 1978 年にヤングバーグとボルによって提案され (Youngberg-Boll)、もう 1 つのアルゴリズムは 1986 年に鹿島とモンレイノー-カシマによって提案されました (モン レイノー)。これらの方法では、次の図に示すように、オクターブが幾何学的に分散されます。

CQT計算

離散時間領域信号x ( n ) x(n)x ( n )の CQT は以下のように計算されます:
XCQ ( k , n ) = ∑ j = n − ⌊ N k / 2 ⌋ n + ⌊ N k / 2 ⌋ x ( j ) ak ∗ ( j − n + N k / 2 ) X^{CQ}(k, n)=\sum_{j=n-\left\lfloor N_k / 2\right\rfloor}^{n+\left\lfloor N_k / 2\right\rfloor} x(j) a_k^*\左(j-n+N_k / 2\右)バツCQ (k,n )=j = n Nk/2 n + Nk/2x ( j ) ak( jn+Nk/2 )
そのうち、k = 1 , 2 , ⋯ , K k = 1,2,\cdots, Kk=1 2 Kは周波数インデックスak ∗ ( n ) a_k^*(n)ak(n) a k ( n ) a_k(n) ak( n )の複素共役N k N_kNk可変ウィンドウ サイズの場合、⌊ ⋅ ⌋ \left\lfloor \cdot \right\rfloorは切り捨てを表し、基底関数ak ( n ) a_k(n)ak( n )定义如下:
ak ( n ) = 1 C ( n N k ) exp ⁡ [ i ( 2 π nfkfs + Φ k ) ] a_k(n)=\frac{1}{C}\left(\frac{ n}{N_k}\right) \exp \left[i\left(2 \pi n \frac{f_k}{f_s}+\Phi_k\right)\right]ak( n )=1(Nkn)指数[( 2 πnsk+ファイk) ]
その中で、fk f_kkkk番目ですk周波数帯域の中心周波数fs f_ssはサンプリング レートw ( t ) w(t)w ( t )は窓関数Φ k \Phi_kファイkは位相オフセット、スケーリング係数CCCによる下式:
C = ∑ l = − ⌊ N k / 2 ⌋ ⌊ N k / 2 ⌋ w ( l + N k / 2 N k ) C=\sum_{l=-\left\lfloor N_k / 2\right\rfloor}^{\left\lfloor N_k / 2\right\rfloor} w\left(\frac{l+N_k / 2}{N_k}\right)=l = Nk/2 ⌊N _k/2w(Nkl+Nk/ 2)
一定の Q 変換を満たすために、中心周波数は次を満たす必要があります。
fk = f 1 2 k − 1 B f_k=f_1 2^{\frac{k-1}{B}}k=12Bk 1
ここでf 1 f_11最低周波数帯域BBの中心周波数Bオクターブあたりの周波数帯域の数を決定します。

たとえば、B = 1 B=1とします。B=1の場合、fk = f 1 2 k − 1 f_k = f_1 2^{k-1}k=12k 1,取fs = 8000 Hz , f 1 = 500 Hz f_s=8000Hz, f_1 = 500Hzs=8000ヘルツ1=500 Hz,则f 2 = 1000 Hz , f 3 = 2000 Hz , f 4 = 4000 Hz , f 5 = 8000 Hz f_2=1000Hz, f_3=2000Hz, f_4=4000Hz, f_5=8000Hz2=1000ヘルツ3=2000Hz _4=4000ヘルツ5=8000Hz これ以上上がれません。ただし、DFT では、これらの周波数は直線的に変化します。

Q
= fkfk + 1 − fk = ( 2 1 / B − 1 ) − 1 Q=\frac{f_k}{f_{k+1}-f_k}=\left(2^ {1 / B}-1\right)^{-1}Q=k + 1kk=( 21/ B1 )1
同時窓関数長N k N_kNk満足:
N k = fsfk Q N_k=\frac{f_s}{f_k} QNk=ksQ

CQCC抽出

まず、MFCC は次のように計算されます
sum_{m =1}^M \log [MF(m)] \cos \left[\frac{q\left(m-\frac{1}{2}\right) \pi}{M}\right]MFCC ( q )=m = 1Mlog [ MF ( m ) ]コス[Mq(メートル21)p]
ここで、メルスペクトルは次のように計算されます:
MF ( m ) = ∑ k = 1 K ∣ XDFT ( k ) ∣ 2 H m ( k ) MF(m)=\sum_{k=1}^K\left|X ^{ DFT}(k)\right|^2 H_m(k)MF (メートル)=k = 1K バツD FT (k) 2Hメートル( k )
そのうちkkkは DFT 後の周波数インデックス係数、H m ( k ) H_m(k)Hメートル( k )mmm Mel スケールの三角加重関数バンドパス フィルターここでMMM はフィルターの数を表します (MF ( m ) MF(m)MF ( m ) =MMM点のシーケンスqqq は、離散コサイン変換ポイントの数を表します。

XCQ ( k ) X^{CQ}(k)であるため、ケプストラム分析を CQT に直接使用することはできません。バツCQ (k)の余弦関数は、この問題は、幾何空間を線形空間に変換することで解決できます。

XCQ ( k ) X^{CQ}(k)からバツCQ(k) 中, k k k 個の周波数帯域は幾何学的に分布しており、信号再構築のプロセスは最初のkkK − k Kkの後、 K 個の周波数帯域 (低周波数部分) がダウンサンプリングされます。Kk個の周波数帯域 (高周波数部分) はアップサンプリングによって取得され、kk 番目k周波数帯域の中心周波数fk f_kkおよび最初のバンドの中心周波数f 1 = fmin f_1=f_{min}1=の周波数差は次のように記録されます:
Δ fk ↔ 1 = fk − f 1 = f 1 ( 2 k − 1 B − 1 ) \Delta f^{k \leftrightarrow 1}=f_k-f_1=f_1\left(2^ {\ frac{k-1}{B}}-1\right)f _k 1=k1=1( 2Bk 11 )
そのうち、k = 1 , 2 , ⋯ , K k = 1,2,\cdots, Kk=1 2 Kは周波数インデックス、距離Δ fk ↔ 1 \Delta f^{k \leftrightarrow 1}f _k 1kkkの増加関数。期間T l T_l をTlkl に関して k_l を決定するのと同じです。kl次のような関数:
T l = Δ fkl ↔ 1 T_l=\Delta f^{k_l \leftrightarrow 1}Tl=f _kl1
以下は、最初のオクターブを周期T l T_lTlddを実行するDは等分され、解はkl k_lkl阿:
f 1 d = f 1 ( 2 kl − 1 B − 1 ) → kl = B log ⁡ 2 ( 1 + 1 d ) \frac{f_1}{d}=f_1\left(2^{\frac{k_l -1}{B}}-1\right) \rightarrow k_l=B \log _2\left(1+\frac{1}{d}\right)d1=1( 2Bkl 11 )kl=Bログ_2( 1+d1)
新しい周波数は次のように計算されます:
F l = 1 T l = [ f 1 ( 2 kl − 1 B − 1 ) ] − 1 F_l=\frac{1}{T_l}=\left[f_1\left(2^ { \frac{k_l-1}{B}}-1\right)\right]^{-1}l=Tl1=[ f1( 2Bkl 11 ) ]1
したがって、最初のオクターブにはddd一様サンプル、 2 番目のオクターブで2d 2d2d , j − 1 j-1j1オクターブ2 jd 2^jd2j d.

信号再構成方法は、多相アンチエイリアシング フィルターとスプライン補間方法を使用し、均一なサンプリング レートF l F_ll信号を再サンプリングします。

最後に、CQCC 係数は次のように計算されます
。 l=1 }^L \log \left|X^{CQ}(l)\right|^2 \cos \left[\frac{p\left(l-\frac{1}{2}\right) \円周率 {L}\右]CQCC ( p )=l = 1Lログ_ バツCQ (l) 2コス[Lp( l21)p]
その中で、p = 0 , 1 , ⋯ , L − 1 p=0,1,\cdots,L-1p=0 1 L1はリサンプリング後の周波数帯域インデックスです。

私の理解では、たとえば、set d = 5 d=5d=5、周波数500~1000Hz 500~1000Hz5001000Hz間サンプリング500,600,700,800,900 500,600,700,800,9005分割可能500 ,600 ,700 ,800 ,900、周波数1000 ~ 2000 Hz 1000 ~ 2000 Hz10002000Hz間のサンプリング2分割 d=10 2d=102=10 の周波数帯域、つまり1000 、 1100 、 1200 、⋯ 、 1900 1000,1100,1200,\cdots,19001000 ,1100 1200 1900、同様に周波数範囲は2000 ~ 4000 Hz 2000 ~ 4000 Hz2000年4000 Hzの間の周波数は2 2 * 5 = 20 2^2*5=20225=20 の周波数帯域など。このように、2 つの中心周波数の間には幾何学的な間隔がありますが、サンプルの数も幾何学的に増加するため、リサンプリングによって得られる最終的な周波数は依然として線形であるため、DCT 変換を実行できます。

おすすめ

転載: blog.csdn.net/weixin_43335465/article/details/128943889