Subscribe to RSS feed

domingo, enero 24, 2010

Conversor de texto a voz




Mediante la segmentación de difonemas he construido una versión primitiva de un conversor de texto a voz. Un difonema no es más que la transición entre la parte estable de un fonema y otro contiguo, incluidas las partes estacionarias. Mediante la concatenación de estas secuencias de muestras conseguimos construir frases de voz.


La mayor dificultad radica en la correcta segmentación de los difonemas. Además, es un trabajo complicado que requiere de paciencia y de asistencia en tiempo real de un usuario. Yo he conseguido realizar una aproximación más o menos válida mediante la grabación de difonemas con agregación de vocales iniciales y finales. Mediante un programa que hice, generé todos los difonemas posibles y, estos, junto a la anterior agregación de vocales, fueron grabados con una separación de 1 segundo con la ayuda de un metrónomo a una frecuencia de muestreo de 44100Hz, lo que permitió, como se explica en la presentación adjunta, una segmentación programada y asistida por ordenador.


Los códigos de MatLab se pueden ver en la presentación y, a continuación, algunos ejemplos generados con el programa creado.


"Calculadora"

"Esto es una prueba de una frase más larga"

"Explícamelo, por favor"

"Impedancia equivalente"

domingo, enero 10, 2010

Numismática

Siempre que he ido de viaje he procurado quedarme con algunas de las monedas del país como recuerdo. Como resultado he conseguido la siguiente muy pequeña colección de monedas, aparte de con alguna que me pude encontrar. También he catalogado las que tengo repetidas. Si alguien desea intercambiarlas conmigo  (todas o algún grupo de las repetidas) por alguna o algunas de valor similar, que se ponga en contacto aquí.


martes, enero 05, 2010

Salt & pepper

Programé en MatLab una primera versión para la eliminación de ruido tipo salt & pepper en fotografías. Esta primera versión únicamente opera con imágenes en blanco y negro (es decir, sobre matrices simples NxM cuyos valores apuntan a distintas intensidades de gris). Me gustaría poder mejorarlo para usarlo en el tratamiento de fotogramas para la limpieza de este tipo de ruido en vídeos almacenados en VHS una vez digitalizados.

El funcionamiento es bien simple: la función recorre cada uno de los valores de la matriz imagen y, si encuentra un potencial píxel de ruido (de máxima o mínima intensidad de gris (pimienta o sal respectivamente)), evalúa los valores de la matriz en torno a dicho píxel, de modo que, mediante la construcción y estudio de un simple histograma, selecciona el de mayor aparición para sustituir el potencial píxel de ruido.

Aquí podéis ver una selección de imágenes a las que le apliqué el programa que hice. A la izquierda se encuentra la imagen original, mientras que a la derecha la tratada.



Click en la imagen para ampliar

También tomé una imagen sin ningún ruido, le agregué ruido tipo salt & pepper con una densidad de 0.02 mediante la función imnoise de MatLab, finalmente tratando la imagen ruidosa mediante la función programada. La secuencia de imágenes es la siguiente que se muestra.


Click en la imagen para ampliar

El programa se comporta prácticamente a la perfección salvo por, como era de esperar, en los bordes de grandes densidades de blanco o negro puro pertenecientes a la imagen, que quedan algo distorsionados como se puede observar.


% SALT & PEPPER NOISE FILTER 1.0
% Autor: Iván López Espejo
%
% Función encargada de eliminar el ruido tipo sal y pimienta de
% una imagen en blanco y negro. Tras ser llamada se devuelve la
% matrix imagen sin ruido y se guarda el fichero Result.fmt en
% el directorio de trabajo con la imagen limpia de ruido (donde
% fmt se corresponde con el formato de la imagen de salida).
%
% Su forma de uso es:
%
% filImg = sap(image,format)
%
% Donde 'filImg' es la matriz con la imagen tratada, siendo 'image'
% la imagen ruidosa de entrada en blanco y negro (matriz NxM), y donde
% format es un string que indica tanto el formato de entrada de la
% imagen como el de salida (por ejemplo, 'png').

function [filImg] = sap(image,format)

% Dimensiones de la matrix imagen.
longRow = length(image(:,1));
longColumn = length(image(1,:));

% Valores correspondientes con el píxel de ruido.
salt = max(max(image));
pepper = min(min(image));

num = [];
for j = 1:1:longRow
    for k = 1:1:longColumn
        if (image(j,k) == pepper) || (image(j,k) == salt)
            index = 1;
            if j-1 > 0
                num(index) = image(j-1,k);
                index = index + 1;
            end
            if j-1 > 0 && k < longColumn
                num(index) = image(j-1,k+1);
                index = index + 1;
            end
            if k < longColumn
                num(index) = image(j,k+1);
                index = index + 1;
            end
            if j < longRow && k < longColumn
                num(index) = image(j+1,k+1);
                index = index + 1;
            end
            if j < longRow
                num(index) = image(j+1,k);
                index = index + 1;
            end
            if j < longRow && k-1 > 0
                num(index) = image(j+1,k-1);
                index = index + 1;
            end
            if k-1 > 0
                num(index) = image(j,k-1);
                index = index + 1;
            end
            if j-1 > 0 && k-1 > 0
                num(index) = image(j-1,k-1);
            end
            [app,dist] = hist(num,8);
            [maximum,pos] = max(app);
            filImg(j,k) = round(dist(pos));
        else
            filImg(j,k) = image(j,k);
        end
        num = [];
    end
end

% Exportación del resultado.
imwrite(filImg,'Result',format)

sábado, enero 02, 2010

Simulador de eco

He programado en MatLab una primera aproximación a un simulador de eco. Es bastante sencillo y primitivo  pero aproxima las circunstancias de eco y reverberación en el interior de una habitación rectangular. Y decimos de eco y reverberación ya que recordemos que el eco no es más que una reverberación del sonido a partir de que la onda reflejada sufre un retardo superior a 50ms.


Los parámetros de entrada son pocos. No se considera la propagación del sonido en todas las direcciones, como sí ocurre en realidad, sino que se consideran únicamente las cuatro perpendiculares de las paredes y supone que el emisor está orientado perpendicularmente a una de las cuatro. Tampoco se tiene en cuenta la velocidad del sonido en función de la temperatura ambiente, aunque es algo inmediato de parametrizar. Asimismo, tampoco se tiene en cuenta la atenuación que el aire produce en el sonido, sino que únicamente se considera la atenuación de la onda reflejada provocada por la absorción de la pared.

La función ha de ser llamada según la estructura:

echoSim(sonido,coeficiente de absorción,weight,height,x,y,tiempo,frecuencia de muestreo)



El sonido (por ejemplo, voz humana) debe ser mono. El coeficiente de absorción es un parámetro que indica la atenuación de la onda reflejada provocada por la absorción de la pared. Dicho coeficiente se moverá entre 0 y 1, donde 0 indica que la onda se refleja sin atenuación y 1 indica absorción total por parte de la pared. Por su parte, tiempo indica el fragmento de temporal que queremos escuchar de la simulación realizada (dado en segundos). Por último, frecuencia de muestreo será la frecuencia de muestreo del audio pasado en hertzios.

A continuación podéis descargar el resultado de la simulación aplicada a un audio que contiene una risa humana sin ningún tipo de eco. Se consideró un habitáculo vacío de 50x100m, situándose la persona en el centro y riendo en la dirección de mayor longitud. Las paredes, por su parte, poseerían un coeficiente de absorción de 0.99.




% echoSim 0.1
% Autor: Iván López Espejo
%
% Función de MatLab encargada de simular el eco o reverberación
% generado en una habitación rectangular y vacía producido por
% un locutor en una determinada posición de la misma.
%
% La función se usa del siguiente modo:
% echoed = echoSim(voice,coef,weight,height,x,y,time,freq)
%
% En 'echoed' se almacenarán las muestras de voz (o sonido)
% tras la simulación en forma de vector. Como parámetros de la
% función tenemos 'voice', que debe ser el vector de muestras
% de voz del locutor encontrado en la habitación. Por otro lado
% 'coef' se corresponde con el coeficiente de absorción de las
% paredes de la habitación, estando comprendido entre '0' y '1',
% valiendo '0' para indicar que no se produce atenuación y '1'
% para indicar absorción total. 'weight' es la anchura de la
% habitación en metros, mientras que 'height' es la longitud de
% la habitación también dada en metros. 'x' es la distancia en
% metros entre el locutor situado en la habitación y la pared
% situada perpendicular a la dirección de anchura. 'y' es la
% distancia en metros entre el locutor situado en la habitación
% y la pared situada perpendicular a la dirección de la longitud.
% El programa supone siempre que el locutor se encuentra de cara
% a la pared con la que mantiene una distancia de 'y' metros.
% El parámetro 'time' se corresponde con el tiempo, en segundos,
% que durará la simulación, es decir, el tiempo durante el cual
% escucharemos voz con eco. Por último, 'freq' es la frecuencia
% de muestreo del audio de entrada dada en hertzios.
%
% Restricciones obvias:
% x < weight
% y < height
% time > 0
% 0 < coef < 1

function [echoed] = echoSim(voice,coef,weight,height,x,y,time,freq)

if (x >= weight) || (y >= height) || (time <= 0) || (coef < 0) || (coef > 1)

    disp('Parámetros de entrada incorrectos. Consulte la ayuda para más información: help echoSim')
    echoed = voice;

else
   
    % Velocidad de propagación del sonido en el aire en m/s.
    vprop = 340;
   
    % Longitud del vector resultado.
    echoedLength = ceil(freq * time);
       
    % Inicializamos el vector resultado.
    echoed = zeros(1,echoedLength);
    for k = 1:1:length(voice)
            echoed(k) = voice(k);
    end
   
    % Pared 1.
    gain = sqrt(1 - coef);
    dist = 0;
    checker = 1;
    while 1 == 1
        if checker > 0
            dist = dist + 2 * y;
        else
            dist = dist + 2 * (height - y);
        end
        desp = ceil(dist * freq / vprop);
        if desp > echoedLength - 1
            break;
        end
        rebound = zeros(1,echoedLength);
        for k = desp+1:1:echoedLength
            if k - desp < length(voice) + 1
                rebound(k) = gain * voice(k-desp);
            end
        end
        echoed = echoed + rebound;
        checker = checker * (-1);
        gain = gain * sqrt(1 - coef);
    end

   
    % Pared 2.
    gain = sqrt(0.2) * sqrt(1 - coef);
    dist = 0;
    checker = 1;
    while 1 == 1
        if checker > 0
            dist = dist + 2 * (height - y);
        else
            dist = dist + 2 * y;
        end
        desp = ceil(dist * freq / vprop);
        if desp > echoedLength - 1
            break;
        end
        rebound = zeros(1,echoedLength);
        for k = desp+1:1:echoedLength
            if k - desp < length(voice) + 1
                rebound(k) = gain * voice(k-desp);
            end
        end
        echoed = echoed + rebound;
        checker = checker * (-1);
        gain = gain * sqrt(1 - coef);
    end

   
    % Pared 3.
    gain = sqrt(0.5) * sqrt(1 - coef);
    dist = 0;
    checker = 1;
    while 1 == 1
        if checker > 0
            dist = dist + 2 * x;
        else
            dist = dist + 2 * (weight - x);
        end
        desp = ceil(dist * freq / vprop);
        if desp > echoedLength - 1
            break;
        end
        rebound = zeros(1,echoedLength);
        for k = desp+1:1:echoedLength
            if k - desp < length(voice) + 1
                rebound(k) = gain * voice(k-desp);
            end
        end
        echoed = echoed + rebound;
        checker = checker * (-1);
        gain = gain * sqrt(1 - coef);
    end

   
    % Pared 4.
    gain = sqrt(0.5) * sqrt(1 - coef);
    dist = 0;
    checker = 1;
    while 1 == 1
        if checker > 0
            dist = dist + 2 * (weight - x);
        else
            dist = dist + 2 * x;
        end
        desp = ceil(dist * freq / vprop);
        if desp > echoedLength - 1
            break;
        end
        rebound = zeros(1,echoedLength);
        for k = desp+1:1:echoedLength
            if k - desp < length(voice) + 1
                rebound(k) = gain * voice(k-desp);
            end
        end
        echoed = echoed + rebound;
        checker = checker * (-1);
        gain = gain * sqrt(1 - coef);
    end

    % Reproducción del resultado final.
    soundsc(echoed,freq)

end

jueves, diciembre 24, 2009

Averiguar clave WEP

Aunque es un tema machacado y requetemachacado, pienso que existe una proliferación del copy-paste del siguiente método por Internet por gente que ni siquiera lo ha probado, por ello aporto el tema desde mi experiencia personal con todos los matices con los que me topé por el camino.

En este tutorial mostraremos cómo averiguar una clave WEP de una red inalámbrica que use este tipo de cifrado. Lo único que necesitamos es un ordenador con tarjeta de red con alguna interfaz wireless y la distribución de Linux Back Track 3, especial para auditorías de seguridad. Podemos descargar dicha distribución en la web:



Una vez hayamos grabado la imagen descargada en un CD, preparamos el ordenador para que arranque desde CD reiniciándolo con el disco dentro del lector (si es necesario, entraremos en la BIOS del sistema presionando la tecla Supr nada más encender el PC o, si es un Mac, directamente dejaremos pulsada la tecla C con el disco dentro para que arranque desde él).

Una vez iniciado Linux Back Track (si nos pide login y password teclearemos root y toor respectivamente) seguiremos los siguientes pasos:


1.       1. Abrimos una ventana de consola y escribimos el siguiente comando a fin de averiguar el nombre de la interfaz wireless de nuestro adaptador:

iwconfig

Dicho nombre de interfaz se corresponderá con aquel de la izquierda tal que presente información sobre el interfaz y no la frase no wireless extensions. Información relevante para nuestro propósito será la correspondiente con Mode y con Access Point. Supongamos que nuestro interfaz se llama, de ahora en adelante, eth1. Cualquier operación que realicemos sobre el mismo se traducirá en la agregación del nombre al final de cada comando, por lo que resulta imprescindible.


2.       2. Ocultaremos nuestra MAC con el siguiente comando. Podemos sustituir la MAC del ejemplo, 11:22:33:44:55:66, por cualquier otra si queremos. Y, como vemos, al final de la sentencia habremos de indicar sobre qué interfaz del adaptador estamos operando para la realización del cambio de MAC.

macchanger –m 11:22:33:44:55:66 eth1


3.       3. Activamos la tarjeta en modo monitor con el siguiente comando:

airmon-ng start eth1


4.       4. Comprobamos que se han realizado correctamente los cambios:

iwconfig

En la nueva configuración debe figurar Mode: Monitor y Access Point: 11:22:33:44:55:66. Las tarjetas de Broadcom suelen dar problemas, figurando Access Point: Invalid. No obstante, por mi experiencia personal, si eso es así, podemos continuar con el siguiente paso sin preocuparnos por ello.



5.       5. Con el siguiente comando comprobamos si existen redes inalámbricas en el entorno cuyas claves WEP podamos desencriptar:

airodump-ng eth1

A continuación se irá desplegando un listado con las redes alcanzables disponibles. Debemos fijarnos en las redes cuya encriptación y cifrado figure como WEP y, a ser posible, que no se requiera de autenticación o esta figure como abierta (OPN). Estas son las redes potenciales de las que podremos averiguar, lógicamente, su clave WEP. Seleccionaremos nuestras redes de interés anotando los siguientes datos:
-          ESSID (Nombre de la red)
-          BSSID (Dirección MAC del punto de acceso de la red)
-          Channel (Canal de transmisión de la red)
Una vez seleccionadas nuestras redes potenciales, presionaremos Ctrl+C para finalizar la búsqueda.


6.       6. Con nuestra tarjeta ya configurada en modo monitor, captaremos paquetes de una red determinada (de la red de la que queremos averiguar la clave WEP) mediante el siguiente comando:

airodump-ng –d BSSID --channel Canal --write Pass eth1

Donde BSSID lo habremos de sustituir por la dirección MAC del punto de acceso de la red de la que queremos obtener su clave WEP, Canal se corresponderá con el número anotado previamente de canal de transmisión de la red en cuestión y donde Pass es el nombre del fichero donde queremos que se almacenen los datos procedentes de la captura de paquetes de dicha red. Sería suficiente con indicar el canal pero, hay casos en los que por el mismo canal captamos más de una red, por lo que si queremos particularizar el ataque sobre una en concreto indicamos también su BSSID. No obstante sería correcto capturar datos de paquetes de las distintas redes confluyentes en un mismo canal pero no lo recomiendo. Mi experiencia fue que de la red de mi interés apenas se recibían datos y sí de otra. En principio, no habría problema, pues espero a tener suficientes paquetes de la red de interés. No obstante, al ser un SO live, la memoria está muy limitada y puede que paquetes de otras redes que no son de nuestro interés la saturen enseguida tirando todo el trabajo a la basura, por lo que es recomendable, por una mayor eficiencia, usar el anterior comando así.


7.       7. Abrimos una nueva ventana de consola. Con el programa aireplay procederemos con la reinyección. Esto fomenta el incremento de recepción de datos procedentes de la red atacada al, por ejemplo, intercambiar paquetes ARP o los propios generados por un proceso de autenticación. No es imprescindible, pero puede agilizar el proceso. Recomiendo consultar la ayuda del programa y ver todas las opciones, esto es, con el comando man aireplay-ng. Aquí van un par de propuestas:

aireplay-ng -1 Interv –e ESSID –a BSSID –h Nuestra MAC eth1

Donde Interv es un número entero mayor o igual a cero que especifica en segundos el intervalo esperado para la repetición del proceso, ESSID es el nombre de la red atacada, BSSID la MAC del punto de acceso a la red, siendo Nuestra MAC la dirección explícita en nuestro macchanger (11:22:33:44:55:66 en este caso). O también, por ejemplo:

aireplay-ng -3 –e ESSID –a BSSID –h Nuestra MAC –x P/s eth1

Donde la opción -3 indica en este caso que se trata de la realización de peticiones ARP, siendo ESSID el nombre de la red atacada, BSSID la MAC del punto de acceso a la red, siendo Nuestra MAC la direción explícita en nuestro macchanger y donde P/s indica los paquetes ARP generados por segundo.


8.       8. Una vez observemos en la primera ventana de consola de todo el proceso que poseemos de la red atacada, al menos, en torno a 50000 paquetes de datos (vectores de inicialización o IV’s), lanzaremos en la segunda ventana de consola el siguiente comando:

aircrack-ng Pass-01.cap

Este último programa se encarga de intentar desencriptar de los vectores de inicialización la clave WEP. Si todo va con éxito, aparecerá Key Found! y la clave. Si no, volverá a reintentar la desencriptación mediante la relectura del fichero una vez se haya incrementado el número de vectores de inicialización observados. Notar que Pass se corresponde con el nombre del fichero que indicamos en el paso 6.

lunes, diciembre 21, 2009

Generador de ruido blanco para JAVA

El siguiente código en JAVA implementa una pequeña aplicación para generar una cantidad de muestras de ruido blanco gaussiano, donde nosotros podremos controlar tanto la media como la varianza del mismo (aparte del número de muestras que deseamos generar).


/**
 *  GENERADOR DE RUIDO ALEATORIO 1.1
 */

import java.util.*;
import java.io.*;
import java.lang.*;


/**
 *
 * @author Iván López Espejo.
 */
public class Aleatorio {

    private static double media, desviacionTipica, muestra;
    private static int numMuestras;

    private static double valorGaussiano(Random generador) {
        double w = 0, v1 = 0, v2 = 0;
        do {
            v1 = 2 * (generador.nextDouble()) - 1;
            v2 = 2 * (generador.nextDouble()) - 1;
            w = v1 * v1 + v2 * v2;
        } while (w > 1);
        double y = Math.sqrt(-2 * Math.log(w) / w);
        return v1 * y;
    }


    public static void main(String args[]){

        media = Double.parseDouble(args[0]);
        desviacionTipica = Double.parseDouble(args[1]);
        numMuestras = Integer.parseInt(args[2]);

        String fSalida = "datos.html";
        File salida = new File(fSalida);

        Random generador = new Random();

        if (salida.exists()){
            try{
                BufferedWriter out = new BufferedWriter(new FileWriter(fSalida));
                out.write("|html||head||/head||body|GENERADOR DE RUIDO ALEATORIO|br||br|");
                for(int k = 0; k < numMuestras; k++){
                    muestra = media + desviacionTipica*(valorGaussiano(generador));
                    out.write(muestra + "|br|");
                    System.out.println(muestra);
                }
                out.write("|/body||/html|");
                out.close();
            } catch(IOException e){
                System.out.println("Error");
            }
        }
    }
}



Tras ser compilado es necesario ser interpretado. Desde nuestra consola de comandos introduciremos, estando en la carpeta donde estén los ficheros .java y .class, lo siguiente:

java Aleatorio media desviaciónTípica númeroDeMuestras

Donde media es un valor de tipo double (con decimales separados por punto) que contiene la media del ruido blanco, desviaciónTípica almacena la desviación típica del ruido, siendo también de tipo double y donde, finalmente, númeroDeMuestras es un número natural que se corresponde con el número de muestras que se desea generar.

Previo a ello es importante crear el fichero datos.html y situarlo en la misma carpeta que los anteriores archivos, de modo que en dicho fichero se almacenarán las muestras de ruido blanco generado como se observa en la siguiente figura:




Por último podemos guardar las muestras en un fichero de texto y comprobar mediante, por ejemplo, MatLab que efectivamente se ha generado lo deseado.

Nota IMPORTANTE: es preciso sustituir en el código el símbolo "|" por "<" o ">" según convenga abrir o cerrar el tag.

martes, diciembre 15, 2009

Filtro de Wiener

La siguiente función implementada en MatLab se encarga de calcular los coeficientes de un filtro FIR según el método de Wiener. Una utilidad puede ser la cancelación de ruido donde, teniendo acceso a la fuente de ruido, podemos conseguir el filtrado de la señal contaminada, donde es imprescindible que el ruido de la señal contaminada esté lo suficientemente correlado con el ruido de la fuente a la que tenemos acceso. Usando, tal y como se explica en la ayuda de la función, el siguiente código, podremos obtener un filtro de orden 'p' con el que realizar una estimación del ruido aditivo de la señal información, restando finalmente este a la señal contaminada.

% FILTRO DE WIENER
% Autor: Iván López Espejo
%
% Calcula y devuelve los coeficientes de un filtro FIR por el método de
% Wiener según la siguiente estructura:
%
% w = fWiener(x1,x2,p,N)
%
% Donde en 'w' se almacenan los coeficientes del filtro FIR de Wiener. 'x1'
% se corresponde con, en el caso de cancelación de ruido, la señal
% contaminada y, 'x2' con el ruido procedente de la fuente de ruido. Es
% necesario que 'x1' y 'x2' se encuentren lo más correlados posible. El
% orden del filtro deseado se pasa a través del parámetro 'p' y, 'N'
% indica finalmente el número de muestras de las que queremos hacer uso de
% las señales 'x1' y 'x2' para la obtención del filtro (para funcionamiento
% óptimo, N = length(x1)).

function [w] = fWiener(x1,x2,p,N)

% Consideramos siempre muestras reales, por lo que no tendremos en cuenta
% el conjugado de la matriz de autocorrelación.
% Cálculo de la autocorrelación de 'x1' y de la correlación de 'x2' con
% 'x1'.
x1 = x1(1:N);
x2 = x2(1:N);
autocorrx1 = xcorr(x1,'biased');
autocorrx1x2 = xcorr(x2,x1,'biased');
% Cálculo de la matriz de Wiener y del vector de términos independientes.
vWiener = autocorrx1(ceil(length(autocorrx1)/2):ceil(length(autocorrx1)/2)+p-1);
mWiener = toeplitz(vWiener);
vWiener = autocorrx1x2(ceil(length(autocorrx1x2)/2):ceil(length(autocorrx1x2)/2)+p-1);
vWiener = vWiener';
% Obtención de los parámetros del filtro de Wiener.
w = inv(mWiener)*vWiener;