Explorar o código

stable version of cluster based feature learning is running

Alexander Freytag %!s(int64=12) %!d(string=hai) anos
pai
achega
d0eb8db132

+ 405 - 54
featureLearning/FeatureLearningClusterBased.cpp

@@ -4,10 +4,12 @@
 #include <iostream>
 
 #include <core/image/FilterT.h>
+#include <core/image/CircleT.h>
+#include <core/image/Convert.h>
 #include <core/vector/VectorT.h>
 
 
-#include <vislearning/baselib/ICETools.h>
+// #include <vislearning/baselib/ICETools.h>
 // 
 #include <vislearning/features/localfeatures/LFonHSG.h>
 #include <vislearning/features/localfeatures/LFColorSande.h>
@@ -28,6 +30,36 @@ using namespace OBJREC;
   //
   //********************************************** 
 
+void FeatureLearningClusterBased::setClusterAlgo( const std::string & _clusterAlgoString, const bool & _setForInitialTraining)
+{
+  //be careful with previously allocated memory
+  if (this->clusterAlgo != NULL)
+    delete clusterAlgo;
+  
+  if (_clusterAlgoString.compare("kmeans") == 0)
+  {
+    if ( _setForInitialTraining )
+      this->clusterAlgo = new OBJREC::KMeans(this->initialNumberOfClusters);
+    else
+      this->clusterAlgo = new OBJREC::KMeans(this->numberOfClustersForNewImage);
+  }
+  else if (_clusterAlgoString.compare("GMM") == 0) 
+  {
+    if ( _setForInitialTraining )
+      this->clusterAlgo = new OBJREC::GMM(this->conf, this->initialNumberOfClusters);
+    else
+      this->clusterAlgo = new OBJREC::GMM(this->conf, this->numberOfClustersForNewImage);      
+  }
+  else
+  {
+    std::cerr << "Unknown cluster algorithm selected, use k-means instead" << std::endl;
+    if ( _setForInitialTraining )      
+      this->clusterAlgo = new OBJREC::KMeans(this->initialNumberOfClusters);
+    else
+      this->clusterAlgo = new OBJREC::KMeans(this->numberOfClustersForNewImage);
+  }    
+}
+
 void FeatureLearningClusterBased::extractFeaturesFromTrainingImages( const OBJREC::MultiDataset *_md, NICE::VVector & examplesTraining )
 {
   examplesTraining.clear();
@@ -79,35 +111,88 @@ void FeatureLearningClusterBased::extractFeaturesFromTrainingImages( const OBJRE
 }
 
 void FeatureLearningClusterBased::train ( const OBJREC::MultiDataset *_md )
-{
- //**********************************************
-  //
-  //     EXTRACT FEATURES FROM TRAINING IMAGES
-  //
-  //**********************************************  
-  
-  std::cerr << " EXTRACT FEATURES FROM TRAINING IMAGES" << std::endl;
+{  
+  bool loadSuccess = this->loadInitialCodebook();
   
-  NICE::VVector examplesTraining;  
-  this->extractFeaturesFromTrainingImages( _md, examplesTraining  );
+  if ( !loadSuccess )
+  {
+    //**********************************************
+    //
+    //     EXTRACT FEATURES FROM TRAINING IMAGES
+    //
+    //**********************************************  
+    
+    std::cerr << " EXTRACT FEATURES FROM TRAINING IMAGES" << std::endl;
+    
+    NICE::VVector examplesTraining;  
+    this->extractFeaturesFromTrainingImages( _md, examplesTraining  );
+    
+    //**********************************************
+    //
+    //    CLUSTER FEATURES FROM TRAINING IMAGES
+    //
+    //    THIS GIVES US AN INITIAL CODEBOOK
+    //
+    //**********************************************  
+    std::cerr << " CLUSTER FEATURES FROM TRAINING IMAGES" << std::endl;
+    //go, go, go...  
+    prototypes.clear();
+    std::vector< double > weights;
+    std::vector< int > assignment;
+    clusterAlgo->cluster ( examplesTraining, prototypes, weights, assignment);
+    weights.clear();
+    assignment.clear();  
+  }
   
-  //**********************************************
-  //
-  //    CLUSTER FEATURES FROM TRAINING IMAGES
-  //
-  //    THIS GIVES US AN INITIAL CODEBOOK
-  //
-  //**********************************************  
-  std::cerr << " CLUSTER FEATURES FROM TRAINING IMAGES" << std::endl;
-  //go, go, go...  
-  prototypes.clear();
-  std::vector< double > weights;
-  std::vector< int > assignment;
-  clusterAlgo->cluster ( examplesTraining, prototypes, weights, assignment);
-  weights.clear();
-  assignment.clear();  
+  this->writeInitialCodebook();
 }
 
+bool FeatureLearningClusterBased::loadInitialCodebook ( )
+{
+  if ( b_loadInitialCodebook  )
+  {
+    std::cerr << " INITIAL CODEBOOK ALREADY COMPUTED - RE-USE IT" << std::endl;    
+    std::cerr << " // WARNING - WE DO NOT VERIFY WHETHER THIS IS THE CORRECT CODEBOOK FOR THIS TRAINING SET!!!!" << std::endl;    
+    
+    prototypes.clear();
+    
+    try
+    {
+      prototypes.read(cacheInitialCodebook);
+    }
+    catch (...)
+    {
+      std::cerr << "Error while loading initial codebook" << std::endl;
+      return false;
+    }  
+    return true;
+  }
+  else
+    return false;
+}
+
+bool FeatureLearningClusterBased::writeInitialCodebook ( )
+{      
+  if (  b_saveInitialCodebook )
+  {
+    std::cerr << " SAVE INITIAL CODEBOOK " << std::endl;
+    
+    try 
+    {
+      prototypes.write( cacheInitialCodebook );
+    }
+    catch (...)
+    {
+      std::cerr << "Error while saving initial codebook" << std::endl;
+      return false;
+    }    
+    return true;
+  } 
+  else
+    return false;
+}
+
+
   //**********************************************
   //
   //                 PUBLIC METHODS
@@ -117,11 +202,8 @@ void FeatureLearningClusterBased::train ( const OBJREC::MultiDataset *_md )
 
 FeatureLearningClusterBased::FeatureLearningClusterBased ( const Config *_conf,
                                const MultiDataset *_md, const std::string & _section )
-    : FeatureLearningGeneric ( _conf )
-{
-  this->section = _section;
-  
-  
+    : FeatureLearningGeneric ( _conf, _section )
+{ 
     //feature stuff
   //! which OpponentSIFT implementation to use {NICE, VANDESANDE}
   std::string opSiftImpl;  
@@ -134,10 +216,15 @@ FeatureLearningClusterBased::FeatureLearningClusterBased ( const Config *_conf,
   writefeat = conf->gB ( "Descriptor", "write", true ); 
   
   showTrainingImages = conf->gB( section, "showTrainingImages", false );  
+  showResults = conf->gB( section, "showResults", false );
+  
+  resultdir = conf->gS( section, "resultdir", "/tmp/");
   
   
   //! define the initial number of clusters our codebook shall contain
   initialNumberOfClusters = conf->gI(section, "initialNumberOfClusters", 10);
+   //! define the number of clusters we want to compute for an unseen image
+  numberOfClustersForNewImage = conf->gI(section, "numberOfClustersForNewImage", 10);
   
   //! define the clustering algorithm to be used
   std::string clusterAlgoString = conf->gS(section, "clusterAlgo", "kmeans");
@@ -199,19 +286,8 @@ FeatureLearningClusterBased::FeatureLearningClusterBased ( const Config *_conf,
     this->featureExtractor = readFeats; 
   }
   
-  if (clusterAlgoString.compare("kmeans") == 0)
-  {
-    clusterAlgo = new OBJREC::KMeans(initialNumberOfClusters);
-  }
-  else if (clusterAlgoString.compare("GMM") == 0) 
-  {
-    clusterAlgo = new OBJREC::GMM(conf, initialNumberOfClusters);
-  }
-  else
-  {
-    std::cerr << "Unknown cluster algorithm selected, use k-means instead" << std::endl;
-    clusterAlgo = new OBJREC::KMeans(initialNumberOfClusters);
-  }
+  this->clusterAlgo = NULL;
+  this->setClusterAlgo( clusterAlgoString, true /*set cluster algo for training*/ );
   
   if (distFunctionString.compare("euclidian") == 0)
   {
@@ -232,7 +308,15 @@ FeatureLearningClusterBased::FeatureLearningClusterBased ( const Config *_conf,
   if ( writeFeats != NULL )
     writeFeats = NULL;
   if ( readFeats != NULL )
-    readFeats  = NULL ;    
+    readFeats  = NULL ;   
+  
+  this->setClusterAlgo( clusterAlgoString, false /*set cluster algo for feature learning*/ );
+  
+  //so far, we have not seen any new image
+  this->newImageCounter = 0;
+  
+  //TODO stupid
+  this->maxValForVisualization = 0.005;
 }
 
 FeatureLearningClusterBased::~FeatureLearningClusterBased()
@@ -246,13 +330,207 @@ FeatureLearningClusterBased::~FeatureLearningClusterBased()
     delete featureExtractor; 
 }
 
-void FeatureLearningClusterBased::learnNewFeatures ( OBJREC::CachedExample *_ce )
+void FeatureLearningClusterBased::learnNewFeatures ( const std::string & _filename )
 {  
+  NICE::ColorImage img( _filename );
+  
+  int xsize ( img.width() );
+  int ysize ( img.height() );
+   
+  //variables to store feature information
+  NICE::VVector newFeatures;
+  NICE::VVector cfeatures;
+  NICE::VVector positions;
+
+  //compute features
+  std::cerr << " EXTRACT FEATURES FROM UNSEEN IMAGE" << std::endl;
+  Globals::setCurrentImgFN ( _filename );
+  featureExtractor->extractFeatures ( img, newFeatures, positions );  
+  
+  //store feature information in larger data structure
+  for ( NICE::VVector::iterator i = newFeatures.begin();
+        i != newFeatures.end();
+        i++)
+  {              
+    //normalization :)
+    i->normalizeL1();
+  }
+  
+  //cluster features
+  std::cerr << " CLUSTER FEATURES FROM UNSEEN IMAGE" << std::endl;
+  NICE::VVector prototypesForNewImage;
+  std::vector< double > weights;
+  std::vector< int > assignment;
+  clusterAlgo->cluster ( newFeatures, prototypesForNewImage, weights, assignment);
+  
+  if ( b_evaluationWhileFeatureLearning )
+  {
+    //visualize new clusters
+    int tmpProtCnt ( 0 );    
+    for (NICE::VVector::const_iterator protIt = prototypesForNewImage.begin(); protIt != prototypesForNewImage.end(); protIt++, tmpProtCnt++)
+    {
+      double distToNewCluster ( std::numeric_limits<double>::max() );
+      int indexOfMostSimFeat( 0 );
+      double tmpDist;
+      int tmpCnt ( 0 );
+      
+      for ( NICE::VVector::iterator i = newFeatures.begin();
+            i != newFeatures.end();
+            i++, tmpCnt++)
+      {
+        tmpDist = this->distFunction->calculate( *i, *protIt );
+        if ( tmpDist < distToNewCluster )
+        {
+          distToNewCluster = tmpDist;
+          indexOfMostSimFeat = tmpCnt;
+        }
+      }
+      
+      int posX ( ( positions[indexOfMostSimFeat] ) [0]  );
+      int posY ( ( positions[indexOfMostSimFeat] ) [1]  );
+      
+      NICE::Circle circ ( Coord( posX, posY), 10 /* radius*/, Color(200,0,255) );
+      img.draw(circ);       
+    }
+    
+    
+    //draw features most similar to old clusters
+    tmpProtCnt = 0;
+    for (NICE::VVector::const_iterator protIt = prototypes.begin(); protIt != prototypes.end(); protIt++, tmpProtCnt++)
+    {
+      double distToNewCluster ( std::numeric_limits<double>::max() );
+      int indexOfMostSimFeat( 0 );
+      double tmpDist;
+      int tmpCnt ( 0 );
+      
+      for ( NICE::VVector::iterator i = newFeatures.begin();
+            i != newFeatures.end();
+            i++, tmpCnt++)
+      {
+        tmpDist = this->distFunction->calculate( *i, *protIt );
+        if ( tmpDist < distToNewCluster )
+        {
+          distToNewCluster = tmpDist;
+          indexOfMostSimFeat = tmpCnt;
+        }
+      }
+      
+      int posX ( ( positions[indexOfMostSimFeat] ) [0]  );
+      int posY ( ( positions[indexOfMostSimFeat] ) [1]  );
+      NICE::Circle circ ( Coord( posX, posY), 5 /* radius*/, Color(200,255,0 ) );
+      img.draw(circ);      
+    }
+    
+    if ( showResults )
+      showImage(img, "Current (new) image and most similar feature for new cluster");     
+    else
+    {
+      std::vector< std::string > list2;
+      StringTools::split ( _filename, '/', list2 );      
+
+      std::string destination ( resultdir + NICE::intToString(this->newImageCounter) + "_" + list2.back() + "_1_oldAndNewClusters.ppm");
+      img.writePPM( destination );
+    }   
+  }
+  
+  //compute score for every cluster: #assigned features * distance to current cluster centers
+  
+  NICE::Vector distancesToCurrentClusters ( numberOfClustersForNewImage, 0.0 );
+  NICE::Vector clusterSizes ( numberOfClustersForNewImage, 0.0 ); //i.e., the number of assignments, or a derived number
+   
+  //compute "relevance" of every new cluster
+ 
+  std::cerr << " COMPUTE SIZES OF NEW CLUSTERS" << std::endl;
+  for (std::vector<int>::const_iterator assignIt = assignment.begin(); assignIt != assignment.end(); assignIt++)
+  {
+    clusterSizes[*assignIt]++;
+  }
+  clusterSizes.normalizeL1();
+  
+  std::cerr << "cluster Sizes: " << clusterSizes << std::endl;
+  
+  
+  //compute distances of new cluster centers to old cluster centers
+  std::cerr << " COMPUTE DISTANCES BETWEEN NEW AND OLD CLUSTERS" << std::endl;
+  NICE::Vector::iterator distanceIt = distancesToCurrentClusters.begin();
+  for ( NICE::VVector::const_iterator newProtIt = prototypesForNewImage.begin(); newProtIt != prototypesForNewImage.end(); newProtIt++, distanceIt++)
+  {
+    double minDist ( std::numeric_limits<double>::max() );
+    double tmpDist;
+    for ( NICE::VVector::const_iterator protIt = prototypes.begin(); protIt != prototypes.end(); protIt ++)
+    {
+        //compute distance
+        tmpDist = this->distFunction->calculate( *protIt, *newProtIt );
+        if (tmpDist < minDist)
+          minDist = tmpDist;      
+    }
+    
+    *distanceIt = minDist;
+  }
+  
+  std::cerr << "distances: " << distancesToCurrentClusters << std::endl;
+  
+  //compute final scores for the new image
+  NICE::Vector clusterScores ( numberOfClustersForNewImage, 0.0 );
+  for (uint i = 0; i < numberOfClustersForNewImage; i++)
+  {
+      clusterScores[i] = clusterSizes[i] * distancesToCurrentClusters[i];
+  }
+  
+  std::cerr << "final cluster scores for new image: " << clusterScores << std::endl;
+    
+  NICE::Vector chosenClusterCenter ( prototypesForNewImage[ clusterScores.MaxIndex()  ] );
+  
+  
+  //include the chosen information into the currently used prototypes
+  prototypes.push_back( chosenClusterCenter );
+  
+  if ( b_evaluationWhileFeatureLearning ) 
+  {
+    
+    NICE::ColorImage imgTmp( _filename );
+    
+    double distToNewCluster ( std::numeric_limits<double>::max() );
+    int indexOfMostSimFeat( 0 );
+    double tmpDist;
+    int tmpCnt ( 0 );
+    
+    for ( NICE::VVector::iterator i = newFeatures.begin();
+          i != newFeatures.end();
+          i++, tmpCnt++)
+    {
+      tmpDist = this->distFunction->calculate( *i, chosenClusterCenter );
+      if ( tmpDist < distToNewCluster )
+      {
+        distToNewCluster = tmpDist;
+        indexOfMostSimFeat = tmpCnt;
+      }
+    }
+    
+    int posX ( ( positions[indexOfMostSimFeat] ) [0]  );
+    int posY ( ( positions[indexOfMostSimFeat] ) [1]  );
+    NICE::Circle circ ( Coord( posX, posY), 10 /* radius*/, Color(200,0,255) );
+    imgTmp.draw(circ); 
+    
+    if ( showResults )
+      showImage(imgTmp, "Current (new) image and most similar feature for new cluster"); 
+    else 
+    {
+      std::vector< std::string > list2;
+      StringTools::split ( _filename, '/', list2 );      
+
+      std::string destination ( resultdir + NICE::intToString(this->newImageCounter) + "_" + list2.back() + "_2_bestNewCluster.ppm");
+      imgTmp.writePPM( destination );
+    }
+  }
+  
+  //this was a new image, so we increase our internal counter
+  (this->newImageCounter)++;  
 }
 
-void FeatureLearningClusterBased::evaluateCurrentCodebook ( const std::string & filename )
+NICE::FloatImage FeatureLearningClusterBased::evaluateCurrentCodebook ( const std::string & _filename , const bool & beforeComputingNewFeatures )
 {
-    NICE::ColorImage img( filename );
+    NICE::ColorImage img( _filename );
     if ( showTrainingImages )
     {
       showImage( img, "Input" );    
@@ -267,7 +545,7 @@ void FeatureLearningClusterBased::evaluateCurrentCodebook ( const std::string &
     NICE::VVector positions;
 
     //compute features
-    Globals::setCurrentImgFN ( filename );
+    Globals::setCurrentImgFN ( _filename );
     featureExtractor->extractFeatures ( img, features, positions );
     
     FloatImage noveltyImage ( xsize, ysize );
@@ -289,7 +567,7 @@ void FeatureLearningClusterBased::evaluateCurrentCodebook ( const std::string &
       for (NICE::VVector::const_iterator it =  prototypes.begin(); it != prototypes.end(); it++)
       {
         //compute distance
-        double tmpDist ( distFunction->calculate(*i,*it) );
+        double tmpDist ( this->distFunction->calculate(*i,*it) );
         if (tmpDist < minDist)
           minDist = tmpDist;
       }
@@ -307,14 +585,87 @@ void FeatureLearningClusterBased::evaluateCurrentCodebook ( const std::string &
     float sigma ( 3.0 );
     FilterT<float, float, float> filter;
     filter.filterGaussSigmaApproximate ( noveltyImage, sigma, &noveltyImageGaussFiltered );
+    double maxFiltered ( noveltyImageGaussFiltered.max() );
 
-    std::cerr << "maximum distance of Training images: " << maxDist;      
+    std::cerr << "maximum distance of Training images: " << maxDist << std::endl;  
+    std::cerr << "maximum distance of Training images after filtering: " << maxFiltered << std::endl;      
+    if ( beforeComputingNewFeatures )
+      this->oldMaxDist = maxFiltered;
     //for suitable visualization of scores between zero (known) and one (unknown)
 //       noveltyImageGaussFiltered( 0 , 0 ) = std::max<double>(maxDist, 1.0);
 
     
     //convert float to RGB
     NICE::ColorImage noveltyImageRGB ( xsize, ysize  );
-    ICETools::convertToRGB ( noveltyImageGaussFiltered, noveltyImageRGB );
-    showImage(noveltyImageRGB, "Novelty Image");  
-}
+//     ICETools::convertToRGB ( noveltyImageGaussFiltered, noveltyImageRGB );
+    if ( beforeComputingNewFeatures )
+    {
+      imageToPseudoColorWithRangeSpecification( noveltyImageGaussFiltered, noveltyImageRGB, 0 /* min */, maxValForVisualization /* maxFiltered*/ /* max */ );
+      std::cerr << "set max value to: " << noveltyImageGaussFiltered.max() << std::endl;
+    }
+    else
+    {
+      imageToPseudoColorWithRangeSpecification( noveltyImageGaussFiltered, noveltyImageRGB, 0 /* min */, maxValForVisualization /*this->oldMaxDist*/ /* max */ );
+      std::cerr << "set max value to: " << this->oldMaxDist << std::endl;
+    }
+    
+    
+    
+    if ( showResults )
+      showImage(noveltyImageRGB, "Novelty Image");  
+    else 
+    {
+      std::vector< std::string > list2;
+      StringTools::split ( _filename, '/', list2 );      
+
+      std::string destination ( resultdir + NICE::intToString(this->newImageCounter -1 ) + "_" + list2.back() + "_3_updatedNoveltyMap.ppm");
+      if ( beforeComputingNewFeatures )
+        destination  =  resultdir + NICE::intToString(this->newImageCounter) + "_" + list2.back() + "_0_initialNoveltyMap.ppm";
+
+      noveltyImageRGB.writePPM( destination );
+    }
+    
+    
+    // now look where the closest features for the current cluster indices are
+    int tmpProtCnt ( 0 );
+    for (NICE::VVector::const_iterator protIt = prototypes.begin(); protIt != prototypes.end(); protIt++, tmpProtCnt++)
+    {
+      double distToNewCluster ( std::numeric_limits<double>::max() );
+      int indexOfMostSimFeat( 0 );
+      double tmpDist;
+      int tmpCnt ( 0 );
+      
+      for ( NICE::VVector::iterator i = features.begin();
+            i != features.end();
+            i++, tmpCnt++)
+      {
+        tmpDist = this->distFunction->calculate( *i, *protIt );
+        if ( tmpDist < distToNewCluster )
+        {
+          distToNewCluster = tmpDist;
+          indexOfMostSimFeat = tmpCnt;
+        }
+      }
+      
+      int posX ( ( positions[indexOfMostSimFeat] ) [0]  );
+      int posY ( ( positions[indexOfMostSimFeat] ) [1]  );
+      NICE::Circle circ ( Coord( posX, posY), 2*tmpProtCnt /* radius*/, Color(200,0,255 ) );
+      img.draw(circ);  
+    }
+    
+   if ( showResults )
+    showImage(img, "Current image and most similar features for current cluster"); 
+   else
+   {
+      std::vector< std::string > list2;
+      StringTools::split ( _filename, '/', list2 );      
+
+      std::string destination ( resultdir + NICE::intToString(this->newImageCounter-1) + "_" + list2.back() + "_3_updatedCurrentCluster.ppm");
+      if ( beforeComputingNewFeatures )
+        destination  =  resultdir + NICE::intToString(this->newImageCounter) + "_" + list2.back() + "_0_initialCurrentCluster.ppm";
+      
+      img.writePPM( destination );
+   }
+   
+   return noveltyImageGaussFiltered;
+}

+ 19 - 5
featureLearning/FeatureLearningClusterBased.h

@@ -27,13 +27,13 @@ namespace OBJREC
   {
 
     protected:
-      
-      std::string section;
-      
+           
       bool showTrainingImages;
       
       //! define the initial number of clusters our codebook shall contain
       int initialNumberOfClusters;       
+      //! define the number of clusters we want to compute for an unseen image
+      int numberOfClustersForNewImage;      
   
       OBJREC::ClusterAlgorithm * clusterAlgo;
       NICE::VectorDistance<double> * distFunction;
@@ -42,16 +42,30 @@ namespace OBJREC
       
       NICE::VVector prototypes;     
       
+      bool showResults;
+      std::string resultdir;
+      
+      int newImageCounter;
+      //just needed for visualisation
+      double oldMaxDist;
+      //TODO stupid!!!
+      double maxValForVisualization;
+            
       /************************
        * 
        *   protected methods
        * 
        **************************/
       
+      void setClusterAlgo( const std::string & _clusterAlgoString, const bool & _setForInitialTraining);
+      
       void extractFeaturesFromTrainingImages(  const OBJREC::MultiDataset *_md,  NICE::VVector & examplesTraining  );
       
       void train ( const OBJREC::MultiDataset *_md ); 
       
+      virtual bool loadInitialCodebook ( );
+      virtual bool writeInitialCodebook ( );
+      
       
 
 
@@ -69,9 +83,9 @@ namespace OBJREC
       /** this function has to be overloaded by all subclasses
           @param imgWithNovelContent 
       */
-      virtual void learnNewFeatures ( OBJREC::CachedExample *_ce ) ;
+      virtual void learnNewFeatures ( const std::string & filename ) ;
       
-      virtual void evaluateCurrentCodebook ( const std::string & filename );
+      virtual NICE::FloatImage evaluateCurrentCodebook ( const std::string & filename , const bool & beforeComputingNewFeatures = true );
       
 
   };

+ 9 - 1
featureLearning/FeatureLearningGeneric.cpp

@@ -15,9 +15,17 @@ using namespace OBJREC;
 using namespace std;
 using namespace NICE;
 
-FeatureLearningGeneric::FeatureLearningGeneric ( const Config *_conf )
+FeatureLearningGeneric::FeatureLearningGeneric ( const Config *_conf, const std::string & _section )
 {
+  this->section = _section;  
   this->conf = _conf;  
+  
+  this->cacheInitialCodebook = this->conf->gS(this->section, "cacheInitialCodebook", "");
+  this-> b_loadInitialCodebook = this->conf->gB(this->section, "loadInitialCodebook", "");
+  this-> b_saveInitialCodebook = this->conf->gB(this->section, "saveInitialCodebook", "");  
+  
+  this->b_evaluationWhileFeatureLearning = this->conf->gB( this->section, "evaluationWhileFeatureLearning", false );
+  
   Preprocess::Init ( _conf );
 }
 

+ 43 - 10
featureLearning/FeatureLearningGeneric.h

@@ -12,6 +12,7 @@
 
 #include <string>
 #include <core/basics/Config.h>
+#include <core/image/ImageT.h>
 #include <vislearning/cbaselib/CachedExample.h>
 
 
@@ -24,30 +25,62 @@ namespace OBJREC
 
     protected:   
       
-    //! Configuration File
-    const NICE::Config *conf;      
+      //! section information for parsing config files
+      std::string section;      
+      
+      //! Configuration File
+      const NICE::Config *conf;    
+      
+      //! where should an initial codebook be located, i.e., read from and written to?
+      std::string cacheInitialCodebook;
+      //! was an initial codebook already computed?
+      bool b_loadInitialCodebook;
+      //!shall the initially computed codebook be stored somewhere?
+      bool  b_saveInitialCodebook; 
+      
+      //! additional evaluation in the process of feature learning, e.g., visualize the most useful features in the current image
+      bool b_evaluationWhileFeatureLearning;
+      
+      /**
+       * @brief Load a previously computed codebook which serves as initial codebook
+       * @author Alexander Freytag
+       * @date 17-04-2013 (dd-mm-yyyy)
+       * @return bool (success of loading)
+       * @note This function has to be overloaded by all subclasses!
+       */
+      virtual bool loadInitialCodebook ( ) = 0;
+      
+      /**
+       * @brief Store the initially computed codebook
+       * @author Alexander Freytag
+       * @date 17-04-2013 (dd-mm-yyyy)
+       * @return bool (success of writing) 
+       * @note This function has to be overloaded by all subclasses!
+       */      
+      virtual bool writeInitialCodebook ( ) = 0;
       
 
     public:
 
-      /** simple constructor
-          @param conf global settings
+      /**
+       * @brief  simple constructor
+       * @author Alexander Freytag
+       * @date 16-04-2013 (dd-mm-yyyy)
+       * @param _conf global settings
+       * @param _section section information for parsing config files
       */
-      FeatureLearningGeneric ( const NICE::Config *_conf );
+      FeatureLearningGeneric ( const NICE::Config *_conf, const std::string & _section = "featureLearning" );
 
       /** simple destructor */
       virtual ~FeatureLearningGeneric();
 
       
-      //TODO  possibly move this to protected...
       /** this function has to be overloaded by all subclasses
           @param imgWithNovelContent 
       */
-      virtual void learnNewFeatures ( OBJREC::CachedExample *_ce ) = 0;  
-
-      virtual void learnNewFeatures ( const std::string & _filename );    
+      virtual void learnNewFeatures ( const std::string & _filename) = 0;  
       
-      virtual void evaluateCurrentCodebook ( const std::string & filename ) = 0;
+      virtual NICE::FloatImage evaluateCurrentCodebook ( const std::string & filename , const bool & beforeComputingNewFeatures = true) = 0;
   
 
   };

+ 77 - 11
featureLearning/progs/testFeatureLearning.cpp

@@ -10,6 +10,7 @@
 
 #include <core/basics/Config.h>
 #include <core/basics/ResourceStatistics.h>
+#include <core/image/Convert.h>
 #include <core/vector/VectorT.h>
 
 #include <vislearning/baselib/Globals.h>
@@ -40,6 +41,7 @@ int main( int argc, char **argv )
   
   ResourceStatistics rs;
   std::string resultdir;
+  resultdir = conf->gS( "featureLearning", "resultdir", "/tmp/");
   
   
   //**********************************************
@@ -69,13 +71,14 @@ int main( int argc, char **argv )
   //evaluate how well the training images are covered with our initial codebook
   //that is, compute these nice "novelty maps" per feature
   
-  LOOP_ALL_S( *trainFiles )
-  {
-      EACH_INFO( classno, info );
-      std::string filename = info.img();
-      
-      featureLearning->evaluateCurrentCodebook( filename );       
-  }
+  //NOTE we skip this currently
+//   LOOP_ALL_S( *trainFiles )
+//   {
+//       EACH_INFO( classno, info );
+//       std::string filename = info.img();
+//       
+//       featureLearning->evaluateCurrentCodebook( filename , true /* beforeComputingNewFeatures */);       
+//   }
 
   //**********************************************
   //
@@ -87,15 +90,78 @@ int main( int argc, char **argv )
   //
   //**********************************************
   const LabeledSet *testFiles = md["test"];
-  
+    
   std::cerr << "start looping over all files" << std::endl;
+  int imageCnt ( 0 );
   LOOP_ALL_S( *testFiles )
   {
       EACH_INFO( classno, info );
-      std::string file = info.img();
+      std::string filename = info.img();
+      
+      NICE::ColorImage orig( filename );
+//       showImage( orig, "Input" );
+      
+      NICE::FloatImage noveltyImageBefore;
+      noveltyImageBefore = featureLearning->evaluateCurrentCodebook( filename , true /* beforeComputingNewFeatures */ );
+      
+      //**********************************************
+      //
+      //             IS THIS IMAGE NOVEL?
+      //
+      //          IF NOT, GO TO THE NEXT ONE
+      //
+      //**********************************************       
+      
+      //TODO currently hard coded, and only stupid averaging of feature novelties for whole image, and hard thresholding!
+      double meanNovelty ( 0.0 );
+      for ( uint y = 0 ; y < ( uint ) noveltyImageBefore.height() ; y++ )
+      {
+        for ( uint x = 0 ; x < ( uint ) noveltyImageBefore.width(); x++ )
+        {
+          meanNovelty += noveltyImageBefore(x,y);
+        }
+      }      
+      int imageSize ( noveltyImageBefore.height() * noveltyImageBefore.width() );
+      meanNovelty /= imageSize;
+      
+      double stupidThreshold ( 0.0022 );
+      
+      std::cerr << "  NOVELTY SCORE FOR CURRENT IMAGE: " <<  meanNovelty << " -- threshold: " << stupidThreshold << std::endl;
+      if ( meanNovelty < stupidThreshold )
+      {
+        std::cerr << " --- NOT NOVEL --- " << std::endl;
+        continue;
+      }
+      else
+      {
+        std::cerr << " --- NOVEL --- " << std::endl;
+      }
+      
+      
+      featureLearning->learnNewFeatures( filename );
+      
+      NICE::FloatImage noveltyImageAfter;
+      noveltyImageAfter = featureLearning->evaluateCurrentCodebook( filename , false /* beforeComputingNewFeatures */ );
+      
+      NICE::FloatImage noveltyImageDifference ( noveltyImageAfter.width(), noveltyImageAfter.height());
+      for ( uint y = 0 ; y < ( uint ) noveltyImageAfter.height() ; y++ )
+      {
+        for ( uint x = 0 ; x < ( uint ) noveltyImageAfter.width(); x++ )
+        {
+          noveltyImageDifference(x,y) = fabs ( noveltyImageBefore(x,y) - noveltyImageAfter(x,y) );
+        }
+      }
+      
+      NICE::ColorImage noveltyImageDifferenceRGB (noveltyImageAfter.width(), noveltyImageAfter.height() );
+      imageToPseudoColor( noveltyImageDifference, noveltyImageDifferenceRGB );      
+      
+      std::vector< std::string > list2;
+      StringTools::split ( filename, '/', list2 );      
+
+      std::string destination ( resultdir + NICE::intToString(imageCnt) + "_" + list2.back() + "_4_differenceOfNovelties.ppm");
+      noveltyImageDifferenceRGB.writePPM( destination );      
       
-      NICE::ColorImage orig( file );
-      showImage( orig, "Input" );
+      imageCnt++;
   } //Loop over all test images
   
    return 0;