torstai 16. maaliskuuta 2017

Luento 17.3: FFT-algoritmi


Tänään tarkasteltiin Fourier-muunnoksen ominaisuuksia, sovelluksia sekä nopeaa toteutusta.

Luennon aluksi käytiin läpi v. 2016 elokuun tentin tehtävä 2b, jossa laskettiin käsin neljän mittainen Fourier muunnos (ks. myös videon esimerkki). 

Seuraavaksi tutustuttiin Fourier-muunnoksen käyttöön alkeellisessa puheentunnistimessa. Kirjan Elements of statistical learning kappaleen 5.2.3 inspiroimana toteutin Matlab-skriptin, joka löytää S-kirjaimet mikrofonin taltioimasta puheesta. Menetelmä on nimeltään logistinen regressio, joka monimutkaisista kaavoista huolimatta on varsin yksinkertainen toteuttaa: menetelmä etsii kertoimet kullekin Fourier-muunnoksen taajuudelle, ja laskee tulokset yhteen. Tuloksen perusteella saadaan todennäköisyys kummallekin luokalle: "S" ja "ei-S".

Menetelmää demottiin Matlab-toteutuksella, jossa luokittelija opetettiin erottamaan S-äänne muista äänteistä näyttämällä luokittimelle esimerkkejä kahteen luokkaan kuuluvista Fourier-muunnoksista. Menetelmä toimi kohtalaisen hyvin ottaen huomioon opetusaineiston erot testiaineistoon. Matlab-koodi löytyy alta.
 
Esimerkki kuvaa hyvin tämän päivän signaalinkäsittelyalgoritmia: perusmenetelmiä (Fourier-muunnos, konvoluutio, jne.) käytetään piirregeneraattoreina, jotka tuottavat hieman parempaa raakadataa kuin suora mittaussignaali (esim. taajuustietoa eikä raakaa mittausdataa). Laskettujen piirteiden perusteella sitten nostetaan tiedon abstraktiotasoa edelleen. Esimerkiksi äänteen tunnistuksessa hierarkia on esimerkiksi seuraava:

16000 aikatason näytettä -> 128 taajuustason kerrointa -> 1 bitti, joka kertoo kumpi äänne on kyseessä

Tämän jälkeen siirryttiin tarkastelemaan Fourier-muunnoksen ominaisuuksia. Ominaisuuksista tutustuttiin lähemmin siirtoon ajassa (esim. laske signaalin x(n+20) muunnos, kun tiedetään x(n):n muunnos) sekä konvoluution muunnokseen (DFT muuntaa konvoluution kertolaskuksi, eli x(n)*y(n) -> X(n)Y(n)). Aivan luennon lopussa mainittiin yhteys ns. dekonvoluutioon joka on konvoluutiolle käänteinen operaatio. Menetelmää käytetään muun muassa matkaviestinnän siirtotien häiriöiden korjaukseen sekä Hubble-teleskoopin alkuaikoina, jolloin yhdessä peilissä olleen hiontavirheen vuoksikuvat olivat sumuisia. Kuvantamisprosessia voidaan nimittäin mallintaa (kaksilulotteisella) konvoluutiolla

y(n,m) = h(n,m) * x(n,m),

missä x on todellinen näkymä, y on havaittu sumuinen kuva, ja h on linssin impulssivaste (nk. point spread function; PSF). Yhtälössä y ja h ovat tunnettuja, ja tehtävänä on ratkaista x. Ratkaisu löytyy taajuustasossa, koska

Y(n,m) = H(n,mX(n,m),

joten (Matlabin syntaksilla ilmaistuna):

x(n,m) = ifft (Y(n,m) ./ H(n,m)).

Dekonvoluutiosta on hyötyä yleisemminkin lineaarisen kanavan aiheuttaman häiriön poistossa. Jos tiedetään signaalin x kulkeneen kanavan h läpi, voidaan vastaanotetusta mittaustuloksesta y päätellä x, jos meillä on joku käsitys kanavasta h. Esimerkkinä tästä on esim.mainittu  langattoman tiedonsiirtokanavan estimointi ja sen aiheuttaman vääristymän kompensointi.

Toinen menetelmän tuottama etu on että Fourier-muunnoksen (käytännössä FFT:n) avulla voidaan laskea konvoluutio kaavasta (Matlabin syntaksilla ilmaistuna):

conv(x,y) = ifft(fft(x) .* fft(y))

Lisäksi käsiteltiin nopeaa Fourier-muunnosta eli FFT:tä, joka on vain nopeampi tapa toteuttaa diskreetti Fourier-muunnos (DFT). FFT perustuu signaalin jakamiseen lyhyempiin pätkiin, jotka muunnetaan jakamalla ne edelleen rekursiivisesti kahtia. Rekursio päättyy, kun muunnoksen pituus on 1, jolloin muunnosta ei tarvitse enää tehdä. 1-ulotteisen vektorin tapauksessa muunnosmatriisi on yksinkertaisesti F = [1], joka tarkoittaa pelkkää ykkösellä kertomista eikä sitä tarvitse tehdä. Lyhyemmistä vektoreista saadaan koostettua pidemmät vektorit kaavoilla (3.3) ja (3.4).

Alla on vielä luennon esimerkkikoodi S-kirjaimen tunnistuksesta. Funktio käyttää Stanfordin yliopistossa kehitettyä glmnet-pakettia. Jos näiden asennus on haastavaa, Matlabissa on nykyään myös valmis glmfit-funktio.

 

function vokaalin_tunnistus()
%
% Esimerkki vokaalin ja S-kirjaimen erottelusta äänisignaalista.
% heikki.huttunen@tut.fi -- 4.2.2015
%

close all

addpath /home/hehu/Documents/Libraries/glmnet_matlab/

% Ladataan opetusaineisto:

[x, Fs] = audioread('seiska.wav');

[X, H, numFrames] = extractFeatures(x, Fs);
title ('Merkitse S-kirjaimet hiirella');

isConsonant = zeros(numFrames, 1);

while true
    
    [x1, y1] = ginput(1);
    [x2, y2] = ginput(1);
    
    if x1 > x2
        xt = x1;
        x1 = x2;
        x2 = xt;
    end
    
    isConsonant(round(x1 * numFrames) : round(x2 * numFrames)) = 1;
    
    response = questdlg('Jatketaanko annotointia?', ...
        'Kysymys', ...
        'Kyllä', 'Ei', 'Kyllä');
    
    if strcmp(response, 'Ei')
        break
    end
    
end

cvob2 = cvglmnet(X, isConsonant, 'binomial', [], 'class');
yHat = glmnetPredict(cvob2.glmnet_fit, X, cvob2.lambda_min, 'response');

coefficients = H * cvob2.glmnet_fit.beta(:, cvob2.lambda == cvob2.lambda_min);

figure()
subplot(211)
plot(yHat);
ylabel('S-kirjaimen TN')
subplot(212)
stem(coefficients)

response = questdlg('Valmiina tunnistamaan?', ...
        'Tunnistus', ...
        'OK', 'OK');

while true
    
    close all

    myRecObj = audiorecorder(Fs, 16, 1);
    recordblocking(myRecObj, 2);
    y = getaudiodata(myRecObj);

    X = extractFeatures(y, Fs);
    yHat = glmnetPredict(cvob2.glmnet_fit, X, cvob2.lambda_min, 'response');

    figure()
    plot(yHat);

    response = questdlg('Jatketaanko tunnistusta?', ...
        'Kysymys', ...
        'Kyllä', 'Ei', 'Kyllä');
    
    if strcmp(response, 'Ei')
        break
    end
    
end

end

function [F, H, numFrames] = extractFeatures(x, Fs)

    [~,f,t,S] = spectrogram(x, 256, 128, 256, Fs, 'yaxis');
    surf(t, f, 10*log10(abs(S)), 'EdgeColor', 'none');
    axis xy; axis tight; colormap(jet); view(0,90);

    S = log10(S)';
    
    H = [];
    n = (1:size(S, 2))';
    
    for k = 0:3
        H = [H, n.^k];
    end
    
    F = S * H;
    numFrames = size(S, 1);
    
end

Ei kommentteja:

Lähetä kommentti