|
|
|
|
|
|
|
|
|
|
|
|
1- كتابخانة تشخيص صحبت – گويندة جيالانگ هي[1]
كتابخانة مزبور كه از
طريق مرجع شمارة 1 قابل دستيابي است به زبان C++
نوشته شده و شامل حدود 20 كلاس است كه الگوريتمهاي رايج استخراج خصيصهها و
تكنيكهاي رايج براي تشخيص صحبت و تأييد هويت گوينده را پيادهسازي مينمايد. محتويات اين كتابخانه
امكان پردازش صحبت به صورت بدون سرباره[2]
و با سرباره در فرمتي خاص را به كاربر ميدهد و امكانات مختلفي را براي كار با
فايلهاي حاصل كه حاصل اعمال يك الگوريتم به صورت يك الگو و يا يك مدل بر دادههاي
ورودي است فراهم ميآورد. در بحث مدلسازي سيگنال
استخراج خصيصهها خصيصههاي مبتني بر الگوريتم فورية سريع با اندازة مل[3]،
خصيصههاي به دست آمده از روش پيشگويانة خطي[4]، خصايص پوياي مبتني (دلتاها كه در فصل ششم
توضيح داده شد) را به همراه زيري و بمي[5]
صدا را با استفاده از اين كتابخانه به دست آورد. روشهاي متداول مدلسازي
صحبت شامل چندين روش مبتني بر مدل نهان ماركف[6]،
مدل مقدارگزيني برداري[7]،
مدل گاوسي[8] و مدلسازي
به شيوة چشمپوشي زماني پويا[9]
نيز در اين كتابخانه وجود دارند. اين موارد در كنار
امكانات اولية پيادهسازي شده در اين كتابخانه شامل تبديل سريع فوريه و عكس آن،
تبديل كسينوسي گسستة فوريه، تحليل پيشگويانة خطي، امكانات طراحي فيلتر، توابع
پنجرهاي (هنينگ، همينگ و ...) و چندين تابع ابتدايي ديگر اين كتابخانه را يك
نقطة آغاز مناسب براي طراحي برنامههاي كامپيوتري تشخيص صحبت و يا گوينده نموده
است. با وجود آن كه اين
كتابخانه به صورت رايگان قابل دستيابي است كد آن در دسترس قرار ندارد و اين نكته
باعث بروز مشكلاتي در تطبيق آن با انتخابهاي برنامهنويسي كاربر ميگردد. در ضمن
با وجود يك سري مستندات ناقص و ارجاعهاي متعدد منابع مرتبط با پردازش كامپيوتري
صحبت به اين كتابخانه مسألة درك چگونگي كاركرد و استفادة درست از اين كتابخانه
به طور كامل نيازمند پيشزمينة قوي در زمينة پردازش صحبت است و مستندات آن براي
درك روش عملكرد آن كافي نيست. در هر صورت ما از اين
كتابخانه براي مدلسازي سيگنال به شيوة استخراج خصيصههاي مبتني بر الگوريتم
فورية سريع با اندازة مل از اين كتابخانه بهره جستهايم و براي مقايسة الگوهاي
به دست آمده نيز از شيوة چشمپوشي زماني پوياي پيادهسازي شده در اين كتابخانه
استفاده كردهايم كه كارايي سيستم وابسته به متن به دست آمده ميتواند گوياي
تواناييهاي اين كتابخانه باشد. 2-
پايگاه دادههاي عبارتهاي عبور[10]- شماي كلي
ما براي پردازش
عبارتهاي عبور ايجاد شده از يك ساختار پايگاه دادهها استفاده ميكنيم كه در آن
الگوهاي به دست آمده از عبارتهاي عبور به همراه نام كاربران را ذخيره مينماييم.
براي سادهتر شدن نحوة پردازش عبارتهاي مختلف براي يك كاربر آنها را به صورت
تكراري با نام يكسان ذخيره ميكنيم و كار طبقهبندي آنها تحت نام يك كاربر را در
هنگام بازيابي آنها انجام ميدهيم. براي نيل به هدف ياد
شده ما كلاسي به نام HSpeaker براي نمايش
هر عبارت گفته شده توسط كاربر خاص تعريف كردهايم و وظيفة پردازش دستهاي از
اشياء از نوع اين كلاس را به كلاسي به نام HSpeakersDB واگذار نمودهايم كه البته اين كلاس از يك كلاس و دو ساختار مياني
براي پردازش اعمال مورد نظر استفاده مينمايد. برنامة نهايي از كلاس HSpeakersDB براي پردازش نهايي استفاده ميكند هر چند در بعضي موقعيتها
ارجاعهاي مستقيمي نيز به كلاس HSpeaker صورت
پذيرفته است. بخشهاي بعدي ساختار
دروني كلاسهاي ياد شده را توضيح ميدهد. 3-
عبارت عبور و پردازشهاي مربوط به آن
هر عبارت عبور به همراه
نام گويندة آن توسط شيئي از نوع كلاس HSpeaker قابل نمايش است: class HSpeaker { public: HSpeaker(); HSpeaker(SV_Model_DTW* pModel); ~HSpeaker(); BOOL LoadFrom(CFile* pFile); BOOL SaveTo(CFile* pFile); void SetUserName(CString strName); CString GetUserName(); void SetPassphrase(int nSamples, short*
pSamples); double Verify(int nSamples, short*
pSamples); double Verify(HSpeaker* pSpeaker); void PrepareFeature(SV_Feature_MFCC
&Feature); void PrepareFeature(SV_Feature_Pitch
&Feature); void PrepareFeature(SV_Feature_LPCC
&Feature); private: CString m_strName; SV_Data* m_pPassphrase; SV_Model_DTW* m_pModel; }; كلاس SV_Data يكي از كلاسهاي كتابخانة SVLib[11] است كه نمايشگر يك
ماتريس ميباشد. نتيجة پردازشها و الگوهاي به دست آمده از آنها در نهايت به دادههايي
از اين نوع تبديل ميشوند. عضو دادة m_pPassphrase الگوي به دست آمده يا ذخيره شده را كه اعمال مقايسهاي روي آن صورت
ميپذيرد نشان ميدهد. علت تعريف اين عضو به صورت اشارهگر آن است كه مقدار
خروجي متدهاي استخراج خصيصه اشارهگر به اين كلاس است و در ضمن در خود اين متدها
فضاي حافظة لازم به دادة مورد نظر تخصيص داده ميشود. كلاس SV_Model_DTW عمل مقايسة دو الگو را كه به صورت اشيائي از نوع SV_Data نمايش داده ميشوند انجام ميدهد و عضو دادة مورد نظر براي چنين
امري در نظر گرفته شده است. علت تعريف اين عضو داده به صورت اشارهگر و اختصاص
فضاي حافظه بدون آزاد كردن آن مشكلي بود كه در استفاده از اين عضو داده در محيط
برنامهنويسي غير متني به واسطة خطاي مراجعه به فضاي حافظة غير مجاز به وجود ميآمد
كه به نظر ميرسد ناشي از تلاش براي آزاد كردن حافظهاي كه قبلاً آزاد شده در
متد ويرانگر اين كلاس ميباشد و به لحاظ عدم وجود كد كتابخانه به اين صورت رفع
شده است[12]. عضو دادة m_strName نام گويندة عبارت را ذخيره ميكند كه توسط متدهاي SetUserName و GetUserName اعمال
مقدارگذاري و دريافت مقدار آن صورت ميپذيرد: void
HSpeaker::SetUserName(CString strName) { m_strName=strName; } CString
HSpeaker::GetUserName() { return m_strName; } كلاس سازنده داراي
دوشكل متفاوت است. علت آن است كه در عمل مقايسه لازم نيست هر دو كلاس داراي عضو m_pModel باشند و تخصيص حافظه به هر دو نوعي هدر دادن حافظه
است لذا اگر براي يك شيء براي اين عضو داده حافظه اختصاص داده شود ديگري ميتواند
از حافظة تخصيص داده شدة ديگري استفده نمايد: HSpeaker::HSpeaker() { m_pModel=new SV_Model_DTW; m_pPassphrase=NULL; m_strName=""; } HSpeaker::HSpeaker(SV_Model_DTW*
pModel) { m_pModel=pModel; m_pPassphrase=NULL; m_strName=""; } در كلاس ويرانگر اگر
حافظهاي به m_pPassphrase اختصاص داده شده باشد آزاد ميگردد: HSpeaker::~HSpeaker() { if(m_pPassphrase) delete m_pPassphrase; } قبل از هر گونه عمل
مقايسه بايستي عبارت عبور الگويابي شود اين عمل با دريافت دادههاي صوتي كه به
صورت يك آرايه از نوع short دريافت ميشود و تبديل آن به الگوي
مورد نظر در متد SetPassphrase صورت ميپذيرد: void
HSpeaker::SetPassphrase(
int nSamples, short* pSamples ) { SV_Feature_MFCC Feature ; PrepareFeature(Feature); Feature.CopySignal(pSamples, nSamples); m_pPassphrase=Feature.ExtractFeature(); } كلاس SV_Feature_MFCC نيز مربوط به كتابخانة SVLib است و
پردازشهاي لازم براي استخراج الگوهاي MFCC از سيگنال
صوت در آن قرار داده شده است. قبل از فراخواني متدهاي آن ميبايست انتخابهاي
لازم انجام شود و اين عمل با استفاده از متد PrepareFeature صورت ميپذيرد: void
HSpeaker::PrepareFeature(SV_Feature_MFCC &Feature) { Feature.Para.MFCC_Order = 12; Feature.Para.NFilter = 60; Feature.Para.FFTSz = 512; Feature.Para.DEnergy = 1;
Feature.Para.WinSz = 512; Feature.Para.StpSz = 256; Feature.Para.Alpha = 0.97; Feature.Para.HammingWin = 1; Feature.Para.RmvSilence = 1; } در اين متد انتخابهاي
مربوط به نوع پنجرهبندي (همينگ يا پنجرهبندي مستطيلي)، اندازة پنجره، اندازة
بازة fft ، انتخاب اين كه آيا انرژي سيگنال هم به الگو ضميمه شود يا نه،
انتخاب اين كه آيا سكوت از سيگنال حذف شود يا نه و... با مقدارگذاري اعضاي دادة
عمومي كلاس SV_Feature انجام ميپذيرد. پس از مقدارگذاري موارد
ياد شده متد CopySignal براي ورود سيگنال به شيء استخراج كنندة الگو فراخواني ميشود.
از آنجا كه اين متد و ساير متدهاي مشابه كلاس SV_Featue_MFCC (وكلاسهاي مشابه براي استخراج خصيصهها از روشهاي ديگر) يك
آرايه از نوع short را به عنوان سيگنال ورودي ميپذيرند همچنان كه
در فصل چهارم اشاره شد اعضاي دادة كلاسهاي پردازش صوت را از اين گونه انتخاب نموديم.
لذا در اين مرحله نيازي به هيچ گونه تغيير نداريم. بعد از انجام تمامي
مقدار گذاريها متد ExtractFeature الگوهاي
خواسته شده را استخراج نموده حاصل را به صورت اشارهگر بازميگرداند. همچنان كه
قبلاً اشاره شد اختصاص حافظه به صورت دروني صورت ميپذيرد و نيازي به انجام اين
كار پيش از فراخواني اين متد وجود ندارد. بعد از استخراج الگوها
يا فراخواني يك متد ساده از كلاس SV_Model_DTW ميتوان عمل
مقايسه را انجام داد و حاصل را كه يك عدد اعشاري نشان دهندة ميزان فاصلة دو الگو
از يك ديگر است به محل فراخواني بازگرداند: double
HSpeaker::Verify(HSpeaker* pSpeaker) { return m_pModel->DTW_Comp( *m_pPassphrase,
*pSpeaker->m_pPassphrase ); } شكل ديگر اين متد براي
انعطافپذيري بيشتر نوشته شده است: double
HSpeaker::Verify(int nSamples, short* pSamples) { HSpeaker Temp(m_pModel); Temp.SetPassphrase(nSamples, pSamples); return Verify(&Temp); } براي ذخيرة شيء از نوع HSpeaker ابتدا نام كاربر ذخيره ميگردد و سپس از يك كلاس ديگر به نام SV_DataIO از كتابخانة SVLib براي ذخيرة
موقت الگو در يك فايل و به دست آوردن اندازة آن بر حسب بايت و سپس ذخيرة اين
اندازه و الگوي به دست آمده در فايل مقصد استفاده ميگردد و در نهايت اين فايل
موقتي نيز حذف ميگردد: BOOL
HSpeaker::SaveTo(CFile* pFile) { BYTE
bNameLength=(BYTE)m_strName.GetLength(); pFile->Write(&bNameLength,sizeof(BYTE)); for(int bc=0;bc<bNameLength;bc++) { char c=m_strName[bc]; pFile->Write(&c,sizeof(char)); } SV_DataIO Temp; Temp.OpenFile("temp.sv",WRITE_REC); Temp.PutDataRec(*m_pPassphrase); Temp.CloseFile(); CFile TempFile("temp.sv",
CFile::modeRead); BYTE byte; int iSize=0; while(TempFile.Read( &byte, sizeof(byte))==sizeof(byte) ) iSize++; TempFile.Close(); pFile->Write(&iSize, sizeof(int)); TempFile.Open("temp.sv",
CFile::modeRead); int i=0; while(i<iSize) { TempFile.Read(&byte, sizeof(byte)); pFile->Write(&byte,
sizeof(byte)); i++; } TempFile.Close(); DeleteFile("temp.sv"); return TRUE; } براي بازيابي نيز ابتدا نام كاربر،
سپس اندازة الگو و در نهايت خود الگو با استفاده از يك فايل موقتي بازيابي ميگردد: BOOL
HSpeaker::LoadFrom(CFile* pFile) { BYTE bNameLength; if(pFile->Read( &bNameLength, sizeof(BYTE) ) != sizeof(BYTE)) return FALSE; char* name=new char[bNameLength+1]; pFile->Read(name,bNameLength*sizeof(char)); name[bNameLength]='\0'; m_strName=name; delete []name; int iSize; pFile->Read(&iSize, sizeof(int)); Cfile TempFile( "temp.sv", CFile::modeWrite|CFile::modeCreate ); BYTE byte; for(int i=0; i<iSize; i++) { pFile->Read(&byte,sizeof(byte)); TempFile.Write(&byte,
sizeof(byte)); } TempFile.Close(); SV_DataIO Temp; Temp.OpenFile("temp.sv",
READ_REC); m_pPassphrase=Temp.GetDataRec(); Temp.CloseFile(); DeleteFile("temp.sv"); return TRUE; } براي فايلي كه در آن
ذخيره با بازيابي صورت ميگيرد اعمال باز كردن و بستن انجام نميشود زيرا نحوة
استفاده از اين متدها معمولاً شامل چندين فراخواني متوالي است كه يك بار بازكردن
فايل و در نهايت يك بار بستن آن به ازاي كلية فراخوانيها كافي است. 4-
چند عبارت عبور براي يك كاربر و پردازشهاي مربوط به
آن
هر كاربر ميتواند
چندين كلمة عبور داشته باشد، براي پردازش اين حالت ساختار زير يك ليست پيوندي از
كلمة عبورهاي يك كاربر تشكيل ميدهد: struct SpeakerPasses { SpeakerPasses(){pNext=NULL;} HSpeaker*
pSpeaker; SpeakerPasses*
pNext; double
Verify(HSpeaker* pSpeaker); }; متد Verify متد هم نام خود را متعلق به عضو دادة pSpeaker
فراخواني ميكند و مقدار بازگشتي آن را ميگرداند و با فراخواني مستقيم اين متد
چندان تفاوتي ندارد. با استفاده از ساختار
فوق و با استفاده از زنجيرهاي از دادههاي از اين نوع ميتوان ليست عبارات عبور
مربوط به يك كاربر را نگهداري نمود. ايجاد زنجيرهاي از
كاربران با استفاده از ساختار زير صورت ميگيرد: struct SpeakersPasses { CString
strName; SpeakersPasses(){pNext=NULL;} SpeakerPasses*
pFirst; SpeakersPasses*
pNext; double
Identify(HSpeaker* pSpeaker); double
Find(HSpeaker* pSpeaker,int &Index); }; هر عضو اين زنجيره
داراي نامي منحصر به فرد است كه در عضو دادة strName
نگهداري ميشود. يك اشارهگر به آغاز زنجيرة عبارات عبور كه با pFirst نشان داده ميشود و يك اشارهگر به عضو بعدي در ليست كاربران كه
با pNext نشان داده ميشود اين ساختار را تشكيل ميدهند. متدهاي اين ساختار
روشهايي براي مقايسة اجزاي داخلي فراهم ميآورد. متد Identify
به سادگي مينيمم فاصلة اجزاي ليست با يك عبارت داده شده را به دست ميدهد: double
SpeakersPasses::Identify(HSpeaker* pSpeaker) { double
fMin=10; double
d; int
i=0; for( SpeakerPasses* p=pFirst; p!=NULL; p=p->pNext,i++ ) if((d=p->Verify(pSpeaker))<fMin) fMin=d; return
fMin; } اين متد امكان مقايسة
كلية عبارات عبور تعريف شده به نام يك كاربر را با يك عبارت عبور فراهم ميآورد. در بخشهايي از كار ما
ميدانيم كه يك عبارت متعلق به يك كاربر است و نياز به اين داريم كه بدانيم
دقيقاُ كدام عبارت متعلق به كاربر است. مثلاً زماني كه بخواهيم يك عبارت پذيرفته
شده از يك كاربر را از ليست عبارتهاي مورد نظر او حذف كنيم به چنين امكاني نياز
داريم. متد زير چنين امكاني را فراهم ميآورد: double SpeakersPasses::Find(
HSpeaker* pSpeaker, int &Index
) { double
fMin=10; double
d; int
i=0; for( SpeakerPasses* p=pFirst; p!=NULL; p=p->pNext,i++ ) if((d=p->Verify(pSpeaker))<fMin) { Index=i; fMin=d; } return
fMin; } كد متد مزبور كاملاُ شبيه
به متد Identify است و تنها يك پردازش اضافي در آن انجام ميشود. اگر به مقدار بازگشتي
متدهايي كه به نحوي با تأييد هويت يا بازشناسي هويت يك كاربر ربط دارند توجه شود
مشخص ميگردد كه همواره يك مقدار اعشاري بازگردانده ميشود و هيچگاه يك نتيجة
منطقي (كه يكسان هستند با نه) بر نميگردد. اين به دليل آن است كه برنامه قابليت
آن را دارد كه طبق خواستة كاربر آستانة مورد استفاده براي يكسان بودن عبارات
عبور را تغيير ميدهد (كه ممكن است باعث كاهش درصد خطاي برنامه و در نتيجة
افزايش امنيت يا كاهش ميزان سختگيري برتامه و در نتيجه كاهش تعداد تلاشهاي لازم
براي گرفتن جواب شود). از اين رو عمل مقايسه با آستانه در كلاسهاي كنترل كنندة
تشخيص كاربر انجام ميگيرد. 5-
ايجاد يك جدول از كاربران
در
مرحلة بعد نياز به آن داريم كه كلية پردازشهاي اشاره شده براي كاربران منفرد را در
كنار هم قرار دهيم و به مجموعة كاربران به عنوان يك كل نگاه كنيم. به اين منظور كلاسي به
نام HSpeakersTable با ساختار زير طراحي شد: class HSpeakersTable { public: HSpeakersTable(){m_pFirst=NULL;} void
Insert(HSpeaker* pSpeaker); HSpeaker*
Get(int iMain, int iSub=0); int
GetSubsNum(int iMain); int
GetIndex(CString str); int
GetNum(); void
RemoveUser(int nIndex); BOOL
RemPass( int nIndex, HSpeaker* pSpeaker, double fThreshold ); int
Identify(HSpeaker* pSpeaker, double fThreshold); private: SpeakersPasses*
m_pFirst; }; تنها عضو دادة اين كلاس
اشارهگر به آغاز ليست كاربران است كه در حالتي كه ليست خالي است مقدار آن برابر
با NULL خواهد بود. متدهاي بعدي اعمال كار با
ليست كاربران را پيادهسازي مينمايند. عملي كه متد Insert
انجام ميدهد شامل مقايسة نام كاربران با نام وارد شده براي عبارت مورد نظر است.
در صورتي كه ليست تهي باشد مقايسهاي انجام نميشود و تنها نود ابتدايي ايجاد ميگردد: void HSpeakersTable::Insert(HSpeaker*
pSpeaker) { if(m_pFirst==NULL) { m_pFirst=new SpeakersPasses; m_pFirst->strName=pSpeaker->GetUserName(); m_pFirst->pFirst=new SpeakerPasses; m_pFirst->pFirst->pSpeaker=pSpeaker; return; } else { int index; if( (index=GetIndex(pSpeaker->GetUserName())) != -1 ) { int i=0; for( SpeakersPasses* p=m_pFirst; i<index; p=p->pNext,i++ ); for( SpeakerPasses* q=p->pFirst; q->pNext!=NULL; q=q->pNext ); q->pNext=new
SpeakerPasses; q->pNext->pSpeaker=pSpeaker; } else { for( SpeakersPasses* p=m_pFirst; p->pNext!=NULL; p=p->pNext); p->pNext=new SpeakersPasses; p->pNext->strName=pSpeaker->GetUserName(); p->pNext->pFirst=new SpeakerPasses; p->pNext->pFirst->pSpeaker=pSpeaker; } } } متد GetIndex كه در متد قبلي فراخواني شده است يك عمل
مقايسة ساده را انجام ميدهد و در صورت يافتن نام داده شده در ليست نام كاربران
انديس كاربر مورد نظر را برميگرداند و در غير اين صورت مقدار -1 در خروجي
اين تابع نشانگر آن است كه كاربر مورد نظر پيدا نشده و بايد يك نود جديد ايجاد
گردد: int
HSpeakersTable::GetIndex(CString str) { int index=0; for( SpeakersPasses* p=m_pFirst; p!=NULL; p=p->pNext,index++ ) { if(str==p->strName) return
index; } return -1; } تعداد كاربران را متد
زير با يك سري پردازش ساده روي ليست به دست ميآيد (متد GetNum)
. اگر لازم داشته باشيم تعداد عبارات عبور يك كاربر را بدانيم از متد GetSubsNum كمك ميگيريم و اگر بخواهيم با يك عبارت به طور مستقيم كار كنيم
از متد Get كمك ميگيريم.كد اين متدها
كه يك سري پردازش ساده روي ليست انجام ميدهند در ادامه آمده است. int HSpeakersTable::GetNum() { if(m_pFirst==NULL) return
0; int
Num=1; for( SpeakersPasses* p=m_pFirst; p->pNext!=NULL; p=p->pNext,Num++ ); return
Num; } int
HSpeakersTable::GetSubsNum(int iMain) { if(m_pFirst==NULL) return
0; int
index=0; for( SpeakersPasses* p=m_pFirst; index<iMain; p=p->pNext,index++ ); index=1; for( SpeakerPasses* q=p->pFirst; q->pNext!=NULL; q=q->pNext,index++ ); return
index; } HSpeaker* HSpeakersTable::Get(int
iMain, int iSub) { int
index=0; for( SpeakersPasses* p=m_pFirst; index<iMain; p=p->pNext,index++ ); index=0; for( SpeakerPasses* q=p->pFirst; index<iSub; q=q->pNext,index++ ); return
q->pSpeaker; } عمل حذف يك كاربر كه انديس آن را
در آراية ليست كاربران ميدانيم نيز يك عمل سادة پردازش ليست است: void HSpeakersTable::RemoveUser(int
nIndex) { if(nIndex==0) { SpeakersPasses
*p=m_pFirst; m_pFirst=m_pFirst->pNext; SpeakerPasses*
q=p->pFirst; for( SpeakerPasses* r=q->pNext; r!=NULL; r=r->pNext ) { delete
q; q=r; } delete
p; return; } int
i=1; for( SpeakersPasses* p=m_pFirst; i<nIndex; p=p->pNext,i++ ); SpeakersPasses
*pp=p->pNext; p->pNext=pp->pNext; SpeakerPasses*
q=pp->pFirst; for( SpeakerPasses* r=q->pNext; r!=NULL; r=r->pNext ) { delete
q; q=r; } delete
pp; } متد Identify عمل بازشناسي كاربر را انجام ميدهد. براي
بازشناسي يك كاربر عبارت عبور داده شده به كمك متد Verify
ساختار SpeakersPasses با تمامي ليست كاربران مقايسه ميشود و مقدار
تفاوت نزديكترين كاربر با مقدار آستانة داده شده مقايسه ميگردد و در صورتي كه
از آن ميزان كمتر باشد انديس كاربر و در غير اين صورت مقدار -1 به محل
فراخواني بازگردانده ميشود: int HSpeakersTable::Identify( HSpeaker* pSpeaker, double fThreshold ) { double
fMin=10,d; int
index; int
i=0; for( SpeakersPasses* p=m_pFirst; p!=NULL; p=p->pNext,i++ ) { if((d=p->Identify(pSpeaker))<fMin) { fMin=d; index=i; } } if(fMin<=fThreshold) return
index; return
-1; } متد RemPass متدي است كه به وسيلة آن برنامهنويس ميتواند
كاربري را كه داراي عبارت عبور داده شده است حذف كند. در صورتي كه عمل موفقيتآميز
باشد يعني كاربري با عبارت داده شده يافته شود و حذف گردد مقدار منطقي TRUE
و در غير اين صورت مقدار FALSE به محل
فراخواني بازگردانده ميشود. ملاحظة اين كه عبارت مزيور از ابتداي ليست حذف ميشود
يا نه از پردازشهاي اين متد است. همچنين فرض بر اين است كه هيچگاه تقاضاي حذف
عبارت عبور كاربري كه تنها يك عبارت عبور دارد نخواهد شد. اين متد براي يافتن
عبارت مزبور از متد Find ساختار SpeakersPasses سود ميجويد: BOOL HSpeakersTable::RemPass( int nIndex, HSpeaker* pSpeaker, double fThreshold ) { int
i=0; for( SpeakersPasses* p=m_pFirst; i<nIndex; p=p->pNext,i++ ); int
Index=-1; if(p->Find(pSpeaker,Index)<=fThreshold) { if(Index==0) { SpeakerPasses
*t=p->pFirst; p->pFirst=t->pNext; delete
t; } else { int
i=0; for( SpeakerPasses *q=p->pFirst; i<Index-1; q=q->pNext,i++ ); SpeakerPasses
*t=q->pNext; q->pNext=t->pNext; delete
t; } return
TRUE; } return
FALSE; } 6-
تشكيل و كار با پايگاه دادهها
پايگاه دادههاي
كاربران معمولاُ تعداد عنصرهاي محدودي دارد و پردازشهاي آن نيز مختص به خود است.
لذا استفاده از يك پايگاه دادههاي تخت مبتني بر يك فايل مناسب به نظر ميرسد.
مزيد بر اين انتخابهاي كاربر را نيز ميتوان در اين ساختار ذخيره نمود. اعمال كار با فايل كه
نهايت استفاده از اين كتابخانه است در كلاس HSpeakersDB
انجام ميشود: class HSpeakersDB { public: int
GetUsersNum(); void
AddUser(HSpeaker* pSpeaker); CString
GetUserName(int index); int
GetPassesNum(int index); void
RemoveUser(int nIndex); int
Identify(HSpeaker* pSpeaker); BOOL
RemPass(int index, HSpeaker* pSpeaker); HSpeakersDB(BOOL
bAutoLoad=FALSE); void
LoadUsers(); void
StoreUsers(); private: void
CreateUsersDB(); private: HSpeakersTable*
m_pTable; CString
m_strDBFileName; private: //user identification options: int
m_nRepetitions; float
m_fMinPassLength; double
m_fThreshold; public: void
SetRepsNum(int iNum); int
GetRepsNum(); void
SetMinPassLen(float fMin); float
GetMinPassLen(); void
SetSecurityLevel(int nLevel); int
GetSecurityLevel(); }; هفت متد ابتدايي تنها
متدهاي متناظر خود در عضو دادة m_pTable را فراخواني ميكنند و دو متد آخر مقدار پارامتر fThreshold در متدهاي متناظر را با عضو دادة m_fThreshold
كه توسط متد SetSecurityLevel مقدار گذاري ميشود جايگزين ميكنند: int HSpeakersDB::GetUsersNum() { return
m_pTable->GetNum(); } void
HSpeakersDB::AddUser(HSpeaker *pSpeaker) { m_pTable->Insert(pSpeaker); } CString
HSpeakersDB::GetUserName(int index) { return
m_pTable->Get(index)->GetUserName(); } int HSpeakersDB::GetPassesNum(int
index) { return
m_pTable->GetSubsNum(index); } void HSpeakersDB::RemoveUser(int
nIndex) { m_pTable->RemoveUser(nIndex); } int
HSpeakersDB::Identify(HSpeaker* pSpeaker) { return
m_pTable->Identify(pSpeaker, m_fThreshold); } BOOL HSpeakersDB::RemPass(int
index, HSpeaker* pSpeaker) { return
m_pTable->RemPass( index, Speaker,m_fThreshold ); } متد SetSecurityLevel مقدار m_fThreshold را با انتخاب
آن از يك ليست از مقادير به دست آمده از تجربه مقدارگزيني مينمايد و متد GetSecurityLevel عكس اين عمل را انجام ميدهد: void
HSpeakersDB::SetSecurityLevel(int nLevel) { switch(nLevel) { case
VERYHIGH: m_fThreshold=0.3; break; case
HIGH: m_fThreshold=0.5; break; case
MEDIUM: m_fThreshold=0.7; break; case
LOW: m_fThreshold=0.9; break; case
VERYLOW: m_fThreshold=1.1; break; } } int
HSpeakersDB::GetSecurityLevel() { int
nLevel; switch(int(m_fThreshold*10)) { case
3: nLevel=VERYHIGH; break; case
5: nLevel=HIGH; break; case
7: nLevel=MEDIUM; break; case
9: nLevel=LOW; break; case
11: nLevel=VERYLOW; break; } return
nLevel; } اعضاي دادة m_nRepetitions و m_fMinPassLength در هيچكدام
از متدهاي كلاس (به غير از متدهاي مقدارگذار و بازگردانندة مقدار آنها) كاربرد
عملي ندارند و تنها براي آن كه در متدهاي ذخيره و بازيابي در پايگاه دادههاي
عبارات رمز ذخيره و يا از آن بازيابي شوند عضو اين كلاس هستند. متد StoreUsers كاربران و انتخابهاي مربوط به آنها را درپايگاه دادهها ثبت مينمايد: void HSpeakersDB::StoreUsers() { CFile UsersDB; if(UsersDB.Open( "USERS.DB", CFile::modeWrite|CFile::modeCreate) ) { UsersDB.Write( &m_nRepetitions, sizeof(m_nRepetitions) ); UsersDB.Write( &m_fMinPassLength, sizeof(m_fMinPassLength) ); UsersDB.Write( &m_fThreshold, sizeof(m_fThreshold) ); int
iNum=m_pTable->GetNum(); for(int
i=0; i<iNum; i++) { int
iSubs=m_pTable->GetSubsNum(i); for(int
j=0; j<iSubs; j++) m_pTable->Get(i,j)->SaveTo(&UsersDB); } UsersDB.Close(); } } متد LoadUsrers كاربران ذخيره شده را بارگذاري ميكند و در صورت عدم وجود، آن
را به وجود ميآورد: void HSpeakersDB::LoadUsers() { CFile
UsersDB; m_nRepetitions=2; m_fMinPassLength=2.0; m_fThreshold=0.7; if(UsersDB.Open("USERS.DB",CFile::modeRead)) { if(UsersDB.Read( &m_nRepetitions, sizeof(m_nRepetitions)) ==sizeof(m_nRepetitions) ) if(UsersDB.Read( &m_fMinPassLength, sizeof(m_fMinPassLength)) ==sizeof(m_fMinPassLength) ) UsersDB.Read( &m_fThreshold, sizeof(m_fThreshold) ); BOOL
Finished=FALSE; while(!Finished) { HSpeaker*
pSpeaker=new HSpeaker; if(pSpeaker->LoadFrom(&UsersDB)) m_pTable->Insert(pSpeaker); else { delete
pSpeaker; Finished=TRUE; } } } else CreateUsersDB(); } متد CreatUsersDB فقط يك فايل خالي ايجاد ميكند. انتخابها بعداً در اين فايل
نوشته خواهند شد. مجموعه كلاسهاي فوق يك
كتابخانة ايستا به نام HSpeakersDBLib
را تشكيل ميدهند كه ميتوان با استفاده از آن يك سيستم تشخيص گويندة وابسته به
متن ساده را پيادهسازي نمود. همچنانكه اشاره شد كتابخانة اصلي مورد استفاده
تواناييهاي گستردهاي براي كار با انواع روشها و الگوريتمها دارد كه ميتوان با
كار روي آنها سيستمهاي تشخيص گوينده با تشخيص صحبت با كارايي عملي ايجاد نمود. فرضيات ما در نحوة
استفاده از اين كلاس نحوة پيادهسازي اين كلاس مؤثر بوده و براي به وجود آوردن
يك مجموعة داراي كاربرد كليتر بايد بيشتر روي اين كتابخانه كار شود اما به نظر
ميرسد همچنان كه از عملكرد برنامة پيادهسازي شده قابل مشاهده است حتي اين
استفادة ساده از اين كتابخانه نيز ميتواند برطرف كنندة نيازهاي يك برنامهنويس
در زمينة طراحي يك سيستم تشخيص گوينده ميباشد. 7-
منابع فصل
1)
Jialong
He, SVLib Library, downloadable from http://tiger.la.asu.edu/personal.htm
|
|
|
|
|