El proyecto consiste en hacer una red neuronal que sea capaz de reconocer al locutor, y así poder validar que entre al sistema o no. Es decir con solo escuchar la voz de una persona es posible decir si es la persona indicada o no.
Mi contribución se divide en 2 partes, primero continuar con el tratamiento del audio y mejorar los cálculos ya hechos, para poder alimentar la neurona, que consistió en codificar en python formulas matemáticas y trabajo de investigación, segundo la realización de la interfaz para que el usuario pueda acceder al sistema.
Repositorio
Otro link
Entradas de datos:
Se terminó con el tratamiento de la señal que se había dado ya en la primera parte, estos fueron los pasos definitivos y correctos que seguí y que utilizamos para la entrada de la red neuronal.
La Investigación y Codificación del tratamiento de las señales, para poder dar las entradas a la red neuronal.
Extracción de Características
Es el proceso en el que se extrae la información de la señal para poder representar al locutor mediante un modelo propio.
Para representar la señal de la voz se usará el coeficiente MFCC (Mel Frequency Cepstrum Coefficients).
Los MFCC se pueden calcular trabajando en el dominio de la frecuencia.
Tramado y Enventanada: Estos dos pasos tienen como meta disminuir la distorsión, la función de wavfile.read nos da una lista de la señal ya aplicado el tramado y enventanado.
FFT: Se hace la transformada rápida de Fourier, que convierte cada trama de N muestras del dominio temporal al dominio de la frecuencia. El resultado de este paso
se conoce como el espectro de la señal.
Transformación a escala Mel: La percepción humana de las frecuencias no sigue una escala lineal. Por esto se debe hacer un escalado de las frecuencias en Hz a una escala subjetiva conocida como la escala Mel. Se da mayor importancia a la información contenida en las bajas frecuencias en consonancia con el comportamiento del oído humano.
Cepstrum: Se convierte el espectro Log Mel al tiempo. Para esto primero se calcula el logaritmo de SK. Luego como los coeficientes Mel (y su logaritmo) son números reales, se los puede convertir al dominio temporal usando la Transformada Discreta del Coseno (DCT). Este resultado es el que llamamos MFCC.
Código:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
import matplotlib.pyplot as plt | |
from scipy.io import wavfile | |
import numpy as np | |
import math | |
import scipy.fftpack | |
from pylab import * | |
GRAFICAS = False | |
def ampl_tiempo(a): | |
"""amplitud contra el tiempo | |
""" | |
datos = a[1] | |
amplitud = [] | |
t = np.arange(len(datos)) | |
for i in range(len(datos)): | |
amplitud.append(a[1][i]) | |
plt.plot(t, amplitud) | |
plt.title('Analisis de audio',size='large', color='r') | |
plt.xlabel('tiempo', size='large') | |
plt.ylabel('amplitud (dB)', size='large') | |
if GRAFICAS: plt.show() | |
#print len(amplitud) | |
return amplitud | |
def frec(amplitud): | |
datos_frecuencia = np.fft.fft(amplitud) | |
#print len(datos_frecuencia) | |
t = np.arange(len(amplitud)) | |
frecuencia = [] | |
for i in range(len(datos_frecuencia)): | |
frecuencia.append(abs(datos_frecuencia[i])) | |
plt.plot(frecuencia, 'r') | |
plt.title('Fast Fourier Transform',size='large', color='r') | |
plt.xlabel('Tiempo', size='large') | |
plt.ylabel('Amplitud (dB)', size='large') | |
if GRAFICAS: plt.show() | |
plt.plot(amplitud,frecuencia, 'r') | |
plt.title('Analisis de audio',size='large', color='r') | |
plt.xlabel('Frecuencia (Hz)', size='large') | |
plt.ylabel('Amplitud (dB)', size='large') | |
if GRAFICAS: plt.show() | |
return frecuencia | |
def spect(amplitud): | |
Pxx, freqs, bins, im= plt.specgram(amplitud) | |
plt.colorbar(im).set_label(u'Intensidad (dB)') | |
plt.title('Espectrograma',size='large', color='r') | |
plt.xlabel('tiempo', size='large') | |
plt.ylabel('Frecuencia (Hz)', size='large') | |
if GRAFICAS: plt.show() | |
def mel(frecuencia, amplitud): | |
frecuencia.sort() | |
mel_datos = [] | |
mel_datos_tot = [] | |
n = 20 | |
total_frec = len(frecuencia) | |
intervalo = total_frec/n | |
frec_40 = [] | |
for i in range(n): | |
frec_40.append(frecuencia[i*intervalo]) | |
#print frec_40 | |
#print len(frec_40) | |
t = np.arange(len(frecuencia)) | |
for i in frec_40: | |
mel_esc = 2595*(math.log10(1+(i/700))) | |
mel_datos.append(mel_esc) | |
#mel_datos.sort() | |
for i in frecuencia: | |
mel_esc = 2595*(math.log10(1+(i/700))) | |
mel_datos_tot.append(mel_esc) | |
#mel_datos_tot.sort() | |
###### | |
plt.plot(frec_40, mel_datos) | |
plt.title('Escala Mel',size='large', color='r') | |
plt.xlabel('Frecuencia (Hz)', size='large') | |
plt.ylabel('mels', size='large') | |
if GRAFICAS: plt.show() | |
###### | |
c = [] | |
s = True | |
for i in range(len(frec_40)): | |
if s == True: | |
c.append(1) | |
s = False | |
else: | |
c.append(0) | |
s = True | |
l = [] | |
f = True | |
for i in range(len(frecuencia)): | |
if f == True: | |
l.append(1) | |
f = False | |
else: | |
l.append(0) | |
f = True | |
##### | |
#print 'mel: ', mel_datos | |
plt.plot(mel_datos,c) | |
plt.title('Escala Mel',size='large', color='r') | |
plt.xlabel('Frecuencia (Hz)', size='large') | |
plt.ylabel('mels', size='large') | |
#plt.show() | |
##### | |
return mel_datos |
La otra parte de mi contribución es la Ventana que verá el usuario cuando entrene la red, se realizó una interfaz sencilla que muestra en la parte principal un texto para que el usuario pueda leerlo, en la parte siguiente un input text para que escriba su nombre de usuario, después botones para iniciar la grabación y otro botón para salir del sistema. Una parte de esto la hizo también mi compañero Roberto Martínez.
También integré el código de la interfaz con el de tratamiento de los datos, para que al grabar el usuario su voz automáticamente se vaya al tratamiento y se pueda meter el dato a la red neuronal y así saber si si es la persona o no lo es. Se importaron las funciones del archivo audio.py que es el que nos ayuda a sacar los datos importantes del audio.
Código:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
#!/usr/bin/python | |
#Redes neuronales | |
#7mo Semestre de ITS UANL | |
#Cecy Urbina, Ramon Gzz, Roberto Mtz | |
#Interfaz grafica para la entrada de audio, donde el usuario leera un texto | |
#mientras se graba en un archivo de audio. | |
from Tkinter import * | |
import random | |
import pyaudio | |
import wave | |
import sys | |
import time | |
import audio | |
import numpy as np | |
from scipy.io import wavfile | |
class Grabadora: | |
'''Clase que graba el audio de la lectura del texto, | |
con tres funciones, iniciar, terminar y grabar | |
''' | |
def __init__(self): | |
self.chunk = 1024 | |
self.format = pyaudio.paInt16 | |
self.channels = 1 | |
self.rate = 44100 | |
self.record_seconds = 5 | |
self.p = pyaudio.PyAudio() | |
self.audio = [] | |
self.stream = self.p.open(format = self.format, | |
channels = self.channels, | |
rate = self.rate, | |
input = True, | |
frames_per_buffer = self.chunk) | |
def iniciar(self): | |
'''Se prepara el archivo para comenzar a grabar y | |
se graba los segundos indicados | |
''' | |
print "* Empezando a grabar . . ." | |
self.all = [] | |
for i in range(0, self.rate / self.chunk * self.record_seconds): | |
data = self.stream.read(self.chunk) | |
self.all.append(data) | |
def terminar(self, nombre): | |
'''Se prepara el archivo final y se guarda | |
en el formato wav con el nombre de temporal | |
''' | |
print u'* Terminando' | |
self.stream.close() | |
self.p.terminate() | |
# escribo los datos en el wav | |
data = ''.join(self.all) | |
WAVE_OUTPUT_FILENAME = ""+nombre+".wav" | |
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb') | |
wf.setnchannels(self.channels) | |
wf.setsampwidth(self.p.get_sample_size(self.format)) | |
wf.setframerate(self.rate) | |
wf.writeframes(data) | |
wf.close() | |
def grabar(self, nombre): | |
'''llama a las funciones para iniciar y | |
terminar la grabacion | |
''' | |
self.iniciar() | |
self.terminar(nombre) | |
class Interfaz: | |
'''Interfaz grafica que muestra el texto que se va a leer | |
con el boton para comenzar a grabar y para cancelar | |
''' | |
def __init__(self, master): | |
self.master = master | |
self.frame = Frame(self.master) | |
self.frame.pack() | |
self.name = "" | |
#texto que aparecera para leer | |
self.texto = Text(self.frame) | |
self.texto.insert(INSERT, self.leerArchivo()) | |
self.texto.configure(state='disabled', padx=15, pady=15, height=10) | |
self.texto.grid(row=0, column=0, columnspan=2, sticky=W+E+N+S, padx=15, pady=15) | |
#para introducir el nombre del usuario | |
self.e = Entry(self.frame) | |
self.e.grid(row=1, column=0, columnspan=2, sticky=W+E+N+S,padx=300, pady=15) | |
#boton que comenzara la grabacion | |
self.hi_there = Button(self.frame, text="COMENZAR", fg="blue", command=self.grabar) | |
self.hi_there.grid(row=2,padx=15, pady=15) | |
#boton para terminar la grabacion | |
self.button = Button(self.frame, text="CANCELAR", fg="red", command=self.frame.quit) | |
#self.button = Button(self.frame, text="CANCELAR", fg="red", command=self.error) | |
self.button.grid(row=2,column=1, padx=15, pady=15) | |
def grabar(self): | |
'''graba el archivo wav de audio | |
''' | |
time.sleep(1) | |
grabadora = Grabadora() | |
self.name = self.e.get() | |
print self.name | |
grabadora.grabar(self.name) | |
datos = self.obtener_datos() | |
###escribe los datos en un archivo dependiendo del usuario | |
f = open("datos_entradas", "w") | |
for i in range(len(datos)): | |
f.write("%f, " % datos[i]) | |
if self.name == 'cecy': | |
f.write("0, 0") | |
elif self.name == "roberto": | |
f.write("0, 1") | |
elif self.name == "ramon": | |
f.write("1, 0") | |
else: | |
f.write("1, 1") | |
f.close() | |
time.sleep(2) | |
self.exito() | |
def error(self): | |
'''Muestra pantalla de error | |
''' | |
x = self.frame.winfo_width() | |
y = self.frame.winfo_height() | |
self.frame.destroy() | |
self.frame = Frame(self.master, width=x, height=y) | |
self.canvas = Canvas(self.frame, width=x, height=y, bg="white") | |
self.canvas.create_text(390, 55, text="No identificado", fill="black", font=("Helvectica","15")) | |
self.canvas.create_text(300, 25, text="Intentalo otra vez!", fill="black", font=("Helvectica","40")) | |
self.canvas.create_text(125, 200, text=":-(", fill="grey", font=("Helvectica", "200")) | |
self.frame.pack() | |
self.canvas.pack(fill=BOTH) | |
def exito(self): | |
'''Muestra pantalla de exito | |
''' | |
x = self.frame.winfo_width() | |
y = self.frame.winfo_height() | |
self.frame.destroy() | |
self.frame = Frame(self.master, width=x, height=y) | |
self.canvas = Canvas(self.frame, width=x, height=y, bg="white") | |
self.canvas.create_text(390, 55,text="Identificado exitosamente", fill="black", font=("Helvectica","15")) | |
self.canvas.create_text(400, 25,text="Bienvenido!", fill="black", font=("Helvectica","40")) | |
self.canvas.create_text(125, 200, text=":-)", fill="grey", font=("Helvectica", "200")) | |
self.frame.pack() | |
self.canvas.pack(fill=BOTH) | |
#time.sleep(2) | |
#self.error() | |
def leerArchivo(self): | |
'''lee una archivo de los disponibles | |
''' | |
eleccion = random.randint(1, 7) | |
archivo_elegido = "lecturas/"+str(eleccion)+".txt" | |
print archivo_elegido | |
f = open(archivo_elegido, "r") | |
completo = f.read() | |
return completo | |
def obtener_datos(self): | |
'''manda llamar las funciones del archivo | |
audio.py, que analiza | |
''' | |
a=wavfile.read(""+self.name+".wav") | |
m = np.fft.fft(a[1]) | |
t = np.arange(110081) | |
amplitud = audio.ampl_tiempo(a) | |
frecuencia = audio.frec(amplitud) | |
audio.spect(amplitud) | |
datos = audio.mel(frecuencia, amplitud) | |
return datos | |
def main(): | |
root = Tk() | |
App = Interfaz(root) | |
root.title("Lectura de texto para entrenamiento de la red neuronal") | |
root.mainloop() | |
if __name__ == "__main__": | |
main() |
Esta vez mis contribuciones corresponden a los commits: [35, 36, 41, 46, 47]
Y los commits anteriores fueron: [12, 13, 26, 27, 28, 29, 30, 31, 32]
Repositorio:
Gráficas del tratamiento de datos:
Aquí también todo lindo; 10 + 20.
ResponderEliminar