<< Inapoi

Cuprins

Inainte >>

4.2.3 Implementarea modulului de operatii.

Într-unul din capitolele precedente vorbean de modularitatea structurii aplicatiei WaveIO ceea ce o face usor de extins din partea unui utilizator ce doreste implementarea propriilor operatii în sistem. Acest lucru este posibil si datorita modului în care o operatie ajunge din interfata grafica pana în interiorul aplicatiei acolo unde operatia este aplicata propriu-zis.

În anterioarele doua capitole am vazut cum acest lucru este implementat în clasele corespunzatoare frame-urilor si coeficientilor.

Totusi în cazul operatiilor asupra semnalului vocal, cu alte cuvinte asupra obiectelor CFrameArray si CFrame s-a impus intercalarea unui nivel superior clasei CFrameArray, un fel; de interfata între aceasta clasa si interfata grafica cu utilizatorul sistemului. Aceasta clasa este reprezentata de COperations . Majoritatea metodelor acestei clase sunt corespondente metodelor de operatii de prelucrare din clasa CFrameArray. Totusi exista si prelucrari aspura semnalului care sunt rezolvate în cadrul acestei clase nefind necesara trasmiterea mai departe. Exemple de astfel de operatii sunt cele de filtrare si eliminare DC.

Facilitatea oferita de introducerea acestei clase este si posibilitatea efectuarii unor operatii intermediare înainte de aplicarea efectiva a uneltelor, evitând transmiterea inutila a acestor operatii în interiorul clasei CFrameArray. O mai usoara întelegere a mecanismului de propagare a operatiei de la interfata utilizator pâna la nivelul clasei CFrame va fi posibila cred dupa studierea urmatoarei digrame:

Fig. 4.6 Diagrama de secventa corespunzatoare unei operatii

4.2.4 Implementarea uneletelor auxiliare

În acelasi spirit al modularitatii aplicatiei au fost create o serie de clase ce au rolul de a simplifica operatiile de prelucrare semnalului vocal implementate în clasele CFrame si CFrameArray . În timp ce unele din ele reprezinta operatii ce se aplica asupra semnalului audio, celalalte reprezinta pur si simplu niste clase ce îsi propun simplificarea modului de lucru cu mediul VC++. Din prima categorie fac parte clasele CFilters si CWindows iar din a doua, CMatrix si CStrOper .

Fig. 4.7 Diagrame claselor reprezentând uneletele auxiliare ale sistemului WaveIO

Clasa CFilters asa dupa cum îi spune si numele impolementeaza niste filtre ce se vor aplica asupra semnalului sonor. Filtrele implementate sunt niste filtre trece sus, filtre de preaccentuare, rolul acestora fiind de a reduce componentele de frecventa înalta la nivelul original. Desi m-am limitat la aceste doua filtre, deoarece acest tip de filtre este utilizat în prelucrarea semnalului sonor în vederea extragerii coeficientilor LPC sau a reprezentarii spectrului de frecvente, adaugarea unui nou filtru este o operatiune usor de realizat. Sablonul unei metode apartinând clasei CFilters specifica existenta în mod obligatoriu printre parametrii metodei a valorii esantionului filtrat si a coeficientilor filtrului.

În continuare ma voi opri cu prezentarea asupra descrierii clasei CWindows , care într-u câtva este asemanatoare ca structura si functionalitate cu clasa CFilters . Dupa cum aratam în partea teoretica a lucrarii, pentru a estompa esantioanele de la marginea cadrelor, si deci pentru obtinerea unor rezultate valabile, asupra fiecarui cadru se aplica o fereastra. Exista mai multe forme de ferestre ce se aplica în practica la prelucrarea semnalului vocal, dintre acestrea cele mai importante fiind: Hamming, Hanning, Blackman si Barlett. Toate aceste ferestre sunt implementate în clasa CWindows . Fiecare din cele patru metode ce implementeaza câte una din ferestre va primi ca parametru numarul esantionului curent din cadru, precum si dimensiunea cadrului, returnând un rezultat de tip float, care înmultit cu valoarea curenta a esantionului curent, va da o noua valoare reprezentând valoarea esantionului dupa ferestruire.

Majoritatea algoritmilor pentru determinarea coeficientilor LPC si cepstrali precum si pentru compararea a doua seturi de coeficienti implica un volum mare de lucru cu matrici. Aceasta a fost motivatia de baza pentru construirea clasei CMatrix , clasa ce va implementa toate operatiile cu matrici necesare în cadrul aplicatiei.

Ca atribute clasa CMatrix are doua valori unsigned ce memoreaza numarul de linii si coloane precum si un atribut pointer la un pointer la double unde vor fi memorate valorile matricii. Dintre operatiile cu matrici implementatet în cadrul clasei voi aminti: calcularea determinantului, calcularea inversei, precum si a a operatorilor: “=”, “+”, “-“, “*”.

Cealalta clasa auxiliara este clasa CStrOper clasa ce lucreaza cu vectori de caractere. De fapt aceasta clasa este o clasa de operatii asupra acestor vectori. Dintre operatiile implementate amintesc:

•  SubStr – extrage un segment precizat dintr-un vector sursa.

•  MatchStr – verifica aparitia unui vector în alt vector

•  InStr – returneaza pozitia la care un vector apare în celalalt.

Motivatia implementarii acestor metode a fost necesitatea utilizarii lor în momentul transmiterii parametrilor prelucrarii vocale de la interfata grafica utilizator spre nucleul aplicatiei. Mai multe despre acest aspect, în Implementarea interfetei video.

O alta unealta auxiliara poate fi considerata si Debugger-ul acestei aplicatii al carui scop este de a trasa si urmarii mersul aplicatiei. Aceasta unelta este contruita pe baza clasei CWaveLogger . Caracteristica acestei unelte este ca face posibila scrierea diferitor mesaje din cadrul aplicatiei, atât într-o fereastra de dialog asociata, cât si într-un fisier pe disc. Metoda care va crea un astfel de obiect este Create , prin intermediul acestei metode stabilindu-se dimensiunile si pozitia pe ecran a dialogului, precum si fisierul de pe disc în care are loc scrierea.

Caracteristic acestei unelte este existanta unui nivel de logare , nivel în functie de care un mesaj va fi scris în log sau nu. Setarea acestui nivel se face prin apelul functiei SetLogPriority . Logarea unui mesaj se face doar în cazul în care nivelul de logare alocat acestuia este mai mare decât nivelul de logare al obiectului instanta CWaveLogger. Functia de logare a unui mesaj este LogMessage , parametrii acestei metode fiind textul si nivelul de logare asociat mesajului.

4.2.5 Implementarea uneltelor comparative

Dupa operatiunea de determinare a coeficientilor, fie ca sunnt LPC sau cepstrali, sau de determinare a spectrului, rezultatele astfel obtinute sunt trimise modulului de comparatie. Acest modul va sti compara oricare doua seturi din tipul celor amintite mai sus, cu conditia sa apartina aceluiasi tip. Tranferul datelor obtinute în modulul de prelucrare spre modulul de comparare se face prin intermediul unor fisiere cu format special. Asadar întâi este necesara salvarea datelor în aceste fisiere pentru ca mai apoi în modulul de comparare doua fisiere de acelasi tip sa fie deschise si printr-o metoda sau alta sa fie comparate datele continute.

Dupa cum aratam în capitolul 2, Impelmentarea lucrului cu diferite tipuri de coeficienti , modulul de comparatie este implementat în cadrul claselor CCoefs si CCoefsArray .

În principiu toti algoritmii de comparare, fie ca este vorba de seturi de coeficienti, fie ca este vorba de spectre, au la baza calcularea distantei între doi vectori de valori.

Pentru calcularea distantei între doi vectori se pot aplica mai multe metode, cea aleasa în prezenta lucrare fiind cea implementata de urmatorul pseudo-cod:

# define Mx 10000

g[ 0 ] [ 0 ]= 0 ;

for (j=l; j<=J; j++) g[ 0 ][j]=Mx

for (I= 1 ; i<=I; i++)

{

g[i][ 0 ]=Mx

for (j= 1 ; j < J; j++)

{

d= dist (i~j);

g[i][j]=min(g[i- 1 ][j]+d, g[i- 1 ][j- 1 ]+d+d, g[i][j- 1 ]+d);

}

}

D=g[I][J] / I+J

Structura generica a unei astfel de opera­tii de diferenta în clasa CCoefsArray, folosind algoritmul anterior e implementata sub urmatoarea forma:

return_type Operation_Distance(CCoefsArray &ccaSrc1, CCoefsArray &ccaSrc2)

{

g[ 0 ][ 0 ] = 0 ;

for (i = 1 ;i <= ccaSrc2.uNoElements; i++)

g[ 0 ][i] = MAX_FOR_DISTANCE;

for (i = 1 ; i <= ccaSrc1.uNoElements; i++ )

{

g[i][ 0 ] = MAX_FOR_DISTANCE;

for ( unsigned j = 1 ; j <= ccaSrc2.uNoElements; j++)

{

d = Operation_Distance 1(ccaSrc1.ccElements[i - 1 ], ccaSrc2.ccElements[j - 1 ]);

g[i][j] = Minimum3_( g[i- 1 ][j]+d,g[i- 1 ][j- 1 ]+d+d,g[i][j- 1 ] + d);

}

}

D = g[ccaSrc1.uNoElements][ccaSrc2.uNoElements] /(ccaSrc1.uNoElements + ccaSrc2.uNoElements);

return D;

}

unde Operation_Distance1 reprezinta metoda analoaga a Operation_Distance în clasa CCoefs.

Impartind metodele clasei CFramesArray dupa tipul de coeficienti între care calculeaza distantele acestea sunt:

•  Metode pentru calcularea distantei între coeficienti LPC.

Aratam în capitolul teoretic, ca în cazul coeficientilor LPC distanta între doua seturi se poate masura prin doua metode. Itakura si Itakura-Saito. Cele doua distante sunt calculate în cadrul metodelor: ItakuraDistArr si ItakuraSaitoDistArr din CCoefsArray si respectiv omoloagele lor ItakuraDistr si ItakuraSaitoDist din CCoefs .

Daca ar fi sa înlocuim aceste metode în algoritmul anterior prezentat atunci ItakuraDistArr si ItakuraSaitoDistArr ar fi analogul lui Operation_Distance iar ItakuraDistr si ItakuraSaitoDist al lui Operation_Distance1. Algoritmii folositi pentru calcularea celor doua distante sunt cei prezentati în partea teoretica. Pentru calcularea distantei Itakura-Saito m-am folosit de clasa CMatrix dat fiind faptul ca metoda este bazata aproape integral pe operatii cu matrici.

•  Metode pentru calcularea distantei între coeficienti cepstrali.

Metodele pentru calcularea acestei distante sunt: CepsDistArr din CCoefsArray , corespunzând lui Operation_Distance din algoritmul generic si respectiv CepsDist din CCoefs , corespunzând lui Operation_Distance1. Pentru calcularea distantei între doi coeficienti s-a folosit distanta euclidiana prezentata în capitolul teoretic.

Cele doua tipuri de distante prezentate anterior sunt folosite pentru recunoasterea cuvintelor.

În ce priveste calcularea distantei între doua spectre de frecvente, rezultatul va fi folosit în identificarea vorbitorului.

•  Calcularea distantei între doua spectre de frecventa

Spectrul de frecventa este determinat folosind functia FFT din utils.h, care implementeaza algoritmul butterfly.

Exista trei algoritmi pentru calcularea distantei între doua spectre si respectiv pentru identificarea vorbitorului.

Prima varianta implementata în metoda FreqDistArr , este de a calcula distanta între cele doua spectre în mod asemanator cu distanta între doi vectori de valori. Desi în urma experimentelor metoda nu ofera o foarte buna peformanta în recunoasterea vorbitorului ea este utila pentru stabilirea asemnanarii între doua spectre.

Marele dezavantaj însa apare atunci când vorbirea nu a fost bine localizata. În acest caz “frecventele parazite” care s-au “infiltrat” în spectru vor influenta decisiv rezultatul operatiei de comparare, procentajul de recunoastere scazând semnificativ incât algoritmul devine chiar nefunctional.

Prezentul algoritm are cele mai bune rezultate în cazul în care semnalul este lipsit de paraziti, localizarea vorbirii este una reusita iar toti vorbitori vor rosti acelasi sablon vocal.

Cea de-a doua varianta de determinare a vorbitorului se bazeaza pe un aspect teoretic amintit în aceasta lucrare si anume ca vorbirea are la baza intrarea în rezonanta a corzilor vocal, mai departe forma finala a vorbirii fiind rezultatul influentei tractului vocal. De aici s-a ajuns la ideea determinarii frecventei fundamentale ce se presupune a fi caracteristca fiecarui vorbitor.

Astfel în cadrul metodei MainFreqDistArr din CCoefsArray primul pas este determinarea frecventei fundamentale, adica cea de rezonanta a corzilor vocale. Pentru aceasta trebuie returnat de fapt indexul primului maxim local din vectorul de frecvente. Acesta sarcina este rezolvata de functia GetMaxIdx din utils.h.

Trebuie amintit aici ca programul WaveIO îsi propune rezolvarea problemei recunoasterii vorbitorului folosind semnale sonore la o rezolutie scazuta de sample-uri pe secunda, pe un singur canal si toate astea într-un interval de timp scurt.

În aceste constrângeri apare problema în care pentru anumite persoane identificarea vorbitorului este imposibila deoarece pozita frecventei fundamentale este aceeasi. Astfel, chiar daca în anumite cazuri stabilirea vorbitorului se face cu succes, într-un inteval foarte scurt, si mai ales cu o rezistenta mare la semnale parazite, apare problema ca pentru anumite persoane algorimul sa nu fie deloc utilizabil, stabilindu-se o relatie de identitate între vorbitori.

Punctul pozitiv al acestui algoritm este indiferenta acestuia fata de cuvintele rostite de fiecare vorbitor în parte, deoarece rezulateul comparii nu este influentat de faptul ca un vorbitor rosteste un cuvânt iar celalat altul.

Pentru a ilustra problema acestui algoritm în care determinarea frecventei fundamentale nu permite identificarea vorbitorului voi prezenta spectrele de frecventa pentru doua persoane ambele pronuntând cunvântul “duminica”.

Vorbitor 1.

Vorbitor 2

Fig. 4.8 Spectrul de frecvente pentru doua pronuntari ale cuvântului “duminica” de catre doi vorbitori.

Se observa ca desi spectrele sunt total diferite, frecventa fundamentala este determinata a fi aceeasi.

Dupa cum se poate observa ambele metode prezentate mai sus au punctele lor pozitive si negative. Astfel am încercat punerea la punct a unui algoritm de identificare a vorbitorului care sa reuneasca partile pozitive ale celor doi algoritmi. Acest algoritm este implementat în cadrul metodei NFreqDistArr . Suportul teoretic al metodei este prezentat în continuare.

Ideea algoritmului mi-a venit în urma observarii existentei unui anumit sablon în spectrul de frecvente obtinut folosind algoritmul impelementat în cadrul aplicatiei. Fie în continuare spectrele a doua rostiri a cuvântelor “duminica” si “luni” pentru acelasi vorbitor.

“luni”

“duminica”

Fig. 4.9 Spectrul de frecvente pentru pronuntari ale cuvintelor “luni” si “duminica” apartinând aceleasi persoane.

În ambele spectre se observa existenta unui anumit sablon ce se repeta în cadrul spectrului.Acest sablon vai fi mai vizibil în cazul unor spectre si mai putin vizibil în cazul altora. Acest aspect are la baza faptul ca vorbirea este produsa de fapt de o serie de multiplii ai frecventei fundamentale fiecare multiplu avand amplitudune si faza determinata de forma tractului vocal.

Prin urmare daca prin compararea frecventei fundamentale nu se poate face o determinare exacta vorbitorului, de ce sa nu implicam si formantii în acest proces. Reamintesc ca formantii sunt de fapt maximele locale din spectrul de frecvente. Dar am aratat anterior ca deja acesti formanti implica si tractul vocal fiind rezultat al actiunii acestuia asupra frecventei fundamentale. Experienta a dovedit ca implicarea unui numar prea mare de formanti face ca recunoasterea sa scada ca performanta.

Pentru determinarea formantilor se foloseste functia GetMaxNLocalized din utlis.h, functie ca va determina valoarea si indexul a N maxime locale dintr-un vector. Antetul functiei este:

unsigned GetMaxNLocalized( double *dSource, unsigned uSize, double *dDestination, unsigned *uIndexes, unsigned uNoOfElements);

unde dSource este vectorul sursa, în cazul nostru vectorul ce contine spectrul, uSize, dimensiunea vectorului, dDestionation vectorul destinatie pentru valorile maximelor locale, uIndexes – vectorul destinatie ce va contine indicii maximelor locale – acesta fiind vectorul ce ne intereseaza si pe noi, si uNoOfElements care reprezinta numarul de maxime locale de determinat.

Pseudocodul pentru determinarea maximelor locale este urmatorul:

while (uCrtNoOfElements < uNoOfElements)

{

for (i = 0 ; i < uSize; i++)

{

if (vuIndexes nu contine valoarea i) &&

(vuEliminatedIndexes nu contine valoarea i)

if (dMax < vdSource[i])

{

dMax = vdSource[i];

uMaxIdx = i;

}

}

elimina valorile de pe curba ascendenta si descendenta a mazimului local si adauga valorile indicilor la vecortul vuEliminatedIndexes

vuIndexes[uCrtNoOfElements] = uMaxIdx;

vdDestination[uCrtNoOfElements++] = dMax;

}

O valoare pentru numarul de maxime locale ce intra în procesul de comparare, ce asigura rezultate bune la recunoasterea vorbitorului este de 4.

Totusi implicarea formatilor în procesul de recunoastere duce la problema în care performanta scade daca vorbitorii vor rosti cuvinte diferite în procesul de recunoastere. Si aceasta datorita faptului ca prin implicarea formantilor, pentru acelasi utilizator rostirea a doua cuvinte diferite, daca acestea sunt produsul a unor “configuratii” diferite a filtrului reprezentat de tractul vocal, duce la scaderea probabilitatii identificarii celor doua cuvinte ca apartinând aceluiasi vorbitor prin modificarea pozitiei formantilor.

Astfel performantele cele mai bune ale sistemului sunt obtinute daca vorbitorii vor rosti acela­si “sablon” fie ca este cuvânt sau propozitie sau ca cel putin vor rosti aceleasi semnale sonore – vocale.

Astfel diferenta între spectrul pentru “Azi e duminica” si “Ana are mere” folosind acest din urma algoritm este egala cu 0, ceea ce înseamna identificare perfecta.

Un algoritm cu rezultate foarte bune ar putea fi cel în care mergându-se de la general spre particular sa integreze ultimele 2 metode prezentate anterior în ordinea: compararea frecventei fundamentale - compararea a N maxime locale; la fiecare pas eliminându-se cele mai “îndepartate” variante analizate.

<< Inapoi

Cuprins

Inainte >>