【語音識別】語音端點檢測及Python實現
- 一、語音信號的分幀處理
- 二、端點檢測方法
- 2.1、短時能量
- 2.2、短時過零率
- 三、Python實現
從接收的語音信號中準確檢測出人聲開始和結束的端點是進行語音識別的前提。本博文介紹基于短時過零率和短時能量的基本語音端點檢測方法及Python實現。如圖所示為語音信號,紅色方框內為人聲:

一、語音信號的分幀處理
語音信號是時序信號,其具有長時隨機性和短時平穩性。長時隨機性指語音信號隨時間變化是一個隨機過程,短時平穩性指在短時間內其特性基本不變,因為人說話是肌肉具有慣性,從一個狀態到另一個狀態不可能瞬時完成。語音通常在10-30ms之間相對平穩,因此語音信號處理的第一步基本都是對語音信號進行分幀處理,幀長度一般取10-30ms。
語音信號的分幀處理通常采用滑動窗的方式,窗口可以采用直角窗、Hamming窗等。窗口長度決定每一幀信號中包含原始語音信號中信息的數量,窗口每次的滑動距離等于窗口長度時,每一幀信息沒有重疊,當窗口滑動距離小于窗口長度時幀信息有重合。本博文采用直角窗進行語音信號的分幀處理:
直角窗:
h ( n ) = { 1 , 0 ≤ n ≤ N ? 1 0 , o t h e r {\rm{h}}(n) = \left\{ {\begin{matrix} {1, 0\le n \le N - 1}\\ {0,{\rm{other}}} \end{matrix}} \right.
h
(
n
)
=
{
1
,
0
≤
n
≤
N
?
1
0
,
o
t
h
e
r
?
二、端點檢測方法
端點檢測是指找出人聲開始和結束的端點。利用人聲信號短時特性與非人聲信號短時特性的差異可以有效地找出人聲開始和結束的端點,本博文介紹短時能量和短時過零率結合進行端點檢測的方法。
2.1、短時能量
第n幀信號的短時平均能量定義為:
E n = ∑ m = n ? N + 1 n [ x ( m ) w ( n ? m ) ] 2 {E_n} = \sum\limits_{m = n - N + 1}^n {{{\left[ {x\left( m \right)w\left( {n - m} \right)} \right]}^2}}
E
n
?
=
m
=
n
?
N
+
1
∑
n
?
[
x
(
m
)
w
(
n
?
m
)
]
2
包含人聲信號的幀的短時平均能量大于非人聲信號的幀。
2.2、短時過零率
過零信號指通過零值,相鄰取樣值改變符號即過零,過零數是樣本改變符號的數量。
第n幀信號的平均短時過零數為:
Z n = ∑ m = n ? N + 1 n ∣ s g n [ x ( m ) ] ? s g n [ x ( m ? 1 ) ] ∣ w ( n ? m ) {Z_n} = \sum\limits_{m = n - N + 1}^n {\left| {{\mathop{\rm sgn}} \left[ {x\left( m \right)} \right] - {\mathop{\rm sgn}} \left[ {x\left( {m - 1} \right)} \right]} \right|w\left( {n - m} \right)}
Z
n
?
=
m
=
n
?
N
+
1
∑
n
?
∣
s
g
n
[
x
(
m
)
]
?
s
g
n
[
x
(
m
?
1
)
]
∣
w
(
n
?
m
)
w ( n ) = { 1 / ( 2 N ) , 0 ≤ n ≤ N ? 1 0 , o t h e r w\left( n \right) = \left\{ {\begin{matrix} {1/\left( {2N} \right),0 \le n \le N - 1}\\ {0,other} \end{matrix}} \right. w ( n ) = { 1 / ( 2 N ) , 0 ≤ n ≤ N ? 1 0 , o t h e r ?
三、Python實現
import
wave
import
numpy as np
import
matplotlib
.
pyplot as plt
def
read
(
data_path
)
:
''
'讀取語音信號
''
'
wavepath
=
data_path
f
=
wave
.
open
(
wavepath
,
'rb'
)
params
=
f
.
getparams
(
)
nchannels
,
sampwidth
,
framerate
,
nframes
=
params
[
:
4
]
#聲道數、量化位數、采樣頻率、采樣點數
str_data
=
f
.
readframes
(
nframes
)
#讀取音頻,字符串格式
f
.
close
(
)
wavedata
=
np
.
fromstring
(
str_data
,
dtype
=
np
.
short
)
#將字符串轉化為浮點型數據
wavedata
=
wavedata
*
1.0
/
(
max
(
abs
(
wavedata
)
)
)
#wave幅值歸一化
return
wavedata
,
nframes
,
framerate
def
plot
(
data
,
time
)
:
plt
.
plot
(
time
,
data
)
plt
.
grid
(
'on'
)
plt
.
show
(
)
def
enframe
(
data
,
win
,
inc
)
:
''
'對語音數據進行分幀處理
input
:
data
(
一維array
)
:
語音信號
wlen
(
int
)
:
滑動窗長
inc
(
int
)
:
窗口每次移動的長度
output
:
f
(
二維array
)
每次滑動窗內的數據組成的二維array
''
'
nx
=
len
(
data
)
#語音信號的長度
try
:
nwin
=
len
(
win
)
except Exception as err
:
nwin
=
1
if
nwin
==
1
:
wlen
=
win
else
:
wlen
=
nwin
nf
=
int
(
np
.
fix
(
(
nx
-
wlen
)
/
inc
)
+
1
)
#窗口移動的次數
f
=
np
.
zeros
(
(
nf
,
wlen
)
)
#初始化二維數組
indf
=
[
inc
*
j
for
j in
range
(
nf
)
]
indf
=
(
np
.
mat
(
indf
)
)
.
T
inds
=
np
.
mat
(
range
(
wlen
)
)
indf_tile
=
np
.
tile
(
indf
,
wlen
)
inds_tile
=
np
.
tile
(
inds
,
(
nf
,
1
)
)
mix_tile
=
indf_tile
+
inds_tile
f
=
np
.
zeros
(
(
nf
,
wlen
)
)
for
i in
range
(
nf
)
:
for
j in
range
(
wlen
)
:
f
[
i
,
j
]
=
data
[
mix_tile
[
i
,
j
]
]
return
f
def
point_check
(
wavedata
,
win
,
inc
)
:
''
'語音信號端點檢測
input
:
wavedata
(
一維array
)
:原始語音信號
output
:
StartPoint
(
int
)
:
起始端點
EndPoint
(
int
)
:
終止端點
''
'
#
1.
計算短時過零率
FrameTemp1
=
enframe
(
wavedata
[
0
:
-
1
]
,
win
,
inc
)
FrameTemp2
=
enframe
(
wavedata
[
1
:
]
,
win
,
inc
)
signs
=
np
.
sign
(
np
.
multiply
(
FrameTemp1
,
FrameTemp2
)
)
# 計算每一位與其相鄰的數據是否異號,異號則過零
signs
=
list
(
map
(
lambda x
:
[
[
i
,
0
]
[
i
>
0
]
for
i in x
]
,
signs
)
)
signs
=
list
(
map
(
lambda x
:
[
[
i
,
1
]
[
i
<
0
]
for
i in x
]
,
signs
)
)
diffs
=
np
.
sign
(
abs
(
FrameTemp1
-
FrameTemp2
)
-
0.01
)
diffs
=
list
(
map
(
lambda x
:
[
[
i
,
0
]
[
i
<
0
]
for
i in x
]
,
diffs
)
)
zcr
=
list
(
(
np
.
multiply
(
signs
,
diffs
)
)
.
sum
(
axis
=
1
)
)
#
2.
計算短時能量
amp
=
list
(
(
abs
(
enframe
(
wavedata
,
win
,
inc
)
)
)
.
sum
(
axis
=
1
)
)
# # 設置門限
#
print
(
'設置門限'
)
ZcrLow
=
max
(
[
round
(
np
.
mean
(
zcr
)
*
0.1
)
,
3
]
)
#過零率低門限
ZcrHigh
=
max
(
[
round
(
max
(
zcr
)
*
0.1
)
,
5
]
)
#過零率高門限
AmpLow
=
min
(
[
min
(
amp
)
*
10
,
np
.
mean
(
amp
)
*
0.2
,
max
(
amp
)
*
0.1
]
)
#能量低門限
AmpHigh
=
max
(
[
min
(
amp
)
*
10
,
np
.
mean
(
amp
)
*
0.2
,
max
(
amp
)
*
0.1
]
)
#能量高門限
# 端點檢測
MaxSilence
=
8
#最長語音間隙時間
MinAudio
=
16
#最短語音時間
Status
=
0
#狀態
0
:
靜音段
,
1
:
過渡段
,
2
:
語音段
,
3
:
結束段
HoldTime
=
0
#語音持續時間
SilenceTime
=
0
#語音間隙時間
print
(
'開始端點檢測'
)
StartPoint
=
0
for
n in
range
(
len
(
zcr
)
)
:
if
Status
==
0
or Status
==
1
:
if
amp
[
n
]
>
AmpHigh or zcr
[
n
]
>
ZcrHigh
:
StartPoint
=
n
-
HoldTime
Status
=
2
HoldTime
=
HoldTime
+
1
SilenceTime
=
0
elif amp
[
n
]
>
AmpLow or zcr
[
n
]
>
ZcrLow
:
Status
=
1
HoldTime
=
HoldTime
+
1
else
:
Status
=
0
HoldTime
=
0
elif Status
==
2
:
if
amp
[
n
]
>
AmpLow or zcr
[
n
]
>
ZcrLow
:
HoldTime
=
HoldTime
+
1
else
:
SilenceTime
=
SilenceTime
+
1
if
SilenceTime
<
MaxSilence
:
HoldTime
=
HoldTime
+
1
elif
(
HoldTime
-
SilenceTime
)
<
MinAudio
:
Status
=
0
HoldTime
=
0
SilenceTime
=
0
else
:
Status
=
3
elif Status
==
3
:
break
if
Status
==
3
:
break
HoldTime
=
HoldTime
-
SilenceTime
EndPoint
=
StartPoint
+
HoldTime
return
StartPoint
,
EndPoint
,
FrameTemp1
if
__name__
==
'__main__'
:
data_path
=
'audio_data.wav'
win
=
240
inc
=
80
wavedata
,
nframes
,
framerate
=
read
(
data_path
)
time_list
=
np
.
array
(
range
(
0
,
nframes
)
)
*
(
1.0
/
framerate
)
plot
(
wavedata
,
time_list
)
StartPoint
,
EndPoint
,
FrameTemp
=
point_check
(
wavedata
,
win
,
inc
)
checkdata
,
Framecheck
=
check_signal
(
StartPoint
,
EndPoint
,
FrameTemp
,
win
,
inc
)
端點檢測結果:
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
