LabelFusion.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_LabelFusion_cpp)
00056 #define ALIZE_LabelFusion_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 <liatools.h>
00064 #include "LabelFusion.h"
00065 
00066 
00067 using namespace alize;
00068 using namespace std;
00069 
00070 
00071 
00072 // return in clusterOutput the clusterOne without the data in clusterTwo
00073 void interSeg(SegCluster& clusterOne,SegCluster& clusterTwo,SegCluster& clusterOutput)
00074 {
00075   unsigned long overlapTime=0;
00076   clusterOne.rewind();
00077   clusterTwo.rewind();
00078   SegServer & segServerOutput=clusterOutput.getServer();                                     // Get the clusterserver reelated to the output
00079   Seg *segOne;                                                                               // Will give the current segment in the hypothese
00080   Seg *segTwo=clusterTwo.getSeg();                                                           // Will give the current segment in the ref
00081   while((segOne=clusterOne.getSeg())!=NULL){                                                 // For each segment of the first file
00082     while ((segTwo) && (endSeg(segTwo)<segOne->begin()))
00083       segTwo=clusterTwo.getSeg();
00084     if ((segTwo) && (segTwo->begin()<=endSeg(segOne))){                                      // there is an  overlap between the two segments
00085       if (debug){
00086         cout << "overlap between :"<<endl;
00087         cout << segOne->sourceName()<<" "<<segOne->begin()<<" "<<segOne->length()<<endl;
00088         cout << segTwo->sourceName()<<" "<<segTwo->begin()<<" "<<segTwo->length()<<endl;   
00089       }
00090       if (segTwo->begin()<=segOne->begin()){                                                  // The seg2 begin before the seg one
00091         if (endSeg(segTwo)>=endSeg(segOne)){                                                  // the seg2 ending after the seg 1, seg1 is suppressed
00092           if (debug) cout << "seg suppressed"<<endl;
00093           overlapTime+=segOne->length();
00094         }
00095         else{                                                                                 // seg2 is ending before seg1, seg1 is shorter
00096           Seg &seg=segServerOutput.createSeg(endSeg(segTwo)+1,endSeg(segOne)-endSeg(segTwo)+1,0,segOne->string(),segOne->sourceName());
00097           if (debug) cout << "Modify1 the seg "<<seg.begin()<<" "<<seg.length()<<endl;   
00098           clusterOutput.add(seg);
00099           overlapTime+=segOne->length()-seg.length();
00100         }
00101       }
00102       else{                                                                                   // Seg2 is beginning after seg1
00103         Seg &seg=segServerOutput.createSeg(segOne->begin(),segTwo->begin()-segOne->begin(),0,segOne->string(),segOne->sourceName());
00104         if (debug) cout << "Modify2 the seg "<<seg.begin()<<" "<<seg.length()<<endl;   
00105         clusterOutput.add(seg);
00106         overlapTime+=segOne->length();
00107         overlapTime-=seg.length();
00108         if (endSeg(segTwo)<endSeg(segOne)){                                                  // Seg2 is ending before SegOne, adding a seg
00109           Seg &seg=segServerOutput.createSeg(endSeg(segTwo)+1,endSeg(segOne)-endSeg(segTwo),0,segOne->string(),segOne->sourceName());
00110           if (debug) cout << "Adding the seg "<<seg.begin()<<" "<<seg.length()<<endl;   
00111           clusterOutput.add(seg);
00112           overlapTime-=seg.length();
00113         } 
00114       } 
00115     }
00116     else{                                                                                     // No overlap, put the seg in the output cluster
00117       Seg & seg=segServerOutput.createSeg(segOne->begin(),segOne->length(),0,segOne->string(),segOne->sourceName());
00118       if (debug) cout << "saving the seg "<<seg.begin()<<" "<<seg.length()<<endl;   
00119       clusterOutput.add(seg);
00120     }
00121   }
00122   if (verbose) cout <<"InterSeg, total overlap time (suppressed frames) ["<<overlapTime<<"]"<<endl;
00123 }
00124 
00125 bool selFrame(double frm,double length, double threshold)
00126 {
00127   return (frm/length>=threshold);  
00128 }
00129 // return in clusterOut the clusterIn after applkying morphological rules
00130 // Apply a time window (length winLength)  and select/converts the label only if 
00131 //    at least selectThreshold of the window frame are in the cluster. 
00132 void morphologicalFilter(SegCluster& clusterIn,SegCluster& clusterOut,
00133                          unsigned long winLength,double selectThreshold)
00134 {
00135   if (verbose) cout <<"beginning the morphological filter"<<endl;
00136   SegServer & segServerOut=clusterOut.getServer();                                       // Get the clusterserver reelated to the output
00137   // Step I
00138   clusterIn.rewind(); 
00139   Seg *segIn=clusterIn.getSeg();                                                         // Will give the current segment in the input
00140   if (segIn!=NULL){
00141     unsigned long suppressedTime=0;
00142     unsigned long addedTime=0;
00143     String label=segIn->string();
00144     String sourceName=segIn->sourceName();
00145     unsigned long winIdx=segIn->begin();                                                 // Pos of the window
00146     unsigned long winFrm=segIn->length();                                                // Number of frames (from the clusterIn) in the window
00147     unsigned long end=endSeg(segIn);
00148     while(segIn!=NULL){                                                                  // General loop for visiting all the seg
00149       while (((segIn=clusterIn.getSeg())!=NULL)&&(endSeg(segIn)<winIdx+winLength)){      // For one window
00150         winFrm+=segIn->length();
00151         end=endSeg(segIn);
00152       }
00153       if ((winFrm)&& selFrame(winFrm,winLength,selectThreshold)){       // A good window is selected
00154         unsigned long length=end-winIdx+1;
00155         Seg &seg=segServerOut.createSeg(winIdx,length,0,label,sourceName);
00156         if (debug) cout << "Adding the seg "<<seg.begin()<<" "<<seg.length()<<endl;   
00157         clusterOut.add(seg);
00158         addedTime+=(length-winFrm);
00159       }
00160       else suppressedTime+=winFrm;
00161       if (segIn!=NULL){                                                                  // It is not the end
00162         winIdx=segIn->begin();
00163         winFrm=segIn->length();
00164         end=endSeg(segIn);
00165       }
00166     }
00167     if (verbose){
00168       long diffTime=addedTime-suppressedTime;
00169       cout <<"morpho filter, suppressed frame["<<suppressedTime<<"] added frame["<<addedTime<<
00170                    "] total["<<diffTime<<"]"<<endl;
00171     }
00172   }
00173 }
00174 void morphologicalFilter(SegCluster& clusterIn,SegCluster& clusterOut,Config &config)
00175 {
00176   unsigned long winLength=config.getParam("winLength").toLong();
00177   double selectThreshold=config.getParam("selectThreshold").toDouble();
00178   morphologicalFilter(clusterIn,clusterOut,winLength,selectThreshold);
00179 }
00180 //-------------------------------------------------------------------------
00181 int labelFusion(Config& config)
00182 {
00183   String extOutput=".lbl";                                               // the extension of the output files    
00184   if (config.existsParam("saveLabelFileExtension")) extOutput=config.getParam("saveLabelFileExtension");   
00185   String pathOutput;//="./";                                                // the path of the output files    
00186   String pathInput;//="./";                                                   // the path of the input files    
00187   if (config.existsParam("labelOutputPath")) pathOutput=config.getParam("labelOutputPath");
00188 
00189   if (config.existsParam("labelInputPath")){
00190         pathInput=config.getParam("labelInputPath");
00191         config.setParam("labelFilesPath", config.getParam("labelInputPath"));
00192   }
00193  
00194   String fileOut=config.getParam("labelOneFilename");
00195   String fileOne=config.getParam("labelOneFilename");
00196   String fileTwo=config.getParam("labelTwoFilename");
00197   if (config.existsParam("outputFilename"))
00198     fileOut=config.getParam("outputFilename");
00199   String labelSelectedFrames=config.getParam("labelSelectedFrames");
00200 
00201   try{
00202     SegServer segServerOne;                
00203     SegServer segServerTwo;     
00204     LabelServer labelServerOne;
00205     LabelServer labelServerTwo;
00206 
00207     loadClusterFile(fileOne,segServerOne,labelServerOne,config);
00208     if (debug) cout <<"label 1 loaded"<<endl;
00209     loadClusterFile(fileTwo,segServerTwo,labelServerTwo,config);
00210     if (debug) cout <<"label 2 loaded"<<endl;
00211     long codeSelectedFrameOne=labelServerOne.getLabelIndexByString(labelSelectedFrames);       // Get the index of the selected cluster
00212     if (codeSelectedFrameOne==-1){                                                             // No data for this model !!!!!!!!!!!!!!
00213       cout << " WARNING - NO DATA with the label["<<labelSelectedFrames<<"] in file ["<<fileOne<<"]"<<endl;
00214       exit(0);
00215     }
00216     long codeSelectedFrameTwo=labelServerTwo.getLabelIndexByString(labelSelectedFrames);       // Get the index of the selected cluster
00217     if (codeSelectedFrameTwo==-1){                                                             // No data for this model !!!!!!!!!!!!!!
00218       cout << " WARNING - NO DATA with the label["<<labelSelectedFrames<<"] in file ["<<fileTwo<<"]"<<endl;
00219     }
00220     SegCluster& clusterOne=segServerOne.getCluster(codeSelectedFrameOne);                 // Gives the cluster of the selected/used segments fil 1    
00221     SegCluster& clusterTwo=segServerTwo.getCluster(codeSelectedFrameTwo);                 // Gives the cluster of the selected/used segments file 2
00222     
00223     SegServer segServerOutput;
00224     SegCluster& clusterInt=segServerOutput.createCluster(0,labelSelectedFrames,clusterOne.sourceName());  
00225     interSeg(clusterOne,clusterTwo,clusterInt);
00226     SegCluster& clusterOutput=segServerOutput.createCluster(1,labelSelectedFrames,clusterOne.sourceName());    
00227     morphologicalFilter(clusterInt,clusterOutput,config);
00228     if (verbose){
00229       unsigned long init=totalFrame(clusterOne);
00230       unsigned long final=totalFrame(clusterOutput);
00231       long suppressed=init-final;
00232       cout <<"File["<<fileOne<<"] Initial number of Frame["<<init<<"] Final number of frame["<<final<<"] Suppressed ["<<suppressed<<"]"<<endl;
00233       cout << "Output the new label file in ["<<pathOutput+fileOut+extOutput <<"]"<<endl;
00234     }
00235     outputLabelFile(clusterOutput,pathOutput+fileOut+extOutput,config);
00236   } // fin try
00237   
00238   
00239   catch (Exception& e)
00240     { 
00241       cout << e.toString().c_str() << endl;
00242     }
00243   return 0;
00244 }
00245 
00246 //-------------------------------------------------------------------------
00247 // Same than labelFusion but applies only the morphological filter one 1 file
00248 int labelMorphing(Config& config)
00249 {
00250   String extOutput=".lbl";                                               // the extension of the output files    
00251   if (config.existsParam("saveLabelFileExtension")) extOutput=config.getParam("saveLabelFileExtension");   
00252   String pathOutput;//="./";                                                // the path of the output files 
00253   String pathInput;//="./";                                                   // the path of the input files 
00254   if (config.existsParam("labelOutputPath")){ pathOutput=config.getParam("labelOutputPath");
00255   }
00256         
00257   if (config.existsParam("labelInputPath")){
00258         pathInput=config.getParam("labelInputPath");
00259         config.setParam("labelFilesPath", config.getParam("labelInputPath"));
00260   }
00261   
00262   String fileIn= config.getParam("labelFilename");
00263   String fileOut=fileIn;
00264   if (config.existsParam("outputFilename"))
00265     fileOut=config.getParam("labelFilename");
00266   String labelSelectedFrames=config.getParam("labelSelectedFrames");
00267 
00268   try{
00269     SegServer segServerIn;                
00270     LabelServer labelServerIn;
00271     loadClusterFile(fileIn,segServerIn,labelServerIn,config);
00272     if (debug) cout <<"label  loaded"<<endl;
00273     long codeSelectedFrameIn=labelServerIn.getLabelIndexByString(labelSelectedFrames);       // Get the index of the selected cluster
00274     if (codeSelectedFrameIn==-1){                                                             // No data for this model !!!!!!!!!!!!!!
00275       cout << " WARNING - NO DATA with the label["<<labelSelectedFrames<<"] in file ["<<fileIn<<"]"<<endl;
00276       exit(0);
00277     }
00278     SegCluster& clusterIn=segServerIn.getCluster(codeSelectedFrameIn);                 // Gives the cluster of the selected/used segments fil 1    
00279    
00280     SegServer segServerOutput;
00281     SegCluster& clusterOutput=segServerOutput.createCluster(1,labelSelectedFrames,clusterIn.sourceName());    
00282     morphologicalFilter(clusterIn,clusterOutput,config);
00283     if (verbose){
00284       unsigned long init=totalFrame(clusterIn);
00285       unsigned long final=totalFrame(clusterOutput);
00286       long suppressed=init-final;
00287       cout <<"File["<<fileIn<<"] Initial number of Frame["<<init<<"] Final number of frame["<<final<<"] Suppressed ["<<suppressed<<"]"<<endl;
00288       cout << "Output the new label file in ["<<fileOut+extOutput <<"]"<<endl;
00289     }
00290 
00291     outputLabelFile(clusterOutput,pathOutput+fileOut+extOutput,config);
00292   } // fin try
00293   
00294   
00295   catch (Exception& e)
00296     { 
00297       cout << e.toString().c_str() << endl;
00298     }
00299   return 0;
00300 }
00301 
00302 
00303 #endif //!defined(ALIZE_LabelFusion_cpp)