Curso de Verão 2017: Teoria e Prática de Processamento de Sinais e Imagens
Curso de Verão 2017: Teoria e Prática de Processamento de Sinais e Imagens
Curso de Verão 2017: Teoria e Prática de Processamento de Sinais e Imagens
Ivandro Sanches
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 1/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Programação
Amostragem e quantização
Áudio
geração
importação/exportação
gravação
Processamento do áudio
segmentação
energia/taxa de cruzamentos por zero
convolução/filtragem
transformada discreta de Fourier
Visualização
Produção da voz
pitch
formantes
vogais/consoantes
Percepção da voz
curvas de loudness
bandas críticas
mascaramento
Laboratório
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 2/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Amostragem e quantização
T : período de amostragem, s
fs =
1
T
: frequência de amostragem, Hz
Exemplos fs , kHz T ,s
Áudio de CD 44 22.7 μs
Telefonia 8 125 μs
0010101001110101
0010101001110101
0010101001110101
...
0010101001110101
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 3/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [1]:
import numpy as np
import matplotlib
%matplotlib inline
In [2]:
"""
input:
output:
Exemplo:
p = np.arange(-1,1,.1)
y0 = requantiza(p, 32, 1)
y1 = requantiza(p, 32, 2)
y2 = requantiza(p, 32, 4)
y3 = requantiza(p, 32, 8)
IS-25jan2017
"""
y = sinal
return y
M = 2**(nbits-1)
Q = 2**(B-1)
sinal2 = sinal * M
if max(sinal2) > M:
print('Checar sinal');
return Null;
sinal2 = np.floor(sinal2);
y = np.floor(sinal2/M*Q);
y = y/Q;
return y
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 4/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [3]:
p = np.arange(-1,1,.1)
n = np.arange(0,len(p))
y0 = requantiza(p, 32, 1)
y1 = requantiza(p, 32, 2)
y2 = requantiza(p, 32, 3)
y3 = requantiza(p, 32, 4)
#matplotlib.pylab.rcParams['figure.figsize'] = (12, 8)
axarr[0, 0].grid()
axarr[0, 0].legend(loc=2)
axarr[0, 1].grid()
axarr[0, 1].legend(loc=2)
axarr[1, 0].grid()
axarr[1, 0].legend(loc=2)
axarr[1, 0].set_xlabel('n')
axarr[1, 1].grid()
axarr[1, 1].legend(loc=2)
axarr[1, 1].set_xlabel('n')
# Fine-tune figure; hide x ticks for top plots and y ticks for right plots
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 5/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [4]:
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 6/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [5]:
# signal original
y = np.cos(2*np.pi*f1*t)
plt.figure(figsize=(12,5))
plt.plot(t, y)
plt.xlabel('t, s')
plt.ylabel('y(t)')
plt.grid()
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 7/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [6]:
f2 = 100
y2 = y[::nf2]
t2 = t[::nf2]
plt.figure(figsize=(12,5))
plt.plot(t, y)
plt.title('y(t)')
plt.xlabel('t, s')
plt.grid()
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 8/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [7]:
plt.figure(figsize=(12,5))
n = range(len(y2))
plt.xlabel('n')
plt.grid()
plt.show()
Áudio
Posteriormente, cria-se um sinal ruidoso, o qual é formado pelo tom gerado anteriormente e ruído gaussiano
a uma determinada relação sinal-ruído a ser definida (SNR: signal-to-noise ratio)
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 9/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [8]:
T = 3 # duração em segundos
A = 0.8 # amplitude
samples = np.floor(s*K)
samples = samples.astype(np.short)
print(rate == fs)
print(np.max(np.abs(data-samples)))
True
In [9]:
import IPython
IPython.display.Audio(fname)
Out[9]:
0:00 / 0:03
In [10]:
import winsound
winsound.PlaySound(fname, winsound.SND_FILENAME)
In [11]:
np.random.seed(0)
r = np.random.randn(N)
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 10/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [12]:
print('Ruído:')
print('\tmédia = {0:9.5f}'.format(np.mean(r)))
Ruído:
média = 0.00368
In [13]:
r = r - np.mean(r)
print('Ruído:')
print('\tmédia = {0:9.5f}'.format(np.mean(r)))
Ruído:
média = 0.00000
In [14]:
es = np.sum(s*s)
er = np.sum(r*r)
In [15]:
snr = 10 * np.log10(es/er)
print(snr)
-4.92906556855
In [16]:
SNR = 5
In [17]:
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 11/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [18]:
er = np.sum(r*r)
In [19]:
snr = 10 * np.log10(es/er)
print(snr)
5.0
In [20]:
x = s + r
In [21]:
plt.figure(figsize=(12,5))
plt.xlabel('n')
plt.grid()
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 12/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [22]:
"""
"""
t = np.arange(0,1,1.0/(td*fs))
if fi <= ff:
freqlin = fi + (ff-fi)*t
else:
freqlin = fi - (fi-ff)*t
return sinal
In [23]:
"""
"""
sinal = np.sin(phi)
return sinal
In [24]:
fs = 44100
fname = 'genlin.wav'
samples = np.floor(sinal1*K)
samples = samples.astype(np.short)
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 13/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [25]:
import IPython
IPython.display.Audio(fname)
Out[25]:
0:00 / 0:04
In [26]:
import winsound
winsound.PlaySound(fname, winsound.SND_FILENAME)
In [27]:
# espectrograma
nfft = 1024
plt.figure(figsize=(10,8))
cmap=plt.cm.gist_stern)
plt.xlabel('tempo, s')
plt.ylabel('frequência, Hz')
axes = plt.gca()
axes.set_ylim([0, fs/2])
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 14/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [28]:
"""
"""
x = np.arange(0, 1, 1./(td*fs))
M = np.exp(m)
if fi <= ff:
freqexp = fi + (np.exp(m*x)-1)/(M-1)*(ff-fi)
else:
freqexp = ff + (np.exp(m*x)-1)/(M-1)*(fi-ff)
freqexp = freqexp[-1::-1]
return sinal
In [29]:
fs = 44100;
fname = 'genexp.wav'
samples = np.floor(sinal2*K)
samples = samples.astype(np.short)
In [30]:
import IPython
IPython.display.Audio(fname)
Out[30]:
0:00 / 0:04
In [31]:
import winsound
winsound.PlaySound(fname, winsound.SND_FILENAME)
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 15/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [32]:
# espectrograma
nfft = 1024
plt.figure(figsize=(10,8))
cmap=plt.cm.gist_stern)
plt.xlabel('tempo, s')
plt.ylabel('frequência, Hz')
axes = plt.gca()
axes.set_ylim([0, fs/2])
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 16/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [33]:
fs = 44100
fname = 'linexp.wav'
samples = np.floor(sinal3*K)
samples = samples.astype(np.short)
nfft = 1024
plt.figure(figsize=(10,8))
cmap=plt.cm.gist_stern)
# cmap=plt.cm.gist_stern)
plt.xlabel('tempo, s')
plt.ylabel('frequência, Hz')
axes = plt.gca()
axes.set_ylim([0, fs/2])
plt.show()
In [34]:
fs = 44100;
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 17/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [35]:
estereo = estereo.T
print('De: https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.io.wavfil
e.write.html')
print(type(estereo))
fname = 'estereo.wav'
samples = np.floor(estereo*K)
samples = samples.astype(np.short)
De: https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.io.w
avfile.write.html
<class 'numpy.ndarray'>
In [36]:
import IPython
IPython.display.Audio(fname)
Out[36]:
0:00 / 0:04
Gravação de áudio
A gravação (criação) de arquivo de áudio (por exemplo .wav) pode ser feita via algum programa/aplicativo
externo e sua posterior importação para o ambiente python.
a. Wavesurfer (http://www.speech.kth.se/wavesurfer)
b. Audacity (http://www.audacityteam.org)
c. SFS (http://www.phon.ucl.ac.uk/resource/sfs)
d. SonicVisualizer (http://www.sonicvisualiser.org)
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 18/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Segmentação
Como o sinal de voz apresenta trechos com características bastante diversas, seu processamento precisa
ser feito por segmentos. O ideal é que cada trecho com características semelhantes pudesse ser tratado
individualmente. Como isso não é possível, o sinal de voz (e de áudio) é processado por segmentação.
O tamanho de cada segmento não pode ser muito pequeno, pois a informação contida não é suficiente, e
também não pode ser muito grande, quando trechos com características distintas seriam tratados em
conjunto.
Dessa forma há um compromisso e o tipo de processamento e sua finalidade acabam por sugerir tamanhos
adequados para segmentos.
Por exemplo, em reconhecimento de fala é usual que segmentos tenham tamanhos de 20 ms.
Adicionalmente, ocorre sobreposição entre segmentos sendo analidados.
Concretamente, suponha um sinal com duração de 10 s. Caso esse sinal seja analisado por segmentos de
25 ms aplicados a cada 10 ms, qual é o número total de segmentos a ser processado?
T– S
M = + 1
dS
ou
T – S + dS
M =
dS
Substituindo os valores
10– 0.025 + 0.010
M = = 998.5
0.010
A parte decimal deve ser descartada pois não se processam trechos com duração menor do que um
segmento.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 19/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Energia
Dado um segmento de duração S de um sinal que foi amostrado à frequência de amostragem fs , é fácil
calcularmos o tamanho em amostras desse segmento.
Seja N o número de amostras em um segmento de duração S , em segundos, extraído de um sinal que foi
amostrado à taxa fs , em Hz. Tem-se que
N = S fs
s0 , s1 , s2 , … , sN −1
N −1
2
E = ∑ s
i
i=0
Uma informação relativamente simples de ser computada e que dá uma ideia do conteúdo em frequência do
sinal sendo analidado é a taxa de cruzamentos por zero (TCZ).
Dado um segmento com N amostras, a taxa de cruzamentos por zero, Z , pode ser computada pela
expressão
N −1
1
Z = ∑ |sign(si ) − sign(si−1 )|
2(N − 1)
i=1
Onde
1, se si ≥ 0
sign(si ) = {
−1, se si < 0
Vê-se que
0 ≤ Z ≤ 1
Quanto mais próximo Z for de 1, maior o conteúdo em frequência do segmento. Note que quando Z = 0, o
sinal não cruzou o eixo.
Vê-se que essa grandeza é muito sensível a eventuais niveis DC presentes no sinal. Dessa forma,
recomenda-se que se retire o nível médio do sinal ou que seja filtrado adequadamente.
Energia e TCZ
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 20/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Seja o sinal de áudio seis.wav. A figura ilustra o comportamento da energia, E, e da TCZ, Z , desse sinal
em segmentos de 20 ms a cada 10 ms. O aspecto geral dessas curvas é o esperado?
In [37]:
fname = '../seis.wav'
T = len(seis)/fsseis
S = 20e-3
dS = 10e-3
M = int(np.floor((T-S+dS)/dS))
N = int(S * fsseis)
N1 = N - 1
dN = int(dS * fsseis)
E = np.zeros((M, 1))
Z = np.zeros((M, 1))
for i in range(M):
s = seis[ind]
E[i] = np.sum(s*s)
Z[i] = np.sum(np.abs(np.sign(s[1:])-np.sign(s[:-1])))/(2*N1)
ind = ind + dN
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 21/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [38]:
t = np.arange(len(seis))/fsseis
n = range(len(E))
axarr[0].plot(t, seis)
axarr[0].set_title('seis.wav')
axarr[0].set_xlabel('t, s')
axarr[0].set_ylabel('amplitude')
axarr[0].axis('tight')
axarr[0].grid()
axarr[1].plot(n, E, 'g')
axarr[1].set_ylabel('E')
axarr[1].grid()
axarr[2].plot(n, Z, 'r')
axarr[2].set_ylabel('Z')
axarr[2].grid()
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 22/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Convolução
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 23/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Seja uma sequência discreta x(n) ou x[n] ou x n . Seja um sistema linear invariante no tempo cuja resposta
ao impulso é h(n) . A saída desse sistema, y(n), dá-se pela convolução entre x(n) e h(n) :
A operação é comutativa:
Graficamente:
Explicitamente, a computação das amostras de saída dá-se pela expressão da convolução discreta:
∞
i=−∞
y = numpy.convolve(h, x)
Filtragem
y = numpy.convolve(h, x)
y = scipy.signal.lfilter(h, 1, x)
Vamos a seguir ilustar o projeto de um filtro digital. Nesse projeto simplificado, o resultado será a resposta
ao impulso finita h, ou os coeficientes a e b que caracterizam o filtro, e que nos permitirá o processo de
filtragem para uma entrada x qualquer.
Posteriormente, vamos ilustrar o projeto de um filtro digital que possui resposta ao impulso infinita. Neste
caso, o projeto retornará 2 vetores, a e b, que utilizaremos no processo de filtragem da seguinte forma:
y = scipy.signal.lfilter(b, a, x)
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 24/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Dados do projeto:
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 25/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [39]:
# projeto do filtro, h
N = 200 # ordem
W, H = signal.freqz(h, 1)
F = W * fs/2/np.pi
fig = plt.figure(figsize=(12,10))
plt.title('Resposta em frequência')
ax1 = fig.add_subplot(111)
plt.xlabel('Frequência [Hz]')
ax2 = ax1.twinx()
fase = np.unwrap(np.angle(H))
plt.grid()
plt.axis('tight')
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 26/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Lembrando que o sinal x é a senoide de 5 kHz mais ruído gaussiano a 5 db de SNR. O sinal y é o sinal x
filtrado pelo filtro projetado anteriormente. Note que após o transitório inicial, que é da ordem do filtro,
praticamente se obtém s, a senoide original de 5 kHz (quase sem ruído)
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 27/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [40]:
axarr[0].plot(ind1, x[ind1])
axarr[0].set_title('x')
axarr[0].set_ylabel('amplitude')
axarr[0].set_xlabel('n')
axarr[0].axis('tight')
axarr[0].grid()
# filtragem
ind2 = range(1000)
#y = signal.filtfilt(h, 1, x[ind2])
y = signal.lfilter(h, 1, x[ind2])
axarr[1].plot(ind1, y[ind1])
axarr[1].set_title('y')
axarr[1].set_ylabel('amplitude')
axarr[1].set_xlabel('n')
axarr[1].axis('tight')
axarr[1].grid()
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 28/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Dados do projeto:
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 29/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [41]:
N = 5 # ordem/2
nyq = fs/2
W, H = signal.freqz(b, a)
F = W * fs/2/np.pi
fig = plt.figure(figsize=(12,10))
plt.title('Resposta em frequência')
ax1 = fig.add_subplot(111)
plt.xlabel('Frequência [Hz]')
ax2 = ax1.twinx()
fase = np.unwrap(np.angle(H))
plt.grid()
plt.axis('tight')
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 30/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [42]:
axarr[0].plot(ind1, x[ind1])
axarr[0].set_title('x')
axarr[0].set_ylabel('amplitude')
axarr[0].set_xlabel('n')
axarr[0].axis('tight')
axarr[0].grid()
# filtragem
#y = signal.filtfilt(b, a, x[ind2])
y = signal.lfilter(b, a, x[ind2])
axarr[1].plot(ind1, y[ind1])
axarr[1].set_title('y')
axarr[1].set_ylabel('amplitude')
axarr[1].set_xlabel('n')
axarr[1].axis('tight')
axarr[1].grid()
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 31/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
A transformada discreta de Fourier (Discrete Fourier Transform, DFT) converte uma sequência finita no
tempo em sua correspondente versão em frequência. Isto é, a DFT é a representação no domínio da
frequência da sequência original de entrada.
Análise:
N −1
2 π
−j k n
X(k) = ∑ x(n) e N , k = 0, 1, 2, … , N − 1
n=0
Síntese:
N −1
2 π
j k n
x(n) = ∑ X(k) e N , n = 0, 1, 2, … , N − 1
k=0
Exemplo em Python:
>>> x1 = np.arange(5)
>>> X1 = fft(x1)
>>> x2 = ifft(X1)
True
In [43]:
x1 = np.arange(5)
X1 = fft(x1)
x2 = ifft(X1)
Out[43]:
True
Geralmente o sinal no tempo, x(n) , é uma sequência real. Normalmente a sequência transformada, X(k) ,
é complexa. Portanto, note abaixo o uso da função abs() nas transformadas de sequências vistas
anteriormente.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 32/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [44]:
# adicionado a 5 dB de SNR
X = fft(x);
Xa = np.abs(X);
axarr[0].plot(x)
axarr[0].set_title('x')
axarr[0].set_xlabel('n')
axarr[0].axis('tight')
axarr[0].grid()
axarr[1].plot(Xa)
axarr[1].set_title('abs(fft(x))')
axarr[1].set_xlabel('k')
axarr[1].axis('tight')
axarr[1].grid()
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 33/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Vamos enxergar melhor o que está ocorrendo nas figuras anteriores ao melhorar a visualização de seu
conteúdo. Assim, vamos:
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 34/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [45]:
# trecho de x
axarr[0].plot(ind, x[ind])
axarr[0].set_title('x')
axarr[0].set_xlabel('n')
axarr[0].axis('tight')
axarr[0].grid()
# eixo da DFT em Hz
fs = 44100
N = len(x)
Xb = Xa / N
ind = range(int(N/2));
axarr[1].plot(freq[ind], Xb[ind])
axarr[1].set_title('abs(fft(x))')
axarr[1].axis('tight')
axarr[1].grid()
msg = 'aproximadamente 0.4, que é a metade da\n' +\
arrowprops=dict(facecolor='red', shrink=0.01))
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 35/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 36/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
sinal1, visto anteriormente, tem sua frequência saindo de 500 Hz e chegando a 15 kHz de forma linear.
Vamos analisar 2 trechos desse sinal, no início e no fim. Cada trecho terá o comprimento de N = 512
amostras. Note que a DFT revela a concentração de energia em 500 Hz e a amplitude de (2 × 0.5 = 1.0)
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 37/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [46]:
N = 512
ind = np.arange(N)
t = ind/fs
s1 = sinal1[ind]
S1 = np.abs(fft(s1))
axarr[0].plot(t, s1)
axarr[0].set_title('s1')
axarr[0].set_xlabel('t, s')
axarr[0].axis('tight')
axarr[0].grid()
# eixo da DFT em Hz
S_1 = S1[0:N//2] / N
axarr[1].plot(freq, S_1)
axarr[1].set_title('abs(fft(s1))')
axarr[1].set_xlabel('f, Hz')
axarr[1].axis('tight')
axarr[1].grid()
msg = 'aproximadamente 0.5, que é a\n' +\
arrowprops=dict(facecolor='red', shrink=0.01))
arrowprops=dict(facecolor='red', shrink=0.01))
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 38/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 39/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Análise do trecho final de N = 512 amostras do sinal1, quando sua frequência atinge 15 kHz.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 40/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [47]:
N = 512
t = ind/fs
s2 = sinal1[ind]
S2 = np.abs(fft(s2))
axarr[0].plot(t, s2)
axarr[0].set_title('s2')
axarr[0].set_xlabel('t, s')
axarr[0].axis('tight')
axarr[0].grid()
axarr[0].add_patch(ellipse)
arrowprops=dict(facecolor='red', shrink=0.01))
# eixo da DFT em Hz
S_2 = S2[0:N//2] / N
axarr[1].plot(freq, S_2)
axarr[1].set_title('abs(fft(s2))')
axarr[1].set_xlabel('f, Hz')
axarr[1].axis('tight')
axarr[1].grid()
msg = 'aproximadamente 15000 Hz,\nque é a' +\
'frequência\nfinal do sinal'
arrowprops=dict(facecolor='red', shrink=0.01))
f.tight_layout()
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 41/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 42/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Espectrograma
O espectrograma combina os dois tipos de gráficos vistos nas células anteriores em um único gráfico.
Usualmente representa-se o tempo ao longo do eixo x, a frequência ao longo do eixo y e a
amplitude/intensidade como a cor do ponto (x,y). Como visto anteriormente, abaixo é ilustrado o
espctrograma do sinal1
In [48]:
# espectrograma
plt.figure(figsize=(10,8))
noverlap=7*nfft/8, # sobreposição
cmap=plt.cm.gist_stern) # colormap
plt.xlabel('tempo, s')
plt.ylabel('frequência, Hz')
axes = plt.gca()
axes.set_ylim([0, fs/2])
plt.show()
Note o limite no valor da escala de frequência: 4 kHz. Esse valor corresponde à metade da frequência de
amostragem do sinal sendo analisado. Neste caso, fs = 8 kHz.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 43/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [49]:
# espectrograma
win = np.hamming(nfft)
plt.figure(figsize=(10,8))
cmap=plt.cm.gist_stern) # colormap
plt.xlabel('tempo, s')
plt.ylabel('frequência, Hz')
axes = plt.gca()
axes.set_ylim([0, fsseis/2])
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 44/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Pitch
Pitch, F0 , ou frequência fundamental, é a frequência de vibração das cordas vocais. Valores baixos, entre
85 e 180 Hz, são frequentes no sexo masculino (adulto em fala normal). Valores femininos podem variar de
165 a 255 Hz. Crianças e, principalmente bebês, podem atingir valores acima de 300 Hz.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 45/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [50]:
t = np.arange(len(seis))/fsseis
axarr[0].plot(t, seis)
axarr[0].set_title('seis.wav')
axarr[0].axis('tight')
axarr[1].plot(t[ind], seis[ind])
axarr[1].axis('tight')
axarr[2].plot(t[ind], seis[ind])
axarr[2].set_xlabel('t, s')
axarr[2].annotate(
s='',
xy=(0.6985,-4000),
xytext=(0.6985+0.008,-4000),
arrowprops=dict(arrowstyle='<->'))
axarr[2].annotate(
xy=(0.7005,-3700))
axarr[2].axis('tight')
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 46/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 47/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Um método simples de se estimar a frequência de pitch, F0 , e por isso nem sempre muito eficiente, é pela
função de autocorrelação. Abaixo apresenta-se a função de autocorrelação e sua aplicação a um trecho
selecionado do sinal da página anterior. Dado um trecho de N amostras do sinal x(n) , sua autocorrelação,
R(n) , pode ser dada por:
N −k−1
n=0
Calculada a autocorrelação, basta agora determinar o valor de n onde R(n) é máxima após os valores
iniciais. Esse processo pode ser feito automaticamente, contudo, da figura, nota-se que esse valor de n vale
65. Lembrando que a frequência de amostragem do sinal seis.wav é 8 kHz, chega-se a
8000
F0 = ≈ 123 Hz
65
que corresponde aproximadamente ao valor estimado anteriormente para o mesmo trecho de sinal.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 48/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [51]:
s = seis[ind]
N = len(s)
R = np.zeros(N)
for k in range(N):
R[k] = sum(s[k:N]*s[0:N-k])
R = R/np.max(R)
offset = 10
xm = np.argmax(R[offset:])
xm += offset
plt.figure(figsize=(10,8))
plt.plot(R)
plt.title('autocorrelação')
plt.ylabel('$R(k)$', fontsize=18)
plt.xlabel('$k$', fontsize=18)
plt.grid()
arrowprops=dict(facecolor='red', shrink=0.01))
plt.axis('tight')
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 49/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
xm = 65
ym = 0.711048956561
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 50/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Formantes
As formantes são as frequências de ressonância do trato vocal. Por exemplo, para cada vogal o trato vocal
assume uma configuração relativamente estável, determinando frequências de ressonância específicas para
cada vogal. A figura ilustra a relação entre a primeira, F1 , e a segunda, F2 , formantes para vogais da língua
inglesa.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 51/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Vogais e consoantes
Abaixo é apresentado o exemplo de uma vogal. Note a quase periodicidade do sinal devido à vibração das
cordas vocais. Processou-se esse trecho pela técnica de predição linear e compararam-se os espectros
obtidos pela FFT e pelos coeficientes de predição linear (LPC). Note que esta última técnica ressalta as
ressonâncias do trato vocal, permitindo uma melhor análise das frequências formantes.
Da figura:
F1 ≈ 0.90 kHz
F2 ≈ 1.76 kHz
F3 ≈ 2.75 kHz
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 52/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Abaixo é apresentado um exemplo de uma consoante. Consoantes podem ser sonoras, quando há vibração
das cordas vocais, e surdas, quando não há vibração das cordas vocais. Abaixo é apresentada uma
consoante surda, como o 'ch' de 'chá'. Note o aspecto de ruidoso desse sinal no tempo e que a
concentração de energia do sinal se encontra em frequências relativamente mais altas, sem existir um claro
padrão de presença de formantes.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 53/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Abaixo é apresentada uma consoante sonora, como o 'z' de 'zê'. Note que existe uma periodicidade no sinal,
devido à vibração das cordas vocais. O espectro tem um comportamento razoavelmente plano quando
comparado aos dois exemplos anteriores.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 54/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Curvas de loudness
A sensibilidade auditiva varia com a frequência do tom. Note a maior sensibilidade para tons em 3.5 kHz
Use a função genlin(), definida anteriormente, para testar sua percepção com alguns tons:
T = 5 # duração em s
fs = 44100
# etc.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 55/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Bandas críticas
O termo banda crítica é usado de diversas formas na literatura. Originalmente foi usado por Fletcher (1940)
com respeito ao mascaramento de um tom por ruído branco. Investigava-se quanto do ruído branco era
responsável para o mascarameto do tom. Em outras palavras, o objetivo era determinar se toda a banda
espectral do ruído contribuía para mascarar o tom ou se há uma certa banda espectral limitada, ou "banda
crítica", do ruído que já bastava para o mascaramento completo. Foi descoberto que apenas uma banda
crítica, centralizada na frequência do tom já era suficiente para o mascaramento. Note, pela tabela a seguir,
que:
1 50 80 13 1850 280
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 56/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
In [52]:
# bandas críticas
fc = [50, 150, 250, 350, 450, 570, 700, 840, 1000, 1170, 1370, 1600, 1850, 2150,\
2500, 2900, 3400, 4000, 4800, 5800, 7000, 8500, 10500, 13500]
bw = [80, 100, 100, 100, 110, 120, 140, 150, 160, 190, 210, 240, 280, 320, 380,\
N = len(fc)
f, ax = plt.subplots(2, 1, figsize=(12,10))
for i in range(N):
fi = fc[i]
di = bw[i]/2
df = bw[i]
ax[0].plot([fi-df, fi-di, fi-di/2, fi+di/2, fi+di, fi+df], [0, .7, 1, 1, .7, 0]);
ax[0].set_xlim([0.1, 18000])
ax[0].set_ylim([0, 1.05])
ax[0].set_xlabel('f, Hz')
ax[0].grid()
ax[1].set_xlim([50, 18000])
ax[1].set_ylim([0, 1.05])
ax[1].set_xlabel('f, Hz')
ax[1].grid()
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 57/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Mascaramento
Um sinal mais fraco é completamente mascarado por um sinal mais intenso com conteúdo similar em
frequência (próximo em termos de banda crítica). Este fenômeno é convenientemente empregado na
codificação mp3 de forma a comprimir a informação pela eliminação de tons perceptuais desnecessários.
T = 5 # duração em s
fs = 44100
Algumas aplicações
Microphone array - filtragem espacial e localização de fontes sonoras
Reconhecimento automático de fala
Autenticação de locutor – biometria
TTS (text-to-speech) - síntese de voz em leitura de textos
Voice morphing - modificação de voz
Codificação - representação mais eficiente para transmissão/armazenam.
Speech enhancement - redução de ruído
Aplicações em música – análise/transformação de música/canto
Estimação de faixa etária/sentimento/stress
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 58/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Exercícios de laboratório
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 59/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
"""
Entradas:
A : amplitude desejada
T : duração, s
Saída:
Se a sua implementação do gerador de tom cossenoidal, como descrito anteriormente, estiver correta e
gerar o tom esperado, o seguinte código gerará a composição via série de Fourier da sequência dente-de-
serra (sawtooth)
T = 2 # duracao, s
plt.plot(t[ind], s[ind])
plt.plot(t[ind], s[ind])
plt.title(msg)
plt.xlabel('t, s');
plt.grid()
plt.show()
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 60/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Introdução de eco
Iremos a seguir criar um novo sinal composto por ecos do sinal original, no caso o sinal de voz do arquivo
seis.wav. A ideia é simplesmente somar repetições do mesmo sinal atrasadas pelos intervalos de eco
desejado. Isso é facilmente feito pela convolução do sinal original por um trem de impulsos, como descrito a
seguir. Reproduza os passos abaixo e escute o sinal resultante. Entenda e varie a receita abaixo para poder
controlar os intervalos de eco, assim como as amplitudes dos sinais ecoados.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 61/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
import winsound
fname = 'seis.wav'
T = 0.6 # echo em s
K = 14 # quantidade de echoes
trem = np.zeros(N*(K+1))
trem[0] = 1
trem[(i+1)*N] = trem[i*N] * A
t = np.arange(len(out))/fsseis
plt.plot(t, out)
plt.grid()
plt.xlabel('t, s')
plt.axis('tight')
plt.show()
# para ouvir:
# fname = 'echoes.wav'
# samples = out.astype(np.short)
# winsound.PlaySound(fname, winsound.SND_FILENAME)
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 62/64
11/12/2021 22:53 Curso de Verão - Processamento do Sinal de Voz
Inversão no tempo
Importe as amostras do arquivo seis.wav e escute-as de trás para frente. Crie um novo arquivo, sies.wav,
com as amostras invertidas no tempo.
sieis = seis[-1::-1]
plt.plot(sieis)
plt.title('seis invertido no tempo')
plt.grid()
plt.show()
# para ouvir:
fname = 'sies.wav'
samples = samples.astype(np.short)
winsound.PlaySound(fname, winsound.SND_FILENAME)
Áudio estéreo
Crie um arquivo estereo.wav de 2 canais (estéreo) no qual em um canal se tem um tom que varia sua
frequência linearmente de 100 Hz a 22 kHz em 5 segundos e no outro canal um tom que varia linearmente
de 22 kHz a 100 Hz em 5 segundos. Use procedimentos análogos aos apresentados anteriormente. Adote
frequência de amostragem de 44100 Hz.
Reproduza o processo de filtragem realizado anteriormente para outras especificações de filtro: teste ordens
e frequências diferentes.
Dado o arquivo de áudio seis.wav, compute a energia, E, e a TCZ, Z , desse sinal em segmentos de 20 ms
a cada 10 ms.
Passos:
Função de autocorrelação
Dica: faça x = y[ind], como no código correspondente, e trabalhe com x para a reprodução do gráfico de
R(n) , após sua implementação.
https://fei.edu.br/~isanches/verao/curso_de_verao_2017.html 64/64