LabelNGram.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_NGram_cpp)
00056 #define ALIZE_LabelNGram_cpp
00057 #include <cstdio>   // pour printf()
00058 #include <cassert> // pour le debug pratique
00059 #include <cmath>
00060 #include <liatools.h>
00061 #include "LabelNGram.h"
00062 
00063 
00064 using namespace alize;
00065 using namespace std;
00066 NGram::NGram(unsigned long order, unsigned long nb){
00067     _order=order;
00068     _nb=nb;
00069     _sym=new short int [_order*_nb];
00070     _count=new unsigned long [_nb];
00071     _totalCount=0;
00072   }
00073 NGram:: ~NGram(){
00074   _order=0;
00075   _nb=0;
00076   delete [] _sym;
00077   delete [] _count;
00078   }
00079 
00080 NGram::NGram(const NGram &ng){ 
00081   _order=ng._order;
00082   _nb=ng._nb;
00083   _totalCount=ng._totalCount;
00084   _sym=new short int [_order*_nb];
00085   _count=new unsigned long [_nb];
00086   memcpy(_sym, ng._sym, _nb*_order*sizeof(short int)); 
00087   memcpy(_count, ng._count, _nb*sizeof(unsigned long)); 
00088 }
00089 const NGram& NGram::operator=(const NGram& ng)
00090 {
00091   if (this==&ng)
00092     return ng;
00093   if ((ng._nb*ng._order)!=(_nb*_order)){
00094     delete _sym;
00095     delete _count;
00096     _sym=new short int [ng._order*ng._nb];
00097     _count=new unsigned long [ng._nb];
00098   }
00099   _nb=ng._nb;
00100   _order=ng._order;
00101   _totalCount=ng._totalCount;
00102   memcpy(_sym, ng._sym, _nb*_order*sizeof(short int)); 
00103   memcpy(_count, ng._count, _nb*sizeof(unsigned long)); 
00104   return *this;
00105 }
00106 void NGram::setSize(const unsigned size){
00107   if (size<=_nb) _nb=size;
00108   else   throw Exception("Resize is allowad only for reducing the size"
00109                          , __FILE__, __LINE__);
00110   short int * sym=new short int [_order*_nb];
00111   unsigned long *count=new unsigned long [_nb];
00112   memcpy(sym,_sym, _nb*_order*sizeof(short int)); 
00113   memcpy(count,_count, _nb*sizeof(unsigned long)); 
00114   delete _sym;
00115   delete _count;
00116   _sym=sym;
00117   _count=count;
00118 }
00119  
00120 short int NGram::getSymbol(const unsigned idx,const unsigned long o){
00121   if ((idx<0) || (idx>=_nb))
00122       throw Exception("out of array"
00123         , __FILE__, __LINE__);
00124   return _sym[(idx*_order)+o];
00125 }
00126 unsigned long NGram::getCount(const unsigned idx){
00127   if ((idx<0) || (idx>=_nb))
00128       throw Exception("out of array"
00129         , __FILE__, __LINE__);
00130   return _count[idx];
00131 }
00132 void NGram::setCount(const unsigned idx, const unsigned long &count){
00133   if ((idx<0) || (idx>=_nb))
00134       throw Exception("out of array"
00135         , __FILE__, __LINE__);
00136   _count[idx]=count;
00137 }
00138 unsigned long NGram::getTotalCount(){
00139   return _totalCount;
00140 }
00141 void NGram::setTotalCount(const unsigned long &count){
00142   _totalCount=count;
00143 }
00144 void NGram::setSymbol(const unsigned idx,const unsigned long o, const short int sym, unsigned long count=0){
00145   if ((idx<0) || (idx>=_nb))
00146       throw Exception("out of array"
00147         , __FILE__, __LINE__);
00148  _sym[(idx*_order)+o]=sym; 
00149 }
00150 void NGram::showTable(ostream &out){
00151   for(unsigned idx=0;idx<_nb;idx++){
00152     out<<"Sym["<<idx<<"]=";
00153     for (unsigned long s=0;s<_order;s++)
00154       out<<"["<<getSymbol(idx,s)<<"]"; 
00155     out<<"count["<<getCount(idx)<<"]"<<endl;
00156   }
00157 }
00158     
00159 
00160 //-------------------------------------------------------------------------
00161 // Load the NGRAM table, selecting the nbSelected first
00162 void NGram::load(const String filename,Config &config){
00163   XList input(filename,config);
00164   XLine *linep;                                                          // Pointor on the current test line
00165   input.getLine(0);
00166  
00167   unsigned long idx=0;
00168   while (((linep=input.getLine()) != NULL)&&(idx<getSize())){
00169     for (unsigned long i=0;i<getOrder();i++){
00170       short int a=linep->getElement(i).toLong();
00171       setSymbol(idx,i,a);
00172     }
00173      if (linep->getElementCount()==(getOrder()+1)){
00174         unsigned long count=linep->getElement(getOrder()).toLong();
00175         setCount(idx,count);_totalCount+=count;}
00176      else setCount(idx,0);
00177     idx++;
00178   }
00179   if (idx!=getSize()){
00180     cout << "WARNING ! Number of ngram in the file["<<idx<<"] < to the number requested ["<<getSize()<<"]"<<endl;
00181     setSize(idx);
00182   }
00183   if (verboseLevel>1){
00184     cout <<"load symbol table from ["<<filename <<"]"<<endl;
00185     showTable();
00186   }
00187 }
00188 bool isNGram(short int *sym,NGram &tabS,unsigned long & tag){      
00189   bool find=false;
00190   unsigned long idx;
00191   for (idx=0;(!find) && (idx<tabS.getSize());idx++){
00192     find=true;
00193     for (unsigned long s=0;(find) && (s<tabS.getOrder());s++)
00194       find=(sym[s]==tabS.getSymbol(idx,s));
00195   }
00196   if (find){
00197     tag=idx;
00198     return true;
00199   }
00200   else return false;
00201 }
00202 
00203 short int recognizeSymbol(unsigned long &idxFrame,unsigned long end,ULongVector &tabS){
00204   unsigned long sym=tabS[idxFrame];
00205   while ((idxFrame<end)&&(tabS[idxFrame]==sym))idxFrame++;
00206   return sym;
00207 }
00208 //-------------------------------------------------------------------------
00209 // Should changed for  a circulate array
00210 void moveTab(unsigned long *begin,short int *sym,unsigned long *end,unsigned long order){
00211   for (unsigned long i=0;i<order-1;i++){
00212     begin[i]=begin[i+1];
00213     end[i]=end[i+1];
00214     sym[i]=sym[i+1];
00215   }      
00216 }
00217 //-------------------------------------------------------------------------
00218 void computeLabelNGram(NGram & NG,SegCluster &cluster,SegCluster &clusterOut,ULongVector &tabS,unsigned long nbSym){
00219   unsigned long begin[100]; // max order 100...
00220   short int sym[100];
00221   unsigned long end[100];
00222   SegServer & segServerOut=clusterOut.getServer();                           // Get the clusterserver reelated to the output
00223   cluster.rewind();
00224   Seg* seg;                                                                  // Reset the reader at the begin of the input stream
00225   while((seg=cluster.getSeg())!=NULL){                                       // For each of the selected segments
00226     unsigned long idxFrame=seg->begin(); 
00227     unsigned long endS=endSeg(seg);
00228     if (endS>=nbSym) endS=nbSym;                                          // Just if there is less symbol in the file than in the label 
00229     if (idxFrame>endS) idxFrame=endS;
00230     unsigned long beginOOV=idxFrame;
00231     bool oov=true;
00232     if (debug) cout <<"begin Seg["<<idxFrame<<"]"<<endl;
00233 
00234     for (unsigned long n=0;(idxFrame<endS) &&(n<NG.getOrder()-1);n++){                      // Recognize the (n-1) first symbols
00235       begin[n]=idxFrame;
00236       sym[n]=recognizeSymbol(idxFrame,endS,tabS);
00237       end[n]=idxFrame-1;
00238       if (debug) cout <<"sym ["<<sym[n]<<"] begin["<<begin[n]<<"] end["<<end[n]<<"] idxframe["<<idxFrame<<"]"<<endl;
00239     }
00240     while(idxFrame<endS){
00241       begin[NG.getOrder()-1]=idxFrame;
00242       sym[NG.getOrder()-1]=recognizeSymbol(idxFrame,endS,tabS);
00243       end[NG.getOrder()-1]=idxFrame-1;
00244       if (debug) cout <<"sym ["<<sym[NG.getOrder()-1]<<"] begin["<<begin[NG.getOrder()-1]
00245                       <<"] end["<<end[NG.getOrder()-1]<<"] idxframe["<<idxFrame<<"]"<<endl;
00246       unsigned long tag;
00247       if (isNGram(sym,NG,tag)){
00248         if ((oov)&&(beginOOV<begin[0])){
00249           if (debug) cout <<"OOV1  begin["<<beginOOV <<"] end["<<begin[0]-1<<"]"<<endl;
00250           Seg &segTmp=segServerOut.createSeg(beginOOV,begin[0]-beginOOV,0,"oov",seg->sourceName());
00251           clusterOut.add(segTmp);
00252         }
00253         if (debug) cout <<"NGRAM ["<<tag<<"] begin["<<begin[0] <<"] end["<<end[NG.getOrder()-1]<<"]"<<endl;
00254         Seg &segTmp=segServerOut.createSeg(begin[0],end[NG.getOrder()-1]-begin[0]+1,0,String::valueOf(tag),seg->sourceName());
00255         clusterOut.add(segTmp);
00256         beginOOV=idxFrame;
00257         oov=false;
00258       }
00259       else oov=true;
00260       moveTab(begin,sym,end,NG.getOrder());
00261     }
00262     if (oov){
00263       Seg &segTmp=segServerOut.createSeg(beginOOV,idxFrame-beginOOV,0,"oov",seg->sourceName());
00264       clusterOut.add(segTmp);
00265       if (debug) cout <<"OOV2  begin["<<beginOOV <<"] end["<<idxFrame-1<<"]"<<endl;  
00266     }
00267   }
00268 }
00269 
00270 
00271 //-------------------------------------------------------------------------
00272 unsigned long loadSymbol(const String &filename,const String &type,ULongVector & ret,Config &config){
00273  
00274   if (type=="ascii"){
00275     unsigned long nbSym=0;
00276     XList infile(filename,config);
00277     XLine list=infile.getAllElements();
00278     ret.setSize(list.getElementCount());
00279     nbSym=list.getElementCount();
00280     for (unsigned long i=0;i<nbSym;i++){
00281       String *tmp=list.getElement();
00282       if ((*tmp)!="oov")
00283         ret[i]=tmp->toLong();
00284       else ret[i]=OOV;
00285     }
00286     if (debug) cout << "nb sym:"<<nbSym<<endl;
00287     return nbSym;
00288   }
00289   else throw Exception(type+" file type non recognized for a symbol file"
00290         , __FILE__, __LINE__);
00291 }  
00292 
00293 //-------------------------------------------------------------------------
00294 int labelNGram(Config& config)
00295 {
00296   if (config.existsParam("debug"))debug=true; else debug=false;  
00297   if (config.existsParam("verbose"))verbose=true; else verbose=false;
00298   String extOutputLabel=".sym.lbl";                                               // the extension of the output files    
00299   if (config.existsParam("saveLabelFileExtension")) extOutputLabel=config.getParam("saveLabelFileExtension");   
00300   String pathOutput="./";                                                // the path of the output files    
00301   if (config.existsParam("labelOutputPath")) pathOutput=config.getParam("labelOutputPath");    
00302   String extSymbol=".sym";                                               // the extension of the symbol files   
00303   if (config.existsParam("symbolFileExtension")) extSymbol=config.getParam("symbolFileExtension");   
00304   String pathSymbol="./";   
00305   if (config.existsParam("symbolPath")) pathSymbol=config.getParam("symbolPath");   
00306   String formatSymbol="ascii";   
00307   if (config.existsParam("symbolFormat")) pathSymbol=config.getParam("symbolFormat");  
00308  
00309   String NGramFilename=config.getParam("NGramFilename");                                        
00310   unsigned long NGramOrder=3;
00311   if (config.existsParam("NGramOrder")) NGramOrder=config.getParam("NGramOrder").toLong();  
00312   unsigned long NGramSelected=16;
00313   if (config.existsParam("NGramSelected")) NGramSelected=config.getParam("NGramSelected").toLong();  
00314   NGram NGramTable(NGramOrder,NGramSelected);
00315   NGramTable.load(NGramFilename,config);                       // Load the NGRAM table, selecting the NGramSelected first
00316  
00317   String inputFilename=config.getParam("inputFilename");
00318   String labelSelectedFrames=config.getParam("labelSelectedFrames");
00319   XLine inputFileList;
00320   try{
00321     if (inputFilename.endsWith(".lst")){ // input is file containing a list of filenames
00322       XList tmp(inputFilename,config);
00323       inputFileList=tmp.getAllElements();
00324     }
00325     else inputFileList.addElement(inputFilename); // a single filename
00326     String *p;
00327     while ((p=inputFileList.getElement())){
00328       String& filename=*p;
00329       if (verbose)
00330         cout <<"labelNGram file["<<filename<<"] Table["<<NGramFilename<<"] Order["<<NGramOrder<<"] Selected["<<NGramSelected<<"]"<<endl;
00331       SegServer segServer;                
00332       LabelServer labelServer;
00333       loadClusterFile(filename,segServer,labelServer,config);
00334       long codeSelectedFrame=labelServer.getLabelIndexByString(labelSelectedFrames);       // Get the index of the selected cluster
00335       if (codeSelectedFrame==-1){                                                             // No data for this model !!!!!!!!!!!!!!
00336       cout << " WARNING - NO DATA with the label["<<labelSelectedFrames<<"] in file ["<<filename<<"]"<<endl;
00337       exit(0);
00338       }
00339       SegCluster& cluster=segServer.getCluster(codeSelectedFrame);                                   // Gives the cluster of the selected/used segments
00340       ULongVector tabS;
00341       unsigned long nbSym=loadSymbol(pathSymbol+filename+extSymbol,formatSymbol,tabS,config);        // Read the stream of symbols
00342       SegServer segServerOutput;
00343       SegCluster& clusterOut=segServerOutput.createCluster(0,labelSelectedFrames,cluster.sourceName());  
00344       //  
00345       computeLabelNGram(NGramTable,cluster,clusterOut,tabS,nbSym);
00346       //
00347     
00348       if (verbose){
00349         cout <<"File["<<filename<<"]" <<endl;
00350         cout << "Output the new label file in ["<<pathOutput+filename+extOutputLabel <<"]"<<endl;
00351       }
00352       outputLabelFile(clusterOut,pathOutput+filename+extOutputLabel,config);
00353     } // end file loop
00354   } // fin try
00355   
00356   
00357   catch (Exception& e)
00358     { 
00359       cout << e.toString().c_str() << endl;
00360     }
00361   return 0;
00362 }
00363 
00364 
00365 
00366 #endif