FusionScore.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_FusionScore_cpp)
00056 #define ALIZE_FusionScore_cpp
00057 
00058 #include <iostream>
00059 #include <fstream>  // pour outFile
00060 #include <cstdio>   // pour printf()
00061 #include <cassert> // pour le debug pratique
00062 #include <cmath>
00063 #include "FusionScore.h"
00064 #include <liatools.h>
00065 
00066 using namespace alize;
00067 using namespace std;
00068 
00069 //-------------------------------------------------------------------------
00070 void computeFusion(XLine & lineTab, XLine & finalScore, XList & weights,Histo &destH, Config & config)
00071 {
00072         String fusionMethod=config.getParam("fusionMethod");
00073 
00074         if (fusionMethod=="ArithMean")
00075         {
00076                 double Score=0;
00077                 double sumWeight=0;
00078                 for (unsigned long i=0; i<lineTab.getElementCount(); i++)
00079                 {
00080                         cout << weights.getLine(0).getElement(i) << " * " << lineTab.getElement(i) << endl;
00081                         sumWeight += weights.getLine(0).getElement(i).toDouble();
00082                         Score+=weights.getLine(0).getElement(i).toDouble()*lineTab.getElement(i).toDouble();
00083                 }
00084                 Score/=sumWeight; 
00085                 finalScore.addElement(String::valueOf(Score));
00086         }
00087         else if (fusionMethod=="GeoMean")
00088         {
00089                 double Score=1;
00090                 double sumWeight=0;
00091                 double nbElm=lineTab.getElementCount();
00092                 for (int i=0; i<nbElm; i++){
00093                         sumWeight += weights.getLine(0).getElement(i).toDouble();                       
00094                         Score*=pow(weights.getLine(0).getElement(i).toDouble(),lineTab.getElement(i).toDouble());
00095                 }
00096                 if (Score < 0)  {Score=pow(-1*Score,1.0/sumWeight); Score=-1*Score;}
00097                 else {Score=pow(Score,1.0/sumWeight);}
00098                 finalScore.addElement(String::valueOf(Score));
00099         }
00100         else if (fusionMethod=="multipleAryth"){
00101                 double Score=0;
00102                 // ici provoque warning  de compilation convert from double to int
00103                 // long bin= destH(lineTab.getElement(0).toDouble(),1); 
00104                 // modifié par éric -> introduction de cast to long pour supprimer warning
00105           long bin= (long)destH(lineTab.getElement(0).toDouble(),1); 
00106 
00107                 if (debug) cout <<"score["<<lineTab.getElement(0).toDouble()<<"], fusion with line["<<bin<<"]"<<endl;
00108                 for (unsigned long i=0; i<lineTab.getElementCount(); i++)
00109                 {
00110                         Score+=weights.getLine(bin).getElement(i).toDouble()*lineTab.getElement(i).toDouble();
00111                 }
00112                 //Score/=lineTab.getElementCount(); give percentage in weight, it's better
00113                 finalScore.addElement(String::valueOf(Score));
00114         }
00115 
00116         else {cerr <<"fusionMethod"<<fusionMethod<<"] unknown"<<endl; exit(1);}
00117 }
00118 
00119 //-------------------------------------------------------------------------
00120 void findNbest(XLine & tabScoreLine, int Nbest, int comp_double(const void *a, const void *b))
00121 {
00122         int nbElm=tabScoreLine.getElementCount();
00123         double *tab = new double[nbElm];
00124 
00125         for (int i=0; i < nbElm; i++)
00126                 tab[i]=tabScoreLine.getElement(i).toDouble();
00127 
00128         qsort(tab,nbElm,sizeof(double),comp_double);
00129 
00130         tabScoreLine.reset();
00131 
00132         for (int j=nbElm-1; j >= nbElm-Nbest; j--)
00133                 tabScoreLine.addElement(String::valueOf(tab[j]));
00134 }
00135 
00136 //-------------------------------------------------------------------------
00137 int comp_double(const void *a, const void *b)
00138 {
00139         if ((*(double*) a - *(double*) b)<=0)   {return -1;}
00140         else    {return 1;}
00141 }
00142 
00143 String extractModel(String &classModel){
00144         String tmp;
00145         String m = classModel;
00146         long ind =  m.find("_", 0);
00147         for(int i=0; i<ind; i++) {
00148                 tmp += m[i];
00149         }
00150         return tmp;
00151 }
00152 
00153 //-------------------------------------------------------------------------
00154 int FusionScore(Config& config)
00155 {
00156         using namespace alize;
00157         using namespace std;
00158 
00159         String inputNISTListFileName = config.getParam("inputFileList");
00160         String outputNISTFileName = config.getParam("outputFile");
00161         String weightsFile = config.getParam("weights");
00162         String format=config.getParam("format");
00163         bool debug=false;
00164         if (config.existsParam("debug")) debug=true;
00165         String fusionMethod=config.getParam("fusionMethod");
00166         Histo destH;
00167         if (fusionMethod=="multipleAryth")
00168           destH.load(config.getParam("destH"));
00169         unsigned long nbFiles;
00170         unsigned long genderField, nameField, decisionField, segField, scoreField;              //Necessary field in files
00171         if (format=="lia") {setLIAInfoFields(genderField, nameField, decisionField, segField,scoreField);}
00172         else if(format=="nist") {setNIST04InfoFields(genderField, nameField, decisionField, segField,scoreField);}
00173         else if (format=="etf") {scoreField=7;} // To remove, but sorry we're in eval!! retrieveETFInfoFields(genderField, nameField, decisionField, segField,scoreField);}
00174         else {cerr << "Error: Format unknown" << endl; exit(1);}
00175         
00176         XList tabScore;
00177         XLine finalScore;
00178 
00179         try{
00180                 XList inputNISTList(inputNISTListFileName,config);      // List of files to fuse
00181                 XList weights(weightsFile,config);      // coeffs
00182                 nbFiles=inputNISTList.getLineCount();
00183 
00184                 for (unsigned long i=0; i< nbFiles; i++) {
00185                         XList inputNISTFile(inputNISTList.getLine(i).getElement(0),config);
00186                         for (unsigned long j=0; j < inputNISTFile.getLineCount(); j++) {
00187                                 if (i==0) {tabScore.addLine();}
00188                                 tabScore.getLine(j).addElement(inputNISTFile.getLine(j).getElement(scoreField));
00189                         }
00190                 }
00191 
00192                 for (unsigned long i=0; i < tabScore.getLineCount(); i++)       {//Fusion Process
00193                         if (config.existsParam("Nbest"))        { // In case of a nbest fusion
00194                                 findNbest(tabScore.getLine(i), config.getParam("Nbest").toLong(), comp_double);
00195                         }
00196 
00197                 if (debug) {
00198                         for (unsigned long j=0; j < tabScore.getLine(i).getElementCount(); j++)
00199                                 cout << tabScore.getLine(i).getElement(j) << " ";
00200                                 cout << endl;
00201                 }
00202                         computeFusion(tabScore.getLine(i),finalScore,weights,destH,config); // See above method
00203                 }
00204                 XList inputNISTFile(inputNISTList.getLine(0).getElement(0),config);
00205                 ofstream outFile(outputNISTFileName.c_str(),ios::out | ios::trunc);     // Preparing output
00206                 String subType, event, channel, source;
00207                 unsigned long duration, start;
00208                 double LLR;
00209                 int decision=0; // no decision in the fusion process
00210  
00211                 if (format=="lia") {
00212                         for (unsigned long i=0; i < tabScore.getLineCount(); i++) {
00213                                 setLIAInfoFields(genderField, nameField, decisionField, segField, scoreField); // retrieve LIA fields
00214                                 subType=inputNISTFile.getLine(i).getElement(genderField);
00215                                 event=inputNISTFile.getLine(i).getElement(nameField);
00216                                 decision=decision; // there is a pb here, outputResult takes a int as decision
00217                                 source=inputNISTFile.getLine(i).getElement(segField);
00218                                 LLR=finalScore.getElement(i).toDouble();
00219                                 outputResultLine(LLR,event,source, subType,decision,outFile);   
00220                         }
00221                 }
00222                 if (format=="nist") {
00223                         for (unsigned long i=0; i < tabScore.getLineCount(); i++) {
00224                                 String trainTypeTest="1side"; // clean this ASAP
00225                                 String adaptationMode="n";
00226                                 String segTypeTest="1side";
00227                                 setNIST04InfoFields(genderField, nameField, decisionField, segField, scoreField); // retrieve NIST fields
00228                                 subType=inputNISTFile.getLine(i).getElement(genderField);
00229                                 event=inputNISTFile.getLine(i).getElement(nameField);
00230                                 String decision="-";// there is a pb here, outputResult takes a int as decision
00231                                 source=inputNISTFile.getLine(i).getElement(segField);
00232                                 LLR=finalScore.getElement(i).toDouble();
00233                                 outputResultNIST04Line(trainTypeTest,adaptationMode,segTypeTest,subType,event,source,decision,LLR,outFile);                             
00234                         }
00235                 }
00236                 else if (format=="etf") {
00237                         for (unsigned long i=0; i < tabScore.getLineCount(); i++) { // retrieve files from etf format has to be done
00238                                 subType=inputNISTFile.getLine(i).getElement(0);
00239                                 event=inputNISTFile.getLine(i).getElement(1);
00240                                 decision=inputNISTFile.getLine(i).getElement(2).toLong();
00241                                 source=inputNISTFile.getLine(i).getElement(3);
00242                                 start=inputNISTFile.getLine(i).getElement(4).toLong();
00243                                 duration=inputNISTFile.getLine(i).getElement(5).toLong();
00244                                 LLR=finalScore.getElement(i).toDouble();
00245                                 outputResultLine(LLR,event,source, start, start+duration, subType,decision,outFile);    
00246                         }       
00247                 }       
00248 } // fin try
00249 
00250 catch (Exception& e)
00251         { 
00252           cout <<" FusionScore" << e.toString().c_str() << endl;
00253         }
00254         return 0;
00255 }
00256 
00257 
00258 int FusionClassScore(Config& config)
00259 {
00260 
00261         using namespace alize;
00262         using namespace std;
00263 
00264         String inputNISTFileName = config.getParam("inputNistFileName");
00265         String outputNISTFileName = config.getParam("outputFile");
00266         ofstream outFile(outputNISTFileName.c_str(),ios::out | ios::trunc);     // Preparing output
00267         String format=config.getParam("format");
00268         bool debug=false;
00269         if (config.existsParam("debug")) debug=true;
00270 
00271         String fusionMethod=config.getParam("fusionMethod");
00272         Histo destH;
00273         if (fusionMethod=="multipleAryth")
00274           destH.load(config.getParam("destH"));
00275         
00276         unsigned long genderField, nameField, decisionField, segField, scoreField;              //Necessary field in files
00277         if (format=="lia") {setLIAInfoFields(genderField, nameField, decisionField, segField,scoreField);}
00278         else if(format=="nist") {setNIST04InfoFields(genderField, nameField, decisionField, segField,scoreField);}
00279         else {cerr << "Error: Format unknown" << endl; exit(1);}
00280 
00281         /* Weight file managing */
00282         String weightFileExt = ".txt";
00283         if(config.existsParam("weightFileExt")){
00284                 weightFileExt = config.getParam("weightFileExt");       
00285         }
00286         String weightFilePath = "./";
00287         if(config.existsParam("weightFilePath")){
00288                 weightFilePath = config.getParam("weightFilePath");     
00289         }
00290         
00291 
00292         try{
00293                 String subType, event, channel, source;
00294                 double LLR;
00295                 //int decision=0; // no decision in the fusion process
00296                 String trainTypeTest="1side"; // clean this ASAP
00297                 String adaptationMode="n";
00298                 String segTypeTest="1side";
00299 
00300                 XList tabScore;
00301                 tabScore.addLine();
00302                 XLine finalScore;
00303                 XList weights;
00304                 weights.addLine();
00305                 XList tmpW;
00306                 
00307                 XList nistList(inputNISTFileName, config);
00308                 XLine * currentlinep = nistList.getLine();
00309                 XLine * nextlinep;
00310                 
00311                 String currentModel = extractModel(currentlinep->getElement(nameField));
00312                 if(debug)
00313                         cout << "Model: " << currentModel << " Accumule:" << currentlinep->getElement(scoreField) << endl;
00314                 tabScore.getLine(0).addElement(currentlinep->getElement(scoreField));
00315                 String weightFileName = weightFilePath+currentlinep->getElement(nameField)+weightFileExt;
00316                 tmpW.load(weightFileName, config);
00317                 weights.getLine(0).addElement(tmpW.getLine(0).getElement(0));
00318 
00319                 while ((nextlinep=nistList.getLine()) != NULL){
00320                         String nextModel = extractModel(nextlinep->getElement(nameField));              
00321                         if(nextModel != currentModel){
00322                                 computeFusion(tabScore.getLine(0),finalScore,weights,destH, config); // See above method
00323                                 if(debug)
00324                                         cout << "Score Fusion: " << finalScore.getElement(0)<< endl;
00325                                 /* Output scores in files */
00326                                 if (format=="lia") {
00327                                         LLR=finalScore.getElement(0).toDouble();
00328                                         outputResultLine(LLR,currentModel,currentlinep->getElement(segField),currentlinep->getElement(genderField),int(currentlinep->getElement(decisionField).toLong()),outFile);      
00329                                 }
00330                                 if (format=="nist") {
00331                                         LLR=finalScore.getElement(0).toDouble();
00332                                         outputResultNIST04Line(trainTypeTest,adaptationMode,segTypeTest,currentlinep->getElement(genderField),currentlinep->getElement(nameField),currentlinep->getElement(segField),currentlinep->getElement(decisionField),LLR,outFile);                              
00333                                 }
00334                                 
00335                                 currentModel = nextModel;
00336                                 tabScore.getLine(0).reset();
00337                                 weights.getLine(0).reset();
00338                                 finalScore.reset();
00339                                 currentlinep = nextlinep;       
00340                                 if(debug)
00341                                         cout << "Model: " << currentModel << " Accumule:" << currentlinep->getElement(scoreField) << endl;
00342                                 tabScore.getLine(0).addElement(currentlinep->getElement(scoreField));
00343                                 String weightFileName = weightFilePath+currentlinep->getElement(nameField)+weightFileExt;
00344                                 tmpW.load(weightFileName, config);
00345                                 weights.getLine(0).addElement(tmpW.getLine(0).getElement(0));
00346                         }
00347                         else{
00348                                 currentlinep = nextlinep;       
00349                                 if(debug)
00350                                         cout << "Model: " << currentModel << " Accumule:" << currentlinep->getElement(scoreField) << endl;
00351                                 tabScore.getLine(0).addElement(currentlinep->getElement(scoreField));
00352                                 String weightFileName = weightFilePath+currentlinep->getElement(nameField)+weightFileExt;
00353                                 tmpW.load(weightFileName, config);
00354                                 weights.getLine(0).addElement(tmpW.getLine(0).getElement(0));
00355                         }
00356                         
00357                 }
00358                 
00359                 /* last line ???!!!! */
00360                 computeFusion(tabScore.getLine(0),finalScore,weights,destH,config); // See above method
00361                 if(debug)
00362                         cout << "Score Fusion: " << finalScore.getElement(0)<< endl;
00363                 /* Output scores in files */
00364                 if (format=="lia") {
00365                         LLR=finalScore.getElement(0).toDouble();
00366                         outputResultLine(LLR,currentModel,currentlinep->getElement(segField),currentlinep->getElement(genderField),int(currentlinep->getElement(decisionField).toLong()),outFile);      
00367                 }
00368                 if (format=="nist") {
00369                         LLR=finalScore.getElement(0).toDouble();
00370                         outputResultNIST04Line(trainTypeTest,adaptationMode,segTypeTest,currentlinep->getElement(genderField),currentlinep->getElement(nameField),currentlinep->getElement(segField),currentlinep->getElement(decisionField),LLR,outFile);                              
00371                 }
00372                 
00373 } // fin try
00374 
00375 catch (Exception& e)
00376         { 
00377           cout << "FusionClasse :"<<e.toString().c_str() << endl;
00378         }
00379         return 0;
00380 }
00381 
00382 #endif //!defined(ALIZE_FusionScore_cpp)