Utilisez matlab/python pour effectuer la détection des points finaux de la méthode à double seuil

La détection des points finaux fait référence à la détermination des points de début et de fin de la parole à partir d'un signal contenant de la parole.
Dans l'apprentissage profond basé sur des signaux audio, la détection des points finaux est effectuée avant la formation du modèle, et chaque signal d'excitation efficace est extrait, non seulement il peut augmenter le nombre de échantillons, réduisez les calculs inutiles lors de la formation du réseau et améliorez la précision de la formation du modèle.

1. Principe de la méthode du double seuil

La méthode à double seuil a été proposée à l'origine sur la base de l'énergie moyenne à court terme et du taux de passage à zéro moyen à court terme. Le principe est que les finales chinoises ont des voyelles et ont une plus grande énergie, de sorte que les finales peuvent être trouvées à partir du court terme. l'énergie moyenne du terme, et les consonnes initiales sont Les consonnes ont des fréquences plus élevées et des taux de passage à zéro moyens à court terme correspondants. Par conséquent, utiliser ces deux caractéristiques pour trouver les consonnes initiales et les finales équivaut à trouver des syllabes chinoises complètes. La méthode à double seuil est mis en œuvre au moyen de décisions à deux niveaux, comme le montre l’image.

a est la forme d'onde de la parole

a est la forme d'onde de la parole, b est l'énergie moyenne de la parole à court terme et c est le taux de passage à zéro moyen à court terme de la parole.

Les étapes spécifiques pour effectuer un jugement sont les suivantes :
1. Le premier niveau de jugement
① effectue un jugement approximatif basé sur un seuil (seuil) T2 plus élevé sélectionné sur l'enveloppe énergétique à court terme de la parole. Ensuite, la parole
au-dessus du seuil T2 doit être de la parole (c'est-à-dire qu'il doit s'agir de parole entre les segments du CD), et les points de début et de fin de la parole doivent être en dehors du point temporel correspondant à l'intersection du seuil et de l'enveloppe énergétique à court terme (c'est-à-dire en dehors du Segment CD). ② Déterminer une valeur inférieure sur l'
énergie moyenne Le seuil (seuil) T1, et rechercher du point C vers la gauche et du point D vers la droite, trouver respectivement les deux points B et E où l'énergie à court terme L'enveloppe coupe le seuil T1, donc le segment BE utilise la méthode du double seuil en fonction de l'enveloppe énergétique à court terme. Les positions des points de départ et d'arrivée du segment vocal sont déterminées par l'énergie temporelle.

2. Le jugement de deuxième niveau
est basé sur le taux de passage à zéro moyen à court terme. Recherchez du point B vers la gauche et du point E vers la droite pour trouver deux points A et F dont le taux de passage à zéro moyen à court terme est inférieur à un certain seuil T3. C'est le point de départ et d'arrivée du segment de parole.

Sur la base de ces deux niveaux de jugement, on obtient la position du point de départ A et la position du point final F du discours. Cependant, étant donné que la zone silencieuse entre les mots pendant la prononciation du discours aura une longueur minimale pour représenter la pause entre les prononciations, cela c'est-à-dire que lorsqu'elle est inférieure au seuil T3, on satisfait Une telle longueur minimale est utilisée pour déterminer la fin du segment de parole, ce qui équivaut en réalité à étendre la longueur de la queue de parole. et les points de fin de la parole sont marqués sur la forme d'onde vocale comme A et F+ respectivement (les points de fin peuvent être vus sur la figure. La position est F, mais dans le traitement réel, elle est étendue à F+
).

Dans l'opération spécifique de détection du point final, la parole est d'abord divisée en trames. Sur la base de la division de trame, l'énergie moyenne à court terme et le taux de passage à zéro moyen à court terme peuvent être obtenus, puis la comparaison et le jugement sont réalisé image par image en fonction du seuil.

2. Exemple de détection de point final à double paramètre et à double seuil

Utilisez l'énergie, la fonction d'autocorrélation et la décision à double seuil pour extraire les points finaux.

La méthode précédente à double seuil qui utilisait l'énergie et le taux de passage à zéro pour détecter les points limites est en fait une méthode de détection à double seuil à deux paramètres, qui implique deux paramètres : l'énergie et le taux de passage à zéro, et donne trois ou quatre valeurs de seuil. T1 en même temps, T2, T3 (T4), parmi lesquels un paramètre est dst1 et l'autre paramètre est dst2.

Ce qui suit introduit en outre la description de la décision de deuxième niveau
1. La décision de premier niveau
① est effectuée une fois sur la base d'un seuil plus élevé T2 sélectionné sur le premier paramètre dst1 (ou d'un seuil plus élevé T4 sélectionné sur le deuxième paramètre dst2). le jugement signifie que la valeur au-dessus du seuil T2 (ou T4) doit être de la parole.②
Déterminez un seuil inférieur T1 sur le premier paramètre dst1, étendez la recherche du point d'intersection en ① aux deux côtés et trouvez respectivement dst1 et le seuil T1. Les deux points d'intersection sont grossièrement jugés comme étant les positions des points de départ et d'arrivée du segment de parole.
2. Le deuxième niveau de jugement
est basé sur dst2. À partir des positions de point de départ et de fin obtenues par le jugement de premier niveau, la recherche est étendue aux deux extrémités pour trouver dst2 et un certain seuil T3.
Les deux points d'intersection sont les points de début et de fin du segment de parole.

Selon cette idée, la fonction correspondante est compilée :
(1)
Fonction vad_param2D : extraire la position du point final voix selon les deux paramètres dst1 et dst2.
Le format d'appel : [voiceseg, vsl, SF, NF] = vad_param2D (dst1 , dst2, T1, T2, T3, T4);

Exemple : Détection de point de terminaison à l'aide de deux paramètres

①Dans les paramètres d'entrée, x est la séquence du signal vocal. Afin de diviser la trame en trames, définissez la longueur de la trame sur wlen et le décalage de trame sur inc. NIS est le nombre de trames dans le segment non vocal principal.

Afin d'estimer le bruit dans le traitement de la parole, il y a souvent un segment silencieux de tête au début d'un discours, et ce segment silencieux de tête est utilisé pour estimer les caractéristiques du bruit.

En pratique, il arrive parfois que le nombre de trames du segment silencieux principal ne soit pas connu, mais la longueur du segment silencieux principal IS peut être estimée à partir de la forme d'onde du signal vocal. Avec IS, le nombre de trames du segment silencieux principal IS NIS peut être calculé
NIS = fix(IS * fs - wlen) / inc + 1)

② Dans la discussion ici, on pense que le bruit est stationnaire, donc l'énergie moyenne estimée à court terme et la fonction d'autocorrélation du bruit sont applicables à l'ensemble de la parole.

③Le signal vocal est x(n), et après fenêtrage et cadrage, il est xi(m), où l'indice i représente la i-ème trame, la longueur de la trame est N, le nombre total de trames est fn et l'énergie de chaque image est calculé comme suit :
AMP i = ∑ m = 1 N xi 2 ( m ) AMP_i\ = \displaystyle \sum^{N} _{m=1} x_i^{2}(m)A M Pje =m = 1NXje2( m )

Le signal vocal est x(n), et après fenêtrage et cadrage, il est xi(m), où l'indice i représente la i-ème trame (i=1,2,3,...,M), et M est le nombre total d'images.
Chacune La fonction d'autocorrélation à court terme des données d'image est définie comme :
R i ( k ) = ∑ m = 1 L − kxi ( m ) xi ( m + k ) R_i(k) = \displaystyle \somme^{Lk} _{m= 1}x_i(m)x_i(m+k)R.je( k )=m = 1L kXje( m ) xje( m+k )
L est la longueur de la trame vocale, k est la quantité de retard.
Si la fonction de corrélation est calculée entre deux trames adjacentes, il s'agit de la fonction de corrélation croisée et son expression est :
R i ( k ) = ∑ m = 1 L − kxi − 1 ( m ) xi ( m + k ) R_i(k) = \displaystyle \sum^{Lk} _{m=1} x_{i-1}(m)x_i(m+k)R.je( k )=m = 1L kXje 1( m ) xje( m+k )
Le signal de parole étant un signal quasi-stationnaire et évoluant lentement, deux seuils T3 et T4 sont fixés en fonction de la situation de bruit. Lorsque la valeur maximale de la fonction de corrélation est supérieure à T4, il est déterminé qu'il s'agit de parole ; lorsque la valeur maximale de la fonction de corrélation est supérieure ou inférieure à T3, elle est déterminée comme point final du signal vocal.

④ Calculez d'abord l'énergie moyenne à court terme et la fonction d'autocorrélation du bruit pour le segment silencieux principal :
etemp=mean(etemp(1:NIS));
thredth=max(Rum(2:NIS));

Ensuite, en fonction de ces deux valeurs, définissez les deux seuils d'énergie (T1) et (T2), ainsi que le seuil de la fonction de corrélation croisée (T3) : T2 = 4 etemp ; T1 = 2 etemp ; T3=
1,1 * thredth
;

Le seuil ici n'est pas fixé à une valeur fixe, mais changera dynamiquement en fonction du bruit de calcul du segment non vocal principal. Une fois le seuil établi, la détection peut être effectuée selon la méthode du double seuil.

⑤ Une fois la détection terminée, les points d'extrémité sont x1 et x2, et les deux séquences SF et NF sont ensuite définies. Ce sont toutes deux des tableaux de 1xfn. SF = 1 signifie que la trame est une trame de conversation
et SF = 0 signifie que la trame est une trame de conversation. La trame est silencieuse. La trame de conversation ;
NF est opposé à SF, NF=1 signifie que la trame est une trame de non-parler, NF=0 signifie que la trame est une trame de conversation.

⑥En même temps, des données structurelles de segment vocal sont définies, qui fournissent également des informations sur le point final de la voix. Comme il peut y avoir plusieurs pauses dans le groupe de voix analysé, chaque groupe a une heure de début et une heure de fin, dans les données structurelles de segment vocal. Il contient l'heure de début, l'heure de fin et la durée de chaque groupe de segments vocaux, qui sont tous exprimés en trames.

Le calcul de voiceseg est complété par les deux instructions suivantes :
SpeechIndex=find(SF==1);
voiceseg=findSegment(speechIndex);

La liste des programmes est la suivante :


clear all; clc; close all;
IS=0.25;                                % 设置前导无话段长度
wlen=200;                               % 设置帧长为25ms
inc=80;                                 % 求帧移
SNR=10;                            		 % 设置信噪比
fle = 'D:\audio\test1\51_674_740001.wav';

[xx,fs]=wavread(fle);                   % 读入数据
xx=xx-mean(xx);                         % 消除直流分量
x=xx/max(abs(xx));                      % 幅值归一化
N=length(x);                            % 取信号长度
time=(0:N-1)/fs;                        % 设置时间
signal=Gnoisegen(x,SNR);                % 叠加噪声
wnd=hamming(wlen);                      % 设置窗函数
overlap=wlen-inc;                       % 求重叠区长度
NIS=fix((IS*fs-wlen)/inc +1);           % 求前导无话段帧数
y=enframe(signal,wnd,inc)';             % 分帧
fn=size(y,2);                           % 求帧数
frameTime=frame2time(fn, wlen, inc, fs);% 计算各帧对应的时间

for k=2 : fn                            % 计算互相关函数
    u1=y(:,k-1);
    u2=y(:,k);
    ru=xcorr(u1,u2);
    Ru(k)=max(ru);
end
Rum=multimidfilter(Ru,1);              % 平滑处理
Rum=Rum/max(Rum);                       % 归一化
%thredth=max(Rum(2:NIS));                % 计算阈值
%T3=1.1*thredth;
%T4=1.3*thredth;
T3 =  0.065;

etemp=sum(y.^2);                        % 求取短时平均能量
etemp=etemp/max(etemp);                 % 能量幅值归一化
fn=size(y,2);                           % 帧数
% etemp = mean(etemp(1:NIS));           %计算初始无话段区间的能量平均值
% T2 = 4*etemp; T1 = 2*etemp;					%设置能量的阈值
T1=0.2;                            	 	  % 设置阈值
T2=0.15;

[voiceseg,vsl,SF,NF]=vad_param2D(etemp,Rum,T1,T2,T3);   % 双参数双门限端点检测

% 作图
subplot 311; plot(time,x,'k');
title('纯语音波形');
ylabel('幅值'); axis([0 max(time) -1 1]);

subplot 312; plot(frameTime,etemp,'k')
title('语音短时能量图');
ylabel('幅值'); axis([0 max(time) 0 1]);
xlabel('时间/s');
line([0 max(time)],[T1 T1],'color','b','LineStyle','--');
line([0 max(time)],[T2 T2],'color','r','LineStyle','--');

subplot 313; plot(frameTime,Rum,'k');
title('短时自相关函数'); axis([0 max(time) 0 1.2]);
xlabel('时间/s'); ylabel('幅值'); 
line([0,frameTime(fn)], [T3 T3], 'color','b','LineStyle','--');

for k=1 : vsl                           % 标出语音端点
    nx1=voiceseg(k).begin; nx2=voiceseg(k).end;
    fprintf('%4d   %4d   %4d    %4d\n',k,nx1,nx2,nx2-nx1);
    subplot 311; 
    line([frameTime(nx1) frameTime(nx1)],[-1 1],'color','b','LineStyle','--');
    line([frameTime(nx2) frameTime(nx2)],[-1 1],'color','r','LineStyle','--');
end

Résultats de la détection des points finaux Figure
Insérer la description de l'image ici
3. Description de la fonction vad_param2D et autres instructions

Nom : vad_param2D
Fonction : Utiliser l'énergie moyenne à court terme et la valeur maximale de la fonction d'autocorrélation pour extraire la position du point final de voix.
Format d'appel : fonction [voiceseg, vsl, SF, NF] = vad_param2D (dst1, dst2, T1, T2 , T3, T4)
Description :
①dst1 et dst2 peuvent être l'énergie et le taux de passage à zéro mentionnés ci-dessus, ou d'autres paramètres, tels que des fonctions d'autocorrélation, etc. ②Les
deux seuils T1 et T2 de dst1, et un ou deux seuils T3 ( et T4) de dst2 sont tous appelés Cette fonction est calculée auparavant. Ces seuils peuvent dépendre des valeurs​​sur les données originales de dst1 et dst2, ou ils peuvent être des valeurs lissées. ③ Vous ne pouvez apporter qu'un seul seuil pour
dst2 (n'écrivez que T3 lors de l'appel de la fonction, pas T4), vous pouvez également apporter deux seuils. Dans la fonction, il sera automatiquement jugé si T4 est inclus. Ce n'est qu'après l'introduction de T4 que T4 sera utilisé pour le jugement.

⑦ Certains paramètres sont définis dans la fonction vad_param2D : maxsilence représente la longueur minimale de la zone de silence à la fin d'un discours, et minlin représente la longueur minimale du segment de parole. Ils sont tous deux fixes.

Bien que les paramètres tels que T1, T2 et T3 changent dynamiquement, leurs coefficients proportionnels sont également fixes.

En fait, une détection correcte dans la détection du point final sera affectée par de nombreux facteurs : l'environnement sonore est un facteur majeur. Différents bruits et différents rapports signal/bruit affecteront la précision de la détection, ces paramètres peuvent donc être modifiés et effectuer des ajustements. selon les conditions réelles.

L'organigramme des fonctions est le suivant
Insérer la description de l'image ici

Nom : findSegment
Fonction : Combinez l'heure de début, l'heure de fin et la longueur de chaque groupe de segments vocaux en fonction de l'adresse avec une valeur de 1 en SF. Format d'appel : function soundSegment=findSegment(express) La liste des programmes est la
suivante
:

function soundSegment=findSegment(express)
if express(1)==0
    voicedIndex=find(express);                     % 寻找express中为1的位置
else
    voicedIndex=express;
end

soundSegment = [];
k = 1;
soundSegment(k).begin = voicedIndex(1);            % 设置第一组有话段的起始位置
for i=1:length(voicedIndex)-1,
	if voicedIndex(i+1)-voicedIndex(i)>1,          % 本组有话段结束
		soundSegment(k).end = voicedIndex(i);      % 设置本组有话段的结束位置
		soundSegment(k+1).begin = voicedIndex(i+1);% 设置下一组有话段的起始位置  
		k = k+1;
	end
end
soundSegment(k).end = voicedIndex(end);            % 最后一组有话段的结束位置
% 计算每组有话段的长度
for i=1 :k
    soundSegment(i).duration=soundSegment(i).end-soundSegment(i).begin+1;
end

Remarque : En fait, la méthode à double seuil avec un seul paramètre (en utilisant uniquement l'énergie moyenne à court terme) peut effectuer une détection de point final sur des ensembles de données audio. L'effet est bon.

3.Python implémente la détection des points de terminaison par méthode à double seuil

"""
    本程序是进行端点检测
    双门限法进行端点检测
"""
import numpy as np
import wave
from scipy.io import wavfile
import soundfile as sf
import numpy as np
import matplotlib.pyplot as plt
import os
import re
from pydub import AudioSegment
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

def STAc(x):
    """
    计算短时相关函数
    :param x:
    :return:
    """
    para = np.zeros(x.shape)
    fn = x.shape[1]
    for i in range(fn):
        R = np.correlate(x[:, i], x[:, i], 'valid')
        para[:, i] = R
    return para


def STEn(x, win, inc):
    """
    计算短时能量函数
    :param x:
    :param win:
    :param inc:
    :return:
    """
    X = enframe(x, win, inc)
    s = np.multiply(X, X)
    return np.sum(s, axis=1)


def STZcr(x, win, inc, delta=0):
    """
    计算短时过零率
    :param x:
    :param win:
    :param inc:
    :return:
    """
    absx = np.abs(x)
    x = np.where(absx < delta, 0, x)
    X = enframe(x, win, inc)
    X1 = X[:, :-1]
    X2 = X[:, 1:]
    s = np.multiply(X1, X2)
    sgn = np.where(s < 0, 1, 0)
    return np.sum(sgn, axis=1)


def FrameTimeC(frameNum, frameLen, inc, fs):
    ll = np.array([i for i in range(frameNum)])
    return ((ll - 1) * inc + frameLen / 2) / fs


def enframe(x, win, inc=None):
    nx = len(x)
    if isinstance(win, list) or isinstance(win, np.ndarray):
        nwin = len(win)
        nlen = nwin  # 帧长=窗长
    elif isinstance(win, int):
        nwin = 1
        nlen = win  # 设置为帧长
    if inc is None:
        inc = nlen
    nf = (nx - nlen + inc) // inc
    frameout = np.zeros((nf, nlen))
    indf = np.multiply(inc, np.array([i for i in range(nf)]))
    for i in range(nf):
        frameout[i, :] = x[indf[i]:indf[i] + nlen]
    if isinstance(win, list) or isinstance(win, np.ndarray):
        frameout = np.multiply(frameout, np.array(win))
    return frameout

def findSegment(express):
    """
    分割成語音段
    :param express:
    :return:
    """
    if express[0] == 0:
        voiceIndex = np.where(express)
    else:
        voiceIndex = express
    d_voice = np.where(np.diff(voiceIndex) > 1)[0]
    voiceseg = {
    
    }
    if len(d_voice) > 0:
        for i in range(len(d_voice) + 1):
            seg = {
    
    }
            if i == 0:
                st = voiceIndex[0]
                en = voiceIndex[d_voice[i]]
            elif i == len(d_voice):
                st = voiceIndex[d_voice[i - 1]+1]
                en = voiceIndex[-1]
            else:
                st = voiceIndex[d_voice[i - 1]+1]
                en = voiceIndex[d_voice[i]]
            seg['start'] = st
            seg['end'] = en
            seg['duration'] = en - st + 1
            voiceseg[i] = seg
    return voiceseg

def vad_TwoThr1(x, wlen, inc, NIS):
    """
    使用门限法检测语音段
    :param x: 语音信号
    :param wlen: 分帧长度
    :param inc: 帧移
    :param NIS:
    :return:
    """
    maxsilence = 15
    minlen = 5
    status = 0
    y = enframe(x, wlen, inc)
    fn = y.shape[0]
    amp = STEn(x, wlen, inc)
    ampth = np.mean(amp[:NIS])
    amp2 = 7.5 * ampth
    amp1 = 10 * ampth
    xn = 0
    count = np.zeros(fn)
    silence = np.zeros(fn)
    x1 = np.zeros(fn)
    x2 = np.zeros(fn)
    for n in range(fn):
        if status == 0 or status == 1:
            if amp[n] > amp1:
                x1[xn] = max(1, n - count[xn] - 1)
                status = 2
                silence[xn] = 0
                count[xn] += 1
            elif amp[n] > amp2 :
                status = 1
                count[xn] += 1
            else:
                status = 0
                count[xn] = 0
                x1[xn] = 0
                x2[xn] = 0

        elif status == 2:
            if amp[n] > amp2 :
                count[xn] += 1
            else:
                silence[xn] += 1
                if silence[xn] < maxsilence:
                    count[xn] += 1
                elif count[xn] < minlen:
                    status = 0
                    silence[xn] = 0
                    count[xn] = 0
                else:
                    status = 3
                    x2[xn] = x1[xn] + count[xn]
        elif status == 3:
            status = 0
            xn += 1
            count[xn] = 0
            silence[xn] = 0
            x1[xn] = 0
            x2[xn] = 0
    el = len(x1[:xn])
    if x1[el - 1] == 0:
        el -= 1
    if x2[el - 1] == 0:
        print('Error: Not find endding point!\n')
        x2[el] = fn
    SF = np.zeros(fn)
    NF = np.ones(fn)
    for i in range(el):
        SF[int(x1[i]):int(x2[i])] = 1
        NF[int(x1[i]):int(x2[i])] = 0
    voiceseg = findSegment(np.where(SF == 1)[0])
    vsl = len(voiceseg.keys())
    return voiceseg, vsl, SF, NF, amp



if __name__ == '__main__':

    wavepath = './vadtest/60_805.wav'
    f = wave.open(wavepath, 'rb')
    params = f.getparams()
    nchannels, sampwidth, fs, nframes = params[:4]  # 声道数、量化位数、采样频率、采样点数
    str_data = f.readframes(nframes)  # 读取音频,字符串格式
    f.close()
    wavedata = np.fromstring(str_data, dtype=np.short)  # 将字符串转化为浮点型数据
    data = wavedata * 1.0 / (max(abs(wavedata)))  # wave幅值归一化

    data /= np.max(data)
    N = len(data)
    wlen = 200
    inc = 80
    IS = 1
    overlap = wlen - inc
    NIS = int((IS * fs - wlen) // inc + 1)
    fn = (N - wlen) // inc + 1

    frameTime = FrameTimeC(fn, wlen, inc, fs)  # 计算出对应的时间
    time = [i / fs for i in range(N)]

    #voiceseg, vsl, SF, NF, amp, zcr = vad_TwoThr(data, wlen, inc, NIS)
    voiceseg, vsl, SF, NF, amp = vad_TwoThr1(data, wlen, inc, NIS)

    plt.subplot(2, 1, 1)
    plt.ylabel('幅值')
    plt.ylim((-1, 1))  # 设置y轴刻度范围为0~11
    plt.title('纯音频波形图')  # 设置绘图标题
    plt.plot(time, data)

    plt.subplot(2, 1, 2)
    plt.plot(frameTime, amp)
    plt.ylabel('幅值')
    plt.xlabel('时间(s)')  # 设置x、y轴标签
    plt.title('音频短时能量图')  # 设置绘图标题

    for i in range(vsl):
        plt.subplot(2, 1, 1)
        plt.vlines(frameTime[voiceseg[i]['start']],-1,1,colors='r',linestyles='dashed')
        plt.vlines(frameTime[voiceseg[i]['end']], -1, 1, colors='g', linestyles='dashed')
    plt.show()

rendusInsérer la description de l'image ici

Les connaissances impliquées sont référencées dans -> Application de Matlab à l'analyse et à la synthèse des signaux vocaux.

Je suppose que tu aimes

Origine blog.csdn.net/tenju/article/details/129533060
conseillé
Classement