TopGauss.cpp

Go to the documentation of this file.
00001 /*
00002 This file is part of LIA_RAL which is a set of software based on ALIZE
00003 toolkit for speaker recognition. ALIZE toolkit is required to use LIA_RAL.
00004 
00005 LIA_RAL project is a development project was initiated by the computer
00006 science laboratory of Avignon / France (Laboratoire Informatique d'Avignon -
00007 LIA) [http://lia.univ-avignon.fr <http://lia.univ-avignon.fr/>]. Then it
00008 was supported by two national projects of the French Research Ministry:
00009         - TECHNOLANGUE program [http://www.technolangue.net]
00010         - MISTRAL program [http://mistral.univ-avignon.fr]
00011 
00012 LIA_RAL is free software: you can redistribute it and/or modify
00013 it under the terms of the GNU Lesser General Public License as
00014 published by the Free Software Foundation, either version 3 of
00015 the License, or any later version.
00016 
00017 LIA_RAL is distributed in the hope that it will be useful,
00018 but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00020 GNU Lesser General Public License for more details.
00021 
00022 You should have received a copy of the GNU Lesser General Public
00023 License along with LIA_RAL.
00024 If not, see [http://www.gnu.org/licenses/].
00025 
00026 The LIA team as well as the LIA_RAL project team wants to highlight the
00027 limits of voice authentication in a forensic context.
00028 The "Person Authentification by Voice: A Need of Caution" paper
00029 proposes a good overview of this point (cf. "Person
00030 Authentification by Voice: A Need of Caution", Bonastre J.F.,
00031 Bimbot F., Boe L.J., Campbell J.P., Douglas D.A., Magrin-
00032 chagnolleau I., Eurospeech 2003, Genova].
00033 The conclusion of the paper of the paper is proposed bellow:
00034 [Currently, it is not possible to completely determine whether the
00035 similarity between two recordings is due to the speaker or to other
00036 factors, especially when: (a) the speaker does not cooperate, (b) there
00037 is no control over recording equipment, (c) recording conditions are not
00038 known, (d) one does not know whether the voice was disguised and, to a
00039 lesser extent, (e) the linguistic content of the message is not
00040 controlled. Caution and judgment must be exercised when applying speaker
00041 recognition techniques, whether human or automatic, to account for these
00042 uncontrolled factors. Under more constrained or calibrated situations,
00043 or as an aid for investigative purposes, judicious application of these
00044 techniques may be suitable, provided they are not considered as infallible.
00045 At the present time, there is no scientific process that enables one to
00046 uniquely characterize a persones voice or to identify with absolute
00047 certainty an individual from his or her voice.]
00048 
00049 Copyright (C) 2004-2010
00050 Laboratoire d'informatique d'Avignon [http://lia.univ-avignon.fr]
00051 LIA_RAL admin [alize@univ-avignon.fr]
00052 Jean-Francois Bonastre [jean-francois.bonastre@univ-avignon.fr]
00053 */
00054 
00055 #if !defined(ALIZE_TopGauss_cpp)
00056 #define ALIZE_TopGauss_cpp
00057 
00058 #define myTINY 0.0000001
00059 
00060 #include <iostream>
00061 #include <fstream>  // pour outFile
00062 #include <cstdio>   // pour printf()
00063 #include <cassert> // pour le debug pratique
00064 #include <cmath>
00065 #include "liatools.h"
00066 #include "TopGauss.h"
00067 #define EPS_LK 1e-200
00068 unsigned long TopGauss::frameToIdx(unsigned long & f) {
00069         unsigned long cnt=0;
00070         unsigned long*_pnbg=_nbg.getArray();
00071         for (unsigned long t=0;t<f;t++)
00072                 cnt+=_pnbg[t];
00073 return cnt;
00074 }
00075 
00076 void TopGauss::read(String & featureFilename,Config &config) {
00077         if (verbose) cout << "(TopGauss::read) File ["<<featureFilename<<"] ... ";
00078         String nbGaussPerFrameFilename=config.getParam("nbGaussianFilesDir")+featureFilename;
00079         if (verboseLevel > 2) cout << "(TopGauss) Reading indexes in file ["<<nbGaussPerFrameFilename<<"]"<<endl;       
00080         ifstream nbGaussFile(nbGaussPerFrameFilename.c_str(),ios::in|ios::binary);                              //ios::binary added by alarcher
00081         if(!nbGaussFile){throw Exception("Cannot find nbGaussian file", __FILE__, __LINE__);}   
00082         // Read number of frames
00083         nbGaussFile.read((char *)&_nt,sizeof(unsigned long));
00084         nbGaussFile.read((char *)&_nbgcnt,sizeof(unsigned long));       
00085         _idx.setAllValues(0);_idx.setSize(_nbgcnt);
00086         _nbg.setAllValues(0);_nbg.setSize(_nt);
00087         _snsl.setAllValues(0);_snsl.setSize(_nt);
00088         _snsw.setAllValues(0);_snsw.setSize(_nt);       
00089         nbGaussFile.read((char *)_nbg.getArray(),sizeof(unsigned long)*_nt);
00090         nbGaussFile.read((char *)_idx.getArray(),sizeof(unsigned long)*_nbgcnt);
00091         nbGaussFile.read((char *)_snsw.getArray(),sizeof(double)*_nt);
00092         nbGaussFile.read((char *)_snsl.getArray(),sizeof(double)*_nt);  
00093         nbGaussFile.close();
00094 }
00095 
00096 void TopGauss::write(String & featureFilename,Config & config) {
00097         if (verbose) cout << "(TopGauss::write) File ["<<featureFilename<<"] "<<endl;   
00098         String nbGaussFilename=config.getParam("nbGaussianFilesDir")+featureFilename;
00099         ofstream nbGaussFile (nbGaussFilename.c_str(),ios::out|ios::binary);                    //ios::binary added by alarcher
00100         if(!nbGaussFile){throw Exception("Cannot find nbGaussian file", __FILE__, __LINE__);}   
00101         nbGaussFile.write((char *)&_nt,sizeof(unsigned long));          // write number of frames in cluster
00102         nbGaussFile.write((char *)&_nbgcnt,sizeof(unsigned long));              // write number of frames in cluster    
00103         // Write number of Gaussians + Gaussian Indexes;
00104         nbGaussFile.write((char *)_nbg.getArray(),sizeof(unsigned long)*_nbg.size());
00105         nbGaussFile.write((char *)_idx.getArray(),sizeof(unsigned long)*_idx.size());
00106         nbGaussFile.write((char *)_snsw.getArray(),sizeof(double)*_snsw.size());
00107         nbGaussFile.write((char *)_snsl.getArray(),sizeof(double)*_snsl.size());        
00108         nbGaussFile.close();
00109 }
00110 
00111 // Init with a Filename 
00112 double TopGauss::compute(MixtureGD & UBM,String & featureFilename, Config & config) {
00113         if (verbose) cout << "(TopGauss::compute on a single file) File ["<<featureFilename<<"] ... ";
00114         FeatureServer fs(config,featureFilename);  
00115         double llk=compute(UBM,fs,featureFilename,config);      
00116         if (verbose) cout <<_nbgcnt/_nt <<" per frames LLK="<<llk<<endl;        
00117 return llk;
00118 }
00119 
00120 //Init with a FeatureServer
00121 RealVector <double> TopGauss::compute(MixtureGD & UBM,FeatureServer &fs,Config & config) {
00122         RealVector <double> llkv;
00123         for (unsigned long i=0;i<fs.getSourceCount();i++) {
00124                 String filename=fs.getNameOfASource(i);
00125                 if (verbose) cout << "(TopGauss::compute on FeatureServer) File ["<<filename<<"] ... ";                 
00126                 llkv.addValue(compute(UBM,fs,filename,config));
00127                 cout << "_nt"<<_nt<<" idx"<<_idx.size()<<" _snsw"<<_snsw.size()<<endl;
00128                 this->write(filename,config); // this should be removed!! but when passing a feature server things get worse            
00129                 if (verbose) cout <<_nbgcnt/_nt <<" per frames LLK="<<llkv[i]<<endl;    
00130         }
00131 return llkv;
00132 }
00133 
00134 
00135 // Main init function
00136 double TopGauss::compute(MixtureGD & UBM,FeatureServer &fs,String & featureFilename,Config & config){
00137         StatServer ss(config);
00138         MixtureGDStat &acc=ss.createAndStoreMixtureStat(UBM);   
00139         unsigned long _mixsize=UBM.getDistribCount();
00140         String labelSelectedFrames =config.getParam("labelSelectedFrames");
00141         unsigned long begin=fs.getFirstFeatureIndexOfASource(featureFilename);
00142         fs.seekFeature(begin);
00143         SegServer segmentsServer;
00144         LabelServer labelServer;
00145         initializeClusters(featureFilename,segmentsServer,labelServer,config);
00146         verifyClusterFile(segmentsServer,fs,config);
00147         unsigned long codeSelectedFrame=labelServer.getLabelIndexByString(labelSelectedFrames); 
00148         SegCluster& selectedSegments=segmentsServer.getCluster(codeSelectedFrame);  
00149         acc.resetLLK();
00150         double topD=config.getParam("topGauss").toDouble();
00151         if (verbose) {if(topD<1.0) cout << "LLK %="<< topD << "% ";else cout << "Top-"<<topD<<" ";}
00152         
00153         // Class values
00154         _nt=totalFrame(selectedSegments);       
00155         _nbg.setSize(_nt); _idx.setSize(0);_snsw.setSize(0); _snsl.setSize(0);
00156         _nbg.setAllValues(0); _idx.setAllValues(0);_snsw.setAllValues(0.0);_snsl.setAllValues(0.0);
00157         _nbgcnt=0;
00158         Seg *seg;          // current selected segment
00159         selectedSegments.rewind();              
00160         unsigned long t=0; //cnt frames
00161         while((seg=selectedSegments.getSeg())!=NULL){                           
00162                 unsigned long begin=seg->begin()+fs.getFirstFeatureIndexOfASource(seg->sourceName()); 
00163                 fs.seekFeature(begin);
00164                 Feature f;
00165                 for (unsigned long idxFrame=0;idxFrame<seg->length();idxFrame++){
00166                         fs.readFeature(f); 
00167                         double llk=acc.computeAndAccumulateLLK(f,1.0,DETERMINE_TOP_DISTRIBS);
00168                         const LKVector &topV=ss.getTopDistribIndexVector();
00169                         double lk_tot=exp(llk);
00170                         
00171                         double val=0.0;
00172                         if (topD<1.0) {
00173                                 for(unsigned long j=0;j<_mixsize;j++){
00174                                         if (val > topD*lk_tot) break;
00175                                         val+=(topV[j].lk);
00176                                         _nbg[t]++;
00177                                 }
00178                         } else _nbg[t]=(unsigned long)topD;
00179                         _nbgcnt+=_nbg[t];
00180                          
00181                         double snsw=1.0;
00182                         double snsl=lk_tot;                                     
00183                         for(unsigned long j=0;j<_nbg[t];j++) {
00184                                 _idx.addValue(topV[j].idx);             
00185                                 snsw -=UBM.weight(topV[j].idx);
00186                                 snsl -=topV[j].lk;
00187                         }
00188 
00189                         _snsw.addValue(snsw);
00190                         if (snsl < EPS_LK)
00191                                 _snsl.addValue(EPS_LK);
00192                         else _snsl.addValue(snsl);
00193                         t++;
00194                 }               
00195         }
00196         if (t!=_nt) cout << "W: t("<<t<<") != _nt(" <<_nt<<")"<<endl;
00197 return acc.getMeanLLK();
00198 }
00199 
00200 
00201 
00202 RealVector <double> TopGauss::get(MixtureGD & UBM,FeatureServer &fs,Config & config){
00203         RealVector <double> llkv;
00204         for (unsigned long i=0;i<fs.getSourceCount();i++) {
00205                 String filename=fs.getNameOfASource(i);
00206                 if (verbose) cout << "(TopGauss::get on FeatureServer) File ["<<filename<<"] ... ";     
00207                 this->read(filename,config); // this should be removed!! but when passing a feature server things get worse                     
00208                 llkv.addValue(get(UBM,fs,filename,config));
00209                 if (verbose) cout <<_nbgcnt/_nt <<" per frames LLK="<<llkv[i]<<endl;    
00210         }
00211 return llkv;
00212 }
00213 
00214 // Can use this function to get likelihood with a topgauss
00215 double TopGauss::get(MixtureGD & UBM,String & featureFilename,Config & config){
00216         FeatureServer fs(config,featureFilename);  
00217         if (verbose) cout << "(TopGauss::get on a single file) File ["<<featureFilename<<"] ... ";      
00218         double llk=get(UBM,fs,featureFilename,config);  
00219         if (verbose) cout <<_nbgcnt/_nt <<" per frames LLK="<<llk<<endl;        
00220 return llk;     
00221 }
00222 
00223 // Can use this function to get likelihood with a topgauss
00224 /*double TopGauss::get(MixtureGD & UBM,FeatureServer &fs,String & featureFilename,Config & config){
00225         double llkcluster=0.0;
00226         StatServer ss(config);
00227         String labelSelectedFrames =config.getParam("labelSelectedFrames");
00228         unsigned long begin=fs.getFirstFeatureIndexOfASource(featureFilename);
00229         fs.seekFeature(begin);
00230         SegServer segmentsServer;
00231         LabelServer labelServer;
00232         initializeClusters(featureFilename,segmentsServer,labelServer,config);
00233         verifyClusterFile(segmentsServer,fs,config);
00234         unsigned long codeSelectedFrame=labelServer.getLabelIndexByString(labelSelectedFrames); 
00235         SegCluster& selectedSegments=segmentsServer.getCluster(codeSelectedFrame);  
00236         
00237         Seg *seg;          // current selected segment
00238         selectedSegments.rewind();              
00239         unsigned long t=0; //cnt frames
00240         double minLLK=config.getParam("minLLK").toDouble();
00241         double maxLLK=config.getParam("maxLLK").toDouble();     
00242         while((seg=selectedSegments.getSeg())!=NULL){  
00243                 double llkseg=0.0;
00244                 unsigned long begin=seg->begin()+fs.getFirstFeatureIndexOfASource(seg->sourceName()); 
00245                 fs.seekFeature(begin);
00246                 Feature f;
00247                 for (unsigned long idxFrame=0;idxFrame<seg->length();idxFrame++){
00248                         double lkframe=0.0;
00249                         fs.readFeature(f); 
00250                         unsigned long idx=this->frameToIdx(t);
00251                         unsigned long nbg=_nbg[t];
00252                         for (unsigned long i=0;i<nbg;i++) {
00253                                 double lkdistrib=0.0;
00254                                 lkdistrib=(UBM.weight(_idx[idx+i])*UBM.getDistrib(_idx[idx+i]).computeLK(f));
00255                                 if (std::isnan(lkdistrib)) lkdistrib=exp(minLLK);
00256                                 lkframe+=lkdistrib;
00257                         }
00258                         if (log(lkframe) <= minLLK) llkseg+=minLLK;
00259                         else if(log(lkframe) >= maxLLK) llkseg+=maxLLK;
00260                         else llkseg+=log(lkframe);                      
00261                         t++;
00262                 }       
00263                 llkcluster+=llkseg;
00264         }
00265         if (t!=_nt) cout << "W: t("<<t<<") != _nt(" <<_nt<<")"<<endl;
00266 return llkcluster/totalFrame(selectedSegments);
00267 }*/
00268 
00269 // Can use this function to get likelihood with a topgauss
00270 double TopGauss::get(MixtureGD & UBM,FeatureServer &fs,String & featureFilename,Config & config){
00271         StatServer ss(config);
00272         String labelSelectedFrames =config.getParam("labelSelectedFrames");
00273         unsigned long begin=fs.getFirstFeatureIndexOfASource(featureFilename);
00274         fs.seekFeature(begin);
00275         SegServer segmentsServer;
00276         LabelServer labelServer;
00277         initializeClusters(featureFilename,segmentsServer,labelServer,config);
00278         verifyClusterFile(segmentsServer,fs,config);
00279         unsigned long codeSelectedFrame=labelServer.getLabelIndexByString(labelSelectedFrames); 
00280         SegCluster& selectedSegments=segmentsServer.getCluster(codeSelectedFrame);  
00281         MixtureGDStat &acc=ss.createAndStoreMixtureStat(UBM);
00282         
00283         Seg *seg;          // current selected segment
00284         selectedSegments.rewind();              
00285         unsigned long t=0; //cnt frames
00286         acc.resetLLK();
00287         unsigned long idxBegin=0;
00288         while((seg=selectedSegments.getSeg())!=NULL){  
00289                 unsigned long begin=seg->begin()+fs.getFirstFeatureIndexOfASource(seg->sourceName()); 
00290                 fs.seekFeature(begin);
00291                 Feature f;
00292                 idxBegin=this->frameToIdx(t);
00293                 for (unsigned long idxFrame=0;idxFrame<seg->length();idxFrame++){
00294                         fs.readFeature(f); 
00295                         //unsigned long idx=this->frameToIdx(t);
00296                         unsigned long nbg=_nbg[t];      
00297                         ULongVector index;
00298                         double sumNonSelectedWeights=_snsw[t];
00299                         double sumNonSelectedLLK=_snsl[t];
00300                         for (unsigned long i=0;i<nbg;i++) {
00301                                 index.addValue(_idx[idxBegin+i]);
00302                         }               
00303                         char c[100];
00304                         sprintf(c,"%d",(int)index.size());
00305                         config.setParam("topDistribsCount",c); // this should be high enough    
00306                         if (t==0) {acc.computeAndAccumulateLLK(f,1.0,DETERMINE_TOP_DISTRIBS);acc.resetLLK();} // to remove in ALIZE, this is to init the LKvector
00307                         ss.setTopDistribIndexVector(index, sumNonSelectedWeights, sumNonSelectedLLK);
00308                         acc.computeAndAccumulateLLK(f,1.0,USE_TOP_DISTRIBS);
00309                         idxBegin+=nbg;
00310                         t++;
00311                 }       
00312         }       
00313         //ss.deleteMixtureStat(acc);
00314         if (t!=_nt || idxBegin !=_nbgcnt) cout << "W: t("<<t<<") != _nt(" <<_nt<<")"<<"W: idxBegin("<<idxBegin<<") != _nbgcnt(" <<_nbgcnt<<")"<<endl;
00315 return acc.getMeanLLK();
00316 }
00317 
00318 #endif