語音信號處理常用度量方法

信噪比(SNR)

有用信號功率與噪聲功率的比(此處功率爲平均功率),也等於幅度比的平方

SNR(dB)=10log10n=0N1s2(n)n=0N1d2(n)=10log10(PsignalPnoise)=20log10(AsignalAnoise)SNR(dB)=10\log_{10}\frac{\sum_{n=0}^{N-1}s^2(n)}{\sum_{n=0}^{N-1}d^2(n)}=10*\log_{10}(\frac{P_{signal}}{P_{noise}})=20*log_{10}(\frac{A_{signal}}{A_{noise}})

SNR(dB)=10log10n=0N1s2(n)n=0N1[x(n)s(n)2]SNR(dB)=10\log_{10}\frac{\sum_{n=0}^{N-1}s^2(n)}{\sum_{n=0}^{N-1}[x(n)-s(n)^2]}

其中:

PsignalP_{signal}爲信號功率;PnoiseP_{noise}爲噪聲功率;AsignalA_{signal}爲信號幅度;AnoiseA_{noise}爲噪聲幅度值,功率等於幅度值的平方

MATLAB版本代碼

# 信號與噪聲長度應該一樣
function snr=SNR_singlech(Signal,Noise)

P_signal = sum(Signal-mean(Signal)).^2;     # 信號的能量
P_noise = sum(Noise-mean(Noise)).^2;     # 噪聲的能量
snr = 10 * log10(P_signal/P_noise)

python代碼

def numpy_SNR(origianl_waveform, target_waveform):
    # 單位 dB
    signal = np.sum(origianl_waveform ** 2)
    noise = np.sum((origianl_waveform - target_waveform) ** 2)
    snr = 10 * np.log10(signal / noise)
    return snr

np.linalg.norm(x)=x12+x22+...+xn2np.linalg.norm(x)=\sqrt{x_1^2+x_2^2+...+x_n^2}

這個公式和上面是一樣的

def wav_snr(ref_wav, in_wav):# 如果ref wav稍長,則用0填充in_wav
    if (abs(in_wav.shape[0] - ref_wav.shape[0]) < 10):
        pad_width = ref_wav.shape[0] - in_wav.shape[0]
        in_wav = np.pad(in_wav, (0, pad_width), 'constant')
    else:
        print("錯誤:參考wav與輸入wav的長度明顯不同")
        return -1

    # 計算 SNR
    norm_diff = np.square(np.linalg.norm(in_wav - ref_wav))
    if (norm_diff == 0):
        print("錯誤:參考wav與輸入wav相同")
        return -1

    ref_norm = np.square(np.linalg.norm(ref_wav))
    snr = 10 * np.log10(ref_norm / norm_diff)
    return snr

峯值信噪比(PSNR)

表示信號的最大瞬時功率和噪聲功率的比值,最大瞬時功率爲語音數據中最大值得平方。

SNR(dB)=10log10(MAX(Psignal)Pnoise)=10log10MAX[s(n)]2d2(n)SNR(dB)=10*\log _{10}(\frac{MAX(P_{signal})}{P_{noise}})=10\log_{10}\frac{MAX[s(n)]^2}{d^2(n)}

SNR(dB)=10log10MAX[s(n)]21Nn=0N1[x(n)s(n)]2=20log10MAX[s(n)]MSESNR(dB)=10\log_{10}\frac{MAX[s(n)]^2}{\frac{1}{N}\sum_{n=0}^{N-1}[x(n)-s(n)]^2}=20\log_{10}\frac{MAX[s(n)]}{\sqrt{MSE}}

import numpy as np 

def psnr(ref_wav, in_wav):
    MSE = numpy.mean((ref_wav - in_wav) ** 2)
    MAX = np.max(ref_wav)       # 信號的最大平時功率
    return 20 * np.log10(MAX / np.sqrt(MSE))

分段信噪比(SegSNR)

由於語音信號是一種緩慢變化的短時平穩信號,因而在不同時間段上的信噪比也應不一樣。爲了改善上面的問題,可以採用分段信噪比。分段信噪比即是先對語音進行分幀,然後對每一幀語音求信噪比,最好求均值。

MATLAB版本的代碼

function [segSNR] = Evaluation(clean_speech,enhanced)

N = 25*16000/1000; %length of the segment in terms of samples
M = fix(size(clean_speech,1)/N); %number of segments
segSNR = zeros(size(enhanced));
for i = 1:size(enhanced,1)
    for m = 0:M-1
        sum1 =0;
        sum2 =0;
        for n = m*N +1 : m*N+N
            sum1 = sum1 +clean_speech(n)^2;
            sum2 = sum2 +(enhanced{i}(n) - clean_speech(n))^2;
        end
        r = 10*log10(sum1/sum2);
        if r>55
            r = 55;
        elseif r < -10
            r = -10;
        end
       
        segSNR(i) = segSNR(i) +r;
    end
    segSNR(i) = segSNR(i)/M;
end

python代碼

def SegSNR(ref_wav, in_wav, windowsize, shift):
    if len(ref_wav) == len(in_wav):
        pass
    else:
        print('音頻的長度不相等!')
        minlenth = min(len(ref_wav), len(in_wav))
        ref_wav = ref_wav[: minlenth]
        in_wav = in_wav[: minlenth]
    # 每幀語音中有重疊部分,除了重疊部分都是幀移,overlap=windowsize-shift
    # num_frame = (len(ref_wav)-overlap) // shift
    # num_frame = (len(ref_wav)-windowsize+shift) // shift
    num_frame = (len(ref_wav) - windowsize) // shift + 1  # 計算幀的數量

    SegSNR = np.zeros(num_frame)
    # 計算每一幀的信噪比
    for i in range(0, num_frame):

        noise_frame_energy = np.sum(ref_wav[i * shift, i * shift+windowsize] ** 2)  # 每一幀噪聲的功率
        speech_frame_energy = np.sum(in_wav[i * shift, i * shift+windowsize] ** 2)  # 每一幀信號的功率
        SegSNR[i] = np.log10(speech_frame_energy / noise_frame_energy)

    return 10 * np.mean(SegSNR)

對數擬然對比度(log Likelihood Ratio Measure)

阪倉距離測度是通過語音信號的線性預測分析來實現的。ISD基於兩組線性預測參數(分別從原純淨語音和處理過的語音的同步幀得到)之間的差異。LLR可以看成一種阪倉距離(Itakura Distance,IS)但是IS距離需要考慮模型增益。而LLR不需要考慮模型爭議引起的幅度位移,更重視整體譜包絡的相似度。

PESQ

PESQ是用於語音質量評估的一種方法,ITU提供了C語言代碼,下載請點擊這裏,但是在使用之前我們需要先編譯C腳本,生成可執行文件exe

編譯方式爲:在命令行進入下載好的文件

  1. cd \Software\source
  2. gcc -o PESQ *.c

經過編譯,會在當前文件夾生成一個pesq.exe的可執行文件

使用方式爲:

  1. 命令行進入pesq.exe所在的文件夾
  2. 執行命令:pesq 採樣率 “原始文件路徑名” "劣化文件路徑名”
  3. 回車
  4. 等待結果即可,值越大,質量越好。
    • 例如:pesq +16000 raw.wav processed.wav

對數譜距離(Log Spectral Distance)

兩個頻譜之間的距離度量(用分貝表示)。兩個頻譜P(W)P(W)P^(w)\hat{P}(w)之間的對數譜距離被定義爲:

DLS=12πππ[10log10P(w)P^(w)]2dwD_{LS}=\sqrt{\frac{1}{2\pi}\int_{-\pi}^{\pi}[10*\log _{10}\frac{P(w)}{\hat{P}(w)}]^2dw}

其中,p(w)p(w)P^(w)\hat{P}(w)是功率譜。對數譜距離是時多對稱的。

def numpy_LSD(origianl_waveform, target_waveform):
    """ 比較原始和目標音頻之間的對數譜距離(LSD),也稱爲對數譜失真,
    是兩個頻譜之間的距離測量值(以dB表示) """

    print("數據形狀爲", origianl_waveform.shape)
    print("數據類型爲", type(origianl_waveform))

    original_spectrogram = librosa.core.stft(origianl_waveform, n_fft=2048)
    target_spectrogram = librosa.core.stft(target_waveform, n_fft=2048)

    original_log = np.log10(np.abs(original_spectrogram) ** 2)
    target_log = np.log10(np.abs(target_spectrogram) ** 2)
    original_target_squared = (original_log - target_log) ** 2
    target_lsd = np.mean(np.sqrt(np.mean(original_target_squared, axis=0)))

    return target_lsd

參考文獻:

非典型廢言的CSDN博客

視頻質量度量指標

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章