Pārlūkot izejas kodu

Merge branch 'master', remote-tracking branch 'alex/featureLearning'

Christoph Käding 12 gadi atpakaļ
vecāks
revīzija
47af3861bb
60 mainītis faili ar 6057 papildinājumiem un 734 dzēšanām
  1. 40 3
      cbaselib/MultiDataset.cpp
  2. 45 18
      classifier/genericClassifierSelection.h
  3. 296 0
      featureLearning/FeatureLearningClusterBased.cpp
  4. 69 0
      featureLearning/FeatureLearningClusterBased.h
  5. 43 0
      featureLearning/FeatureLearningGeneric.cpp
  6. 130 0
      featureLearning/FeatureLearningGeneric.h
  7. 707 0
      featureLearning/FeatureLearningPrototypes.cpp
  8. 127 0
      featureLearning/FeatureLearningPrototypes.h
  9. 324 0
      featureLearning/FeatureLearningRegionBased.cpp
  10. 73 0
      featureLearning/FeatureLearningRegionBased.h
  11. 8 0
      featureLearning/Makefile
  12. 103 0
      featureLearning/Makefile.inc
  13. 7 0
      featureLearning/libdepend.inc
  14. 8 0
      featureLearning/progs/Makefile
  15. 88 0
      featureLearning/progs/Makefile.inc
  16. 7 0
      featureLearning/progs/libdepend.inc
  17. 346 0
      featureLearning/progs/testFeatureLearning.cpp
  18. 159 58
      features/localfeatures/GenericLFSelection.h
  19. 4 0
      features/localfeatures/GenericLocalFeatureSelection.h
  20. 16 14
      features/localfeatures/IDRandomSampling.h
  21. 7 3
      features/localfeatures/LFColorSande.cpp
  22. 29 27
      features/localfeatures/LFGenericLocal.h
  23. 2 0
      features/localfeatures/LFReadCache.tcc
  24. 5 4
      features/localfeatures/LFWriteCache.cpp
  25. 54 49
      features/localfeatures/LFWriteCache.h
  26. 27 24
      features/localfeatures/LocalFeature.h
  27. 14 12
      features/localfeatures/LocalFeatureLFInterface.cpp
  28. 44 41
      features/localfeatures/LocalFeatureLFInterface.h
  29. 8 2
      features/localfeatures/LocalFeatureRepresentation.h
  30. 1 1
      features/localfeatures/libdepend.inc
  31. 142 0
      features/simplefeatures/BoWFeatureConverter.cpp
  32. 101 0
      features/simplefeatures/BoWFeatureConverter.h
  33. 106 28
      features/simplefeatures/Codebook.cpp
  34. 160 64
      features/simplefeatures/Codebook.h
  35. 116 68
      features/simplefeatures/CodebookPrototypes.cpp
  36. 60 31
      features/simplefeatures/CodebookPrototypes.h
  37. 5 5
      math/cluster/GMM.cpp
  38. 0 1
      math/cluster/GMM.h
  39. 256 241
      math/cluster/KMeans.cpp
  40. 99 39
      math/cluster/KMeans.h
  41. 431 0
      math/cluster/KMedian.cpp
  42. 131 0
      math/cluster/KMedian.h
  43. 157 0
      math/cluster/RandomClustering.cpp
  44. 112 0
      math/cluster/RandomClustering.h
  45. 89 0
      math/cluster/tests/Makefile.inc
  46. 89 0
      math/cluster/tests/TestKMedian.cpp
  47. 32 0
      math/cluster/tests/TestKMedian.h
  48. 1 1
      math/pdf/tests/TestPDF.cpp
  49. 8 0
      noveltyDetection/Makefile
  50. 103 0
      noveltyDetection/Makefile.inc
  51. 72 0
      noveltyDetection/NDCodebookLevelImagePooling.cpp
  52. 97 0
      noveltyDetection/NDCodebookLevelImagePooling.h
  53. 24 0
      noveltyDetection/NoveltyDetector.cpp
  54. 84 0
      noveltyDetection/NoveltyDetector.h
  55. 230 0
      noveltyDetection/NoveltyDetectorCodebookLevel.cpp
  56. 125 0
      noveltyDetection/NoveltyDetectorCodebookLevel.h
  57. 6 0
      noveltyDetection/libdepend.inc
  58. 8 0
      noveltyDetection/progs/Makefile
  59. 5 0
      noveltyDetection/progs/libdepend.inc
  60. 417 0
      progs/evaluateCompleteBoWPipeline.cpp

+ 40 - 3
cbaselib/MultiDataset.cpp

@@ -42,8 +42,12 @@ void MultiDataset::selectExamples ( const std::string & examples_command,
     vector<string> parts;
     StringTools::split ( cmd, ' ', parts );
 
-    if ( (parts.size() != 3) && ((parts.size() != 2) || (parts[0] != "all")) )
+    if ( (parts.size() != 3) && ((parts.size() != 2) || (parts[0].compare("all") != 0)) )
+    {
+      std::cerr << "parts.size(): "<<parts.size()<<std::endl;
+      std::cerr << " parts[0]: " << parts[0] << std::endl;
       fthrow( Exception, "Syntax error " << examples_command );
+    }
 
     const std::string & mode = parts[0];
     const std::string & csel = parts[1];
@@ -135,13 +139,27 @@ void MultiDataset::selectExamples ( const std::string & examples_command,
 /** MultiDataset ------- constructor */
 MultiDataset::MultiDataset( const Config *conf , LabeledSetFactory *pSetFactory)
 {
+  //read all blocks from our config file
   std::set<string> blocks;
   conf->getAllBlocks ( blocks );
+  
+#ifdef DEBUG_MultiDataset  
+  std::cerr << "found the following config blocks: " << std::endl;
+  for ( std::set<string>::const_iterator blockIt = blocks.begin(); blockIt != blocks.end(); blockIt++)
+  {
+    std::cerr << *blockIt << " ";
+  }
+  std::cerr << std::endl;
+#endif  
 
   lfl.setFactory( pSetFactory );
 
+  //for every dataset (e.g., train and test), we store a single confog file
   map<string, Config> dsconfs;
+  //for every dataset (e.g., train and test), we store the position of the file directory
   map<string, string> dirs;
+  
+  //first of all, remove all blocks which do correspond to specified datasets, i.e., that do not contain a "dataset" entry
   for ( set<string>::iterator i = blocks.begin();
         i != blocks.end();  )
   {
@@ -165,7 +183,17 @@ MultiDataset::MultiDataset( const Config *conf , LabeledSetFactory *pSetFactory)
       i++;
     }
   }
+  
+#ifdef DEBUG_MultiDataset  
+  std::cerr << "found the following datasets within all config blocks: " << std::endl;
+  for ( std::set<string>::const_iterator blockIt = blocks.begin(); blockIt != blocks.end(); blockIt++)
+  {
+    std::cerr << *blockIt << " ";
+  }
+  std::cerr << std::endl;  
+#endif
 
+  //is there a dataset specified that contains images for both, training and testing?
   if ( blocks.find("traintest") != blocks.end() )
   {
     LabeledSet ls_base;
@@ -217,11 +245,13 @@ MultiDataset::MultiDataset( const Config *conf , LabeledSetFactory *pSetFactory)
     datasets["train"] = ls_train;
   }
 
+  //now read files for every specified dataset (e.g., train and test)
   for ( set<string>::const_iterator i = blocks.begin();
         i != blocks.end();
         i++ )
   {
     std::string name = *i;
+    std::cerr << "read: " << name << std::endl;
     if ( classnames.find(name) != classnames.end() )
       continue;
 
@@ -254,8 +284,11 @@ MultiDataset::MultiDataset( const Config *conf , LabeledSetFactory *pSetFactory)
 #endif
         classnames[name].readFromConfig ( dsconfs[name], classselection );
     }
-		
-
+    
+#ifdef DEBUG_MultiDataset
+    std::cerr << "we set up everything to read this dataset - so now call lfl.get" << std::endl;
+#endif
+    
     lfl.get (   dirs[name],
                 dsconfs[name],
                 classnames[name],
@@ -270,6 +303,10 @@ MultiDataset::MultiDataset( const Config *conf , LabeledSetFactory *pSetFactory)
     fprintf (stderr, "MultiDataset: all information about %s set obtained ! (size %d)\n", name.c_str(), ls_base.count() );
 #endif
 
+#ifdef DEBUG_MultiDataset    
+    std::cerr << "we now call selectExamples to pick only a subset if desired" << std::endl;
+#endif
+    
     std::string examples = conf->gS(name, "examples", "all *" );
     selectExamples ( examples, ls_base, ls, dummy, classnames[name] );
 

+ 45 - 18
classifier/genericClassifierSelection.h

@@ -1,8 +1,14 @@
 #ifndef _NICE_OBJREC_GENERICCLASSIFIERSELECTION_INCLUDE
 #define _NICE_OBJREC_GENERICCLASSIFIERSELECTION_INCLUDE
 
+//STL
+#include <vector>
+
+//core
 #include "core/basics/StringTools.h"
 
+
+//vislearning  -- vector classifiers
 #include "vislearning/classifier/vclassifier/VCAmitSVM.h"
 #include "vislearning/classifier/vclassifier/VCNearestClassMean.h"
 #include "vislearning/classifier/vclassifier/VCSimpleGaussian.h"
@@ -12,18 +18,9 @@
 #include "vislearning/classifier/vclassifier/VCOneVsOne.h"
 #include "vislearning/classifier/vclassifier/VCOneVsAll.h"
 #include "vislearning/classifier/vclassifier/VCDTSVM.h"
+#include "vislearning/classifier/vclassifier/VCTransform.h"
 
-
-#ifdef NICE_USELIB_SVMLIGHT
-#include "vislearning/classifier/vclassifier/VCSVMLight.h"
-#include "vislearning/classifier/kernelclassifier/KCSVMLight.h"
-#include "vislearning/classifier/vclassifier/VCSVMOneClass.h"
-#endif
-
-#ifdef NICE_USELIB_NICEDTSVM
-#include "nice-dtsvm/VCTreeBasedClassifier.h"
-#endif
-
+//vislearning -- kernel classifiers
 #include "vislearning/classifier/kernelclassifier/KCGPRegression.h"
 #include "vislearning/classifier/kernelclassifier/KCGPLaplace.h"
 #include "vislearning/classifier/kernelclassifier/KCGPLaplaceOneVsAll.h"
@@ -31,22 +28,42 @@
 #include "vislearning/classifier/kernelclassifier/KCGPRegOneVsAll.h"
 #include "vislearning/classifier/kernelclassifier/KCMinimumEnclosingBall.h"
 #include "vislearning/classifier/kernelclassifier/KCGPOneClass.h"
+
+//vislearning -- kernels
 #include "vislearning/math/kernels/KernelStd.h"
 #include "vislearning/math/kernels/KernelExp.h"
 #include "vislearning/math/kernels/KernelRBF.h"
+#include "vislearning/math/kernels/genericKernel.h"
 
-#include "vislearning/classifier/vclassifier/VCTransform.h"
-
+//vislearning -- feature pool classifier
 #include "vislearning/classifier/fpclassifier/boosting/FPCBoosting.h"
 #include "vislearning/classifier/fpclassifier/randomforest/FPCRandomForests.h"
 #include "vislearning/classifier/fpclassifier/randomforest/FPCDecisionTree.h"
 #include "vislearning/classifier/fpclassifier/logisticregression/FPCSMLR.h"
 
+//vislearning -- classifier combinations
 #include "vislearning/classifier/classifiercombination/VCPreRandomForest.h"
 
-#include "vislearning/math/kernels/genericKernel.h"
 
-#include <vector>
+//vislearning -- SVM-based classifiers (vclassifier, kernelclassifier)
+#ifdef NICE_USELIB_SVMLIGHT
+#include "vislearning/classifier/vclassifier/VCSVMLight.h"
+#include "vislearning/classifier/vclassifier/VCSVMOneClass.h"
+#include "vislearning/classifier/kernelclassifier/KCSVMLight.h"
+#endif
+
+//external stuff
+#ifdef NICE_USELIB_NICEDTSVM
+#include "nice-dtsvm/VCTreeBasedClassifier.h"
+#endif
+
+// #include "gp-hik-exp/GPHIKClassifierNICE.h"
+
+
+
+
+
+
 
 namespace OBJREC {
 
@@ -69,11 +86,21 @@ class GenericClassifierSelection
      } else if ( classifier_type == "nearest_classmean" ) {
         classifier = new VCNearestClassMean( conf, new NICE::EuclidianDistance<double>() );
 #endif
-      } else if ( classifier_type == "random_forest" ) {
+      }
+      ////////////////////////////////////////
+      //                                    //
+      //    all Feature Pool Classifiers    //
+      //                                    //
+      ////////////////////////////////////////
+//       else if ( classifier_type == "GPHIK" ) {
+//         FeaturePoolClassifier *fpc = new GPHIKClassifierNICE ( conf, "GPHIK" );
+//         classifier = new VCFeaturePool ( conf, fpc );
+//       }      
+      else if ( classifier_type == "random_forest" ) {
         FeaturePoolClassifier *fpc = new FPCRandomForests ( conf, "RandomForest" );
         classifier = new VCFeaturePool ( conf, fpc );
-
-      } else if ( classifier_type == "sparse_logistic_regression" ) {
+      }
+      else if ( classifier_type == "sparse_logistic_regression" ) {
         FeaturePoolClassifier *fpc = new FPCSMLR ( conf, "SparseLogisticRegression" );
         classifier = new VCFeaturePool ( conf, fpc );
 

+ 296 - 0
featureLearning/FeatureLearningClusterBased.cpp

@@ -0,0 +1,296 @@
+
+#include "FeatureLearningClusterBased.h"
+
+//STL
+#include <iostream>
+
+//core
+#include <core/image/FilterT.h>
+#include <core/image/CircleT.h>
+#include <core/image/Convert.h>
+// 
+#include <core/vector/VectorT.h>
+
+//vislearning
+#include <vislearning/baselib/Globals.h>
+// 
+#include <vislearning/math/cluster/KMeans.h>
+#include <vislearning/math/cluster/GMM.h>
+
+
+using namespace std;
+using namespace NICE;
+using namespace OBJREC;
+
+  //**********************************************
+  //
+  //                 PROTECTED METHODS
+  //
+  //********************************************** 
+
+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);
+  }    
+}
+
+
+  //**********************************************
+  //
+  //                 PUBLIC METHODS
+  //
+  //********************************************** 
+
+
+FeatureLearningClusterBased::FeatureLearningClusterBased ( const Config *_conf,
+                               const MultiDataset *_md, const std::string & _section )
+    : FeatureLearningPrototypes ( _conf, _md, _section )
+{ 
+  
+   // define the number of clusters we want to compute for an unseen image
+  numberOfClustersForNewImage = conf->gI(section, "numberOfClustersForNewImage", 10);
+   
+  //**********************************************
+  //
+  //      SET UP VARIABLES AND METHODS
+  //             - FEATURE TYPE
+  //             - CLUSTERING ALGO
+  //             - DISTANCE FUNCTION
+  //             - ...
+  //
+  //**********************************************  
+  
+   
+  //run the training to initially compute a codebook and stuff like that
+//  this->train( _md );
+   
+  // define the clustering algorithm to be used
+  std::string clusterAlgoString = conf->gS(section, "clusterAlgo", "kmeans");  
+  this->setClusterAlgo( clusterAlgoString, false /*set cluster algo for feature learning*/ );
+}
+
+FeatureLearningClusterBased::~FeatureLearningClusterBased()
+{
+  // clean-up
+}
+
+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 );  
+  
+  //normalization :)
+  for ( NICE::VVector::iterator i = newFeatures.begin();
+        i != newFeatures.end();
+        i++)
+  {              
+    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 ( b_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 ( s_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 ( b_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 ( s_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)++;  
+}

+ 69 - 0
featureLearning/FeatureLearningClusterBased.h

@@ -0,0 +1,69 @@
+/**
+ * @file FeatureLearningClusterBased.h
+ * @brief compute new histogram entries by clustering new features and take centers of clusters which are relevant and novel
+ * @author Alexander Freytag
+ * @date 16-04-2013 (dd-mm-yyyy)
+*/
+#ifndef _INCLUDEFEATURELEARNINGCLUSTERBASED
+#define _INCLUDEFEATURELEARNINGCLUSTERBASED
+
+#include "FeatureLearningPrototypes.h"
+
+#include <string>
+
+
+namespace OBJREC
+{
+
+  /**
+   * @class FeatureLearningClusterBased
+   * @brief compute new histogram entries by clustering new features and take centers of clusters which are relevant and novel
+   * @author Alexander Freytag
+   * @date 16-04-2013 (dd-mm-yyyy)
+  */  
+  
+  class FeatureLearningClusterBased : public FeatureLearningPrototypes
+  {
+
+    protected:
+           
+      /************************
+       * 
+       *   protected variables
+       * 
+       **************************/       
+      
+       //! define the number of clusters we want to compute for an unseen image
+      int numberOfClustersForNewImage;      
+
+      /************************
+       * 
+       *   protected methods
+       * 
+       **************************/  
+      
+      void setClusterAlgo( const std::string & _clusterAlgoString, const bool & _setForInitialTraining);
+
+
+    public:
+
+      /** constructor
+        *  @param _conf needs a configfile
+        *  @param _md and a MultiDataset (contains images and other things)
+        */
+      FeatureLearningClusterBased ( const NICE::Config *_conf, const OBJREC::MultiDataset *_md , const std::string & _section = "featureLearning");
+
+      /** simple destructor */
+      virtual ~FeatureLearningClusterBased();
+      
+      /** this function has to be overloaded by all subclasses
+          @param imgWithNovelContent 
+      */
+      virtual void learnNewFeatures ( const std::string & filename ) ;      
+
+  };
+
+
+} // namespace
+
+#endif

+ 43 - 0
featureLearning/FeatureLearningGeneric.cpp

@@ -0,0 +1,43 @@
+/**
+* @file FeatureLearningGeneric.cpp
+* @brief abstract interface for feature learning algorithms
+* @author Alexander Freytag
+* @date 16-04-2013 (dd-mm-yyyy)
+
+*/
+#include <iostream>
+
+#include <vislearning/baselib/Preprocess.h>
+
+#include "FeatureLearningGeneric.h"
+
+using namespace OBJREC;
+using namespace std;
+using namespace NICE;
+
+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 );
+  
+  this->b_showTrainingImages = conf->gB( section, "showTrainingImages", false );  
+  this->b_showResults = conf->gB( section, "showResults", false );
+  
+  this->s_resultdir = conf->gS( section, "resultdir", "/tmp/");  
+  
+  Preprocess::Init ( _conf );
+}
+
+FeatureLearningGeneric::~FeatureLearningGeneric()
+{
+}
+
+void FeatureLearningGeneric::learnNewFeatures ( const std::string & _filename )
+{
+}

+ 130 - 0
featureLearning/FeatureLearningGeneric.h

@@ -0,0 +1,130 @@
+/**
+ * @file FeatureLearningGeneric.h
+ * @brief abstract interface for feature learning algorithms
+ * @author Alexander Freytag
+ * @date 16-04-2013 (dd-mm-yyyy)
+*/
+#ifndef _INCLUDEFEATURELEARNING
+#define _INCLUDEFEATURELEARNING
+
+#define ROADWORKS fthrow(NICE::Exception, "Feature Learning -- not yet implemented!");
+
+//STL
+#include <string>
+//
+//core
+#include <core/basics/Config.h>
+#include <core/image/ImageT.h>
+#include <core/vector/VVector.h>
+//
+//vislearning
+#include <vislearning/cbaselib/CachedExample.h>
+
+
+
+namespace OBJREC
+{
+
+  /**
+   * @class FeatureLearningGeneric
+   * @brief abstract interface for feature learning algorithms
+   * @author Alexander Freytag
+   * @date 16-04-2013 (dd-mm-yyyy)
+  */
+  class FeatureLearningGeneric
+  {
+
+    protected:   
+      
+      /************************
+       * 
+       *   protected variables
+       * 
+       **************************/       
+      
+      //! 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;
+      
+      bool b_showTrainingImages;
+      bool b_showResults;
+  
+      std::string s_resultdir;
+     
+      
+      /************************
+       * 
+       *   protected methods
+       * 
+       **************************/      
+      
+      /**
+       * @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:
+
+      /**
+       * @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, const std::string & _section = "featureLearning" );
+
+      /** simple destructor */
+      virtual ~FeatureLearningGeneric();
+
+      
+      /** 
+       * @brief  Learn new features to explain a previously unseen image with novel content
+       * @author Alexander Freytag
+       * @date 16-04-2013 (dd-mm-yyyy)
+       * @param _filename of the new image 
+       * @note This function has to be overloaded by all subclasses!          
+      */
+      virtual void learnNewFeatures ( const std::string & _filename) = 0;  
+      
+      virtual NICE::FloatImage evaluateCurrentCodebookByDistance ( const std::string & _filename , const bool & beforeComputingNewFeatures = true) = 0;
+      
+      virtual NICE::ImageT<int> evaluateCurrentCodebookByAssignments ( const std::string & _filename , const bool & beforeComputingNewFeatures = true, const bool & _binaryShowLatestPrototype = false) = 0;      
+      
+      virtual void evaluateCurrentCodebookByConfusionMatrix( NICE::Matrix & _confusionMat ) = 0; 
+      
+      virtual NICE::VVector * getCurrentCodebook() = 0;
+  
+
+  };
+
+
+} // namespace
+
+#endif

+ 707 - 0
featureLearning/FeatureLearningPrototypes.cpp

@@ -0,0 +1,707 @@
+
+#include "FeatureLearningPrototypes.h"
+
+//STL
+#include <iostream>
+
+//core
+#include <core/image/FilterT.h>
+#include <core/image/CircleT.h>
+#include <core/image/Convert.h>
+#include <core/imagedisplay/ImageDisplay.h>
+#include <core/vector/VectorT.h>
+
+//vislearning
+#include <vislearning/features/localfeatures/LFonHSG.h>
+#include <vislearning/features/localfeatures/LFColorSande.h>
+#include <vislearning/features/localfeatures/LFColorWeijer.h>
+#include <vislearning/features/localfeatures/LFReadCache.h>
+#include <vislearning/features/localfeatures/LFWriteCache.h>
+// 
+#include <vislearning/math/cluster/KMeans.h>
+#include <vislearning/math/cluster/KMedian.h>
+#include <vislearning/math/cluster/GMM.h>
+
+using namespace std;
+using namespace NICE;
+using namespace OBJREC;
+
+  //**********************************************
+  //
+  //                 PROTECTED METHODS
+  //
+  //********************************************** 
+
+void FeatureLearningPrototypes::setClusterAlgo( const std::string & _clusterAlgoString)
+{
+  //be careful with previously allocated memory
+  if (this->clusterAlgo != NULL)
+    delete clusterAlgo;
+  
+  if (_clusterAlgoString.compare("kmeans") == 0)
+  {
+    this->clusterAlgo = new OBJREC::KMeans(this->initialNumberOfClusters);
+  }
+  else if (_clusterAlgoString.compare("kmedian") == 0)
+  {
+    this->clusterAlgo = new OBJREC::KMedian(this->initialNumberOfClusters);
+  }  
+  else if (_clusterAlgoString.compare("GMM") == 0) 
+  {
+    this->clusterAlgo = new OBJREC::GMM(this->conf, this->initialNumberOfClusters);
+  }
+  else
+  {
+    std::cerr << "Unknown cluster algorithm selected, use k-means instead" << std::endl;
+    this->clusterAlgo = new OBJREC::KMeans(this->initialNumberOfClusters);
+  }    
+}
+
+void FeatureLearningPrototypes::setFeatureExtractor( const bool & _setForTraining )
+{  
+  //be careful with previously allocated memory
+  if (this->featureExtractor != NULL)
+    delete featureExtractor;  
+  
+    //feature stuff
+  // which OpponentSIFT implementation to use {NICE, VANDESANDE}
+  std::string opSiftImpl;  
+  opSiftImpl = this->conf->gS ( "Descriptor", "implementation", "VANDESANDE" );
+  // read features?
+  bool readfeat;
+  readfeat = this->conf->gB ( "Descriptor", "read", true );
+  // write features?
+  bool writefeat;  
+  writefeat = this->conf->gB ( "Descriptor", "write", true );   
+  
+  // Welche Opponentsift Implementierung soll genutzt werden ?
+  LocalFeatureRepresentation *cSIFT = NULL;
+  LocalFeatureRepresentation *writeFeats = NULL;
+  LocalFeatureRepresentation *readFeats = NULL;
+  this->featureExtractor = NULL;
+  if ( opSiftImpl == "NICE" )
+  {
+    if ( _setForTraining )
+      cSIFT = new OBJREC::LFonHSG ( this->conf, "HSGTrain" );
+    else
+      cSIFT = new OBJREC::LFonHSG ( this->conf, "HSGTest" );
+  }
+  else if ( opSiftImpl == "VANDESANDE" )
+  {
+    if ( _setForTraining )
+      cSIFT = new OBJREC::LFColorSande ( this->conf, "LFColorSandeTrain" );
+    else
+      cSIFT = new OBJREC::LFColorSande ( this->conf, "LFColorSandeTest" );
+  }
+  else
+  {
+    fthrow ( Exception, "feattype: %s not yet supported" << opSiftImpl );
+  }
+
+  this->featureExtractor = cSIFT;
+  
+  if ( writefeat )
+  {
+    // write the features to a file, if there isn't any to read
+    writeFeats = new LFWriteCache ( this->conf, cSIFT );
+    this->featureExtractor = writeFeats;
+  }
+
+  if ( readfeat )
+  {
+    // read the features from a file
+    if ( writefeat )
+    {
+      readFeats = new LFReadCache ( this->conf, writeFeats, -1 );
+    }
+    else
+    {
+      readFeats = new LFReadCache ( this->conf, cSIFT, -1 );
+    }
+    this->featureExtractor = readFeats; 
+  }  
+  
+  //only set feature stuff to NULL, deletion of the underlying object is done in the destructor
+  if ( cSIFT != NULL )
+    cSIFT = NULL;
+  if ( writeFeats != NULL )
+    writeFeats = NULL;
+  if ( readFeats != NULL )
+    readFeats  = NULL ;   
+}
+
+void FeatureLearningPrototypes::extractFeaturesFromTrainingImages( const OBJREC::MultiDataset *_md, NICE::VVector & examplesTraining )
+{
+  examplesTraining.clear();
+  
+  int numberOfTrainImage ( 0 );
+  
+  const LabeledSet *trainFiles = (*_md)["train"]; 
+  
+  //run over all training images
+  LOOP_ALL_S( *trainFiles )
+  {
+      EACH_INFO( classno, info );
+      std::string filename = info.img();
+            
+      NICE::ColorImage img( filename );
+      if ( b_showTrainingImages )
+      {
+        showImage( img, "Input" );    
+      }
+      
+      //variables to store feature informatio
+      NICE::VVector features;
+      NICE::VVector cfeatures;
+      NICE::VVector positions;
+
+      //compute features
+      Globals::setCurrentImgFN ( filename );
+      if (featureExtractor == NULL)
+        std::cerr << "feature Extractor is NULL" << std::endl;
+      else
+        featureExtractor->extractFeatures ( img, features, positions );
+            
+      //store feature information in larger data structure
+      for ( NICE::VVector::iterator i = features.begin();
+            i != features.end();
+            i++)
+      {              
+        //normalization :)
+        i->normalizeL1();
+
+        examplesTraining.push_back(*i);
+      }
+      
+      //don't waste memory
+      features.clear();
+      positions.clear();      
+      numberOfTrainImage++;
+  }//Loop over all training images    
+}
+
+void FeatureLearningPrototypes::train ( const OBJREC::MultiDataset *_md )
+{  
+  bool loadSuccess = this->loadInitialCodebook();
+  
+  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();  
+    
+    //normalization
+    for (NICE::VVector::iterator protoIt = prototypes.begin(); protoIt != prototypes.end(); protoIt++)
+    {
+      protoIt->normalizeL1();
+    }
+  }
+  
+  this->writeInitialCodebook();
+}
+
+bool FeatureLearningPrototypes::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 from " << cacheInitialCodebook << std::endl;
+      return false;
+    }  
+    return true;
+  }
+  else
+    return false;
+}
+
+bool FeatureLearningPrototypes::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;
+}
+
+
+void FeatureLearningPrototypes::evaluateCurrentCodebookForGivenFeatures(  const NICE::VVector & _features,
+                                                                          const NICE::VVector & _positions,
+                                                                          NICE::FloatImage & _noveltyImageGaussFiltered,
+                                                                          NICE::FloatImage * _noveltyImage                                                                        )
+{
+  bool wasNoveltyImageGiven ( true );
+  if ( _noveltyImage == NULL )
+  {
+    _noveltyImage = new FloatImage ( _noveltyImageGaussFiltered.width(), _noveltyImageGaussFiltered.height() );
+    wasNoveltyImageGiven = false;
+  }
+  
+  _noveltyImageGaussFiltered.set( 0.0 );
+  _noveltyImage->set( 0.0 );
+     
+  
+  NICE::VVector::const_iterator posIt = _positions.begin();
+  for ( NICE::VVector::const_iterator i = _features.begin();
+        i != _features.end();
+        i++, posIt++)
+  {              
+    
+    //loop over codebook representatives
+    double minDist ( std::numeric_limits<double>::max() );
+    for (NICE::VVector::const_iterator it =  prototypes.begin(); it != prototypes.end(); it++)
+    {
+      //compute distance
+      double tmpDist ( this->distFunction->calculate(*i,*it) );
+      if (tmpDist < minDist)
+        minDist = tmpDist;
+    }
+
+    //take minimum distance and store in in a float image    
+    (*_noveltyImage) ( (*posIt)[0], (*posIt)[1]  ) = minDist;
+  } 
+
+  
+  //gauss-filtering for nicer visualization
+  float sigma ( 3.0 );
+  FilterT<float, float, float> filter;
+  filter.filterGaussSigmaApproximate ( *_noveltyImage, sigma, & _noveltyImageGaussFiltered );
+    
+  if ( ! wasNoveltyImageGiven )
+    delete _noveltyImage;
+}
+
+  //**********************************************
+  //
+  //                 PUBLIC METHODS
+  //
+  //********************************************** 
+
+
+FeatureLearningPrototypes::FeatureLearningPrototypes ( const Config *_conf,
+                               const MultiDataset *_md, const std::string & _section )
+    : FeatureLearningGeneric ( _conf, _section )
+{ 
+  
+  // define the initial number of clusters our codebook shall contain
+  initialNumberOfClusters = conf->gI(section, "initialNumberOfClusters", 10);
+  
+  // define the clustering algorithm to be used
+  std::string clusterAlgoString = conf->gS(section, "clusterAlgo", "kmeans");
+  
+  // define the distance function to be used
+  std::string distFunctionString = conf->gS(section, "distFunction", "euclidian");    
+  
+  
+  //**********************************************
+  //
+  //      SET UP VARIABLES AND METHODS
+  //             - FEATURE TYPE
+  //             - CLUSTERING ALGO
+  //             - DISTANCE FUNCTION
+  //             - ...
+  //
+  //**********************************************  
+  
+  std::cerr << " SET UP VARIABLES AND METHODS " << std::endl;
+
+  //feature extraction for initial codebook
+  this->featureExtractor = NULL;
+  this->setFeatureExtractor( true /* set for training */ );  
+  
+  //clustering algorithm
+  this->clusterAlgo = NULL;
+  this->setClusterAlgo( clusterAlgoString );
+  
+  if (distFunctionString.compare("euclidian") == 0)
+  {
+    distFunction = new NICE::EuclidianDistance<double>();
+  }
+  else
+  {
+    std::cerr << "Unknown vector distance selected, use euclidian instead" << std::endl;
+    distFunction = new NICE::EuclidianDistance<double>();
+  }    
+  
+  //run the training to initially compute a codebook and stuff like that
+  this->train( _md );  
+    
+  //so far, we have not seen any new image
+  this->newImageCounter = 0;
+  
+  //TODO stupid
+  this->maxValForVisualization = conf->gD( section, "stupidMaxValForVisualization", 0.005 ) ;
+  
+  
+  //feature extraction for unseen images
+  this->setFeatureExtractor( false /* set for training */ );    
+}
+
+FeatureLearningPrototypes::~FeatureLearningPrototypes()
+{
+  // clean-up
+  if ( clusterAlgo != NULL )
+    delete clusterAlgo;
+  if ( distFunction != NULL )
+    delete distFunction;
+  if ( featureExtractor != NULL )
+    delete featureExtractor; 
+}
+
+NICE::FloatImage FeatureLearningPrototypes::evaluateCurrentCodebookByDistance ( const std::string & _filename , const bool & beforeComputingNewFeatures )
+{
+  std::cerr << " VISUALIZATION -----    maxValForVisualization: " << maxValForVisualization << std::endl;
+  
+    NICE::ColorImage img( _filename );
+    if ( b_showTrainingImages )
+    {
+      showImage( img, "Input" );    
+    }
+    
+    int xsize ( img.width() );
+    int ysize ( img.height() );
+    
+    //variables to store feature information
+    NICE::VVector features;
+    NICE::VVector cfeatures;
+    NICE::VVector positions;
+
+    //compute features
+    Globals::setCurrentImgFN ( _filename );
+    featureExtractor->extractFeatures ( img, features, positions );
+    
+    //normalization
+    for ( NICE::VVector::iterator i = features.begin();
+          i != features.end();
+          i++)
+    {              
+      //normalization :)
+      i->normalizeL1();
+    }
+    
+    FloatImage noveltyImage ( xsize, ysize );
+    FloatImage noveltyImageGaussFiltered ( xsize, ysize );
+    
+    this->evaluateCurrentCodebookForGivenFeatures( features, positions, noveltyImageGaussFiltered, &noveltyImage );
+    
+    double maxDist ( noveltyImage.max() );
+    double maxFiltered ( noveltyImageGaussFiltered.max() );
+
+    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;
+
+    
+    //convert float to RGB
+    NICE::ColorImage noveltyImageRGB ( xsize, ysize  );
+    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 ( b_showResults )
+      showImage(noveltyImageRGB, "Novelty Image");  
+    else 
+    {
+      std::vector< std::string > list2;
+      StringTools::split ( _filename, '/', list2 );      
+
+      std::string destination ( s_resultdir + NICE::intToString(this->newImageCounter -1 ) + "_" + list2.back() + "_3_updatedNoveltyMap.ppm");
+      if ( beforeComputingNewFeatures )
+        destination  =  s_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 distToCurrentCluster ( std::numeric_limits<double>::max() );
+      int indexOfMostSimFeat( 0 );
+      double tmpDist;
+      int featureCnt ( 0 );
+      
+      for ( NICE::VVector::iterator i = features.begin();
+            i != features.end();
+            i++, featureCnt++)
+      {
+        tmpDist = this->distFunction->calculate( *i, *protIt );
+        if ( tmpDist < distToCurrentCluster )
+        {
+          distToCurrentCluster = tmpDist;
+          indexOfMostSimFeat = featureCnt;
+        }
+      }
+      
+      int posX ( ( positions[indexOfMostSimFeat] ) [0]  );
+      int posY ( ( positions[indexOfMostSimFeat] ) [1]  );
+      
+      //position (for OpponentSIFT of van de Sande): x y scale orientation cornerness
+      
+      /*What is the interpretation of scale?
+
+      The scale parameter was implemented to correspond with the Gaussian filter sigma at which points were detected. Therefore, the
+      scale is not directly interpretable as the size of the region described in terms of number of pixels. However, it is linearly
+      related the radius of the circular area described. To capture the area of the Gaussian originally used, we have a 3x 
+      magnification factor. But, remember that SIFT has 4x4 cells, and this is a measure for a single cell. So, because we are 
+      working with a radius, multiply by 2. Due to the square shape of the region, we need to extend the outer radius even further 
+      by sqrt(2), otherwise the corners of the outer cells are cut off by our circle. So, the largest outer radius is 
+      Round(scale * 3 * 2 * sqrt(2)). The area from which the SIFT descriptor is computed is a square which fits within a circle 
+      of this radius. Also, due to the Gaussian weighting applied within SIFT, the area that really matters is much, much smaller: 
+      the outer parts get a low weight. 
+
+      For the default scale of 1.2, we get a outer circle radius of 10. The potential sampling area then becomes -10..10, e.g. a 
+      21x21patch. However, the square area which fits inside this circle is smaller: about 15x15. The corners of this 15x15square 
+      touch the outer circle. */
+      
+      /*Why is the direction (angle) field always 0?
+
+
+      Estimating the dominant orientation of a descriptor is useful in matching scenarios. However, in an object/scene categorization
+      setting, the additional invariance reduces accuracy. Being able to discriminate between dominant directions of up and right 
+      is very useful here, and rotated down images are quite rare in an object categorization setting. Therefore, orientation 
+      estimation is disabled in the color descriptor software. The subsequent rotation of the descriptor to achieve 
+      rotation-invariance is still possible by supplying your own regions and angles for an image (through --loadRegions). However, 
+      by default, no such rotation is performed, since the default angle is 0. */      
+      
+      
+      //adapt the pseudo color transformation as done in Convert.cpp
+        size_t seg = ( size_t ) ( tmpProtCnt/(float)prototypes.size() * 6.0 );
+        double y   = ( 6 * tmpProtCnt/(float)prototypes.size() - seg );
+
+        Color circleColor;
+        switch ( seg ) {
+          case 0:
+            circleColor = Color( 0,0,(int)round(y*255) );
+            break;
+          case 1:
+            circleColor = Color( 0,(int)round(y*255),255 );
+            break;
+          case 2:
+            circleColor = Color( 0,255,(int)round((1-y)*255) );
+            break;
+          case 3:
+            circleColor = Color( (int)round(y*255),255,0 );
+            break;
+          case 4:
+            circleColor = Color( 255,(int)round((1-y)*255),0 );
+            break;
+          case 5:
+            circleColor = Color( 255,(int)round(y*255),(int)round(y*255) );
+            break;
+          default:
+            circleColor = Color( 255,255,255 );
+            break;
+        }      
+      
+      NICE::Circle circ ( Coord( posX, posY), (int) round(2*3*sqrt(2)*( positions[indexOfMostSimFeat] )[2]) /* radius*/, circleColor );
+      img.draw(circ);  
+    }
+        
+   if ( b_showResults )
+      showImage(img, "Current image and most similar features for current prototypes"); 
+   else
+   {
+      std::vector< std::string > list2;
+      StringTools::split ( _filename, '/', list2 );      
+
+      std::string destination ( s_resultdir + NICE::intToString(this->newImageCounter-1) + "_" + list2.back() + "_3_updatedCurrentCluster.ppm");
+      if ( beforeComputingNewFeatures )
+        destination  =  s_resultdir + NICE::intToString(this->newImageCounter) + "_" + list2.back() + "_0_initialCurrentCluster.ppm";
+      
+      img.writePPM( destination );
+   }
+   
+   return noveltyImageGaussFiltered;
+}
+
+NICE::ImageT< int > FeatureLearningPrototypes::evaluateCurrentCodebookByAssignments(const std::string& _filename, const bool& beforeComputingNewFeatures, const bool & _binaryShowLatestPrototype)
+{
+  std::cerr << "evaluateCurrentCodebookByAssignments" << std::endl;
+  NICE::ColorImage img( _filename );
+  
+  int xsize ( img.width() );
+  int ysize ( img.height() );
+  
+  //variables to store feature information
+  NICE::VVector features;
+  NICE::VVector cfeatures;
+  NICE::VVector positions;
+
+  //compute features
+  Globals::setCurrentImgFN ( _filename );
+  featureExtractor->extractFeatures ( img, features, positions );
+  
+  //normalization
+  for ( NICE::VVector::iterator i = features.begin();
+        i != features.end();
+        i++)
+  {              
+    //normalization :)
+    i->normalizeL1();
+  }
+  
+  //this is the image we will return finally
+  NICE::ImageT< int > clusterImage ( xsize, ysize );
+  clusterImage.set ( 0 );
+  
+  // after iterating over all features from the new image, this vector contains
+  // distances to the most similar feature for every prototype
+  NICE::Vector minDistances ( this->prototypes.size() );
+  
+  NICE::VVector::const_iterator posIt = positions.begin();
+  for ( NICE::VVector::const_iterator i = features.begin();
+        i != features.end();
+        i++, posIt++ )
+  {              
+    
+    //loop over codebook representatives
+    double minDist ( std::numeric_limits<double>::max() );
+    int indexOfNearestPrototype ( 0 );
+    int prototypeCounter ( 0 );
+    for (NICE::VVector::const_iterator it =  this->prototypes.begin(); it != this->prototypes.end(); it++, prototypeCounter++)
+    {
+      //compute distance
+      double tmpDist ( this->distFunction->calculate(*i,*it) );
+      //check what the closest prototype is
+      if (tmpDist < minDist)
+      {
+        minDist = tmpDist;
+        indexOfNearestPrototype = prototypeCounter;
+      }
+      
+      //check whether we found a feature for the current prototype which is more similar then the previous best one
+      if ( minDistances[ prototypeCounter ] > tmpDist )
+        minDistances[ prototypeCounter ] = tmpDist;      
+    }
+    
+
+    
+    
+    //take minimum distance and store in in a float image    
+    // for nice visualization, we plot the cluster index into a square of size 3 x 3
+    //TODO currently hard coded!!!
+    int noProtoTypes ( this->prototypes.size() -1 );
+    
+    for ( int tmpY = (*posIt)[1]  - 1; tmpY < (*posIt)[1]  + 1; tmpY++)
+    {
+      for ( int tmpX = (*posIt)[0]  - 1; tmpX < (*posIt)[0]  + 1; tmpX++)
+      {
+        if ( _binaryShowLatestPrototype )
+        {
+          //just a binary image - 1 if newest prototype is nearest - 0 if not
+          if  ( indexOfNearestPrototype == noProtoTypes)
+            clusterImage ( tmpX, tmpY ) = 1;
+          else
+            clusterImage ( tmpX, tmpY ) = 0;
+        }
+        else
+          //as many different values as current prototypes available
+          clusterImage ( tmpX, tmpY ) = indexOfNearestPrototype;  
+      }
+    }
+  } 
+  
+  std::cerr << "Codebook evaluation by assignments... min distances in image for every prototype: " << std::endl << "    " << minDistances << std::endl;
+  
+  //show how many clusters we have
+  if ( !_binaryShowLatestPrototype )  
+  {
+    int tmpCnt ( 0 );
+    for (NICE::VVector::const_iterator protoIt = prototypes.begin(); protoIt != prototypes.end(); protoIt++, tmpCnt++)
+    {
+      for ( int tmpY = 1 + 2 - 2; tmpY < (2  + 2); tmpY++)
+        {
+          for ( int tmpX = 1 + 4*tmpCnt ; tmpX < (1 + 4*tmpCnt  + 3); tmpX++)
+          {
+            //Take care, this might go "over" the image
+            clusterImage ( tmpX, tmpY ) = (Ipp8u) tmpCnt;
+          }
+        }   
+    }
+  }
+  
+  return clusterImage;
+}
+
+void FeatureLearningPrototypes::evaluateCurrentCodebookByConfusionMatrix( NICE::Matrix & _confusionMat )
+{
+  _confusionMat.resize ( this->prototypes.size(), this->prototypes.size() );
+  _confusionMat.set( 0.0 );
+  
+  double tmpDist ( 0.0 );
+  NICE::VVector::const_iterator protoItJ = prototypes.begin();
+  for ( int j = 0; j < prototypes.size(); j++, protoItJ++)
+  {
+    NICE::VVector::const_iterator protoItI = protoItJ; 
+    for ( int i = j; i < prototypes.size(); i++, protoItI++)
+    {
+      tmpDist = this->distFunction->calculate( *protoItJ, *protoItI );
+      
+      //assuming symmetric distances
+      _confusionMat ( i, j ) = tmpDist;
+//       if ( i != j )
+//         _confusionMat ( j, i ) = tmpDist;  
+    }
+  }
+}
+
+NICE::VVector * FeatureLearningPrototypes::getCurrentCodebook()
+{
+  return &(this->prototypes);
+}
+

+ 127 - 0
featureLearning/FeatureLearningPrototypes.h

@@ -0,0 +1,127 @@
+/**
+ * @file FeatureLearningPrototypes.h
+ * @brief compute new histogram entries somehow, represent clusters only based on representatives, no additional information like dimensionwise variances...
+ * @author Alexander Freytag
+ * @date 17-04-2013 (dd-mm-yyyy)
+*/
+#ifndef _INCLUDEFEATURELEARNINGPROTOTYPES
+#define _INCLUDEFEATURELEARNINGPROTOTYPES
+
+#include "FeatureLearningGeneric.h"
+
+
+#include <core/vector/Distance.h>
+#include <core/vector/VVector.h>
+
+#include <vislearning/cbaselib/MultiDataset.h>
+// 
+#include <vislearning/features/localfeatures/GenericLocalFeatureSelection.h>
+#include <vislearning/features/simplefeatures/CodebookPrototypes.h>
+// 
+#include <vislearning/math/cluster/ClusterAlgorithm.h>
+
+
+
+
+namespace OBJREC
+{
+
+  /**
+   * @class FeatureLearningPrototypes
+   * @brief compute new histogram entries somehow, represent clusters only based on representatives, no additional information like dimensionwise variances...
+   * @author Alexander Freytag
+   * @date 17-04-2013 (dd-mm-yyyy)
+  */  
+  
+  class FeatureLearningPrototypes : public FeatureLearningGeneric
+  {
+
+    protected:
+      
+      /************************
+       * 
+       *   protected variables
+       * 
+       **************************/       
+           
+      bool showTrainingImages;
+      
+      //! define the initial number of clusters our codebook shall contain
+      int initialNumberOfClusters;       
+      
+      OBJREC::ClusterAlgorithm * clusterAlgo;      
+  
+      NICE::VectorDistance<double> * distFunction;
+
+      LocalFeatureRepresentation * featureExtractor;
+      
+      //! The currently known "cluster" centers, which are used for BoW histogram computation 
+//       NICE::VVector prototypes;     
+      OBJREC::CodebookPrototypes prototypes;   
+            
+      int newImageCounter;
+      //just needed for visualisation
+      double oldMaxDist;
+      
+      //TODO stupid!!!
+      double maxValForVisualization;
+      
+      //! just for temporary debugging
+      int i_posXOfNewPrototype;
+      int i_posYOfNewPrototype;      
+            
+      
+      /************************
+       * 
+       *   protected methods
+       * 
+       **************************/
+            
+      void setClusterAlgo( const std::string & _clusterAlgoString);
+      void setFeatureExtractor( const bool & _setForTraining = false);
+      
+      void extractFeaturesFromTrainingImages(  const OBJREC::MultiDataset *_md,  NICE::VVector & examplesTraining  ); 
+      
+      void train ( const OBJREC::MultiDataset *_md ); 
+      
+      virtual bool loadInitialCodebook ( );
+      virtual bool writeInitialCodebook ( );      
+      
+      virtual void evaluateCurrentCodebookForGivenFeatures (  const NICE::VVector & _features,
+                                                              const NICE::VVector & _positions,
+                                                              NICE::FloatImage & _noveltyImageGaussFiltered,
+                                                              NICE::FloatImage * _noveltyImage = NULL                                                              
+                                                            );
+
+    public:
+
+      /** constructor
+        * @param _conf needs a configfile
+        * @param _md and a MultiDataset (contains images and other things)
+        * @param _section section information for parsing config files
+        */
+      FeatureLearningPrototypes ( const NICE::Config *_conf, const OBJREC::MultiDataset *_md , const std::string & _section = "featureLearning");
+
+      /** simple destructor */
+      virtual ~FeatureLearningPrototypes();
+      
+      /** this function has to be overloaded by all subclasses
+          @param imgWithNovelContent 
+      */
+      virtual void learnNewFeatures ( const std::string & _filename ) = 0 ;
+      
+      virtual NICE::FloatImage evaluateCurrentCodebookByDistance ( const std::string & _filename , const bool & beforeComputingNewFeatures = true );
+      
+      virtual NICE::ImageT<int> evaluateCurrentCodebookByAssignments ( const std::string & _filename , const bool & beforeComputingNewFeatures = true, const bool & _binaryShowLatestPrototype = false);            
+      
+      virtual void evaluateCurrentCodebookByConfusionMatrix( NICE::Matrix & _confusionMat ); 
+      
+      virtual NICE::VVector * getCurrentCodebook();
+      
+
+  };
+
+
+} // namespace
+
+#endif

+ 324 - 0
featureLearning/FeatureLearningRegionBased.cpp

@@ -0,0 +1,324 @@
+
+#include "FeatureLearningRegionBased.h"
+
+//STL
+#include <iostream>
+
+//core
+#include <core/image/FilterT.h>
+#include <core/image/CircleT.h>
+#include <core/image/Convert.h>
+#include <core/vector/VectorT.h>
+
+#include <segmentation/GenericRegionSegmentationMethodSelection.h>
+
+//vislearning
+#include <vislearning/baselib/Globals.h>
+
+
+using namespace std;
+using namespace NICE;
+using namespace OBJREC;
+
+  //**********************************************
+  //
+  //                 PROTECTED METHODS
+  //
+  //********************************************** 
+
+
+  //**********************************************
+  //
+  //                 PUBLIC METHODS
+  //
+  //********************************************** 
+
+
+FeatureLearningRegionBased::FeatureLearningRegionBased ( const Config *_conf,
+                               const MultiDataset *_md, const std::string & _section )
+    : FeatureLearningPrototypes ( _conf, _md, _section )
+{ 
+
+   //save and read segmentation results from files  
+  this->reuseSegmentation = conf->gB ( "FPCPixel", "reuseSegmentation", true );  
+  
+  // select your segmentation method here
+  // currently, the following options are supported: "MarkovCluster", "GraphBased", "MeanShift", "SLIC"
+  string rsMethode = conf->gS ( section, "segmentationMethod", "MeanShift" );
+ 
+  OBJREC::RegionSegmentationMethod *tmpRegionSeg = OBJREC::GenericRegionSegmentationMethodSelection::selectRegionSegmentationMethod(conf, rsMethode);    
+  
+  if ( reuseSegmentation )
+    this->segmentationAlgo = new RSCache ( conf, tmpRegionSeg );
+  else
+    this->segmentationAlgo = tmpRegionSeg;
+  
+  this->i_gridSize = conf->gI( "LFColorSandeTest" , "grid" , 5 );
+}
+
+FeatureLearningRegionBased::~FeatureLearningRegionBased()
+{
+  // clean-up
+}
+
+void FeatureLearningRegionBased::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 );  
+    
+  //normalization :)
+  for ( NICE::VVector::iterator i = newFeatures.begin();
+        i != newFeatures.end();
+        i++)
+  {              
+    i->normalizeL1();
+  }
+  
+  //compute region segmentation
+  std::cerr << " COMPUTE REGION SEGMENTATION" << std::endl;
+  NICE::Matrix mask;
+  int amountRegions = segmentationAlgo->segRegions ( img, mask );
+  
+  //compute novelty scores on a feature level
+  std::cerr << " COMPUTE NOVELTY SCORES ON A FEATURE LEVEL" << std::endl;
+  FloatImage noveltyImageGaussFiltered ( xsize, ysize );    
+  this->evaluateCurrentCodebookForGivenFeatures( newFeatures, positions, noveltyImageGaussFiltered );  
+  
+  // compute scores for every region
+  std::cerr << " COMPUTE SCORES FOR EVERY REGION" << std::endl;
+  std::vector<double> regionNoveltyMeasure (amountRegions, 0.0); 
+  std::vector<int> regionSize (amountRegions, 0);
+  
+  for ( int y = 0; y < ysize; y += i_gridSize) //y++)
+  {
+    for (int x = 0; x < xsize; x += i_gridSize) //x++)
+    {
+      int r = mask(x,y);
+      regionSize[r]++;
+
+      //count the amount of "novelty" for the corresponding region
+      regionNoveltyMeasure[r] += noveltyImageGaussFiltered(x,y);
+    }
+  }
+  
+  //loop over all regions and compute averaged novelty scores
+  //NOTE this might be unuseful, since lateron we combine novelty score and region size (e.g. by multiplying)
+  // however, we do not want to settle the combination in adavance
+  for(int r = 0; r < amountRegions; r++)
+  {      
+    regionNoveltyMeasure[r] /= regionSize[r];  
+  }
+
+   //a new region should cover at least 3% of the image for not being penalized
+  double d_minimalImageAmountForAcceptableRegions ( 0.03 );
+  int minimalImageAmountForAcceptableRegions ( round( d_minimalImageAmountForAcceptableRegions * (xsize/i_gridSize) * (ysize/i_gridSize) )  );
+  
+  std::cerr << "minimalImageAmountForAcceptableRegions: " << minimalImageAmountForAcceptableRegions << std::endl;
+  
+  //compute final scores for all regions
+  NICE::Vector regionScores ( amountRegions, 0.0 );  
+  std::cerr << "used region sizes for computation: " << std::endl;
+  for(int r = 0; r < amountRegions; r++)
+  {      
+    regionScores[r] = std::min( regionSize[r], minimalImageAmountForAcceptableRegions ) * regionNoveltyMeasure[r];
+    std::cerr << " " << std::min( regionSize[r], minimalImageAmountForAcceptableRegions );
+  }
+  std::cerr << std::endl << std::endl;
+  
+  int indexOfBestRegion ( regionScores.MaxIndex() );
+  
+  
+  ////////////////////////////////////
+  //
+  //       VISUALIZE REGION SCORES
+  //
+  ////////////////////////////////////
+  
+  NICE::FloatImage regionScoreImage ( xsize, ysize );
+  NICE::FloatImage regionNoveltyImage ( xsize, ysize ); //contains novelty score averaged over region
+  NICE::FloatImage regionRelevanceImage ( xsize, ysize ); //contains higher scores for larger regions (but with upper limit)
+  
+  regionScoreImage.set( 0.0 );
+  regionNoveltyImage.set( 0.0 );
+  regionRelevanceImage.set( 0.0 );
+  
+    for ( int y = 0; y < ysize; y++)
+    {
+      for (int x = 0; x < xsize; x++)
+      {
+        int r = mask(x,y);
+
+        regionNoveltyImage(x,y) = regionNoveltyMeasure[r];
+        regionRelevanceImage(x,y) = regionSize[r];
+        regionScoreImage(x,y) = regionScores[r];
+      }
+    }  
+    
+    std::cerr << "highest region score: " << regionScoreImage.max()<< " -- smallest region score: " << regionScoreImage.min() << std::endl;
+    std::cerr << "highest region novelty score: " << regionNoveltyImage.max() << "  -- smallest region novelty score: " << regionNoveltyImage.min() << std::endl;
+    
+  NICE::ColorImage regionScoreImageRGB  ( xsize, ysize );
+  NICE::ColorImage regionNoveltyScoreImageRGB  ( xsize, ysize );
+  NICE::ColorImage regionRelevanceScoreImageRGB  ( xsize, ysize );
+  //TODO properly specify the maximum value for score visualization here :)
+  imageToPseudoColorWithRangeSpecification( regionScoreImage, regionScoreImageRGB, 0 /* min */, 2.2 /* max */ );  
+  imageToPseudoColorWithRangeSpecification( regionNoveltyImage, regionNoveltyScoreImageRGB, 0 /* min */, 0.012 /* max */ );  
+  imageToPseudoColorWithRangeSpecification( regionRelevanceImage, regionRelevanceScoreImageRGB, 0 /* min */, minimalImageAmountForAcceptableRegions /* max */ );  
+
+    if ( b_showResults )
+    {
+      showImage(regionNoveltyScoreImageRGB, "Current (new) image with region NOVELTY scores"); 
+      showImage(regionRelevanceScoreImageRGB, "Current (new) image with region RELEVANCE scores"); 
+      showImage(regionScoreImageRGB, "Current (new) image with FINAL region scores"); 
+    }
+    else 
+    {
+      std::vector< std::string > list2;
+      StringTools::split ( _filename, '/', list2 );      
+     
+      //write novelty score image
+      std::string destinationNovelty ( s_resultdir + NICE::intToString(this->newImageCounter) + "_" + list2.back() + "_1_1_regionNoveltyScores.ppm");
+      regionNoveltyScoreImageRGB.writePPM( destinationNovelty );
+      
+      //write relevance score image
+      std::string destinationRelevance ( s_resultdir + NICE::intToString(this->newImageCounter) + "_" + list2.back() + "_1_2_regionRelevanceScores.ppm");
+      regionRelevanceScoreImageRGB.writePPM( destinationRelevance );      
+
+      //write image with final scores for region
+      std::string destination ( s_resultdir + NICE::intToString(this->newImageCounter) + "_" + list2.back() + "_1_3_regionScores.ppm");
+      regionScoreImageRGB.writePPM( destination );
+    }  
+  
+  
+  //compute representative for best region
+  
+  NICE::Vector representative ( newFeatures.begin()->size(), 0.0 );
+  
+//   //FIRST TRY: average feature vectors of the "best" region
+//   NICE::VVector::const_iterator posIt = positions.begin();
+//   for ( NICE::VVector::const_iterator featIt = newFeatures.begin();
+//         featIt != newFeatures.end();
+//         featIt++, posIt++)
+//   {              
+//     
+//     //only considere features that actually belong to the best region
+//     if ( mask( (*posIt)[0], (*posIt)[1] ) != indexOfBestRegion )
+//       continue;
+//     
+//     representative += *featIt;
+//   } 
+//   
+//   //simple mean feature vector
+//   representative /= regionSize[indexOfBestRegion];
+//   //normalization
+//   representative.normalizeL1();
+  
+//   //SECOND TRY: simply take the first feature vector of the "best" region (although this one should lay on the border, and so on...)
+//   NICE::VVector::const_iterator posIt = positions.begin();
+//   for ( NICE::VVector::const_iterator featIt = newFeatures.begin();
+//         featIt != newFeatures.end();
+//         featIt++, posIt++)
+//   {              
+//     
+//     //only considere features that actually belong to the best region
+//     if ( mask( (*posIt)[0], (*posIt)[1] ) != indexOfBestRegion )
+//       continue;
+//     
+//     representative = *featIt;
+//     i_posXOfNewPrototype = (*posIt)[0];
+//     i_posYOfNewPrototype = (*posIt)[1];
+//     //break after the first positive feature
+//     break;
+//   }   
+  
+  //THIRD TRY: simply take the feature vector of the "best" region with largest novelty score within this region
+  // ... (hopefully, this is no outlier wrt to this region...)
+  
+  double maxNovelty ( 0.0 );
+  NICE::VVector::const_iterator mostNovelFeature = newFeatures.begin() ;
+  
+  NICE::VVector::const_iterator posIt = positions.begin();
+  for ( NICE::VVector::const_iterator featIt = newFeatures.begin();
+        featIt != newFeatures.end();
+        featIt++, posIt++)
+  {              
+    
+    //only considere features that actually belong to the best region
+    if ( mask( (*posIt)[0], (*posIt)[1] ) != indexOfBestRegion )
+      continue;
+    
+    //did we found a feature of the "best"region with larger novelty score then the current most novel one?
+    if ( noveltyImageGaussFiltered( (*posIt)[0], (*posIt)[1] ) > maxNovelty )
+    {
+      maxNovelty = noveltyImageGaussFiltered( (*posIt)[0], (*posIt)[1] );
+      mostNovelFeature = featIt;
+      i_posXOfNewPrototype = (*posIt)[0];
+      i_posYOfNewPrototype = (*posIt)[1];
+    }
+  }
+  representative = *mostNovelFeature;
+  
+  std::cerr << " New representative: " << std::endl << representative << std::endl;
+  
+  //include the chosen information into the currently used prototypes
+  prototypes.push_back( representative ); 
+  
+  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, representative );
+      if ( tmpDist < distToNewCluster )
+      {
+        distToNewCluster = tmpDist;
+        indexOfMostSimFeat = tmpCnt;
+      }
+    }
+    
+    std::cerr << "** minDist to new cluster: " <<distToNewCluster << std::endl;
+    
+    int posX ( ( positions[indexOfMostSimFeat] ) [0]  );
+    int posY ( ( positions[indexOfMostSimFeat] ) [1]  );
+    
+    std::cerr << "position of most similar feature --- posX: " << posX << " - posY " << posY << std::endl;
+    std::cerr << "position of new prototype        --- posX: " << i_posXOfNewPrototype << " - posY " << i_posYOfNewPrototype << std::endl;
+    NICE::Circle circ ( Coord( posX, posY), 10 /* radius*/, Color(200,0,255) );
+    imgTmp.draw(circ); 
+    
+    if ( b_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 ( s_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)++;  
+}

+ 73 - 0
featureLearning/FeatureLearningRegionBased.h

@@ -0,0 +1,73 @@
+/**
+ * @file FeatureLearningRegionBased.h
+ * @brief compute new histogram entries by extracting features from (moderately large) regions which have high novelty scores
+ * @author Alexander Freytag
+ * @date 17-04-2013 (dd-mm-yyyy)
+*/
+#ifndef _INCLUDEFEATURELEARNINGREGIONBASED
+#define _INCLUDEFEATURELEARNINGREGIONBASED
+
+#include "FeatureLearningPrototypes.h"
+
+#include "segmentation/RegionSegmentationMethod.h"
+
+
+namespace OBJREC
+{
+
+  /**
+   * @class FeatureLearningRegionBased
+   * @briefcompute new histogram entries by extracting features from (moderately large) regions which have high novelty scores
+   * @author Alexander Freytag
+   * @date 17-04-2013 (dd-mm-yyyy)
+  */  
+  
+  class FeatureLearningRegionBased : public FeatureLearningPrototypes
+  {
+
+    protected:
+
+      /************************
+       * 
+       *   protected variables
+       * 
+       **************************/  
+      
+      //! low level Segmentation method
+      RegionSegmentationMethod *segmentationAlgo;      
+      
+      //! boolean whether to reuse segmentation results for single images in different runs
+      bool reuseSegmentation;      
+      
+      //! determine a grid on which we extract local features
+      int i_gridSize;
+
+      /************************
+       * 
+       *   protected methods
+       * 
+       **************************/      
+
+
+    public:
+
+      /** constructor
+        *  @param _conf needs a configfile
+        *  @param _md and a MultiDataset (contains images and other things)
+        */
+      FeatureLearningRegionBased ( const NICE::Config *_conf, const OBJREC::MultiDataset *_md , const std::string & _section = "featureLearning");
+
+      /** simple destructor */
+      virtual ~FeatureLearningRegionBased();
+      
+      /** this function has to be overloaded by all subclasses
+          @param imgWithNovelContent 
+      */
+      virtual void learnNewFeatures ( const std::string & filename );
+      
+  };
+
+
+} // namespace
+
+#endif

+ 8 - 0
featureLearning/Makefile

@@ -0,0 +1,8 @@
+#TARGETS_FROM:=$(notdir $(patsubst %/,%,$(shell pwd)))/$(TARGETS_FROM)
+#$(info recursivly going up: $(TARGETS_FROM) ($(shell pwd)))
+
+all:
+
+%:
+	$(MAKE) TARGETS_FROM=$(notdir $(patsubst %/,%,$(shell pwd)))/$(TARGETS_FROM) -C .. $@
+

+ 103 - 0
featureLearning/Makefile.inc

@@ -0,0 +1,103 @@
+# LIBRARY-DIRECTORY-MAKEFILE
+# conventions:
+# - all subdirectories containing a "Makefile.inc" are considered sublibraries
+#   exception: "progs/" and "tests/" subdirectories!
+# - all ".C", ".cpp" and ".c" files in the current directory are linked to a
+#   library
+# - the library depends on all sublibraries 
+# - the library name is created with $(LIBNAME), i.e. it will be somehow
+#   related to the directory name and with the extension .a
+#   (e.g. lib1/sublib -> lib1_sublib.a)
+# - the library will be added to the default build list ALL_LIBRARIES
+
+# --------------------------------
+# - remember the last subdirectory
+#
+# set the variable $(SUBDIR) correctly to the current subdirectory. this
+# variable can be used throughout the current makefile.inc. The many 
+# SUBDIR_before, _add, and everything are only required so that we can recover
+# the previous content of SUBDIR before exitting the makefile.inc
+
+SUBDIR_add:=$(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
+SUBDIR_before:=$(SUBDIR)
+SUBDIR:=$(strip $(SUBDIR_add))
+SUBDIR_before_$(SUBDIR):=$(SUBDIR_before)
+ifeq "$(SUBDIR)" "./"
+SUBDIR:=
+endif
+
+# ------------------------
+# - include subdirectories
+#
+# note the variables $(SUBDIRS_OF_$(SUBDIR)) are required later on to recover
+# the dependencies automatically. if you handle dependencies on your own, you
+# can also dump the $(SUBDIRS_OF_$(SUBDIR)) variable, and include the
+# makefile.inc of the subdirectories on your own...
+
+SUBDIRS_OF_$(SUBDIR):=$(patsubst %/Makefile.inc,%,$(wildcard $(SUBDIR)*/Makefile.inc))
+include $(SUBDIRS_OF_$(SUBDIR):%=%/Makefile.inc)
+
+# ----------------------------
+# - include local dependencies
+#
+# you can specify libraries needed by the individual objects or by the whole
+# directory. the object specific additional libraries are only considered
+# when compiling the specific object files
+# TODO: update documentation...
+
+-include $(SUBDIR)libdepend.inc
+
+$(foreach d,$(filter-out %progs %tests,$(SUBDIRS_OF_$(SUBDIR))),$(eval $(call PKG_DEPEND_INT,$(d))))
+
+# ---------------------------
+# - objects in this directory
+#
+# the use of the variable $(OBJS) is not mandatory. it is mandatory however
+# to update $(ALL_OBJS) in a way that it contains the path and name of
+# all objects. otherwise we can not include the appropriate .d files.
+
+OBJS:=$(patsubst %.cpp,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.cpp))) \
+      $(patsubst %.C,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.C))) \
+	  $(shell grep -ls Q_OBJECT $(SUBDIR)*.h | sed -e's@^@/@;s@.*/@$(OBJDIR)moc_@;s@\.h$$@.o@') \
+      $(patsubst %.c,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.c)))
+ALL_OBJS += $(OBJS)
+
+# ----------------------------
+# - binaries in this directory
+#
+# output of binaries in this directory. none of the variables has to be used.
+# but everything you add to $(ALL_LIBRARIES) and $(ALL_BINARIES) will be
+# compiled with `make all`. be sure again to add the files with full path.
+
+LIBRARY_BASENAME:=$(call LIBNAME,$(SUBDIR))
+ifneq "$(SUBDIR)" ""
+ALL_LIBRARIES+=$(LIBDIR)$(LIBRARY_BASENAME).$(LINK_FILE_EXTENSION)
+endif
+
+# ---------------------
+# - binary dependencies
+#
+# there is no way of determining the binary dependencies automatically, so we
+# follow conventions. the current library depends on all sublibraries.
+# all other dependencies have to be added manually by specifying, that the
+# current .pc file depends on some other .pc file. binaries depending on
+# libraries should exclusivelly use the .pc files as well.
+
+ifeq "$(SKIP_BUILD_$(OBJDIR))" "1"
+$(LIBDIR)$(LIBRARY_BASENAME).a:
+else
+$(LIBDIR)$(LIBRARY_BASENAME).a:$(OBJS) \
+	$(call PRINT_INTLIB_DEPS,$(PKGDIR)$(LIBRARY_BASENAME).a,.$(LINK_FILE_EXTENSION))
+endif
+
+$(PKGDIR)$(LIBRARY_BASENAME).pc: \
+	$(call PRINT_INTLIB_DEPS,$(PKGDIR)$(LIBRARY_BASENAME).pc,.pc)
+
+# -------------------
+# - subdir management
+#
+# as the last step, always add this line to correctly recover the subdirectory
+# of the makefile including this one!
+
+SUBDIR:=$(SUBDIR_before_$(SUBDIR))
+

+ 7 - 0
featureLearning/libdepend.inc

@@ -0,0 +1,7 @@
+$(call PKG_DEPEND_INT,core/)
+$(call PKG_DEPEND_INT,vislearning/baselib/)
+$(call PKG_DEPEND_INT,vislearning/cbaselib/)
+$(call PKG_DEPEND_INT,vislearning/features/)
+$(call PKG_DEPEND_INT,vislearning/math/)
+$(call PKG_DEPEND_INT,vislearning/noveltyDetecion/)
+$(call PKG_DEPEND_INT,segmentation/)

+ 8 - 0
featureLearning/progs/Makefile

@@ -0,0 +1,8 @@
+#TARGETS_FROM:=$(notdir $(patsubst %/,%,$(shell pwd)))/$(TARGETS_FROM)
+#$(info recursivly going up: $(TARGETS_FROM) ($(shell pwd)))
+
+all:
+
+%:
+	$(MAKE) TARGETS_FROM=$(notdir $(patsubst %/,%,$(shell pwd)))/$(TARGETS_FROM) -C .. $@
+

+ 88 - 0
featureLearning/progs/Makefile.inc

@@ -0,0 +1,88 @@
+# BINARY-DIRECTORY-MAKEFILE
+# conventions:
+# - there are no subdirectories, they are ignored!
+# - all ".C", ".cpp" and ".c" files in the current directory are considered
+#   independent binaries, and linked as such.
+# - the binaries depend on the library of the parent directory
+# - the binary names are created with $(BINNAME), i.e. it will be more or less
+#   the name of the .o file
+# - all binaries will be added to the default build list ALL_BINARIES
+
+# --------------------------------
+# - remember the last subdirectory
+#
+# set the variable $(SUBDIR) correctly to the current subdirectory. this
+# variable can be used throughout the current makefile.inc. The many 
+# SUBDIR_before, _add, and everything are only required so that we can recover
+# the previous content of SUBDIR before exitting the makefile.inc
+
+SUBDIR_add:=$(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
+SUBDIR_before:=$(SUBDIR)
+SUBDIR:=$(strip $(SUBDIR_add))
+SUBDIR_before_$(SUBDIR):=$(SUBDIR_before)
+
+# ------------------------
+# - include subdirectories
+#
+# note the variables $(SUBDIRS_OF_$(SUBDIR)) are required later on to recover
+# the dependencies automatically. if you handle dependencies on your own, you
+# can also dump the $(SUBDIRS_OF_$(SUBDIR)) variable, and include the
+# makefile.inc of the subdirectories on your own...
+
+#SUBDIRS_OF_$(SUBDIR):=$(patsubst %/Makefile.inc,%,$(wildcard $(SUBDIR)*/Makefile.inc))
+#include $(SUBDIRS_OF_$(SUBDIR):%=%/Makefile.inc)
+
+# ----------------------------
+# - include local dependencies
+#
+# include the libdepend.inc file, which gives additional dependencies for the
+# libraries and binaries. additionally, an automatic dependency from the library
+# of the parent directory is added (commented out in the code below).
+
+-include $(SUBDIR)libdepend.inc
+
+PARENTDIR:=$(patsubst %/,%,$(dir $(patsubst %/,%,$(SUBDIR))))
+$(eval $(call PKG_DEPEND_INT,$(PARENTDIR)))
+
+# ---------------------------
+# - objects in this directory
+#
+# the use of the variable $(OBJS) is not mandatory. it is mandatory however
+# to update $(ALL_OBJS) in a way that it contains the path and name of
+# all objects. otherwise we can not include the appropriate .d files.
+
+OBJS:=$(patsubst %.cpp,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.cpp))) \
+      $(patsubst %.C,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.C))) \
+      $(shell grep -ls Q_OBJECT $(SUBDIR)*.h | sed -e's@^@/@;s@.*/@$(OBJDIR)moc_@;s@\.h$$@.o@') \
+      $(patsubst %.c,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.c)))
+ALL_OBJS += $(OBJS)
+
+# ----------------------------
+# - binaries in this directory
+#
+# output of binaries in this directory. none of the variables has to be used.
+# but everything you add to $(ALL_LIBRARIES) and $(ALL_BINARIES) will be
+# compiled with `make all`. be sure again to add the files with full path.
+
+BINARIES:=$(patsubst %.o,$(BINDIR)%,$(filter-out moc_%,$(notdir $(OBJS))))
+ALL_BINARIES+=$(BINARIES)
+
+# ---------------------
+# - binary dependencies
+#
+# there is no way of determining the binary dependencies automatically, so we
+# follow conventions. each binary depends on the corresponding .o file and
+# on the libraries specified by the INTLIBS/EXTLIBS. these dependencies can be
+# specified manually or they are automatically stored in a .bd file.
+
+$(foreach head,$(wildcard $(SUBDIR)*.h),$(eval $(shell grep -q Q_OBJECT $(head) && echo $(head) | sed -e's@^@/@;s@.*/\(.*\)\.h$$@$(BINDIR)\1:$(OBJDIR)moc_\1.o@')))
+-include $(OBJS:%.o=%.bd)
+
+# -------------------
+# - subdir management
+#
+# as the last step, always add this line to correctly recover the subdirectory
+# of the makefile including this one!
+
+SUBDIR:=$(SUBDIR_before_$(SUBDIR))
+

+ 7 - 0
featureLearning/progs/libdepend.inc

@@ -0,0 +1,7 @@
+$(call PKG_DEPEND_INT,vislearning/baselib/)
+$(call PKG_DEPEND_INT,vislearning/cbaselib/)
+$(call PKG_DEPEND_INT,vislearning/features/)
+$(call PKG_DEPEND_INT,vislearning/math/)
+$(call PKG_DEPEND_INT,vislearning/noveltyDetection/)
+$(call PKG_DEPEND_INT,segmentation/)
+$(call PKG_DEPEND_EXT_ESSENTIAL,VLFEAT)

+ 346 - 0
featureLearning/progs/testFeatureLearning.cpp

@@ -0,0 +1,346 @@
+/**
+* @file testFeatureLearning.cpp
+* @brief test the feature learning routines to incrementally increase / adapt the codebook currently used
+* @author Alexander Freytag
+* @date 11-04-2013
+*/
+
+//STL
+#include <iostream>
+#include <limits>
+
+//core
+#include <core/basics/Config.h>
+#include <core/basics/ResourceStatistics.h>
+#include <core/image/Convert.h>
+#include <core/vector/VectorT.h>
+
+//vislearning
+#include <vislearning/baselib/Globals.h>
+#include <vislearning/baselib/ICETools.h>
+#include <vislearning/cbaselib/MultiDataset.h>
+#include <vislearning/cbaselib/Example.h>
+// 
+#include "vislearning/featureLearning/FeatureLearningGeneric.h"
+#include "vislearning/featureLearning/FeatureLearningClusterBased.h"
+#include "vislearning/featureLearning/FeatureLearningRegionBased.h"
+//
+#include "vislearning/noveltyDetection/NDCodebookLevelImagePooling.h"
+
+
+using namespace std;
+using namespace NICE;
+using namespace OBJREC;
+
+
+/**
+ test feature learning routines
+*/
+int main( int argc, char **argv )
+{
+  std::set_terminate( __gnu_cxx::__verbose_terminate_handler );
+
+  Config * conf = new Config ( argc, argv );
+  
+  bool showTrainingImages = conf->gB( "featureLearning", "showTrainingImages", false );
+  bool showTestImages = conf->gB( "featureLearning", "showTestImages", false );
+  bool showResults = conf->gB( "featureLearning", "showResults", false );
+  
+  ResourceStatistics rs;
+  std::string resultdir;
+  resultdir = conf->gS( "featureLearning", "resultdir", "/tmp/");
+  
+  
+  NICE::ImageT<int> noveltyScale ( 20, 100 );
+  for (int j = 0; j < 100; j++)
+  {
+    for (int i = 0; i < 20; i++)
+      noveltyScale(i,j) = 99-j;
+  }
+  
+  NICE::ColorImage noveltyScaleRGB (noveltyScale.width(), noveltyScale.height() );
+  imageToPseudoColor( noveltyScale, noveltyScaleRGB );
+  
+  std::string destinationNoveltyScale ( resultdir + "_" + "_noveltyScale.ppm");
+  noveltyScaleRGB.writePPM( destinationNoveltyScale );   
+  
+  
+  
+  //**********************************************
+  //
+  //      READ INITIAL TRAINING SET TO COMPUTE
+  //             AN INITIAL CODEBOOK
+  //
+  //**********************************************
+  
+  std::cerr << " READ INITIAL TRAINING SET TO COMPUTE AN INITIAL CODEBOOK" << std::endl;
+  
+  MultiDataset md( conf );
+  const LabeledSet *trainFiles = md["train"];
+  
+  //**********************************************
+  //
+  //      SET UP THE FEATURE LEARNING ALGO
+  //
+  //********************************************** 
+  
+  OBJREC::FeatureLearningGeneric * featureLearning;
+  
+  std::string featureLearningMethod = conf->gS( "featureLearning", "featureLearningMethod", "clusterBased" );
+  
+  if (featureLearningMethod.compare("clusterBased") == 0)
+  {
+    featureLearning = new OBJREC::FeatureLearningClusterBased( conf, &md );
+  }
+  else if (featureLearningMethod.compare("regionBased") == 0) 
+  {
+    featureLearning = new OBJREC::FeatureLearningRegionBased( conf, &md );
+  }
+  else
+  {
+    std::cerr << "Unknown feature learning algorithm selected, use cluster based instead" << std::endl;
+    featureLearning = new OBJREC::FeatureLearningClusterBased( conf, &md );
+  }    
+  
+  //**********************************************
+  //
+  //      SET UP THE NOVELTY DECTION ALGO
+  //
+  //**********************************************   
+  
+  OBJREC::NDCodebookLevelImagePooling * novDetector;
+  novDetector = new OBJREC::NDCodebookLevelImagePooling( conf, &md, "featureLearning" );
+    
+  //evaluate how well the training images are covered with our initial codebook
+  //that is, compute these nice "novelty maps" per feature
+  
+  //NOTE we skip this currently
+  LOOP_ALL_S( *trainFiles )
+  {
+      EACH_INFO( classno, info );
+      std::string filename = info.img();
+//       
+//       featureLearning->evaluateCurrentCodebook( filename , true /* beforeComputingNewFeatures */);       
+      
+      NICE::ImageT< int > imgClusterAssignments;
+      imgClusterAssignments = featureLearning->evaluateCurrentCodebookByAssignments(filename , false /* beforeComputingNewFeatures */, false /* _binaryShowLatestPrototype*/ );
+      
+      std::cerr << "now do image To pseude color and show the initial cluster assignments" << std::endl;
+      NICE::ColorImage imgClusterAssignmentsRGB (imgClusterAssignments.width(), imgClusterAssignments.height() );
+      imageToPseudoColor( imgClusterAssignments, imgClusterAssignmentsRGB );      
+      
+      if ( showResults )
+        showImage(imgClusterAssignmentsRGB, "cluster Assignments" ) ;
+      else
+      {
+        std::vector< std::string > list2;
+        StringTools::split ( filename, '/', list2 );        
+        std::string destination ( resultdir + NICE::intToString(0) + "_" + list2.back() + "_00_initialClusterAssignments.ppm");
+        imgClusterAssignmentsRGB.writePPM( destination );        
+      }
+  }
+  
+  //**********************************************
+  //
+  //        EVALUATE INITIAL CLUSTER
+  //
+  //       COMPUTE A NICE CONFUSION MATRIX
+  //          FOR OUR INITIAL CODEBOOK
+  //
+  //**********************************************  
+  NICE::Matrix confusionMatInitial;
+  featureLearning->evaluateCurrentCodebookByConfusionMatrix( confusionMatInitial );
+  std::cerr << "initial Confusion matrix: " << std::endl << confusionMatInitial << std::endl;
+  
+  //set the initially computed codebook to the novelty detection mechanism
+  //TODO this should be done, but currently we do not care about
+//   novDetector->setCodebook( featureLearning->getCurrentCodebook() );
+  
+
+  //**********************************************
+  //
+  //        FOR-LOOP OVER UNSEEN IMAGES
+  //
+  //       EXTRACT FEATURES, CLUSTER THEM, TAKE
+  //       MOST "VALUABLE" CLUSTERS AS NEW 
+  //    REPRESENTATIVES IN AN INCREASED CODEBOK
+  //
+  //**********************************************
+  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 filename = info.img();
+      
+      NICE::ColorImage orig( filename );
+      
+      NICE::ImageT< int > imgClusterAssignments;
+      imgClusterAssignments = featureLearning->evaluateCurrentCodebookByAssignments(filename , false /* beforeComputingNewFeatures */, false /* _binaryShowLatestPrototype*/ );      
+      
+      NICE::ColorImage imgClusterAssignmentsRGB (imgClusterAssignments.width(), imgClusterAssignments.height() );
+      imageToPseudoColor( imgClusterAssignments, imgClusterAssignmentsRGB );       
+      
+      NICE::FloatImage noveltyImageBefore;
+      noveltyImageBefore = featureLearning->evaluateCurrentCodebookByDistance( filename , true /* beforeComputingNewFeatures */ );
+      
+      NICE::ColorImage noveltyImageBeforeRGB (noveltyImageBefore.width(), noveltyImageBefore.height() );
+      imageToPseudoColor( noveltyImageBefore, noveltyImageBeforeRGB );        
+      
+      if ( showResults )
+        showImage(imgClusterAssignmentsRGB, "cluster Assignments" ) ;
+      else
+      {
+        std::vector< std::string > list2;
+        StringTools::split ( filename, '/', list2 );        
+        std::string destination ( resultdir + NICE::intToString(0) + "_" + list2.back() + "_00_initialClusterAssignments.ppm");
+        imgClusterAssignmentsRGB.writePPM( destination );        
+        
+        std::string destinationNovelty ( resultdir + NICE::intToString(0) + "_" + list2.back() + "_01_initialClusterDistances.ppm");
+        noveltyImageBeforeRGB.writePPM( destinationNovelty );          
+      }      
+      
+      //**********************************************
+      //
+      //             IS THIS IMAGE NOVEL?
+      //
+      //          IF NOT, GO TO THE NEXT ONE
+      //
+      //**********************************************       
+      
+      bool b_isImageNovel ( novDetector->evaluateNoveltyOfImage( noveltyImageBefore ) );
+      
+      if ( ! b_isImageNovel )
+      {
+        std::cerr << " --- NOT NOVEL --- " << std::endl << std::endl;
+        continue;
+      }
+      else
+      {
+        std::cerr << " --- NOVEL --- " << std::endl;
+      }
+      
+      while ( b_isImageNovel ) 
+      {
+              
+        //**********************************************
+        //
+        //       LEARN NEW FEATURE FOR A NOVEL IMAGE
+        //
+        //**********************************************       
+        
+        featureLearning->learnNewFeatures( filename );
+        
+        //and update the codebook pointer within our novelty detection algorithm
+        //TODO this should be done, but currently we do not care about
+  //       novDetector->setCodebook( featureLearning->getCurrentCodebook() );      
+        
+        //**********************************************
+        //
+        //       EVALUATE HOW WELL THE CURRENT IMAGE
+        //            CAN BE EXPLAINED AFTER 
+        //           COMPUTING A NEW FEATURE
+        //
+        //       SHOULD WE REPEAT THIS UNTIL THE IMAGE
+        //       IS NOT CLASSIFIED AS "NOVEL" ANYMORE?
+        //
+        //**********************************************       
+        
+        NICE::FloatImage noveltyImageAfter;
+        noveltyImageAfter = featureLearning->evaluateCurrentCodebookByDistance( 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) );
+          }
+        }
+        
+        std::cerr << "min diff: " << noveltyImageDifference.min() << " and max diff: " << noveltyImageDifference.max() << std::endl;
+        
+        NICE::ColorImage noveltyImageDifferenceRGB (noveltyImageAfter.width(), noveltyImageAfter.height() );
+        imageToPseudoColor( noveltyImageDifference, noveltyImageDifferenceRGB );      
+        
+        std::vector< std::string > list2;
+        StringTools::split ( filename, '/', list2 );      
+
+        if ( showResults )
+        {
+          showImage( noveltyImageDifferenceRGB, "Difference of novelty images" );
+        }
+        else
+        {          
+          std::string destination ( resultdir + NICE::intToString(imageCnt) + "_" + list2.back() + "_02_initialLoopClusterAssignments.ppm");  
+          imgClusterAssignmentsRGB.writePPM( destination );    
+
+          std::string destinationNoveltyDifference ( resultdir + NICE::intToString(imageCnt) + "_" + list2.back() + "_03_noveltyDifferences.ppm");  
+          noveltyImageDifferenceRGB.writePPM( destinationNoveltyDifference );                        
+        }
+
+        NICE::ImageT< int > imgClusterAssignmentsBinary;
+        imgClusterAssignmentsBinary = featureLearning->evaluateCurrentCodebookByAssignments(filename , true, true /* _binaryShowLatestPrototype*/ );      
+        
+        NICE::ImageT< int > imgClusterAssignments;
+        imgClusterAssignments = featureLearning->evaluateCurrentCodebookByAssignments(filename , true, false /* _binaryShowLatestPrototype*/ );
+
+        
+        NICE::ColorImage imgClusterAssignmentsBinaryRGB (imgClusterAssignmentsBinary.width(), imgClusterAssignmentsBinary.height() );
+        imageToPseudoColor( imgClusterAssignmentsBinary, imgClusterAssignmentsBinaryRGB );   
+        
+        NICE::ColorImage imgClusterAssignmentsRGB (imgClusterAssignments.width(), imgClusterAssignments.height() );
+        imageToPseudoColor( imgClusterAssignments, imgClusterAssignmentsRGB );           
+        
+        if ( showResults )
+        {
+          showImage(imgClusterAssignmentsBinaryRGB, "cluster Assignments Binary (latest prototype)" ) ;      
+          showImage(imgClusterAssignmentsRGB, "cluster Assignments" ) ;
+        }
+        else
+        {
+            std::string destination ( resultdir + NICE::intToString(imageCnt) + "_" + list2.back() + "_5_clusterAssignments.ppm");
+            std::string destinationBinary ( resultdir + NICE::intToString(imageCnt) + "_" + list2.back() + "_6_clusterAssignmentsBinary.ppm");
+            
+            imgClusterAssignmentsRGB.writePPM( destination );
+            imgClusterAssignmentsBinaryRGB.writePPM( destinationBinary );
+        }      
+        
+        //check, whether the image will still be seen as novel or not
+        // i.e., are we able to "explain" the image with the lately computed codebook entry?
+        b_isImageNovel = novDetector->evaluateNoveltyOfImage( noveltyImageAfter ) ;
+        
+        if ( ! b_isImageNovel )
+        {
+          std::cerr << " --- NOT NOVEL ANYMORE --- " << std::endl << std::endl;
+        }
+        else
+        {
+          std::cerr << " --- STILL NOVEL --- " << std::endl;
+          noveltyImageBefore = noveltyImageAfter;
+        }        
+        
+        //artifial break for the moment
+        break;
+      }
+
+      
+      imageCnt++;
+  } //Loop over all test images
+  
+  
+  //don't waste memory
+  std::cerr << "don't waste memory - cleaning up" << std::endl;
+//   if (trainFiles != NULL)
+//     delete trainFiles;
+  if (featureLearning != NULL)
+    delete featureLearning;
+  if (novDetector != NULL)
+    delete novDetector;
+  if (conf != NULL)
+    delete conf;
+  
+   return 0;
+}

+ 159 - 58
features/localfeatures/GenericLFSelection.h

@@ -1,5 +1,5 @@
 /** 
-* @file GenericLocalFeatureSelection.h
+* @file GenericLFSelection.h
 * @brief This class provides a generic chooser function for different descriptors, which are derived from LocalFeatureRepresentation.
 * @date 26.10.2011
 */
@@ -19,79 +19,180 @@
 #include "vislearning/features/localfeatures/LFColorSande.h"
 #include "vislearning/features/localfeatures/LFonHSG.h"
 
-/** NOTE: This class only returns Descriptors, which NOT need any given positions. All Descriptors calculate there own positions, on DIFFERENT ways. **/
-
 
 namespace OBJREC {
 
+  /** @class GenericLFSelection
+ * @brief Select a specific LF-Type (local feature representation). LFs compute Descriptors, which DO NOT need given positions, but calculate them ON THEIR OWN!
+ * All Descriptor-Methods calculate there own positions previously, and on DIFFERENT ways.
+ */
 class GenericLFSelection
 {
     public:
+      
+      //! enum specifying for which step the feature extractor shall be used (this influences the config section which will be used)
+      enum UsageForTrainOrTest { NOTSPECIFIED = 0,
+             TRAINING,
+             TESTING
+      };
 
       /** LocalFeature Selector
        * @brief This methode switches between the different LocalFeature-Types. One has to set the "localfeature_type" on a valid value and the methode returns a LocalFeatureRepresentation derived Object.
-       * @param[in] Config* - A pointer to the given configfile, which must contain "section" - "localfeature_type"
-       * @param[in] string  - This string defines the value for "section" in the configfile.
+       * @param[in] conf - A pointer to the given configfile, which must contain "section" - "localfeature_type"
+       * @param[in] section - This string defines the value for "section" in the configfile.
+       * @param[in] useForTrainOrTest - Specify whether we use the LFRep for Training, Testing, or for both - this influences the choice of the config section that is handed over to the LFRep-method
        * @return LocalFeatureRepresentation* - The LocalFeatureRepresentation which contains the selected LocalFeature-Type.
        */
       static
-      LocalFeatureRepresentation *selectLocalFeatureRep ( const NICE::Config *conf, std::string section = "features" )
+      LocalFeatureRepresentation *selectLocalFeatureRep ( const NICE::Config *conf, std::string section = "features", const UsageForTrainOrTest & useForTrainOrTest = NOTSPECIFIED )
       {
-	  // return Value
-	  LocalFeatureRepresentation *lfrep = NULL;
-	
-	  // string which defines the localfeature_type (for Ex. Sande, ...)
-	  std::string localfeature_type = conf->gS(section, "localfeature_type", "");
-	
-	  if ( localfeature_type == "mikolajczyk" )
-	  {
-	    lfrep = new LFMikolajczyk ( conf );
-	  }
-	  else if ( localfeature_type == "color" )
-	  {
-	    lfrep = new LFColorSande ( conf );
-	  }
-	  else if ( ( localfeature_type == "sift" ) || ( localfeature_type == "siftpp" ) ) 
-	  {
-            lfrep = new LFSiftPP ( conf );
-	  }
-	  else if ( ( localfeature_type == "generic_local" ) || ( localfeature_type == "generic" ) )
-	  {
-            int numFeatures = conf->gI(section, "localfeature_count");
-	    lfrep = new LFGenericLocal ( conf, numFeatures );
-	  }
-	  else if ( ( localfeature_type == "grey" ) || ( localfeature_type == "patches" ) )
-	  {
-	    int numFeatures = conf->gI(section, "localfeature_count");
-	    lfrep = new LFPatches ( conf, numFeatures );
-	  } 
-	  else if( ( localfeature_type == "onHSG" ) )
-	  {
-	    lfrep = new LFonHSG( conf);
-	  }
-	  else 
-	  {
-		  lfrep = NULL;
-	  }
+        // return Value
+        LocalFeatureRepresentation *lfrep = NULL;
+      
+        // string which defines the localfeature_type (for Ex. Sande, ...)
+        std::string localfeature_type = conf->gS(section, "localfeature_type", "");
+      
+        if ( localfeature_type == "mikolajczyk" )
+        {
+          lfrep = new LFMikolajczyk ( conf );
+        }
+        else if ( localfeature_type == "VANDESANDE" ) //previously: "color"
+        {
+          if ( useForTrainOrTest == TRAINING )
+          {
+            lfrep = new LFColorSande ( conf, "LFColorSandeTrain" );
+          }
+          else if ( useForTrainOrTest == TESTING )
+          {
+            lfrep = new LFColorSande ( conf, "LFColorSandeTest" );
+          }
+          else //not specified whether for training or testing, so we do not care about
+          {
+            lfrep = new LFColorSande ( conf );            
+          }          
+        }
+        else if ( ( localfeature_type == "sift" ) || ( localfeature_type == "siftpp" ) ) 
+        {
+                lfrep = new LFSiftPP ( conf );
+        }
+        else if ( ( localfeature_type == "generic_local" ) || ( localfeature_type == "generic" ) )
+        {
+                int numFeatures = conf->gI(section, "localfeature_count");
+          lfrep = new LFGenericLocal ( conf, numFeatures );
+        }
+        else if ( ( localfeature_type == "grey" ) || ( localfeature_type == "patches" ) )
+        {
+          int numFeatures = conf->gI(section, "localfeature_count");
+          lfrep = new LFPatches ( conf, numFeatures );
+        } 
+        else if( ( localfeature_type == "onHSG" ) )
+        {
+          if ( useForTrainOrTest == TRAINING )
+          {
+            lfrep = new OBJREC::LFonHSG ( conf, "HSGTrain" );
+          }
+          else if ( useForTrainOrTest == TESTING )
+          {
+            lfrep = new OBJREC::LFonHSG ( conf, "HSGTest" );
+          }
+          else //not specified whether for training or testing, so we do not care about
+          {
+            lfrep = new OBJREC::LFonHSG( conf);
+          }           
+          
+        }
+        else 
+        {
+          lfrep = NULL;
+        }
+
+//         if ( conf->gB(section, "localfeature_cache_read", false) )
+//         {
+//           int numFeatures = conf->gI(section, "localfeature_count", -1);
+//           LocalFeatureRepresentation *lfrep_read = new LFReadCache ( conf, lfrep, numFeatures );
+//           lfrep = lfrep_read;
+//         }
+// 
+//         // no correct localfeature_type was given
+//         if ( lfrep == NULL )
+//           fthrow(NICE::Exception, "Local feature type not found: " << localfeature_type );
+// 
+//         if ( conf->gB(section, "localfeature_cache_write", false) )
+//         {
+//           LocalFeatureRepresentation *lfrep_write = new LFWriteCache ( conf, lfrep );
+//           lfrep = lfrep_write;
+//         }
+
+        // read features?
+        bool readfeat;
+        readfeat = conf->gB ( section, "localfeature_cache_read", true );
+        // write features?
+        bool writefeat;  
+        writefeat = conf->gB ( section, "localfeature_cache_write", true );   
 
-	  if ( conf->gB(section, "localfeature_cache_read", false) )
-	  {
-		  int numFeatures = conf->gI(section, "localfeature_count", -1);
-		  LocalFeatureRepresentation *lfrep_read = new LFReadCache ( conf, lfrep, numFeatures );
-		  lfrep = lfrep_read;
-	  }
+        LocalFeatureRepresentation *writeFeats = NULL;
+        LocalFeatureRepresentation *readFeats = NULL;
+        if ( writefeat )
+        {
+          // write the features to a file, if there isn't any to read
+          
+          if ( useForTrainOrTest == TRAINING )
+          {
+            writeFeats = new LFWriteCache ( conf, lfrep, "cacheTrain" );
+            lfrep = writeFeats;
+          }
+          else if ( useForTrainOrTest == TESTING )
+          {
+            writeFeats = new LFWriteCache ( conf, lfrep, "cacheTest" );
+            lfrep = writeFeats;
+          }
+          else //not specified whether for training or testing, so we do not care about
+          {
+            writeFeats = new LFWriteCache ( conf, lfrep );
+            lfrep = writeFeats;
+          }              
 
-	  // no correct localfeature_type was given
-	  if ( lfrep == NULL )
-		  fthrow(NICE::Exception, "Local feature type not found: " << localfeature_type );
+        }
 
-	  if ( conf->gB(section, "localfeature_cache_save", false) )
-	  {
-		  LocalFeatureRepresentation *lfrep_save = new LFWriteCache ( conf, lfrep );
-		  lfrep = lfrep_save;
-	  }
-		  
-	  return lfrep;
+//TODO add the section into constructor for readCache
+        if ( readfeat )
+        {
+          int numFeatures = conf->gI(section, "localfeature_count", -1);
+          // read the features from a file
+          if ( writefeat )
+          {
+            if ( useForTrainOrTest == TRAINING )
+            {
+              readFeats = new LFReadCache ( conf, writeFeats, numFeatures );
+            }
+            else if ( useForTrainOrTest == TESTING )
+            {
+              readFeats = new LFReadCache ( conf, writeFeats, numFeatures );
+            }
+            else //not specified whether for training or testing, so we do not care about
+            {
+              readFeats = new LFReadCache ( conf, writeFeats, numFeatures );
+            }                         
+          }
+          else
+          {
+            if ( useForTrainOrTest == TRAINING )
+            {
+              readFeats = new LFReadCache (conf, lfrep, numFeatures );
+            }
+            else if ( useForTrainOrTest == TESTING )
+            {
+              readFeats = new LFReadCache (conf, lfrep, numFeatures );
+            }
+            else //not specified whether for training or testing, so we do not care about
+            {
+              readFeats = new LFReadCache (conf, lfrep, numFeatures );
+            }
+          }
+          lfrep = readFeats; 
+        }  
+          
+        return lfrep;
       }
 };
 

+ 4 - 0
features/localfeatures/GenericLocalFeatureSelection.h

@@ -21,6 +21,10 @@
 
 namespace OBJREC {
 
+  /** @class GenericLocalFeatureSelection
+ * @brief Select a specific LocalFeature-Type. LocalFeatures compute Descriptors, which DO need given positions (no internal position calculation)!
+ *
+ */  
 class GenericLocalFeatureSelection
 {
   public:

+ 16 - 14
features/localfeatures/IDRandomSampling.h

@@ -18,25 +18,27 @@
 namespace OBJREC
 {
 
-/** random interest point sampling */
-class IDRandomSampling: public InterestDetector
-{
+  /** @class IDRandomSampling
+ * @brief random interest point sampling
+ */
+  class IDRandomSampling: public InterestDetector
+  {
 
-protected:
-	int numSamples;
-	double minScale;
+  protected:
+    int numSamples;
+    double minScale;
 
-public:
+  public:
 
-	IDRandomSampling(const NICE::Config *conf, int numSamples);
+    IDRandomSampling(const NICE::Config *conf, int numSamples);
 
-	virtual ~IDRandomSampling();
-	int getInterests(const NICE::Image & img,
-			std::vector<NICE::Vector> & positions) const;
-	int getInterests(const ImagePyramid & imp,
-			std::vector<NICE::Vector> & positions) const;
+    virtual ~IDRandomSampling();
+    int getInterests(const NICE::Image & img,
+        std::vector<NICE::Vector> & positions) const;
+    int getInterests(const ImagePyramid & imp,
+        std::vector<NICE::Vector> & positions) const;
 
-};
+  };
 
 } // namespace
 

+ 7 - 3
features/localfeatures/LFColorSande.cpp

@@ -30,9 +30,12 @@ using namespace NICE;
 
 LFColorSande::LFColorSande ( const Config *conf, std::string section )
 {
+  std::cerr << "LF COLOR SANDE SECTION ====================== :" << section << ":"<<std::endl;
+  
   c_binaryExecutable = conf->gS ( section, "binaryExecutable", "/home/bachi/libs/van_de_sande/x86_64-linux-gcc/colorDescriptor" );
   c_params = conf->gS ( section, "params", "--descriptor opponentsift" );
   scales = conf->gS ( section, "scales", "1+1.5+3.0+4.5+6" );
+  std::cerr << "scales: " << scales << std::endl;
 
   descriptor_size = conf->gI ( section, "descriptor_size", -1 );
 
@@ -42,7 +45,7 @@ LFColorSande::LFColorSande ( const Config *conf, std::string section )
   std::ostringstream temp;
   temp << g;
   gridsize = temp.str();
-
+  
   if ( descriptor_size <= 0 )
   {
     fprintf ( stderr, "LFColorSande::LFColorSande: No descriptor size found in config -> self test\n" );
@@ -57,11 +60,11 @@ LFColorSande::LFColorSande ( const Config *conf, std::string section )
 
     fprintf ( stderr, "LFColorSande::LFColorSande Self Test features:%d dimension:%d\n", ( int ) features.size(), descriptor_size );
   }
-
+  
   if ( descriptor_size != conf->gI ( "features", "descriptor_size", descriptor_size ) )
   {
     cerr << "Warning: LFColorSande: descriptor sizes do not match !!!" << endl;
-  }
+  }  
 }
 
 LFColorSande::~LFColorSande()
@@ -205,6 +208,7 @@ int LFColorSande::extractFeatures ( const NICE::ColorImage & img, VVector & feat
   while ( ! feof ( f ) )
   {
     // <CIRCLE 119 307 1.26134 0 0.00014763>; 0 0 6 2 0 6 25 7 9 4 4 0 0 4 20 36 78 4 5 0 0
+    //<CIRCLE x y scale orientation cornerness>
     if ( fgets ( buffer, buffersize, f ) == NULL )
       break;
 

+ 29 - 27
features/localfeatures/LFGenericLocal.h

@@ -1,9 +1,8 @@
 /** 
 * @file LFGenericLocal.h
-* @brief generic local features
+* @brief generic local features ( Random Sampling of POIs and SIFT as descriptor)
 * @author Erik Rodner
 * @date 02/05/2008
-
 */
 #ifndef LFGENERICLOCALINCLUDE
 #define LFGENERICLOCALINCLUDE
@@ -21,33 +20,36 @@
 
 namespace OBJREC {
 
-/** generic local features */
-class LFGenericLocal : public LocalFeatureRepresentation
-{
+  /** generic local features */
+  /** @class LFGenericLocal
+ * @brief Generic local features ( actually: Random Sampling of POIs followed by SIFT as descriptor)
+ */  
+  class LFGenericLocal : public LocalFeatureRepresentation
+  {
+
+      protected:
+    LocalFeature *lf;
+    InterestDetector *id;
+
+      public:
+    
+    /** simple constructor */
+    LFGenericLocal( const NICE::Config *conf, int numFeatures );
+        
+    /** simple destructor */
+    virtual ~LFGenericLocal();
+      
+    int getDescSize () const;
 
-    protected:
-	LocalFeature *lf;
-	InterestDetector *id;
+    int extractFeatures ( const NICE::Image & img, 
+              NICE::VVector & features, 
+              NICE::VVector & positions) const;
+              
+    void visualizeFeatures ( NICE::Image & mark,
+          const NICE::VVector & positions,
+          size_t color ) const;
 
-    public:
-  
-	/** simple constructor */
-	LFGenericLocal( const NICE::Config *conf, int numFeatures );
-      
-	/** simple destructor */
-	virtual ~LFGenericLocal();
-     
-	int getDescSize () const;
-
-	int extractFeatures ( const NICE::Image & img, 
-			      NICE::VVector & features, 
-			      NICE::VVector & positions) const;
-			      
-	void visualizeFeatures ( NICE::Image & mark,
-				 const NICE::VVector & positions,
-				 size_t color ) const;
-
-};
+  };
 
 
 } // namespace

+ 2 - 0
features/localfeatures/LFReadCache.tcc

@@ -40,7 +40,9 @@ int LFReadCache::extractFeaturesTemplate ( const ImageClass & img,
     if ( lfrep == NULL )
       fthrow ( NICE::Exception, "LocalFeatureRepresentation not available, recovering is impossible!" );
 
+    std::cerr << "extract features with lfrep used in ReadCache" << std::endl;
     lfrep->extractFeatures ( img, features, positions );
+    std::cerr << "features extracted" << std::endl;
 
     features.save ( filename_desc, descFormat );
     positions.save ( filename_pos, NICE::VVector::FILEFORMAT_LINE );

+ 5 - 4
features/localfeatures/LFWriteCache.cpp

@@ -29,12 +29,13 @@ using namespace NICE;
 
 
 LFWriteCache::LFWriteCache ( const Config *conf,
-                             const LocalFeatureRepresentation *_lfrep ) : lfrep ( _lfrep )
+                             const LocalFeatureRepresentation *_lfrep,
+                             const std::string & _section ) : lfrep ( _lfrep )
 {
-  cachedir = conf->gS ( "cache", "root" );
-  cachemode = Globals::getCacheMode ( conf->gS ( "cache", "mode", "cat" ) );
+  cachedir = conf->gS ( _section, "root" );
+  cachemode = Globals::getCacheMode ( conf->gS ( _section, "mode", "cat" ) );
 
-  std::string descFormat_s = conf->gS ( "cache", "descriptor_format", "binary_double" );
+  std::string descFormat_s = conf->gS ( _section, "descriptor_format", "binary_double" );
   if ( descFormat_s == "binary_double" )
     descFormat = VVector::FILEFORMAT_BINARY_DOUBLE;
   else if ( descFormat_s == "binary_uchar" )

+ 54 - 49
features/localfeatures/LFWriteCache.h

@@ -1,6 +1,6 @@
 /** 
 * @file LFWriteCache.h
-* @brief write local features to file
+* @brief Write local features to file (whenever a descriptor is computed, it will be checked whether a corresponding file already exists. If not, we save the descriptor)
 * @author Erik Rodner
 * @date 02/14/2008
 
@@ -18,59 +18,64 @@
 
 namespace OBJREC {
 
-/** write local features to file */
-class LFWriteCache : public LocalFeatureRepresentation
-{
+  /** @class LFWriteCache
+  * @brief Write local features to file (whenever a descriptor is computed, it will be checked whether a corresponding file already exists. If not, we save the descriptor)
+  *
+  */  
+  class LFWriteCache : public LocalFeatureRepresentation
+  {
 
-    protected:
-	const LocalFeatureRepresentation *lfrep;
+      protected:
+        const LocalFeatureRepresentation *lfrep;
 
-	std::string cachedir;
+        std::string cachedir;
 
-	int descFormat;
-	int cachemode;
+        int descFormat;
+        int cachemode;
 
 
-    public:
-  
-		/** simple constructor */
-		LFWriteCache( const NICE::Config *conf, 
-				const LocalFeatureRepresentation *lfrep ); 
-		
-		/** simple destructor */
-		virtual ~LFWriteCache();
-	
-		int getDescSize () const;
-	
-		/**
-		* extract features for gray images
-		* @param img input image
-		* @param features output features
-		* @param positions position of the features
-		* @return 
-		*/
-		int extractFeatures ( const NICE::Image & img, 
-					NICE::VVector & features, 
-					NICE::VVector & positions ) const;
-		
-		/**
-		* extract features for color images
-		* @param img input image
-		* @param features output features
-		* @param positions position of the features
-		* @return 
-		*/
-		int extractFeatures ( const NICE::ColorImage & img, 
-					NICE::VVector & features, 
-					NICE::VVector & positions ) const;
-	
-		void visualize ( NICE::Image & img, 
-			const NICE::Vector & feature ) const;
-	
-		void visualizeFeatures ( NICE::Image & mark,
-					const NICE::VVector & positions,
-					size_t color ) const;
-};
+      public:
+    
+        /** simple constructor */
+        LFWriteCache( const NICE::Config *conf, 
+            const LocalFeatureRepresentation *lfrep,
+            const std::string & _section = "cache"
+                    ); 
+        
+        /** simple destructor */
+        virtual ~LFWriteCache();
+      
+        int getDescSize () const;
+      
+        /**
+        * extract features for gray images
+        * @param img input image
+        * @param features output features
+        * @param positions position of the features
+        * @return 
+        */
+        int extractFeatures ( const NICE::Image & img, 
+              NICE::VVector & features, 
+              NICE::VVector & positions ) const;
+        
+        /**
+        * extract features for color images
+        * @param img input image
+        * @param features output features
+        * @param positions position of the features
+        * @return 
+        */
+        int extractFeatures ( const NICE::ColorImage & img, 
+              NICE::VVector & features, 
+              NICE::VVector & positions ) const;
+      
+        void visualize ( NICE::Image & img, 
+          const NICE::Vector & feature ) const;
+      
+        void visualizeFeatures ( NICE::Image & mark,
+              const NICE::VVector & positions,
+              size_t color ) const;
+  };
 
 
 } // namespace

+ 27 - 24
features/localfeatures/LocalFeature.h

@@ -1,7 +1,7 @@
 /** 
 * @file LocalFeature.h
-* @brief local feature interface
-* @author Erik Rodner
+* @brief Abstract class for Local Features ( ONLY DESCRIPTORS, NO DETECTORS )
+* @author Erik Rodner, Alexander Freytag
 * @date 02/05/2008
 
 */
@@ -17,31 +17,34 @@
 
 namespace OBJREC {
 
-/** local feature interface */
-class LocalFeature
-{
+    /** @class LocalFeature
+  * @brief Abstract class for Local Features ( ONLY DESCRIPTORS, NO DETECTORS )
+  *
+  */
+  class LocalFeature
+  {
 
-    protected:
+      protected:
 
-    public:
-  
-	/** simple constructor */
-	LocalFeature();
-      
-	/** simple destructor */
-	virtual ~LocalFeature();
-
-	virtual int getDescSize() const = 0;
-
-	virtual int getDescriptors ( const NICE::Image & img, NICE::VVector & positions, NICE::VVector & descriptors) const = 0;
-	
-	virtual int getDescriptors ( const NICE::ColorImage & img, NICE::VVector & positions, NICE::VVector & descriptors) const;
-	
-	virtual void visualizeFeatures ( NICE::Image & mark,
-				 const NICE::VVector & positions,
-				 size_t color ) const;
+      public:
     
-};
+    /** simple constructor */
+    LocalFeature();
+        
+    /** simple destructor */
+    virtual ~LocalFeature();
+
+    virtual int getDescSize() const = 0;
+
+    virtual int getDescriptors ( const NICE::Image & img, NICE::VVector & positions, NICE::VVector & descriptors) const = 0;
+    
+    virtual int getDescriptors ( const NICE::ColorImage & img, NICE::VVector & positions, NICE::VVector & descriptors) const;
+    
+    virtual void visualizeFeatures ( NICE::Image & mark,
+          const NICE::VVector & positions,
+          size_t color ) const;
+      
+  };
 
 
 } // namespace

+ 14 - 12
features/localfeatures/LocalFeatureLFInterface.cpp

@@ -10,34 +10,36 @@ using namespace NICE;
 
 LocalFeatureLFInterface::LocalFeatureLFInterface( const Config *conf, LocalFeatureRepresentation *_lfpres )
 {
-	lfpres = _lfpres;
+  lfpres = _lfpres;
 }
 
 LocalFeatureLFInterface::~LocalFeatureLFInterface()
 {
-	delete lfpres;
+  delete lfpres;
 }
 
 
 int LocalFeatureLFInterface::getDescriptors ( const NICE::Image & img, VVector & positions, VVector & descriptors ) const
 {
-	lfpres->extractFeatures(img, descriptors, positions);
-	assert(descriptors.size() == positions.size());
-    return 0;
+  //TODO do we want to ignore the positions of lfrep and use the given ones, or do we indeed want to compute positions on our own? If so, why do we use the Interface to LocalFeature, and not directly LocalFeatureRepresentation?    
+  lfpres->extractFeatures(img, descriptors, positions);
+  assert(descriptors.size() == positions.size());
+  return 0;
 }
 
 int LocalFeatureLFInterface::getDescriptors ( const NICE::ColorImage & img, VVector & positions, VVector & descriptors) const
 {
-	lfpres->extractFeatures(img, descriptors, positions);
-	assert(descriptors.size() == positions.size());
-	return 0;
+  //TODO do we want to ignore the positions of lfrep and use the given ones, or do we indeed want to compute positions on our own? If so, why do we use the Interface to LocalFeature, and not directly LocalFeatureRepresentation?
+  lfpres->extractFeatures(img, descriptors, positions);
+  assert(descriptors.size() == positions.size());
+  return 0;
 }
 
 void LocalFeatureLFInterface::visualizeFeatures ( NICE::Image & mark,
-				 const VVector & positions,
-				 size_t color ) const
+        const VVector & positions,
+        size_t color ) const
 {
-	//cerr << "LocalFeatureLFInterface::visualizeFeatures(...) not yet implemented" << endl;
-	lfpres->visualizeFeatures(mark, positions, color);
+  //cerr << "LocalFeatureLFInterface::visualizeFeatures(...) not yet implemented" << endl;
+  lfpres->visualizeFeatures(mark, positions, color);
 }
 

+ 44 - 41
features/localfeatures/LocalFeatureLFInterface.h

@@ -1,6 +1,6 @@
 /** 
 * @file LocalFeatureLFInterface.h
-* @brief interface to LF
+* @brief Interface to use a LocalFeature (Descriptor only) with routines of LocalFeatureRepresentations (Detector + Descriptor)
 * @author Björn Fröhlich
 * @date 09/07/2010
 
@@ -19,50 +19,53 @@
 
 namespace OBJREC {
 
-/** local feature with LF */
-class LocalFeatureLFInterface : public LocalFeature
-{
 
-    protected:
-		LocalFeatureRepresentation *lfpres;
+  /** @class LocalFeatureLFInterface
+  * @brief Interface to use a LocalFeature (Descriptor only) with routines of LocalFeatureRepresentations (Detector + Descriptor)
+  *
+  */  
+  class LocalFeatureLFInterface : public LocalFeature
+  {
 
-    public:
-  
-	/** simple constructor */
-		LocalFeatureLFInterface ( const NICE::Config *conf, LocalFeatureRepresentation *_lfpres );
-      
-	/** simple destructor */
+      protected:
+      LocalFeatureRepresentation *lfpres;
+
+      public:
+    
+    /** simple constructor */
+      LocalFeatureLFInterface ( const NICE::Config *conf, LocalFeatureRepresentation *_lfpres );
+        
+    /** simple destructor */
 
-	virtual ~LocalFeatureLFInterface();
-	
-	/**
-	 * returns the size of each the SIFT-Feature
-	 * @return 128
-	 */
-	int getDescSize() const { return lfpres->getDescSize(); };
-	
-	/** 
-	 * get the descriptor
-	 * @param img input image
-	 * @param positions positions for the SIFT features
-	 * @param descriptors output
-	 * @return 0
-	 */
-	int getDescriptors ( const NICE::Image & img, NICE::VVector & positions, NICE::VVector & descriptors ) const;
-	
-	/** 
-	 * get the descriptor
-	 * @param img input color image
-	 * @param positions positions for the SIFT features
-	 * @param descriptors output
-	 * @return 0
-	 */
-	int getDescriptors ( const NICE::ColorImage & img, NICE::VVector & positions, NICE::VVector & descriptors) const;
-				     
-	void visualizeFeatures ( NICE::Image & mark, const NICE::VVector & positions, size_t color ) const;
+    virtual ~LocalFeatureLFInterface();
+    
+    /**
+    * @brief returns the size of each the SIFT-Feature
+    */
+    int getDescSize() const { return lfpres->getDescSize(); };
+    
+    /** 
+    * @brief get the descriptor
+    * @param img input image
+    * @param positions positions for local features
+    * @param descriptors output
+    * @return 0
+    */
+    int getDescriptors ( const NICE::Image & img, NICE::VVector & positions, NICE::VVector & descriptors ) const;
+    
+    /** 
+    * @brief get the descriptor
+    * @param img input color image
+    * @param positions positions for the SIFT features
+    * @param descriptors output
+    * @return 0
+    */
+    int getDescriptors ( const NICE::ColorImage & img, NICE::VVector & positions, NICE::VVector & descriptors) const;
+              
+    void visualizeFeatures ( NICE::Image & mark, const NICE::VVector & positions, size_t color ) const;
 
-     
-};
+      
+  };
 
 
 } // namespace

+ 8 - 2
features/localfeatures/LocalFeatureRepresentation.h

@@ -1,7 +1,7 @@
 /** 
 * @file LocalFeatureRepresentation.h
-* @brief absract class for the representation of an image with local feature descriptors
-* @author Erik Rodner
+* @brief Absract class for Local Feature Representations (Detector + Descriptor)
+* @author Erik Rodner, Alexander Freytag
 * @date 11/19/2007
 
 */
@@ -21,6 +21,12 @@
 namespace OBJREC {
 
 /** absract class for the representation of an image with local feature descriptors */
+/** NOTE: This class returns Descriptors, which DO NOT need any given positions. All Descriptors calculate there own positions, on DIFFERENT ways. **/
+
+  /** @class LocalFeatureRepresentation
+ * @brief Absract class for Local Feature Representations (Detector + Descriptor)
+ *
+ */
 class LocalFeatureRepresentation
 {
 

+ 1 - 1
features/localfeatures/libdepend.inc

@@ -3,4 +3,4 @@ $(call PKG_DEPEND_EXT,OPENMP)
 $(call PKG_DEPEND_INT,core)
 $(call PKG_DEPEND_INT,vislearning/baselib)
 $(call PKG_DEPEND_INT,vislearning/image)
-$(call PKG_DEPEND_INT,vislearning/features/fbase)
+$(call PKG_DEPEND_INT,vislearning/features/fbase)

+ 142 - 0
features/simplefeatures/BoWFeatureConverter.cpp

@@ -0,0 +1,142 @@
+/** 
+* @file BoWFeatureConverter.cpp
+* @brief Convert a set of features into a Bag of visual Words histogram (either by Vector Quantization, or Hard / Soft Assignment)
+* @author Alexander Freytag
+* @date 11-06-2013 (dd-mm-yyyy)
+*/
+#include <iostream>
+#include <fstream>
+
+#include "vislearning/features/simplefeatures/BoWFeatureConverter.h"
+
+using namespace OBJREC;
+
+using namespace std;
+using namespace NICE;
+
+
+
+BoWFeatureConverter::BoWFeatureConverter( 
+    const Config *conf, 
+    const Codebook *_codebook, const std::string _section )
+    : codebook(_codebook)
+{
+  this->s_section = _section;
+  this->p_conf = conf;
+  
+  std::string normalizationMethod = conf->gS( _section, "normalizationMethod", "sum" );
+  
+  if ( normalizationMethod == "sum" )
+    this->n_normalizationMethod = NORMALIZE_SUM;
+  else if ( normalizationMethod == "binzero" )
+    this->n_normalizationMethod = NORMALIZE_BINZERO;
+  else if ( normalizationMethod == "raw" )
+    this->n_normalizationMethod = NORMALIZE_RAW;
+  else if ( normalizationMethod == "thresh" )
+    this->n_normalizationMethod = NORMALIZE_THRESH;
+  else
+  {
+    //default: L1 normalization
+    this->n_normalizationMethod = NORMALIZE_SUM;    
+  }
+  
+  std::string n_quantizationMethod = conf->gS( _section, "n_quantizationMethod", "VQ" );
+  
+  if ( normalizationMethod == "VQ" )
+    this->n_quantizationMethod = VECTOR_QUANTIZATION;
+  else if ( normalizationMethod == "VA" )
+    this->n_quantizationMethod = VECTOR_ASSIGNMENT;
+  else
+  {
+    //default: standard vector quantization
+    this->n_quantizationMethod = VECTOR_QUANTIZATION;    
+  }  
+}
+
+BoWFeatureConverter::~BoWFeatureConverter()
+{
+}
+
+void BoWFeatureConverter::calcHistogram ( const NICE::VVector & features,
+          NICE::Vector & histogram , const bool & b_resetHistogram )
+{
+  if ( b_resetHistogram || (histogram.size() != this->codebook->getCodebookSize() ) )
+  {
+    histogram.resize( this->codebook->getCodebookSize() );
+    histogram.set(0);
+  }
+
+  int cluster_index = 0;
+  double weight = 0;
+  double distance = 0.0;
+  if ( this->n_quantizationMethod == VECTOR_QUANTIZATION )
+  {
+    for ( NICE::VVector::const_iterator featIt  = features.begin();
+            featIt != features.end();
+            featIt++ )
+    {
+      const NICE::Vector & x = *featIt;
+      this->codebook->voteVQ ( x, cluster_index, weight, distance );
+      histogram[ cluster_index ] ++;
+    }    
+  }
+  else // VECTOR_ASSIGNMENT (hard or soft can be set directly in the codebook-object)
+  {
+    
+    NICE::Vector assignment (this->codebook->getCodebookSize() );;
+    for ( NICE::VVector::const_iterator featIt  = features.begin();
+            featIt != features.end();
+            featIt++ )
+    { 
+      assignment.set(0);
+      const NICE::Vector & x = *featIt;
+      this->codebook->voteVA ( x, assignment );
+      histogram += assignment;
+    }      
+  }
+
+}
+
+void BoWFeatureConverter::normalizeHistogram ( NICE::Vector & histogram )
+{
+    if ( n_normalizationMethod == NORMALIZE_RAW ) {
+    // do nothing
+    } else if ( n_normalizationMethod == NORMALIZE_BINZERO ) {
+    for ( size_t i = 0 ; i < histogram.size() ; i++ )
+      if ( histogram[i] > 0 ) histogram[i] = 1.0; 
+    } else if ( n_normalizationMethod == NORMALIZE_SUM  ) {
+    double sum = 0.0;
+    for ( size_t i = 0 ; i < histogram.size() ; i++ )
+    {
+      assert ( histogram[i] >= 0.0 );
+      sum += histogram[i];
+    }
+
+    if ( sum < 1e-5 ) {
+      fprintf (stderr, "BoWFeatureConverter::normalizeHistogram: WARNING histogram is zero !!\n");
+      return;
+    }
+
+    for ( size_t i = 0 ; i < histogram.size() ; i++ )
+      histogram[i] /= sum;
+    } else if ( n_normalizationMethod == NORMALIZE_THRESH ) {
+    const NICE::Vector & thresholds = codebook->getThresholds();
+
+    if ( thresholds.size() <= 0 ) {
+      fprintf (stderr, "BoWFeatureConverter:: This is maybe an OLD codebook ! \n");
+      exit(-1);
+    }
+    for ( size_t i = 0 ; i < histogram.size() ; i++ )
+      histogram[i] = (histogram[i] > thresholds[i]) ? 1.0 : 0.0; 
+    }
+}
+
+int BoWFeatureConverter::getNormalizationMethod () const
+{
+    return n_normalizationMethod;
+}
+
+void BoWFeatureConverter::setNormalizationMethod ( int normalizationMethod )
+{
+    n_normalizationMethod = normalizationMethod;
+}

+ 101 - 0
features/simplefeatures/BoWFeatureConverter.h

@@ -0,0 +1,101 @@
+/** 
+* @file BoWFeatureConverter.h
+* @brief Convert a set of features into a Bag of visual Words histogram (either by Vector Quantization, or Hard / Soft Assignment)
+* @author Alexander Freytag
+* @date 11-06-2013 (dd-mm-yyyy)
+*/
+#ifndef BOWFEATURECONVERTERINCLUDE
+#define BOWFEATURECONVERTERINCLUDE
+
+#include "core/vector/VectorT.h"
+#include "core/vector/VVector.h"
+#include "core/vector/MatrixT.h"
+
+#include "core/basics/Config.h"
+
+#include "Codebook.h"
+
+
+namespace OBJREC {
+
+
+  /**
+   * @class BoWFeatureConverter
+   * @brief Convert a set of features into a Bag of visual Words histogram (either by Vector Quantization, or Hard / Soft Assignment)
+   * @author Alexander Freytag
+   * @date 11-06-2013 (dd-mm-yyyy)
+  */   
+  class BoWFeatureConverter 
+  {
+
+    protected:
+      //! normalization method used (enum type)
+      int n_normalizationMethod;
+      
+      //! quantization method used (enum type)
+      int n_quantizationMethod;
+
+      //! pointer to our codebook
+      const Codebook *codebook;
+      
+      //! the section name if we want to read something from the config file lateron
+      std::string s_section;
+      
+      //! the config file to specify parameter settings
+      const NICE::Config * p_conf;
+
+
+
+    public:
+      //! enum type used for normalization method
+      enum {
+        NORMALIZE_RAW = 0,
+        NORMALIZE_BINZERO,
+        NORMALIZE_SUM,
+        NORMALIZE_THRESH
+      };
+      
+      //! enum type used to specify feature -> clusters (vector quant. , or vector assignment (either hard or soft) )
+      enum {
+        VECTOR_QUANTIZATION = 0,
+        VECTOR_ASSIGNMENT
+      };
+         
+
+      /**
+      * @brief standard constructor 
+      *
+      * @param conf pointer to a Config object with some parameter settings (currently not used)
+      * @param codebook pointer to the codebook (keep the pointer!)
+      */
+      BoWFeatureConverter( const NICE::Config *conf,
+          const Codebook *codebook, const std::string _section = "BoWFeatureConverter" );
+          
+      /** simple destructor */
+      virtual ~BoWFeatureConverter();
+      
+      void calcHistogram ( const NICE::VVector & features,
+              NICE::Vector & histogram, const bool & b_resetHistogram = true );
+
+      void normalizeHistogram ( NICE::Vector & histogram );
+
+      /**
+      * @brief set the type of the normalization method (see the enum of the class)
+      *
+      * @param normalizationMethod see enum type
+      */
+      void setNormalizationMethod ( int normalizationMethod );
+
+      /**
+      * @brief get the currently used normalization method
+      *
+      * @return see enum type
+      */
+      int getNormalizationMethod () const;
+      
+  };
+
+
+} // namespace
+
+#endif

+ 106 - 28
features/simplefeatures/Codebook.cpp

@@ -1,9 +1,8 @@
 /** 
 * @file Codebook.cpp
 * @brief feature codebook
-* @author Erik Rodner
-* @date 02/15/2008
-
+* @author Erik Rodner, Alexander Freytag
+* @date 05-06-2013 (dd-mm-yyyy ) (original: 02/15/2008)
 */
 #include <iostream>
 
@@ -14,33 +13,71 @@ using namespace OBJREC;
 using namespace std;
 using namespace NICE;
 
-void Codebook::vote ( const NICE::Vector & feature, 
-			NICE::Vector & histogram, 
-			int & codebookEntry,
-			double & weight,
-			double & distance ) const
+Codebook::Codebook ( )
+{
+  this->i_noOfNearestClustersToConsidere = 1;
+}
+
+Codebook::Codebook ( const int & _noOfNearestClustersToConsidere )
+{
+  this->i_noOfNearestClustersToConsidere = _noOfNearestClustersToConsidere;
+}
+
+Codebook::Codebook( NICE::Config * _conf, const std::string & _section)
+{
+  this->p_conf = _conf;
+  this->s_section = _section;
+  
+  this->i_noOfNearestClustersToConsidere = this->p_conf->gI( _section, "noOfNearestClustersToConsidere", 1);
+}
+
+
+Codebook::~Codebook ( )
+{
+  //NOTE the config does not need to be deleted, it is just a pointer to an external data structure  
+}
+
+void Codebook::voteVQ (const NICE::Vector &feature, NICE::Vector &histogram, int &codebookEntry, double &weight, double &distance) const
+{
+    this->voteVQ ( feature, codebookEntry, weight, distance );
+    //VQ, but we directly increase the corresponding entry without setting histogram to zero before (useful if we work on multiple images)
+    histogram[codebookEntry] += weight;  
+}
+
+void Codebook::voteVQ ( const NICE::Vector & feature, NICE::SparseVector & histogram, int &codebookEntry, double &weight, double &distance ) const
 {
-    vote ( feature, codebookEntry, weight, distance );
-    histogram[codebookEntry] += weight;
+    this->voteVQ ( feature, codebookEntry, weight, distance );
+    
+    //is this cluster already non-zero in the histogram?
+    NICE::SparseVector::iterator entryIt = histogram.find( codebookEntry ) ;
+    if ( entryIt  == histogram.end() )
+    {
+      //entry does not exist
+      histogram.insert ( histogram.begin(), pair<int, double> ( codebookEntry, weight ) );
+    }
+    else
+    {
+      //entry already exists, so we increase the weight
+      entryIt->second += weight;
+    }
 }
 
-void Codebook::vote ( const NICE::Vector & feature, SparseVector & votes ) const
+bool Codebook::allowsMultipleVoting () const
 {
-    int codebookEntry;
-    double weight;
-    double distance;
-    vote ( feature, codebookEntry, weight, distance );
-    votes.insert ( votes.begin(), pair<int, double> ( codebookEntry, weight ) );
+  if ( this->i_noOfNearestClustersToConsidere > 1 )
+    return true;
+  else
+    return false;
 }
-	
+
 void Codebook::reinit ( int numCodebookEntries )
 {
-    thresholds.resize ( numCodebookEntries );
-	thresholds.set(0.0);
-    informativeMeasure.resize ( numCodebookEntries );
-	informativeMeasure.set(0.0);
-    classnos.resize ( numCodebookEntries );
-	classnos.resize(0);
+  thresholds.resize ( numCodebookEntries );
+  thresholds.set(0.0);
+  informativeMeasure.resize ( numCodebookEntries );
+  informativeMeasure.set(0.0);
+  classnos.resize ( numCodebookEntries );
+  classnos.resize(0);
 }
 
 void Codebook::clear ()
@@ -54,20 +91,61 @@ void Codebook::restore ( istream & is, int format )
 {
    is >> thresholds;
    is >> informativeMeasure;
-   is >> classnos;
+//    is >> classnos;
+   
+   //TODO use a flag for compatibility with old systems?
+/*   try
+   {
+     
+     is >> i_noOfNearestClustersToConsidere;
+     is >> s_section;
+
+     this->p_conf->restore( is, format );
+   }
+   catch( ... )
+   {
+     std::cerr << "something went wrong while Loading the codebook - use default values instead" << std::endl;
+     //TODO use suitable default values
+   }  */ 
 }
 
 void Codebook::store ( ostream & os, int format ) const
 {
-    os << thresholds << endl;
-    os << informativeMeasure << endl;
-    os << classnos << endl;
+    os << this->thresholds << endl;
+    os << this->informativeMeasure << endl;
+    os << this->classnos << endl;
+//     os << this->i_noOfNearestClustersToConsidere << endl;
+//     os << this->s_section << endl;
+//     
+//     this->p_conf->store( os, format );
 }
 
 void Codebook::copy ( const Codebook *codebook )
 {
-    reinit ( codebook->thresholds.size() );
+    this->reinit ( codebook->thresholds.size() );
     thresholds = codebook->thresholds;
     informativeMeasure = codebook->informativeMeasure;
     classnos = codebook->classnos;
+    this->p_conf = codebook->p_conf;
+    this->setNoOfNearestClustersToConsidere ( codebook->getNoOfNearestClustersToConsidere() );
 }
+
+void Codebook::setNoOfNearestClustersToConsidere ( const int & _noOfNearestClustersToConsidere )
+{
+  this->i_noOfNearestClustersToConsidere = _noOfNearestClustersToConsidere;
+}
+
+int Codebook::getNoOfNearestClustersToConsidere ( ) const
+{
+  return this->i_noOfNearestClustersToConsidere;
+}
+
+void Codebook::setHardAssignment( const bool & _hardAssignment )
+{
+  this->b_hardAssignment = _hardAssignment;
+}
+
+bool Codebook::getHardAssignment ( ) const
+{
+  return this->b_hardAssignment;
+}

+ 160 - 64
features/simplefeatures/Codebook.h

@@ -1,83 +1,179 @@
 /**
 * @file Codebook.h
 * @brief feature codebook
-* @author Erik Rodner
-* @date 02/15/2008
-
+* @author Erik Rodner, Alexander Freytag
+* @date 05-06-2013 (dd-mm-yyyy ) (original: 02/15/2008)
 */
 #ifndef CODEBOOKINCLUDE
 #define CODEBOOKINCLUDE
 
-#include "core/vector/VectorT.h"
-#include "core/vector/MatrixT.h"
-#include "core/image/ImageT.h"
-#include "core/imagedisplay/ImageDisplay.h"
 
 #include <string>
 
+#include <core/basics/Config.h>
 #include "core/basics/Persistent.h"
+//
+#include "core/image/ImageT.h"
+#include "core/imagedisplay/ImageDisplay.h"
+//
+#include "core/vector/VectorT.h"
+#include "core/vector/MatrixT.h"
 #include "core/vector/SparseVectorT.h"
 
 
 namespace OBJREC {
 
-/** feature codebook */
-class Codebook : public NICE::Persistent
-{
-protected:
-    NICE::Vector thresholds;
-    NICE::Vector informativeMeasure;
-    NICE::VectorT<int> classnos;
-
-public:
-
-    /** simple destructor */
-    virtual ~Codebook() {};
-
-    virtual void vote ( const NICE::Vector & feature, int & codebookEntry,
-                        double & weight, double & distance ) const = 0;
-
-    virtual void vote ( const NICE::Vector & feature, NICE::Vector & histogram, int & codebookEntry,
-                        double & weight, double & distance ) const;
-
-    virtual void vote ( const NICE::Vector & feature, NICE::SparseVector & votes ) const;
-
-    virtual bool allowsMultipleVoting () {
-        return false;
-    };
-    virtual void add ( const Codebook *codebook ) = 0;
-    virtual void copy ( const Codebook *codebook );
-    virtual Codebook *clone () const = 0;
-
-    size_t getCodebookSize() const {
-        return informativeMeasure.size();
-    };
-    void reinit ( int numCodebookEntries );
-
-    const NICE::Vector & getThresholds () const {
-        return thresholds;
-    };
-    const NICE::Vector & getInformativeMeasure () const {
-        return informativeMeasure;
-    };
-    const NICE::VectorT<int> & getClassNos () const {
-        return classnos;
-    };
-
-    void setThresholds ( const NICE::Vector & _thresholds ) {
-        thresholds = _thresholds;
-    };
-    void setInformativeMeasure ( const NICE::Vector & _informativeMeasure ) {
-        informativeMeasure = _informativeMeasure;
-    };
-    void setClassNos ( const NICE::VectorT<int> & _classnos ) {
-        classnos = _classnos;
-    };
-
-    virtual void clear ();
-    virtual void restore ( std::istream & is, int format );
-    virtual void store ( std::ostream & os, int format ) const;
-};
+  /** feature codebook */
+  class Codebook : virtual public NICE::Persistent
+  {
+    protected:
+        NICE::Vector thresholds;
+        NICE::Vector informativeMeasure;
+        NICE::VectorT<int> classnos;
+        
+        /** if Soft Assignment or Hard Assignment instead of Vector Quantization, how many entries are allowed to be non-zero?*/
+        int i_noOfNearestClustersToConsidere;
+        
+        NICE::Config * p_conf;
+        std::string s_section;
+        
+        /** hard or soft assignment? */
+        bool b_hardAssignment;
+
+    public:
+
+        /**
+         * @brief simple constructor
+         */
+        Codebook ( );      
+      
+        /**
+         * @brief standard constructor
+         */
+        Codebook ( const int & _noOfNearestClustersToConsidere);
+
+         /**
+         * @brief recommended constructor
+         */
+        Codebook( NICE::Config * _conf, const std::string & _section);
+
+        /** simple destructor */
+        virtual ~Codebook();
+        
+
+        //vote for a single feature vector
+        /**
+         * @brief Vector Quantization for a single vector
+         * @date 11-06-2013 (dd-mm-yyyy)
+         * @author Erik Rodner, Alexander Freytag
+         * @param feature the feature that shall be quantized
+         * @param codebookEntry the corresponding cluster index for the quantized feature
+         * @param weight the weight of the nearest cluster
+         * @param distance the distance to its nearest cluster
+         */        
+        virtual void voteVQ ( const NICE::Vector & feature, int & codebookEntry,
+                            double & weight, double & distance ) const = 0;
+                            
+        /**
+         * @brief Vector Quantization for a single vector, directly increases the corresponding entry in histogram 
+         * @date 11-06-2013 (dd-mm-yyyy)
+         * @author Erik Rodner, Alexander Freytag
+         * @param feature the feature that shall be quantized
+         * @param histogram a BoW-histogram already given (does not need to be empty before) - the corresponding entry for feature will be increased
+         * @param codebookEntry  the corresponding cluster index for the quantized feature
+         * @param weight the weight of the nearest cluster
+         * @param distance the distance to its nearest cluster
+         */        
+        virtual void voteVQ (const NICE::Vector &feature, NICE::Vector &histogram, int & codebookEntry, double & weight, double & distance ) const; 
+        
+        /**
+         * @brief Vector Quantization for a single vector, directly increases the corresponding entry in histogram 
+         * @date 11-06-2013 (dd-mm-yyyy)
+         * @author Erik Rodner, Alexander Freytag
+         * @param feature the feature that shall be quantized
+         * @param histogram a BoW-histogram already given (does not need to be empty before) - the corresponding entry for feature will be increased
+         * @param codebookEntry the corresponding cluster index for the quantized feature
+         * @param weight the weight of the nearest cluster
+         * @param distance the distance to its nearest cluster
+         */        
+        virtual void voteVQ (const NICE::Vector &feature, NICE::SparseVector &histogram, int &codebookEntry, double &weight, double &distance ) const;         
+
+        
+        /**
+         * @brief Hard or Soft Assignment into NICE::Vector for a single vector
+         * @date 11-06-2013 (dd-mm-yyyy)
+         * @author Alexander Freytag
+         * @param feature the feature that shall be quantized
+         * @param votes BoW histogram adding non-zero entries at the k nearest clusters (hard or soft),  (does not need to be empty before)
+         */    
+        virtual void voteVA ( const NICE::Vector & feature, NICE::Vector & votes ) const = 0;
+
+        /**
+         * @brief Hard or Soft Assignment into NICE::SparseVector for a single vector 
+         * @date 11-06-2013 (dd-mm-yyyy)
+         * @author Erik Rodner, Alexander Freytag
+         * @param feature the feature that shall be quantized
+         * @param votes BoW histogram adding non-zero entries for the k nearest clusters (hard or soft),  (does not need to be empty before)
+         */                              
+        virtual void voteVA ( const NICE::Vector & feature, NICE::SparseVector & votes ) const = 0;
+        
+        
+        //for backward-compatibility
+        virtual void   vote (const NICE::Vector &feature, NICE::Vector &histogram, int &codebookEntry , double &weight , double &distance ) const { this->voteVQ ( feature, histogram, codebookEntry, weight, distance ); };
+        
+        virtual void   vote (const NICE::Vector &feature, NICE::SparseVector &histogram ) const {
+          int codebookEntry;
+          double weight, distance;
+          this->voteVQ ( feature, histogram, codebookEntry, weight, distance );          
+        };        
+   
+        
+        
+       
+        virtual void add ( const Codebook *codebook ) = 0;
+        virtual void copy ( const Codebook *codebook );
+        virtual Codebook *clone () const = 0;
+
+        size_t getCodebookSize() const {
+            return informativeMeasure.size();
+        };
+        void reinit ( int numCodebookEntries );
+
+        const NICE::Vector & getThresholds () const {
+            return thresholds;
+        };
+        const NICE::Vector & getInformativeMeasure () const {
+            return informativeMeasure;
+        };
+        const NICE::VectorT<int> & getClassNos () const {
+            return classnos;
+        };
+
+        void setThresholds ( const NICE::Vector & _thresholds ) {
+            thresholds = _thresholds;
+        };
+        void setInformativeMeasure ( const NICE::Vector & _informativeMeasure ) {
+            informativeMeasure = _informativeMeasure;
+        };
+        void setClassNos ( const NICE::VectorT<int> & _classnos ) {
+            classnos = _classnos;
+        };
+        
+        void setHardAssignment( const bool & _hardAssignment );
+        bool getHardAssignment ( ) const ;
+        
+        /** 
+         * @brief false, if only a single entry for soft or hard assignment can be larger than zero (=> effectively vector quantization)
+         */
+        virtual bool allowsMultipleVoting () const ;
+        
+        void setNoOfNearestClustersToConsidere ( const int & _noOfNearestClustersToConsidere );
+        int getNoOfNearestClustersToConsidere ( ) const ;
+
+        virtual void clear ();
+        virtual void restore ( std::istream & is, int format );
+        virtual void store ( std::ostream & os, int format ) const;
+  };
 
 
 } // namespace

+ 116 - 68
features/simplefeatures/CodebookPrototypes.cpp

@@ -1,15 +1,16 @@
 /** 
 * @file CodebookPrototypes.cpp
 * @brief feature CodebookPrototypes
-* @author Erik Rodner
-* @date 02/15/2008
-
+* @author Erik Rodner, Alexander Freytag
+* @date 05-06-2013 (dd-mm-yyyy ) (original: 02/15/2008)
 */
-#include <core/image/Convert.h>
-
 #include <iostream>
 #include <assert.h>
 
+#include <core/image/Convert.h>
+
+#include "vislearning/math/distances/genericDistance.h"
+
 #include "CodebookPrototypes.h"
 
 using namespace OBJREC;
@@ -20,6 +21,9 @@ using namespace NICE;
 
 CodebookPrototypes::CodebookPrototypes()
 {
+  this->clear();
+  this->distanceType = "euclidean";
+  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);   
 }
 
 CodebookPrototypes::CodebookPrototypes( const std::string & filename )
@@ -27,15 +31,27 @@ CodebookPrototypes::CodebookPrototypes( const std::string & filename )
     Codebook::read(filename);
 }
 
-CodebookPrototypes::CodebookPrototypes( const VVector & vv )
+CodebookPrototypes::CodebookPrototypes( const NICE::VVector & vv )
 {
-    for ( const_iterator i = vv.begin(); i != vv.end(); i++ )
-	push_back (*i);
-    reinit ( vv.size() );
+  this->append ( vv, true /* _copyData*/ ); 
+
+  reinit ( vv.size() );
+  
+  this->distanceType = "euclidean";
+  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);     
+}
+
+CodebookPrototypes::CodebookPrototypes( NICE::Config * _conf, const std::string & _section) : Codebook ( _conf, _section )
+{    
+  this->distanceType = _conf->gS( _section, "distanceType", "euclidean" );
+  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);  
+  
+  this->clear();
 }
 
 CodebookPrototypes::~CodebookPrototypes()
 {
+  //NOTE the config does not need to be deleted, it is just a pointer to an external data structure
 }
 
 void CodebookPrototypes::copy ( const Codebook *codebook )
@@ -45,9 +61,12 @@ void CodebookPrototypes::copy ( const Codebook *codebook )
     const CodebookPrototypes *codebookp = dynamic_cast<const CodebookPrototypes *> ( codebook );
     assert ( codebookp != NULL );
     insert ( begin(), codebookp->begin(), codebookp->end() );
+    
+  this->distanceType = this->p_conf->gS( this->s_section, "distanceType", "euclidean" );
+  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);      
 }
 
-void CodebookPrototypes::vote ( const NICE::Vector & feature, int & codebookEntry, double & weight, double & distance ) const
+void CodebookPrototypes::voteVQ ( const NICE::Vector & feature, int & codebookEntry, double & weight, double & distance ) const
 {
     const double *xp = feature.getDataPointer();
     size_t k = 0;
@@ -55,34 +74,50 @@ void CodebookPrototypes::vote ( const NICE::Vector & feature, int & codebookEntr
     size_t my_cluster = 0;
     
     int len = feature.size();
-    for ( vector<Vector>::const_iterator i = begin();
-					 i != end();
-					 i++,k++ )
+    // iterate over all cluster centers
+    for ( std::vector<NICE::Vector>::const_iterator i = this->begin();
+            i != this->end();
+            i++,k++ )
     {
 
-	const NICE::Vector & cluster = *i;
-	//slow ICE variant
-	//double distance = (x - cluster).Length();
-	double distance = 0.0;
-	const double *clusterp = cluster.getDataPointer();
-	for ( int i = 0 ; i < len ; i++ )
-	{
-	    double h = clusterp[i] - xp[i];
-	    distance += h*h;
-	}
-
-	if ( distance < mindist )
-	{
-	    my_cluster = k;
-	    mindist = distance;
-	}
+      const NICE::Vector & cluster = *i;
+      //slow ICE variant
+      //double distance = (x - cluster).Length();
+      
+//       const double *clusterp = cluster.getDataPointer();
+      double distance = this->distancefunction->calculate(*i, feature);
+      
+      //old version without distance function
+//       for ( int i = 0 ; i < len ; i++ )
+//       {
+//           double h = clusterp[i] - xp[i];
+//           distance += h*h;
+//       }
+
+      if ( distance < mindist )
+      {
+          my_cluster = k;
+          mindist = distance;
+      }
     }
 
     codebookEntry = my_cluster;
     weight = 1.0;
     distance = mindist;
 }
-	
+
+void CodebookPrototypes::voteVA ( const NICE::Vector & feature, NICE::Vector & votes ) const
+{
+  votes.set( 0.0 );
+  //TODO
+}
+
+void CodebookPrototypes::voteVA ( const NICE::Vector & feature, NICE::SparseVector & votes ) const
+{
+  votes.clear(); 
+  //TODO
+}
+
 void CodebookPrototypes::add ( const Codebook *codebook )
 {
     informativeMeasure.append ( codebook->getInformativeMeasure() );
@@ -93,9 +128,10 @@ void CodebookPrototypes::add ( const Codebook *codebook )
     assert ( codebookp != NULL );
     insert ( begin(), codebookp->begin(), codebookp->end() );
 }
-	
+
 Codebook *CodebookPrototypes::clone () const
 {
+  //TODO !
     return (new CodebookPrototypes());
 }
 
@@ -109,6 +145,8 @@ void CodebookPrototypes::restore ( istream & is, int format )
 {
     Codebook::restore ( is, format );
     VVector::restore ( is, format );
+    
+    std::cerr << "initial Codebook : " << std::endl; VVector::print ( std::cerr );
 }
 
 void CodebookPrototypes::store ( ostream & os, int format ) const
@@ -119,47 +157,57 @@ void CodebookPrototypes::store ( ostream & os, int format ) const
 
 void CodebookPrototypes::displayCodebook ( int xsize, int ysize ) const
 {
-    NICE::Image bigimg ( xsize * 5 , ysize * size() );
+    NICE::Image bigimg ( xsize * 5 , ysize * this->size() );
     bigimg.set(0);
 
     NICE::Image img_s (xsize, ysize);
-    for ( int k = 0 ; k < (int)size() ; k++ )
+    for ( int k = 0 ; k < (int)this->size() ; k++ )
     {
-	NICE::Vector vimg = *(begin() + k);
-	NICE::Image img ((int)sqrt(vimg.size()), (int)sqrt(vimg.size()));
-	int i = 0;
-	double max = - numeric_limits<double>::max();
-	double min = numeric_limits<double>::min();
-	for ( int y = 0 ; y < img.height() ; y++ )
-	    for ( int x = 0 ; x < img.width() ; x++,i++ )
-	    {
-		if ( max < vimg[i] ) max = vimg[i];
-		if ( min > vimg[i] ) min = vimg[i];
-	    }
-
-	i = 0;
-	for ( int y = 0 ; y < img.height() ; y++ )
-	    for ( int x = 0 ; x < img.width() ; x++,i++ )
-	    {
-		img.setPixel( x, y, (int)((vimg[i] - min)*255/(max-min)) );
-	    }
-
-	
-	NICE::scale ( img, &img_s );
-
-	for ( int y = 0 ; y < ysize ; y++ )
-	    for ( int x = 0 ; x < xsize ; x++ )
-		bigimg.setPixel(x, y+k*ysize, img_s.getPixel(x,y) );
-
-	{
-	    std::ostringstream os;
-	    os << "no: " << k;
-	    if ( (int)informativeMeasure.size() > k ) 
-		os << " " << informativeMeasure[k];
-	    // refactor-nice.pl: check this substitution
-	    // old: Text ( os.str().c_str(), xsize+1, k*ysize + 1, 255, 0, bigimg );
-	    // REFACTOR-FIXME Unable to map this statement
-	}
+      NICE::Vector vimg = *(this->begin() + k);
+      NICE::Image img ((int)sqrt(vimg.size()), (int)sqrt(vimg.size()));
+      int i = 0;
+      double max = - numeric_limits<double>::max();
+      double min = numeric_limits<double>::min();
+      for ( int y = 0 ; y < img.height() ; y++ )
+      {
+          for ( int x = 0 ; x < img.width() ; x++,i++ )
+            {
+              if ( max < vimg[i] )
+                max = vimg[i];
+              if ( min > vimg[i] )
+                min = vimg[i];
+            }
+      }
+
+      i = 0;
+      for ( int y = 0 ; y < img.height() ; y++ )
+      {
+          for ( int x = 0 ; x < img.width() ; x++,i++ )
+          {
+            img.setPixel( x, y, (int)((vimg[i] - min)*255/(max-min)) );
+          }
+      }
+
+      
+      NICE::scale ( img, &img_s );
+
+      for ( int y = 0 ; y < ysize ; y++ )
+      {
+          for ( int x = 0 ; x < xsize ; x++ )
+          {
+            bigimg.setPixel(x, y+k*ysize, img_s.getPixel(x,y) );
+          }
+      }
+
+      {
+          std::ostringstream os;
+          os << "no: " << k;
+          if ( (int)informativeMeasure.size() > k ) 
+          os << " " << informativeMeasure[k];
+          // refactor-nice.pl: check this substitution
+          // old: Text ( os.str().c_str(), xsize+1, k*ysize + 1, 255, 0, bigimg );
+          // REFACTOR-FIXME Unable to map this statement
+      }
     }
 
     bigimg.write ("/tmp/display.bmp");

+ 60 - 31
features/simplefeatures/CodebookPrototypes.h

@@ -1,9 +1,8 @@
 /** 
 * @file CodebookPrototypes.h
 * @brief feature CodebookPrototypes
-* @author Erik Rodner
-* @date 02/15/2008
-
+* @author Erik Rodner, Alexander Freytag
+* @date 05-06-2013 (dd-mm-yyyy ) (original: 02/15/2008)
 */
 #ifndef CodebookPrototypesINCLUDE
 #define CodebookPrototypesINCLUDE
@@ -15,42 +14,72 @@
 #include <string>
 
 #include "core/vector/VVector.h"
+#include <core/vector/Distance.h>
+
 #include "Codebook.h"
 
 
 namespace OBJREC {
 
-/** feature CodebookPrototypes */
-class CodebookPrototypes : public Codebook, public NICE::VVector
-{
-
-    protected:
-
-    public:
-  
-	/** simple constructor */
-	CodebookPrototypes();
-	
-	CodebookPrototypes( const std::string & filename );
-	
-	CodebookPrototypes( const NICE::VVector & vv );
-	
-	CodebookPrototypes( const CodebookPrototypes *cs );
-      
-	/** simple destructor */
-	virtual ~CodebookPrototypes();
+  /** feature CodebookPrototypes */
+  class CodebookPrototypes : public Codebook, public NICE::VVector
+  {
+
+      protected:
+        
+        //! specify which distance to use for calculating assignments
+        std::string distanceType;
+        
+        //! the actual distance metric
+        NICE::VectorDistance<double> *distancefunction;         
+
+      public:
     
-	void vote ( const NICE::Vector & feature, int & codebookEntry, double & weight, double & distance ) const;
-	void add ( const Codebook *codebook );
-	void copy ( const Codebook *codebook );
-	Codebook *clone () const;
+        /** simple constructor */
+        CodebookPrototypes();
+        
+        /** constructor reading the codebook from a file*/
+        CodebookPrototypes( const std::string & filename );
+        
+        /** constructor taking the given VVector as new codebook prototypes*/
+        CodebookPrototypes( const NICE::VVector & vv );
+        
+        /** copy constructor*/
+        CodebookPrototypes( const CodebookPrototypes *cs );
+        
+        /** recommended constructor*/
+        CodebookPrototypes( NICE::Config * _conf, const std::string & _section);
+            
+        /** simple destructor */
+        virtual ~CodebookPrototypes();
+        
+        //vote for a single feature vector
+        /**
+         * @brief Vector Quantization for a single vector
+         */        
+        void voteVQ ( const NICE::Vector & feature, int & codebookEntry, double & weight, double & distance ) const;
+
+        /**
+         * @brief Hard or Soft Assignment into NICE::Vector for a single vector
+         */        
+        virtual void voteVA ( const NICE::Vector & feature, NICE::Vector & votes ) const;
+
+        /**
+         * @brief Hard or Soft Assignment into NICE::SparseVector for a single vector
+         */                            
+        virtual void voteVA ( const NICE::Vector & feature, NICE::SparseVector & votes ) const;        
+         
+        
+        void add ( const Codebook *codebook );
+        void copy ( const Codebook *codebook );
+        Codebook *clone () const;
 
-	void clear ();
-	void restore ( std::istream & is, int format );
-	void store ( std::ostream & os, int format ) const;
+        void clear ();
+        void restore ( std::istream & is, int format );
+        void store ( std::ostream & os, int format ) const;
 
-	void displayCodebook (int xsize, int ysize) const;
-};
+        void displayCodebook (int xsize, int ysize) const;
+  };
 
 
 } // namespace

+ 5 - 5
math/cluster/GMM.cpp

@@ -175,16 +175,16 @@ inline NICE::Vector diagInverse ( const NICE::Vector &sparse_mat )
 void GMM::initEMkMeans ( const VVector &DataSet )
 {
   /*init GMM with k-Means*/
-  KMeans k ( gaussians );
-  VVector means;
-  vector<double> weights;
-  vector<int> assignment;
+  OBJREC::KMeans k ( gaussians );
+  NICE::VVector means;
+  std::vector<double> weights;
+  std::vector<int> assignment;
   k.cluster ( DataSet, means, weights, assignment );
 
   int nData = DataSet.size();
   this->dim = DataSet[0].size();
   cdimval = dim * log ( 2 * 3.14159 );
-  vector<int> pop ( gaussians, 0 );
+  std::vector<int> pop ( gaussians, 0 );
   priors.resize ( gaussians );
   mu = VVector ( gaussians, dim );
   log_det_sigma.clear();

+ 0 - 1
math/cluster/GMM.h

@@ -104,7 +104,6 @@ class GMM : public ClusterAlgorithm
      * standard destructor
      */
     ~GMM() {
-      std::cerr << "dadada" << std::endl;
     };
 
     /**

+ 256 - 241
math/cluster/KMeans.cpp

@@ -1,9 +1,8 @@
 /**
  * @file KMeans.cpp
  * @brief K-Means
- * @author Erik Rodner
- * @date 10/29/2007
-
+ * @author Erik Rodner, Alexander Freytag
+ * @date 29-10-2007 (dd-mm-yyyy)
  */
 
 #include <iostream>
@@ -21,277 +20,293 @@ using namespace NICE;
 
 #undef DEBUG_KMEANS
 
-KMeans::KMeans(int _noClasses, string _distanceType) :
-	noClasses(_noClasses), distanceType(_distanceType)
+KMeans::KMeans(const int & _noClasses, const std::string & _distanceType) :
+        noClasses(_noClasses), distanceType(_distanceType)
 {
-	//srand(time(NULL));
-	distancefunction = GenericDistanceSelection::selectDistance(distanceType);
+    //srand(time(NULL));
+    this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);
+}
+
+KMeans::KMeans( const NICE::Config *conf, const std::string & _section)
+{       
+  this->distanceType = conf->gS( _section, "distanceType", "euclidean" );
+  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);
+  
+  this->d_minDelta  = conf->gD( _section, "minDelta", 1e-5 );
+  this->i_maxIterations = conf->gI( _section, "maxIterations", 200);
+  
+  this->noClasses = conf->gI( _section, "noClasses", 20);
 }
 
 KMeans::~KMeans()
 {
 }
 
-void KMeans::initial_guess(const VVector & features, VVector & prototypes)
+void KMeans::initial_guess(const NICE::VVector & features, NICE::VVector & prototypes)
 {
-	int j = 0;
-	std::set<int, std::greater<int> > mark;
+    int j = 0;
+    std::set<int, std::greater<int> > mark;
 
-	for (VVector::iterator i = prototypes.begin(); i != prototypes.end(); i++, j++)
-	{
-		int k;
+    for (VVector::iterator i = prototypes.begin(); i != prototypes.end(); i++, j++)
+    {
+        int k;
 
-		do
-		{
-			k = rand() % features.size();
-		} while (mark.find(k) != mark.end());
+        do
+        {
+            k = rand() % features.size();
+        } while (mark.find(k) != mark.end());
 
-		mark.insert(mark.begin(), k);
+        mark.insert(mark.begin(), k);
 
-		*i = features[k];
-	}
+        *i = features[k];
+    }
 }
 
 int KMeans::compute_prototypes(const VVector & features, VVector & prototypes,
-		std::vector<double> & weights, const std::vector<int> & assignment)
+                               std::vector<double> & weights, const std::vector<int> & assignment)
 {
-	int j = 0;
-	// fprintf (stderr, "KMeans::compute_prototypes: init noClasses=%d\n", noClasses);
-	for (int k = 0; k < noClasses; k++)
-	{
-		prototypes[k].set(0);
-		weights[k] = 0;
-	}
-
-	// fprintf (stderr, "KMeans::compute_prototypes: compute means\n");
-	for (VVector::const_iterator i = features.begin(); i != features.end(); i++, j++)
-	{
-		int k = assignment[j];
-
-		NICE::Vector & p = prototypes[k];
-
-		const NICE::Vector & x = *i;
-
-#ifdef DEBUG_KMEANS
-
-		fprintf(
-				stderr,
-				"KMeans::compute_prototypes: std::vector %d has assignment %d\n",
-				j, k);
-#endif
-
-		p += x;
-
-#ifdef DEBUG_KMEANS
-		cerr << "vector was : " << x << endl;
-		cerr << "prototype for this class is now : " << p << endl;
-#endif
-		weights[k]++;
-	}
-
-	// fprintf (stderr, "KMeans::compute_prototypes: scaling\n");
-	for (int k = 0; k < noClasses; k++)
-	{
-
-		NICE::Vector & p = prototypes[k];
-
-#ifdef DEBUG_KMEANS
-		cerr << "prototype for this class before scaling : " << p << endl;
-#endif
-
-		if (weights[k] <= 0)
-		{
-			return -1;
-		}
-
-		p *= (1.0 / weights[k]);
-
-		weights[k] = weights[k] / features.size();
-
-#ifdef DEBUG_KMEANS
-		cerr << "prototype for this class after scaling with " << weights[k]
-				<< " : " << p << endl;
-#endif
-	}
-
-	return 0;
+    int j = 0;
+    // fprintf (stderr, "KMeans::compute_prototypes: init noClasses=%d\n", noClasses);
+    for (int k = 0; k < this->noClasses; k++)
+    {
+        prototypes[k].set(0);
+        weights[k] = 0;
+    }
+
+    // fprintf (stderr, "KMeans::compute_prototypes: compute means\n");
+    for (VVector::const_iterator i = features.begin(); i != features.end(); i++, j++)
+    {
+        int k = assignment[j];
+
+        NICE::Vector & p = prototypes[k];
+
+        const NICE::Vector & x = *i;
+
+        #ifdef DEBUG_KMEANS
+          fprintf(
+              stderr,
+              "KMeans::compute_prototypes: std::vector %d has assignment %d\n",
+              j, k);
+        #endif
+
+        p += x;
+
+        #ifdef DEBUG_KMEANS
+          std::cerr << "vector was : " << x << std::endl;
+          std::cerr << "prototype for this class is now : " << p << std::endl;
+        #endif
+          
+        weights[k]++;
+    }
+
+    // fprintf (stderr, "KMeans::compute_prototypes: scaling\n");
+    for (int k = 0; k < this->noClasses; k++)
+    {
+
+        NICE::Vector & p = prototypes[k];
+
+        #ifdef DEBUG_KMEANS
+          std::cerr << "prototype for this class before scaling : " << p << std::endl;
+        #endif
+
+        if (weights[k] <= 0)
+        {
+            return -1;
+        }
+
+        p *= (1.0 / weights[k]);
+
+        weights[k] = weights[k] / features.size();
+
+        #ifdef DEBUG_KMEANS
+          std::cerr << "prototype for this class after scaling with " << weights[k]
+             << " : " << p << std::endl;
+        #endif
+    }
+
+    return 0;
 }
 
-double KMeans::compute_delta(const VVector & oldprototypes,
-		const VVector & prototypes)
+double KMeans::compute_delta(const NICE::VVector & oldprototypes,
+                             const NICE::VVector & prototypes)
 {
-	double distance = 0;
-
-	for (uint k = 0; k < oldprototypes.size(); k++)
-	{
-		distance
-				+= distancefunction->calculate(oldprototypes[k], prototypes[k]);
-#ifdef DEBUG_KMEANS
-	fprintf(stderr, "KMeans::compute_delta: Distance:",
-			distancefunction->calculate(oldprototypes[k], prototypes[k]));
-#endif
-	}
-	return distance;
+    double distance = 0;
+
+    for (uint k = 0; k < oldprototypes.size(); k++)
+    {
+        distance += this->distancefunction->calculate(oldprototypes[k], prototypes[k]);
+        
+        #ifdef DEBUG_KMEANS
+          fprintf(stderr, "KMeans::compute_delta: Distance: %f",
+                distancefunction->calculate(oldprototypes[k], prototypes[k]));
+        #endif
+    }
+    return distance;
 }
 
-double KMeans::compute_assignments(const VVector & features,
-		const VVector & prototypes, std::vector<int> & assignment)
+double KMeans::compute_assignments(const NICE::VVector & features,
+                                   const NICE::VVector & prototypes, std::vector<int> & assignment)
 {
-	int index = 0;
-	for (VVector::const_iterator i = features.begin(); i != features.end(); i++, index++)
-	{
-
-		const NICE::Vector & x = *i;
-		double mindist = std::numeric_limits<double>::max();
-		int minclass = 0;
-
-		int c = 0;
-#ifdef DEBUG_KMEANS
-
-		fprintf(stderr, "computing nearest prototype for std::vector %d\n",
-				index);
-#endif
-		for (VVector::const_iterator j = prototypes.begin(); j
-				!= prototypes.end(); j++, c++)
-		{
-
-			const NICE::Vector & p = *j;
-			double distance = distancefunction->calculate(p, x);
-#ifdef DEBUG_KMEANS
-	fprintf(stderr, "KMeans::compute_delta: Distance:",
-			distancefunction->calculate(p, x));
-#endif
-
-#ifdef DEBUG_KMEANS
-			cerr << p << endl;
-			cerr << x << endl;
-			fprintf(stderr, "distance to prototype %d is %f\n", c, distance);
-#endif
-
-			if (distance < mindist)
-			{
-				minclass = c;
-				mindist = distance;
-			}
-		}
-
-		assignment[index] = minclass;
-	}
-
-	return 0.0;
+    int index = 0;
+    for (VVector::const_iterator i = features.begin(); i != features.end(); i++, index++)
+    {
+
+        const NICE::Vector & x = *i;
+        double mindist = std::numeric_limits<double>::max();
+        int minclass = 0;
+
+        int c = 0;
+        #ifdef DEBUG_KMEANS
+
+          fprintf(stderr, "computing nearest prototype for std::vector %d\n",
+                index);
+        #endif
+        for (VVector::const_iterator j = prototypes.begin(); j
+                != prototypes.end(); j++, c++)
+        {
+
+            const NICE::Vector & p = *j;
+            double distance = this->distancefunction->calculate(p, x);
+            #ifdef DEBUG_KMEANS
+              fprintf(stderr, "KMeans::compute_delta: Distance: %f",
+                    this->distancefunction->calculate(p, x));
+            #endif
+
+            #ifdef DEBUG_KMEANS
+              std::cerr << p << std::endl;
+              std::cerr << x << std::endl;
+              fprintf(stderr, "distance to prototype %d is %f\n", c, distance);
+            #endif
+
+            if (distance < mindist)
+            {
+                minclass = c;
+                mindist = distance;
+            }
+        }
+
+        assignment[index] = minclass;
+    }
+
+    return 0.0;
 }
 
-double KMeans::compute_weights(const VVector & features,
-		std::vector<double> & weights, std::vector<int> & assignment)
+double KMeans::compute_weights(const NICE::VVector & features,
+                               std::vector<double> & weights, std::vector<int> & assignment)
 {
-	for (int k = 0; k < noClasses; k++)
-		weights[k] = 0;
+    for (int k = 0; k < this->noClasses; k++)
+        weights[k] = 0;
 
-	int j = 0;
+    int j = 0;
 
-	for (VVector::const_iterator i = features.begin(); i != features.end(); i++, j++)
-	{
-		int k = assignment[j];
-		weights[k]++;
-	}
+    for (NICE::VVector::const_iterator i = features.begin(); i != features.end(); i++, j++)
+    {
+        int k = assignment[j];
+        weights[k]++;
+    }
 
-	for (int k = 0; k < noClasses; k++)
-		weights[k] = weights[k] / features.size();
+    for (int k = 0; k < this->noClasses; k++)
+        weights[k] = weights[k] / features.size();
 
-	return 0.0;
+    return 0.0;
 }
 
-void KMeans::cluster(const VVector & features, VVector & prototypes,
-		std::vector<double> & weights, std::vector<int> & assignment)
+void KMeans::cluster(const NICE::VVector & features, NICE::VVector & prototypes,
+                     std::vector<double> & weights, std::vector<int> & assignment)
 {
-	VVector oldprototypes;
-
-	prototypes.clear();
-	weights.clear();
-	assignment.clear();
-	weights.resize(noClasses, 0);
-	assignment.resize(features.size(), 0);
-
-	int dimension;
-
-	if ((int) features.size() >= noClasses)
-		dimension = features[0].size();
-	else
-	{
-		fprintf(stderr,
-				"FATAL ERROR: Not enough feature vectors provided for kMeans\n");
-		exit(-1);
-	}
-
-	for (int k = 0; k < noClasses; k++)
-	{
-		prototypes.push_back(Vector(dimension));
-		prototypes[k].set(0);
-	}
-
-	KMeans_Restart:
-
-	initial_guess(features, prototypes);
-
-	int iterations = 0;
-	double delta = std::numeric_limits<double>::max();
-	const double minDelta = 1e-5;
-	const int maxIterations = 200;
-
-	do
-	{
-		iterations++;
-		compute_assignments(features, prototypes, assignment);
-
-		if (iterations > 1)
-			oldprototypes = prototypes;
-
-#ifdef DEBUG_KMEANS
-		fprintf(stderr, "KMeans::cluster compute_prototypes\n");
-
-#endif
-		if (compute_prototypes(features, prototypes, weights, assignment) < 0)
-		{
-			fprintf(stderr, "KMeans::cluster restart\n");
-			goto KMeans_Restart;
-		}
-
-#ifdef DEBUG_KMEANS
-		fprintf(stderr, "KMeans::cluster compute_delta\n");
-
-#endif
-		if (iterations > 1)
-			delta = compute_delta(oldprototypes, prototypes);
-
-#ifdef DEBUG_KMEANS
-		print_iteration(iterations, prototypes, delta);
-
-#endif
-
-	} while ((delta > minDelta) && (iterations < maxIterations));
-
-#ifdef DEBUG_KMEANS
-	fprintf(stderr, "KMeans::cluster: iterations = %d, delta = %f\n",
-			iterations, delta);
-
-#endif
-
-	compute_weights(features, weights, assignment);
+  NICE::VVector oldprototypes;
+
+  prototypes.clear();
+  weights.clear();
+  assignment.clear();
+  weights.resize(noClasses, 0);
+  assignment.resize(features.size(), 0);
+
+  int dimension;
+
+  if ((int) features.size() >= this->noClasses)
+      dimension = features[0].size();
+  else
+  {
+      fprintf(stderr,
+              "FATAL ERROR: Not enough feature vectors provided for kMeans\n");
+      exit(-1);
+  }
+
+  for (int k = 0; k < this->noClasses; k++)
+  {
+      prototypes.push_back(Vector(dimension));
+      prototypes[k].set(0);
+  }
+
+  bool successKMeans ( false );
+  int iterations ( 0 );
+  double delta ( std::numeric_limits<double>::max() );
+  
+  while ( !successKMeans )
+  {
+    //we assume that this run will be successful
+    successKMeans = true;  
+
+    this->initial_guess(features, prototypes);
+    
+    iterations = 0;
+    delta =  std::numeric_limits<double>::max();    
+
+    do
+    {
+        iterations++;
+        this->compute_assignments(features, prototypes, assignment);
+
+        if (iterations > 1)
+            oldprototypes = prototypes;
+
+        #ifdef DEBUG_KMEANS
+        fprintf(stderr, "KMeans::cluster compute_prototypes\n");
+        #endif
+        
+        if ( this->compute_prototypes(features, prototypes, weights, assignment) < 0 )
+        {
+            fprintf(stderr, "KMeans::cluster restart\n");
+            successKMeans = false;
+            break;
+        }
+
+        #ifdef DEBUG_KMEANS
+          fprintf(stderr, "KMeans::cluster compute_delta\n");
+        #endif
+        
+        if (iterations > 1)
+            delta = this->compute_delta(oldprototypes, prototypes);
+
+        #ifdef DEBUG_KMEANS
+          print_iteration(iterations, prototypes, delta);
+        #endif
+
+    } while ((delta > d_minDelta) && (iterations < i_maxIterations));
+    
+  }
+
+  #ifdef DEBUG_KMEANS
+    fprintf(stderr, "KMeans::cluster: iterations = %d, delta = %f\n", iterations, delta);
+  #endif
+
+  this->compute_weights(features, weights, assignment);
 }
 
-void KMeans::print_iteration(int iterations, VVector & prototypes, double delta)
+void KMeans::print_iteration(int iterations, NICE::VVector & prototypes, double delta)
 {
-	if (iterations > 1)
-		fprintf(stderr, "KMeans::cluster: iteration=%d delta=%f\n", iterations,
-				delta);
-	else
-		fprintf(stderr, "KMeans::cluster: iteration=%d\n", iterations);
-
-	int k = 0;
-
-	for (VVector::const_iterator i = prototypes.begin(); i != prototypes.end(); i++, k++)
-	{
-		fprintf(stderr, "class (%d)\n", k);
-		cerr << "prototype = " << (*i) << endl;
-	}
+    if (iterations > 1)
+        fprintf(stderr, "KMeans::cluster: iteration=%d delta=%f\n", iterations,
+                delta);
+    else
+        fprintf(stderr, "KMeans::cluster: iteration=%d\n", iterations);
+
+    int k = 0;
+
+    for (NICE::VVector::const_iterator i = prototypes.begin(); i != prototypes.end(); i++, k++)
+    {
+        fprintf(stderr, "class (%d)\n", k);
+        std::cerr << "prototype = " << (*i) << std::endl;
+    }
 }

+ 99 - 39
math/cluster/KMeans.h

@@ -1,13 +1,14 @@
 /** 
 * @file KMeans.h
 * @brief K-Means
-* @author Erik Rodner
-* @date 10/29/2007
-
+* @author Erik Rodner, Alexander Freytag
+* @date 29-10-2007 (dd-mm-yyyy)
 */
 #ifndef KMEANSINCLUDE
 #define KMEANSINCLUDE
 
+#include <core/basics/Config.h>
+#include <core/vector/Distance.h>
 #include "core/vector/VectorT.h"
 #include "core/vector/MatrixT.h"
   
@@ -17,51 +18,110 @@
 namespace OBJREC {
 
 /** K-Means */
-class KMeans : public ClusterAlgorithm
-{
+  /**
+   * @class K-Means
+   * @brief K-Means
+   * @author Erik Rodner, Alexander Freytag
+   * @date 29-10-2007 (dd-mm-yyyy)
+  */  
+  class KMeans : public ClusterAlgorithm
+  {
 
     protected:
-	int noClasses;
-	std::string distanceType;
-	NICE::VectorDistance<double> *distancefunction;
-	double vectorDistance(const NICE::Vector &vector1, const NICE::Vector &vector2, uint distancetype);
-	double compute_assignments ( const NICE::VVector & features,
-				     const NICE::VVector & prototypes,
-				     std::vector<int> & assignment );
+          
+        /************************
+        * 
+        *   protected variables
+        * 
+        **************************/         
+          
+          //! desired number of clusters
+          int noClasses;
+          
+          //! specify which distance to use for calculating assignments
+          std::string distanceType;
+          
+          //! the actual distance metric
+          NICE::VectorDistance<double> *distancefunction;
+          
+          //! maximum difference between prototype-solutions of two iterations for convergence
+          double d_minDelta;
+          
+          //! maximum number of iterations until convergence
+          int i_maxIterations;          
+          
+        /************************
+        * 
+        *   protected methods
+        * 
+        **************************/          
+          
+        //! compute the distance between two features using the specified distance metric
+          double vectorDistance(const NICE::Vector &vector1, const NICE::Vector &vector2, uint distancetype);
+          
+        //! compute assignments of all given features wrt to the currently known prototypes (cluster centroids) == ~ E-step
+          double compute_assignments ( const NICE::VVector & features,
+                                      const NICE::VVector & prototypes,
+                                      std::vector<int> & assignment );
 
-	double compute_weights ( const NICE::VVector & features,
-				 std::vector<double> & weights,
-				 std::vector<int>    & assignment );
+        //! compute number of assignments for every currently found cluster
+          double compute_weights ( const NICE::VVector & features,
+                                  std::vector<double> & weights,
+                                  std::vector<int>    & assignment );
 
-	double compute_delta ( const NICE::VVector & oldprototypes,
-				       const NICE::VVector & prototypes );
+        //! compute the difference between prototypes of previous iteration and those currently found
+          double compute_delta ( const NICE::VVector & oldprototypes,
+                                const NICE::VVector & prototypes );
 
-	int compute_prototypes ( const NICE::VVector & features,
-				  NICE::VVector & prototypes,
-				  std::vector<double> & weights,
-				  const std::vector<int>    & assignment );
+        //! compute (update) prototypes given the current assignments == ~ M-step
+          int compute_prototypes ( const NICE::VVector & features,
+                                  NICE::VVector & prototypes,
+                                  std::vector<double> & weights,
+                                  const std::vector<int>    & assignment );
+          
+        //! have an initial guess, i.e., randomly pick some features as initial cluster centroids
+          void initial_guess ( const NICE::VVector & features,
+                              NICE::VVector & prototypes );
+        //! give additional information for the current iteration
+          void print_iteration ( int iterations,
+                                NICE::VVector & prototypes,
+                                double delta );
 
-	void initial_guess ( const NICE::VVector & features,
-			     NICE::VVector & prototypes );
-	
-	void print_iteration ( int iterations, 
-			       NICE::VVector & prototypes,
-			       double delta );
+      public:
 
-    public:
-  
-	/** simple constructor */
-	KMeans( int noClasses , std::string distanceMode="euclidean");
+        /**
+        * @brief simple constructor
+        * @param _noClasses the number of clusters to be computed
+        * @param _distanceMode a string specifying the distance function to be used (default: euclidean)
+        */
+        KMeans( const int & _noClasses , const std::string & _distanceMode="euclidean");
       
-	/** simple destructor */
-	virtual ~KMeans();
-     
-	void cluster ( const NICE::VVector & features,
-		       NICE::VVector & prototypes,
-		       std::vector<double> & weights,
-		       std::vector<int>    & assignment );
+        /**
+        * @brief standard constructor
+        * @param conf config file specifying all relevant variable settings
+        * @param _section name of the section within the configfile where the settings can be found (default: KMeans)
+        */
+        KMeans( const NICE::Config *conf, const std::string & _section = "KMeans");      
+
+        /** simple destructor */
+        virtual ~KMeans();
+
+        
+          /**
+          *@brief this is the actual method that performs the clustering for a given set of features
+          *@author Erik Rodner, Alexander Freytag
+          *@date 29-10-2007 (dd-mm-yyyy)
+          *@param   features input features to be clustered
+          *@param   prototypes computed prototypes (cluster medoids) for the given samples
+          *@param   weights number of assignments for every cluster
+          *@param   assignment explicite assignments of features to computed cluster medoids
+          */            
+        void cluster ( const NICE::VVector & features,
+                      NICE::VVector & prototypes,
+                      std::vector<double> & weights,
+                      std::vector<int>    & assignment );
 
-};
+  };
 
 
 } // namespace

+ 431 - 0
math/cluster/KMedian.cpp

@@ -0,0 +1,431 @@
+/**
+ * @file KMedian.cpp
+ * @brief KMedian (aka K-medoid)
+ * @author Alexander Freytag
+ * @date 23-04-2013 (dd-mm-yyyy)
+ */
+
+#ifdef NICE_USELIB_OPENMP
+#include <omp.h>
+#endif
+
+#include <iostream>
+#include <map>
+#include <algorithm> //to easily find the smallest value in a map
+
+#include "vislearning/math/cluster/KMedian.h"
+#include "vislearning/math/distances/genericDistance.h"
+
+#include <set>
+
+using namespace OBJREC;
+
+using namespace std;
+
+using namespace NICE;
+
+
+typedef std::pair<int, double> MyPairType;
+struct CompareSecond
+{
+    bool operator()(const MyPairType& left, const MyPairType& right) const
+    {
+        return left.second < right.second;
+    }
+};
+
+#undef DEBUG_KMEDIAN_ASSIGNMENTS
+// #define DEBUG_KMEDIAN_ASSIGNMENTS
+
+#undef DEBUG_KMEDIAN_PROTOCOMP
+// #define DEBUG_KMEDIAN_PROTOCOMP
+
+
+
+KMedian::KMedian(const int & _noClusters, const std::string & _distanceType) :
+  noClusters(_noClusters), distanceType(_distanceType)
+{
+  //srand(time(NULL));
+  distancefunction = GenericDistanceSelection::selectDistance(distanceType);
+  
+  this->d_minDelta  = 1e-5;
+  this->i_maxIterations = 200;
+}
+
+KMedian::KMedian( const NICE::Config *conf, const std::string & _section)
+{       
+  this->distanceType = conf->gS( _section, "distanceType", "euclidean" );
+  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);
+  
+  this->d_minDelta  = conf->gD( _section, "minDelta", 1e-5 );
+  this->i_maxIterations = conf->gI( _section, "maxIterations", 200);
+  
+  this->noClusters = conf->gI( _section, "noClusters", 20);
+}
+
+KMedian::~KMedian()
+{
+}
+
+void KMedian::initial_guess(const VVector & features, VVector & prototypes)
+{
+  int j = 0;
+  std::set<int, std::greater<int> > mark;
+
+  for (VVector::iterator i = prototypes.begin(); i != prototypes.end(); i++, j++)
+  {
+    int k;
+
+    do
+    {
+      k = rand() % features.size();
+    } while (mark.find(k) != mark.end());
+
+    mark.insert(mark.begin(), k);
+
+    *i = features[k];
+  }
+}
+
+int KMedian::compute_prototypes(const VVector & features, VVector & prototypes,
+    std::vector<double> & weights, const std::vector<int> & assignment)
+{
+  
+  #ifdef DEBUG_KMEDIAN_PROTOCOMP  
+    std::cerr << "initial assignments: ";
+    for (std::vector<int>::const_iterator assignIt = assignment.begin(); assignIt != assignment.end(); assignIt++)
+    { 
+      std::cerr << " " << *assignIt;
+    } 
+    std::cerr << std::endl;
+  #endif
+  
+  //initialization
+  for (int k = 0; k < noClusters; k++)
+  {
+    prototypes[k].set(0);
+    weights[k] = 0;
+  }
+  
+  NICE::VectorT<int> numberOfCurrentAssignments ( noClusters ) ;
+  numberOfCurrentAssignments.set ( 0 );
+  
+  int exCnt = 0;  
+  //how many examples are assigned to the current clusters?
+  for (VVector::const_iterator i = features.begin(); i != features.end(); i++, exCnt++)
+  {
+    int k = assignment[exCnt];
+    //increase counter for assigned cluster
+    numberOfCurrentAssignments[ k ] ++;    
+  }
+    
+  #ifdef DEBUG_KMEDIAN_PROTOCOMP    
+    std::cerr << "k-median -- current assignmens: " << numberOfCurrentAssignments << std::endl << "noClusters: " << noClusters << std::endl;
+  #endif
+  
+  //compute the median for every cluster
+  #pragma omp parallel for
+  for (int clusterCnt = 0; clusterCnt < noClusters; clusterCnt++)
+  {    
+    NICE::Vector overallDistances ( numberOfCurrentAssignments[ clusterCnt ] );
+    VVector::const_iterator lastExampleWorkedOn = features.begin();
+    int i_idxOfLastExampleWorkedOn ( 0 );
+    uint i_exCntInt ( 0 );
+
+    //this map will contain overall distances of all examples within the current clusters
+    //we need separate maps for every cluster to allow parallelization
+    std::map<int,double> distancesWithinCluster;
+    for (VVector::const_iterator featIt = features.begin(); featIt != features.end(); featIt++, i_exCntInt++)
+    {
+      int k = assignment[i_exCntInt];
+      
+      //only considere examples currently assigned to cluster clusterCnt
+      if ( k != clusterCnt)
+      {
+        continue;      
+      }
+      
+      uint exCntIntTmp ( i_idxOfLastExampleWorkedOn ); //idx going over all features 
+      for (VVector::const_iterator j = lastExampleWorkedOn ; j != features.end(); j++, exCntIntTmp++)
+      {
+        int kTmp;
+        if ( exCntIntTmp < assignment.size() )
+          kTmp = assignment[exCntIntTmp];
+        else
+        {
+          //actually, this will be never be reached :)
+          std::cerr << "ERROR: exCntIntTmp >= assignment.size() " << exCntIntTmp << " " << assignment.size() << std::endl;
+        }
+        
+        //only considere examples currently assigned to cluster clusterCnt
+        if ( kTmp != clusterCnt)
+          continue;         
+       
+        
+        double dist ( distancefunction->calculate( *featIt, *j) );
+        if ( i_exCntInt < features.size() )
+        {
+          distancesWithinCluster[ i_exCntInt ] += dist;
+          #ifdef DEBUG_KMEDIAN_PROTOCOMP
+            std::cerr << "increase " << i_exCntInt << " by " << dist << " for " <<*featIt << " and " << *j << std::endl;
+          #endif
+        }
+        else
+        {
+          //actually, this will be never be reached :)          
+          std::cerr << "ERROR: i_exCntInt >= features.size() " << i_exCntInt << " " << features.size() << std::endl;
+        }
+
+        if ( i_exCntInt != exCntIntTmp )
+        {
+          if (exCntIntTmp < features.size() )
+          {
+            distancesWithinCluster[ exCntIntTmp ] += dist;
+            #ifdef DEBUG_KMEDIAN_PROTOCOMP
+              std::cerr << "increase also " << exCntIntTmp << " by " << dist << std::endl;
+            #endif
+          }
+          else
+            std::cerr << "ERROR: exCntIntTmp >= features.size() " << exCntIntTmp << " " << features.size() << std::endl;
+        }
+        
+      }      
+      
+      //inc by one to avoid calculating some distances twice
+      if ( ( featIt != features.end()) && ( (featIt +1 ) != features.end()) )
+      {
+        lastExampleWorkedOn = ( featIt + 1 );      
+        i_idxOfLastExampleWorkedOn = i_exCntInt+1;
+      }
+    }
+       
+    #ifdef DEBUG_KMEDIAN_PROTOCOMP
+      std::cerr << "distances for cluster " << clusterCnt << " ";
+      for(std::map<int,double>::const_iterator distIt = distancesWithinCluster.begin(); distIt != distancesWithinCluster.end(); distIt++)
+      {
+        std::cerr << distIt->first << " " << distIt->second << " ";
+      }
+      std::cerr << std::endl;
+    #endif
+      
+    //now compute the index of example with min overall distance
+    int idxOfClusterMedian ( (min_element(distancesWithinCluster.begin(), distancesWithinCluster.end(), CompareSecond()))->first );
+        
+    #pragma omp critical
+    prototypes[clusterCnt] = features[idxOfClusterMedian]; 
+ 
+    //finished computations for cluster k
+  }
+  
+  #ifdef DEBUG_KMEDIAN_PROTOCOMP
+    std::cerr << " ----   prototypes after current iteration:  --- " << std::endl;
+    for (NICE::VVector::const_iterator protoIt = prototypes.begin(); protoIt != prototypes.end(); protoIt++)
+    {
+      std::cerr << *protoIt << " ";
+    }
+    
+    std::cerr << std::endl;
+  #endif
+
+  return 0;
+}
+
+double KMedian::compute_delta(const VVector & oldprototypes,
+    const VVector & prototypes)
+{
+  double distance = 0;
+
+  for (uint k = 0; k < oldprototypes.size(); k++)
+  {
+    distance += distancefunction->calculate(oldprototypes[k], prototypes[k]);
+    
+    #ifdef DEBUG_KMEDIAN_ASSIGNMENTS
+      fprintf(stderr, "KMedian::compute_delta: Distance:",
+          distancefunction->calculate(oldprototypes[k], prototypes[k]));
+    #endif
+  }
+  return distance;
+}
+
+double KMedian::compute_assignments(const VVector & features,
+                                    const VVector & prototypes,
+                                    std::vector<int> & assignment)
+{
+  int index = 0;
+  for (VVector::const_iterator i = features.begin(); i != features.end(); i++, index++)
+  {
+
+    const NICE::Vector & x = *i;
+    double mindist = std::numeric_limits<double>::max();
+    int minclass = 0;
+
+    int c = 0;
+    
+    #ifdef DEBUG_KMEDIAN_ASSIGNMENTS
+
+        fprintf(stderr, "computing nearest prototype for std::vector %d\n",
+            index);
+    #endif
+        
+    for (VVector::const_iterator j = prototypes.begin(); j
+        != prototypes.end(); j++, c++)
+    {
+
+      const NICE::Vector & p = *j;
+      double distance = distancefunction->calculate(p, x);
+      
+      #ifdef DEBUG_KMEDIAN_ASSIGNMENTS
+        fprintf(stderr, "KMedian::compute_delta: Distance: %f\n",
+            distancefunction->calculate(p, x));
+      #endif
+
+      #ifdef DEBUG_KMEDIAN_ASSIGNMENTS
+            cerr << p << endl;
+            cerr << x << endl;
+            fprintf(stderr, "distance to prototype %d is %f\n", c, distance);
+      #endif
+
+      if (distance < mindist)
+      {
+        minclass = c;
+        mindist = distance;
+      }
+    }
+
+    assignment[index] = minclass;
+  }
+
+  return 0.0;
+}
+
+double KMedian::compute_weights(const VVector & features,
+                                std::vector<double> & weights,
+                                std::vector<int> & assignment)
+{
+  for (int k = 0; k < noClusters; k++)
+    weights[k] = 0;
+
+  int j = 0;
+
+  for (VVector::const_iterator i = features.begin(); i != features.end(); i++, j++)
+  {
+    int k = assignment[j];
+    weights[k]++;
+  }
+
+  for (int k = 0; k < noClusters; k++)
+    weights[k] = weights[k] / features.size();
+
+  return 0.0;
+}
+
+void KMedian::cluster(const NICE::VVector & features,
+                      NICE::VVector & prototypes,
+                      std::vector<double> & weights,
+                      std::vector<int> & assignment)
+{
+  NICE::VVector oldprototypes;
+
+  prototypes.clear();
+  weights.clear();
+  assignment.clear();
+  weights.resize(noClusters, 0);
+  assignment.resize(features.size(), 0);
+
+  int dimension;
+
+  if ((int) features.size() >= noClusters)
+    dimension = features[0].size();
+  else
+  {
+    fprintf(stderr,
+        "FATAL ERROR: Not enough feature vectors provided for kMedians -- number of Features: %i - number of clusters: %i\n", (int) features.size(), noClusters);
+    exit(-1);
+  }
+
+  for (int k = 0; k < noClusters; k++)
+  {
+    prototypes.push_back( NICE::Vector(dimension) );
+    prototypes[k].set(0);
+  }
+ 
+  bool successKMedian ( false );
+  int iterations ( 0 );
+  double delta ( std::numeric_limits<double>::max() );
+  
+  while ( !successKMedian )
+  {
+    //we assume that this run will be successful
+    successKMedian = true;
+    
+    this->initial_guess(features, prototypes);
+
+    iterations = 0;
+    delta =  std::numeric_limits<double>::max();
+
+    //until-loop over iterations
+    do
+    {
+      iterations++;
+      
+//       #ifdef DEBUG_KMEDIAN_ASSIGNMENTS
+        std::cerr << "k-median iteration " << iterations << std::endl;
+//       #endif
+        
+      this->compute_assignments( features, prototypes, assignment );
+
+      if (iterations > 1)
+        oldprototypes = prototypes;
+
+      #ifdef DEBUG_KMEDIAN_ASSIGNMENTS
+          fprintf(stderr, "KMedian::cluster compute_prototypes\n");
+      #endif
+      
+      if ( this->compute_prototypes( features, prototypes, weights, assignment ) < 0 )
+      {
+        fprintf(stderr, "KMedian::cluster restart\n");
+        successKMedian = false;
+        break;
+      }
+
+      #ifdef DEBUG_KMEDIAN_ASSIGNMENTS
+          fprintf(stderr, "KMedian::cluster compute_delta\n");
+      #endif
+      
+      if (iterations > 1)
+        delta = this->compute_delta( oldprototypes, prototypes );
+
+      #ifdef DEBUG_KMEDIAN_ASSIGNMENTS
+          this->print_iteration( iterations, prototypes, delta );
+      #endif
+
+    } while ((delta > d_minDelta) && (iterations < i_maxIterations));
+    
+  }
+
+  std::cerr << "ended optimization  -- delta: " << delta  << " of d_minDelta: " << d_minDelta << " --- and iterations: " << iterations << " of i_maxIterations: " << i_maxIterations << std::endl;
+    
+  #ifdef DEBUG_KMEDIAN_ASSIGNMENTS
+    fprintf(stderr, "KMedian::cluster: iterations = %d, delta = %f\n",
+        iterations, delta);
+  #endif
+
+  this->compute_weights( features, weights, assignment );
+}
+
+void KMedian::print_iteration( int iterations, VVector & prototypes, double delta )
+{
+  if (iterations > 1)
+    fprintf(stderr, "KMedian::cluster: iteration=%d delta=%f\n", iterations,
+        delta);
+  else
+    fprintf(stderr, "KMedian::cluster: iteration=%d\n", iterations);
+
+  int k = 0;
+
+  for (VVector::const_iterator i = prototypes.begin(); i != prototypes.end(); i++, k++)
+  {
+    fprintf(stderr, "class (%d)\n", k);
+    cerr << "prototype = " << (*i) << endl;
+  }
+}

+ 131 - 0
math/cluster/KMedian.h

@@ -0,0 +1,131 @@
+/** 
+* @file KMedian.h
+ * @brief KMedian (aka K-medoid)
+* @author Alexander Freytag
+* @date 23-04-2013 (dd-mm-yyyy)
+
+*/
+#ifndef KMEDIANINCLUDE
+#define KMEDIANINCLUDE
+
+#include <core/basics/Config.h>
+#include <core/vector/Distance.h>
+#include <core/vector/MatrixT.h>
+#include <core/vector/VectorT.h>
+  
+#include "ClusterAlgorithm.h"
+
+namespace OBJREC {
+
+  /**
+   * @class KMedian
+   * @brief KMedian (aka K-medoid)
+   * @author Alexander Freytag
+   * @date 23-04-2013 (dd-mm-yyyy)
+  */    
+  class KMedian : public ClusterAlgorithm
+  {
+
+      protected:
+        
+      /************************
+       * 
+       *   protected variables
+       * 
+       **************************/ 
+      
+        //! desired number of clusters
+        int noClusters;
+        
+        //! specify which distance to use for calculating assignments
+        std::string distanceType;
+        
+        //! the actual distance metric
+        NICE::VectorDistance<double> *distancefunction;
+        
+        //! maximum difference between prototype-solutions of two iterations for convergence
+        double d_minDelta;
+        
+        //! maximum number of iterations until convergence
+        int i_maxIterations;
+        
+  
+       /************************
+       * 
+       *   protected methods
+       * 
+       **************************/  
+      
+        //! compute the distance between two features using the specified distance metric
+        double vectorDistance(const NICE::Vector &vector1, const NICE::Vector &vector2, uint distancetype);
+        
+        //! compute assignments of all given features wrt to the currently known prototypes (cluster medoids) == ~ E-step
+        double compute_assignments ( const NICE::VVector & features,
+                  const NICE::VVector & prototypes,
+                  std::vector<int> & assignment );
+
+        //! compute number of assignments for every currently found cluster
+        double compute_weights ( const NICE::VVector & features,
+              std::vector<double> & weights,
+              std::vector<int>    & assignment );
+
+        //! compute the difference between prototypes of previous iteration and those currently found
+        double compute_delta ( const NICE::VVector & oldprototypes,
+                    const NICE::VVector & prototypes );
+
+        //! compute (update) prototypes given the current assignments == ~ M-step
+        int compute_prototypes ( const NICE::VVector & features,
+                NICE::VVector & prototypes,
+                std::vector<double> & weights,
+                const std::vector<int>    & assignment );
+
+        //! have an initial guess, i.e., randomly pick some features as initial cluster centroids
+        void initial_guess ( const NICE::VVector & features,
+                NICE::VVector & prototypes );
+        
+        //! give additional information for the current iteration
+        void print_iteration ( int iterations, 
+                  NICE::VVector & prototypes,
+                  double delta );
+
+      public:
+    
+        /**
+        * @brief simple constructor
+        * @param _noClusters the number of clusters to be computed
+        * @param _distanceMode a string specifying the distance function to be used (default: euclidean)
+        */
+        KMedian( const int & _noClusters , const std::string & _distanceMode="euclidean");
+        
+        /**
+        * @brief standard constructor
+        * @param conf config file specifying all relevant variable settings
+        * @param _section name of the section within the configfile where the settings can be found (default: KMedian)
+        */
+        KMedian( const NICE::Config *conf, const std::string & _section = "KMedian");
+
+        
+            
+        /** simple destructor */
+        virtual ~KMedian();
+          
+        /**
+        *@brief this is the actual method that performs the clustering for a given set of features
+        *@author Alexander Freytag
+        *@date 25-04-2013 (dd-mm-yyyy)
+        *@param   features input features to be clustered
+        *@param   prototypes computed prototypes (cluster medoids) for the given samples
+        *@param   weights number of assignments for every cluster
+        *@param   assignment explicite assignments of features to computed cluster medoids
+        */        
+        void cluster ( const NICE::VVector & features,
+                NICE::VVector & prototypes,
+                std::vector<double> & weights,
+                std::vector<int>    & assignment );
+
+  };
+
+
+} // namespace
+
+#endif

+ 157 - 0
math/cluster/RandomClustering.cpp

@@ -0,0 +1,157 @@
+/**
+ * @file RandomClustering.cpp
+* @brief Clustering by randomly picking some samples from the set of features as representatives
+* @author Alexander Freytag
+* @date 03-06-2013 (dd-mm-yyyy)
+ */
+
+#ifdef NICE_USELIB_OPENMP
+#include <omp.h>
+#endif
+
+#include <iostream>
+#include <map>
+
+#include "vislearning/math/distances/genericDistance.h"
+
+#include "vislearning/math/cluster/RandomClustering.h"
+
+#include <set>
+
+using namespace OBJREC;
+
+using namespace std;
+
+using namespace NICE;
+
+
+
+RandomClustering::RandomClustering(const int & _noClusters, const std::string & _distanceType) :
+  noClusters(_noClusters), distanceType(_distanceType)
+{
+}
+
+RandomClustering::RandomClustering( const NICE::Config *conf, const std::string & _section)
+{  
+  this->noClusters = conf->gI( _section, "noClusters", 20);
+  
+  this->distanceType = conf->gS( _section, "distanceType", "euclidean" );
+  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);  
+}
+
+RandomClustering::~RandomClustering()
+{
+}
+
+int RandomClustering::compute_prototypes(const NICE::VVector & _features, NICE::VVector & _prototypes,
+    std::vector<double> & _weights, const std::vector<int> & _assignment)
+{
+  
+  int noFeatures ( _features.size() );
+  
+  std::set<int, std::greater<int> > chosenIdx;
+  
+  //empty init
+  chosenIdx.clear();
+  
+  //pick k distinct cluster representatives randomly
+  for (int cnt = 0; cnt < this->noClusters; cnt++)
+  {
+    int idx;
+    do
+    {
+      idx = rand() % noFeatures;
+    }
+    while ( chosenIdx.find ( idx ) != chosenIdx.end() );
+                             
+    //if a new (distinct) idx was computed, insert it into the set of randomly picked indicees
+    chosenIdx.insert ( idx );
+  }
+  
+  _prototypes.resize( this->noClusters ); 
+  
+  int clusterCnt ( 0 );
+  for ( std::set<int>::const_iterator idxIt = chosenIdx.begin(); idxIt != chosenIdx.end(); idxIt++, clusterCnt++ )
+  {
+     _prototypes[clusterCnt] = _features[ *idxIt ];
+  }
+
+  return 0;
+}
+
+double RandomClustering::compute_assignments(const NICE::VVector & _features,
+                                    const NICE::VVector & _prototypes,
+                                    std::vector<int> & _assignment)
+{
+  _assignment.resize( _features.size() );
+  
+  int index = 0;
+  for (NICE::VVector::const_iterator i = _features.begin(); i != _features.end(); i++, index++)
+  {
+
+    const NICE::Vector & x = *i;
+    double mindist = std::numeric_limits<double>::max();
+    int minclass = 0;
+
+    int c = 0;
+           
+    for (NICE::VVector::const_iterator j = _prototypes.begin(); j
+        != _prototypes.end(); j++, c++)
+    {
+
+      const NICE::Vector & p = *j;
+      double distance = this->distancefunction->calculate(p, x);
+      
+      if (distance < mindist)
+      {
+        minclass = c;
+        mindist = distance;
+      }
+    }
+
+    _assignment[index] = minclass;
+  }
+
+  return 0.0;
+}
+
+double RandomClustering::compute_weights(const NICE::VVector & _features,
+                                std::vector<double> & _weights,
+                                std::vector<int> & _assignment)
+{
+  _weights.resize( this->noClusters );
+  
+  //initalization
+  for (int k = 0; k < noClusters; k++)
+    _weights[k] = 0;
+
+  int j = 0;
+
+  //increase weight for every assignment
+  for (NICE::VVector::const_iterator i = _features.begin(); i != _features.end(); i++, j++)
+  {
+    int k = _assignment[j];
+    _weights[k]++;
+  }
+
+  //normalize weights
+  for (int k = 0; k < noClusters; k++)
+    _weights[k] = _weights[k] / _features.size();
+
+  return 0.0;
+}
+
+void RandomClustering::cluster(const NICE::VVector & _features,
+                      NICE::VVector & _prototypes,
+                      std::vector<double> & _weights,
+                      std::vector<int> & _assignment)
+{
+  //randomly pick cluster centers
+  this->compute_prototypes( _features, _prototypes, _weights, _assignment );
+  
+  //compute assignments for every cluster
+  this->compute_assignments( _features, _prototypes, _assignment );
+  
+  //compute corresponding weights
+  this->compute_weights( _features, _weights, _assignment );
+}

+ 112 - 0
math/cluster/RandomClustering.h

@@ -0,0 +1,112 @@
+/** 
+* @file RandomClustering.h
+* @brief Clustering by randomly picking some samples from the set of features as representatives
+* @author Alexander Freytag
+* @date 03-06-2013 (dd-mm-yyyy)
+*/
+#ifndef RANDOMCLUSTERERNINCLUDE
+#define RANDOMCLUSTERERNINCLUDE
+
+#include <core/basics/Config.h>
+#include <core/vector/Distance.h>
+#include <core/vector/MatrixT.h>
+#include <core/vector/VectorT.h>
+  
+#include "ClusterAlgorithm.h"
+
+namespace OBJREC {
+
+  /**
+   * @class RandomClustering
+   * @brief Clustering by randomly picking some samples from the set of features as representatives
+   * @author Alexander Freytag
+   * @date 03-06-2013 (dd-mm-yyyy)
+  */    
+  class RandomClustering : public ClusterAlgorithm
+  {
+
+      protected:
+        
+      /************************
+       * 
+       *   protected variables
+       * 
+       **************************/ 
+      
+        //! desired number of clusters
+        int noClusters;
+        
+        //! specify which distance to use for calculating assignments
+        std::string distanceType;
+        
+        //! the actual distance metric
+        NICE::VectorDistance<double> *distancefunction;        
+        
+  
+       /************************
+       * 
+       *   protected methods
+       * 
+       **************************/  
+      
+        
+        //! compute assignments of all given features wrt to the currently known prototypes (cluster medoids) == ~ E-step
+        double compute_assignments ( const NICE::VVector & features,
+                  const NICE::VVector & prototypes,
+                  std::vector<int> & assignment );
+
+        //! compute number of assignments for every currently found cluster
+        double compute_weights ( const NICE::VVector & features,
+              std::vector<double> & weights,
+              std::vector<int>    & assignment );
+
+
+        //! compute (update) prototypes given the current assignments == ~ M-step
+        int compute_prototypes ( const NICE::VVector & features,
+                NICE::VVector & prototypes,
+                std::vector<double> & weights,
+                const std::vector<int>    & assignment );
+
+      public:
+    
+        /**
+        * @brief simple constructor
+        * @param _noClasses the number of clusters to be computed
+        * @param _distanceMode a string specifying the distance function to be used (default: euclidean)* 
+        * @date 03-06-2013 (dd-mm-yyyy)
+        */
+        RandomClustering( const int & _noClasses , const std::string & _distanceMode="euclidean" );
+        
+        /**
+        * @brief standard constructor
+        * @param conf config file specifying all relevant variable settings
+        * @param _section name of the section within the configfile where the settings can be found (default: RandomClustering)
+        * @date 03-06-2013 (dd-mm-yyyy)
+        */
+        RandomClustering( const NICE::Config *conf, const std::string & _section = "RandomClustering");
+
+        
+            
+        /** simple destructor */
+        virtual ~RandomClustering();
+          
+        /**
+        * @brief this is the actual method that performs the clustering for a given set of features
+        * @author Alexander Freytag
+        * @date 03-06-2013 (dd-mm-yyyy)
+        * @param   features input features to be clustered
+        * @param   prototypes computed prototypes (randomly chosen) for the given samples
+        * @param   weights number of assignments for every cluster
+        * @param   assignment explicite assignments of features to computed cluster medoids
+        */        
+        void cluster ( const NICE::VVector & features,
+                NICE::VVector & prototypes,
+                std::vector<double> & weights,
+                std::vector<int>    & assignment );
+
+  };
+
+
+} // namespace
+
+#endif

+ 89 - 0
math/cluster/tests/Makefile.inc

@@ -0,0 +1,89 @@
+# BINARY-DIRECTORY-MAKEFILE
+# conventions:
+# - there are no subdirectories, they are ignored!
+# - all ".C", ".cpp" and ".c" files in the current directory are considered
+#   independent binaries, and linked as such.
+# - the binaries depend on the library of the parent directory
+# - the binary names are created with $(BINNAME), i.e. it will be more or less
+#   the name of the .o file
+# - all binaries will be added to the default build list ALL_BINARIES
+
+# --------------------------------
+# - remember the last subdirectory
+#
+# set the variable $(SUBDIR) correctly to the current subdirectory. this
+# variable can be used throughout the current makefile.inc. The many 
+# SUBDIR_before, _add, and everything are only required so that we can recover
+# the previous content of SUBDIR before exitting the makefile.inc
+
+SUBDIR_add:=$(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
+SUBDIR_before:=$(SUBDIR)
+SUBDIR:=$(strip $(SUBDIR_add))
+SUBDIR_before_$(SUBDIR):=$(SUBDIR_before)
+
+# ------------------------
+# - include subdirectories
+#
+# note the variables $(SUBDIRS_OF_$(SUBDIR)) are required later on to recover
+# the dependencies automatically. if you handle dependencies on your own, you
+# can also dump the $(SUBDIRS_OF_$(SUBDIR)) variable, and include the
+# makefile.inc of the subdirectories on your own...
+
+#SUBDIRS_OF_$(SUBDIR):=$(patsubst %/Makefile.inc,%,$(wildcard $(SUBDIR)*/Makefile.inc))
+#include $(SUBDIRS_OF_$(SUBDIR):%=%/Makefile.inc)
+
+# ----------------------------
+# - include local dependencies
+#
+# include the libdepend.inc file, which gives additional dependencies for the
+# libraries and binaries. additionally, an automatic dependency from the library
+# of the parent directory is added (commented out in the code below).
+
+-include $(SUBDIR)libdepend.inc
+
+PARENTDIR:=$(patsubst %/,%,$(dir $(patsubst %/,%,$(SUBDIR))))
+$(call PKG_DEPEND_INT,$(PARENTDIR))
+$(call PKG_DEPEND_EXT,CPPUNIT)
+
+# ---------------------------
+# - objects in this directory
+#
+# the use of the variable $(OBJS) is not mandatory. it is mandatory however
+# to update $(ALL_OBJS) in a way that it contains the path and name of
+# all objects. otherwise we can not include the appropriate .d files.
+
+OBJS:=$(patsubst %.cpp,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.cpp))) \
+      $(patsubst %.C,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.C))) \
+      $(shell grep -ls Q_OBJECT $(SUBDIR)*.h | sed -e's@^@/@;s@.*/@$(OBJDIR)moc_@;s@\.h$$@.o@') \
+      $(patsubst %.c,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.c)))
+ALL_OBJS += $(OBJS)
+
+# ----------------------------
+# - binaries in this directory
+#
+# output of binaries in this directory. none of the variables has to be used.
+# but everything you add to $(ALL_LIBRARIES) and $(ALL_BINARIES) will be
+# compiled with `make all`. be sure again to add the files with full path.
+
+CHECKS:=$(BINDIR)$(call LIBNAME,$(SUBDIR))
+ALL_CHECKS+=$(CHECKS)
+
+# ---------------------
+# - binary dependencies
+#
+# there is no way of determining the binary dependencies automatically, so we
+# follow conventions. each binary depends on the corresponding .o file and
+# on the libraries specified by the INTLIBS/EXTLIBS. these dependencies can be
+# specified manually or they are automatically stored in a .bd file.
+
+$(foreach head,$(wildcard $(SUBDIR)*.h),$(eval $(shell grep -q Q_OBJECT $(head) && echo $(head) | sed -e's@^@/@;s@.*/\(.*\)\.h$$@$(BINDIR)\1:$(OBJDIR)moc_\1.o@')))
+$(eval $(foreach c,$(CHECKS),$(c):$(BUILDDIR)$(CPPUNIT_MAIN_OBJ) $(OBJS) $(call PRINT_INTLIB_DEPS,$(c),.a)))
+
+# -------------------
+# - subdir management
+#
+# as the last step, always add this line to correctly recover the subdirectory
+# of the makefile including this one!
+
+SUBDIR:=$(SUBDIR_before_$(SUBDIR))
+

+ 89 - 0
math/cluster/tests/TestKMedian.cpp

@@ -0,0 +1,89 @@
+#ifdef NICE_USELIB_CPPUNIT
+
+#include <string>
+#include <exception>
+
+#include "TestKMedian.h"
+
+#include <core/basics/Config.h>
+#include "vislearning/math/distances/genericDistance.h"
+
+
+const bool verboseStartEnd = true;
+const bool verbose = false;
+const std::string distanceType = "euclidean";
+
+using namespace OBJREC;
+using namespace NICE;
+using namespace std;
+
+CPPUNIT_TEST_SUITE_REGISTRATION( TestKMedian );
+
+void TestKMedian::setUp() {
+}
+
+void TestKMedian::tearDown() {
+}
+
+void TestKMedian::testKMedianClustering() 
+{
+  if (verboseStartEnd)
+    std::cerr << "================== TestKMedian::testKMedianClustering ===================== " << std::endl;
+  
+  Config * conf = new Config;
+  std::string section ( "KMedian" );
+  conf->sS( section, "distanceType", "euclidean" );
+  conf->sI( section, "maxIterations", 200 );
+  conf->sI( section, "noClusters", 2 );
+   
+  OBJREC::KMedian kMedian ( conf, section );
+  
+  //create some artificial data
+  NICE::VVector features;
+  NICE::Vector x1 (2); x1[0] = 1;  x1[1] = 1; features.push_back(x1);
+  NICE::Vector x2 (2); x2[0] = 4;  x2[1] = 1; features.push_back(x2);
+  NICE::Vector x3 (2); x3[0] = 2;  x3[1] = 4; features.push_back(x3);
+  NICE::Vector x4 (2); x4[0] = 10; x4[1] = 3; features.push_back(x4);
+  NICE::Vector x5 (2); x5[0] = 8;  x5[1] = 3; features.push_back(x5);
+  NICE::Vector x6 (2); x6[0] = 4;  x6[1] = 3; features.push_back(x6);
+  NICE::Vector x7 (2); x7[0] = 3;  x7[1] = 2; features.push_back(x7);
+  NICE::Vector x8 (2); x8[0] = 1;  x8[1] = 3; features.push_back(x8);
+  NICE::Vector x9 (2); x9[0] = 9;  x9[1] = 2; features.push_back(x9);
+  
+  //cluster data
+  NICE::VVector prototypes;
+  std::vector<double> weights;
+  std::vector<int> assignment;
+  
+  kMedian.cluster ( features, prototypes, weights, assignment );  
+
+  //check whether the results fits the ground truth  
+  //NOTE
+  // If no random initialization is activated, we initially grab x2 and x8.
+  // After 3 iterations, we should have converged and obtain x5 and x7.
+
+  NICE::VectorDistance<double> * distancefunction = GenericDistanceSelection::selectDistance(distanceType);
+
+  if ( verbose )
+  {
+    std::cerr << " x9: " << x9 << " cl1: " << prototypes[0] << std::endl;
+    std::cerr << " x7: " << x7 << " cl2: " << prototypes[1] << std::endl;
+  }
+  
+  double distX9Cl1 ( distancefunction->calculate( x9, prototypes[0] ) );
+  double distX7Cl2 ( distancefunction->calculate( x7, prototypes[1] ) );
+  
+  CPPUNIT_ASSERT_DOUBLES_EQUAL( distX9Cl1, 0.0, 1e-8);
+  CPPUNIT_ASSERT_DOUBLES_EQUAL( distX7Cl2, 0.0, 1e-8); 
+  
+  std::cerr << "                               successfull              " << std::endl;
+        
+  //don't waste memory
+  delete conf;
+  
+  if (verboseStartEnd)
+    std::cerr << "================== TestKMedian::testKMedianClustering done ===================== " << std::endl;
+}
+
+
+#endif

+ 32 - 0
math/cluster/tests/TestKMedian.h

@@ -0,0 +1,32 @@
+#ifndef _TESTKMEDIAN_H
+#define _TESTKMEDIAN_H
+
+#include <cppunit/extensions/HelperMacros.h>
+#include "vislearning/math/cluster/KMedian.h"
+
+/**
+ * CppUnit-Testcase. 
+ * @brief CppUnit-Testcase to verify that the KMedian-clusterin works correctly
+ */
+class TestKMedian : public CppUnit::TestFixture {
+
+    CPPUNIT_TEST_SUITE( TestKMedian );
+    
+    CPPUNIT_TEST(testKMedianClustering);
+    
+    CPPUNIT_TEST_SUITE_END();
+  
+ private:
+ 
+ public:
+    void setUp();
+    void tearDown();
+
+    /**
+    * Constructor / Destructor testing 
+    */  
+    void testKMedianClustering();
+
+};
+
+#endif // _TESTKMEDIAN_H

+ 1 - 1
math/pdf/tests/TestPDF.cpp

@@ -25,7 +25,7 @@
 #include "core/vector/VVector.h"
 #include "vislearning/math/pdf/PDFGaussian.h"
 
-#include "objrec/nice_nonvis.h"
+// #include "objrec/nice_nonvis.h"
 
 using namespace std;
 using namespace NICE;

+ 8 - 0
noveltyDetection/Makefile

@@ -0,0 +1,8 @@
+#TARGETS_FROM:=$(notdir $(patsubst %/,%,$(shell pwd)))/$(TARGETS_FROM)
+#$(info recursivly going up: $(TARGETS_FROM) ($(shell pwd)))
+
+all:
+
+%:
+	$(MAKE) TARGETS_FROM=$(notdir $(patsubst %/,%,$(shell pwd)))/$(TARGETS_FROM) -C .. $@
+

+ 103 - 0
noveltyDetection/Makefile.inc

@@ -0,0 +1,103 @@
+# LIBRARY-DIRECTORY-MAKEFILE
+# conventions:
+# - all subdirectories containing a "Makefile.inc" are considered sublibraries
+#   exception: "progs/" and "tests/" subdirectories!
+# - all ".C", ".cpp" and ".c" files in the current directory are linked to a
+#   library
+# - the library depends on all sublibraries 
+# - the library name is created with $(LIBNAME), i.e. it will be somehow
+#   related to the directory name and with the extension .a
+#   (e.g. lib1/sublib -> lib1_sublib.a)
+# - the library will be added to the default build list ALL_LIBRARIES
+
+# --------------------------------
+# - remember the last subdirectory
+#
+# set the variable $(SUBDIR) correctly to the current subdirectory. this
+# variable can be used throughout the current makefile.inc. The many 
+# SUBDIR_before, _add, and everything are only required so that we can recover
+# the previous content of SUBDIR before exitting the makefile.inc
+
+SUBDIR_add:=$(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
+SUBDIR_before:=$(SUBDIR)
+SUBDIR:=$(strip $(SUBDIR_add))
+SUBDIR_before_$(SUBDIR):=$(SUBDIR_before)
+ifeq "$(SUBDIR)" "./"
+SUBDIR:=
+endif
+
+# ------------------------
+# - include subdirectories
+#
+# note the variables $(SUBDIRS_OF_$(SUBDIR)) are required later on to recover
+# the dependencies automatically. if you handle dependencies on your own, you
+# can also dump the $(SUBDIRS_OF_$(SUBDIR)) variable, and include the
+# makefile.inc of the subdirectories on your own...
+
+SUBDIRS_OF_$(SUBDIR):=$(patsubst %/Makefile.inc,%,$(wildcard $(SUBDIR)*/Makefile.inc))
+include $(SUBDIRS_OF_$(SUBDIR):%=%/Makefile.inc)
+
+# ----------------------------
+# - include local dependencies
+#
+# you can specify libraries needed by the individual objects or by the whole
+# directory. the object specific additional libraries are only considered
+# when compiling the specific object files
+# TODO: update documentation...
+
+-include $(SUBDIR)libdepend.inc
+
+$(foreach d,$(filter-out %progs %tests,$(SUBDIRS_OF_$(SUBDIR))),$(eval $(call PKG_DEPEND_INT,$(d))))
+
+# ---------------------------
+# - objects in this directory
+#
+# the use of the variable $(OBJS) is not mandatory. it is mandatory however
+# to update $(ALL_OBJS) in a way that it contains the path and name of
+# all objects. otherwise we can not include the appropriate .d files.
+
+OBJS:=$(patsubst %.cpp,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.cpp))) \
+      $(patsubst %.C,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.C))) \
+	  $(shell grep -ls Q_OBJECT $(SUBDIR)*.h | sed -e's@^@/@;s@.*/@$(OBJDIR)moc_@;s@\.h$$@.o@') \
+      $(patsubst %.c,$(OBJDIR)%.o,$(notdir $(wildcard $(SUBDIR)*.c)))
+ALL_OBJS += $(OBJS)
+
+# ----------------------------
+# - binaries in this directory
+#
+# output of binaries in this directory. none of the variables has to be used.
+# but everything you add to $(ALL_LIBRARIES) and $(ALL_BINARIES) will be
+# compiled with `make all`. be sure again to add the files with full path.
+
+LIBRARY_BASENAME:=$(call LIBNAME,$(SUBDIR))
+ifneq "$(SUBDIR)" ""
+ALL_LIBRARIES+=$(LIBDIR)$(LIBRARY_BASENAME).$(LINK_FILE_EXTENSION)
+endif
+
+# ---------------------
+# - binary dependencies
+#
+# there is no way of determining the binary dependencies automatically, so we
+# follow conventions. the current library depends on all sublibraries.
+# all other dependencies have to be added manually by specifying, that the
+# current .pc file depends on some other .pc file. binaries depending on
+# libraries should exclusivelly use the .pc files as well.
+
+ifeq "$(SKIP_BUILD_$(OBJDIR))" "1"
+$(LIBDIR)$(LIBRARY_BASENAME).a:
+else
+$(LIBDIR)$(LIBRARY_BASENAME).a:$(OBJS) \
+	$(call PRINT_INTLIB_DEPS,$(PKGDIR)$(LIBRARY_BASENAME).a,.$(LINK_FILE_EXTENSION))
+endif
+
+$(PKGDIR)$(LIBRARY_BASENAME).pc: \
+	$(call PRINT_INTLIB_DEPS,$(PKGDIR)$(LIBRARY_BASENAME).pc,.pc)
+
+# -------------------
+# - subdir management
+#
+# as the last step, always add this line to correctly recover the subdirectory
+# of the makefile including this one!
+
+SUBDIR:=$(SUBDIR_before_$(SUBDIR))
+

+ 72 - 0
noveltyDetection/NDCodebookLevelImagePooling.cpp

@@ -0,0 +1,72 @@
+
+#include "NDCodebookLevelImagePooling.h"
+
+//STL
+#include <iostream>
+
+//core
+#include <core/vector/VectorT.h>
+
+using namespace std;
+using namespace NICE;
+using namespace OBJREC;
+
+  //**********************************************
+  //
+  //                 PROTECTED METHODS
+  //
+  //********************************************** 
+
+
+
+  //**********************************************
+  //
+  //                 PUBLIC METHODS
+  //
+  //********************************************** 
+
+
+NDCodebookLevelImagePooling::NDCodebookLevelImagePooling ( const Config *_conf,
+                               const MultiDataset *_md, const std::string & _section )
+    : NoveltyDetectorCodebookLevel ( _conf, _md, _section )
+{ 
+  this->section = _section;
+//   d_noveltyThreshold = conf->gD ( section , "noveltyThreshold", 0.0064 );  
+  d_noveltyThreshold = conf->gD ( section , "stupidNoveltyThreshold", 0.0064 );  
+}
+
+NDCodebookLevelImagePooling::~NDCodebookLevelImagePooling()
+{
+}
+
+bool NDCodebookLevelImagePooling::evaluateNoveltyOfImage ( const std::string & _filename )
+{
+  ROADWORKSNOVDET;
+}
+
+bool NDCodebookLevelImagePooling::evaluateNoveltyOfImage ( const NICE::FloatImage & _noveltyImage )
+{
+    double meanNovelty ( 0.0 );
+    for ( uint y = 0 ; y < ( uint ) _noveltyImage.height() ; y++ )
+    {
+      for ( uint x = 0 ; x < ( uint ) _noveltyImage.width(); x++ )
+      {
+        meanNovelty += _noveltyImage(x,y);
+      }
+    }      
+    int imageSize ( _noveltyImage.height() * _noveltyImage.width() );
+    meanNovelty /= imageSize;
+
+      std::cerr << "  NOVELTY SCORE FOR CURRENT IMAGE: " <<  meanNovelty << " -- threshold: " << d_noveltyThreshold << std::endl;    
+    
+    if ( meanNovelty < d_noveltyThreshold )
+    {
+      // --- NOT NOVEL ---
+      return false;
+    }
+    else
+    {
+      // --- NOVEL ---
+      return true;
+    }
+}

+ 97 - 0
noveltyDetection/NDCodebookLevelImagePooling.h

@@ -0,0 +1,97 @@
+/**
+ * @file NDCodebookLevelImagePooling.h
+ * @brief Compute novelty of images based on relations between extracted features and current codebook, pool novelty scores over the whole image without considering spatial information
+ * @author Alexander Freytag
+ * @date 02-05-2013 (dd-mm-yyyy)
+*/
+#ifndef _NDCODEBOOKLEVELIMAGEPOOLING
+#define _NDCODEBOOKLEVELIMAGEPOOLING
+
+#include "NoveltyDetectorCodebookLevel.h"
+
+//core
+#include <core/vector/Distance.h>
+#include <core/vector/VVector.h>
+
+// vislearning
+#include <vislearning/cbaselib/MultiDataset.h>
+// 
+#include <vislearning/features/localfeatures/GenericLocalFeatureSelection.h>
+// 
+#include <vislearning/math/cluster/ClusterAlgorithm.h>
+
+
+
+
+namespace OBJREC
+{
+
+  /**
+   * @class NDCodebookLevelImagePooling
+   * @brief Compute novelty of images based on relations between extracted features and current codebook, pool novelty scores over the whole image without considering spatial information
+   * @author Alexander Freytag
+   * @date 02-05-2013 (dd-mm-yyyy)
+  */  
+  
+  class NDCodebookLevelImagePooling : public NoveltyDetectorCodebookLevel
+  {
+
+    protected:
+      
+      /************************
+       * 
+       *   protected variables
+       * 
+       **************************/       
+      
+      //TODO ENUM HOW TO POOL
+      
+      //! this is the (stupid) global threshold for novelty within an image (i.e., if novelty scores summed over all pixels and divided by image size is larger than this score, the image is considered as "novel")      
+      double d_noveltyThreshold;
+                       
+      
+      /************************
+       * 
+       *   protected methods
+       * 
+       **************************/
+    
+
+
+    public:
+
+      /** constructor
+        * @param _conf needs a configfile
+        * @param _md and a MultiDataset (contains images and other things)
+        * @param _section section information for parsing config files
+        */
+      NDCodebookLevelImagePooling ( const NICE::Config *_conf, const OBJREC::MultiDataset *_md , const std::string & _section = "noveltyDetector");
+
+      /** simple destructor */
+      virtual ~NDCodebookLevelImagePooling();
+      
+      /** 
+       * @brief  Evaluate whether or not the image of the specified filename is novel with respect to the current known examples or not
+       * @author Alexander Freytag
+       * @date 02-05-2013 (dd-mm-yyyy)
+       * @param _filename of the new image 
+       * @return bool (true: class/content/... of image is novel, false: class/content/... of image is known)
+       * @note This function has to be overloaded by all subclasses!          
+      */
+      virtual bool evaluateNoveltyOfImage ( const std::string & _filename );
+      
+      /** 
+       * @brief  Evaluate whether or not the image of the specified filename is novel with respect to the current known examples or not
+       * @author Alexander Freytag
+       * @date 02-05-2013 (dd-mm-yyyy)
+       * @param _noveltyImage contains min distances of features to the current codebook
+       * @return bool (true: class/content/... of image is novel, false: class/content/... of image is known)
+      */      
+      virtual bool evaluateNoveltyOfImage ( const NICE::FloatImage & _noveltyImage ) ;                  
+
+  };
+
+
+} // namespace
+
+#endif

+ 24 - 0
noveltyDetection/NoveltyDetector.cpp

@@ -0,0 +1,24 @@
+/**
+* @file NoveltyDetector.cpp
+* @brief abstract interface for novelty detection algorithms
+* @author Alexander Freytag
+* @date 16-04-2013 (dd-mm-yyyy)
+
+*/
+#include <iostream>
+
+#include "NoveltyDetector.h"
+
+using namespace OBJREC;
+using namespace std;
+using namespace NICE;
+
+NoveltyDetector::NoveltyDetector ( const Config *_conf, const std::string & _section )
+{
+  this->section = _section;  
+  this->conf = _conf;  
+}
+
+NoveltyDetector::~NoveltyDetector()
+{
+}

+ 84 - 0
noveltyDetection/NoveltyDetector.h

@@ -0,0 +1,84 @@
+/**
+ * @file NoveltyDetector.h
+ * @brief abstract interface for novelty detection algorithms
+ * @author Alexander Freytag
+ * @date 02-05-2013 (dd-mm-yyyy)
+*/
+#ifndef _INCLUDENOVELTYDETECTOR
+#define _INCLUDENOVELTYDETECTOR
+
+#define ROADWORKSNOVDET fthrow(NICE::Exception, "Novelty Detector -- not yet implemented!");
+
+// STL
+#include <string>
+
+//core
+#include <core/basics/Config.h>
+
+
+namespace OBJREC
+{
+
+  /**
+   * @class NoveltyDetector
+   * @brief abstract interface for novelty detection algorithms
+   * @author Alexander Freytag
+   * @date 02-05-2013 (dd-mm-yyyy)
+  */
+  class NoveltyDetector
+  {
+
+    protected:   
+      
+      /************************
+       * 
+       *   protected variables
+       * 
+       **************************/       
+      
+      //! section information for parsing config files
+      std::string section;      
+      
+      //! Configuration File
+      const NICE::Config *conf;    
+           
+      
+      /************************
+       * 
+       *   protected methods
+       * 
+       **************************/      
+            
+
+    public:
+
+      /**
+       * @brief  simple constructor
+       * @author Alexander Freytag
+       * @date 02-05-2013 (dd-mm-yyyy)
+       * @param _conf global settings
+       * @param _section section information for parsing config files
+      */
+      NoveltyDetector ( const NICE::Config *_conf, const std::string & _section = "noveltyDetector" );
+
+      /** simple destructor */
+      virtual ~NoveltyDetector();
+
+      
+      /** 
+       * @brief  Evaluate whether or not the image of the specified filename is novel with respect to the current known examples or not
+       * @author Alexander Freytag
+       * @date 02-05-2013 (dd-mm-yyyy)
+       * @param _filename of the new image 
+       * @return bool (true: class/content/... of image is novel, false: class/content/... of image is known)
+       * @note This function has to be overloaded by all subclasses!          
+      */
+      virtual bool evaluateNoveltyOfImage ( const std::string & _filename) = 0;  
+      
+
+  };
+
+
+} // namespace
+
+#endif

+ 230 - 0
noveltyDetection/NoveltyDetectorCodebookLevel.cpp

@@ -0,0 +1,230 @@
+
+#include "NoveltyDetectorCodebookLevel.h"
+
+//STL
+#include <iostream>
+
+//core
+#include <core/vector/VectorT.h>
+
+//vislearning
+#include <vislearning/features/localfeatures/LFonHSG.h>
+#include <vislearning/features/localfeatures/LFColorSande.h>
+#include <vislearning/features/localfeatures/LFColorWeijer.h>
+#include <vislearning/features/localfeatures/LFReadCache.h>
+#include <vislearning/features/localfeatures/LFWriteCache.h>
+
+using namespace std;
+using namespace NICE;
+using namespace OBJREC;
+
+  //**********************************************
+  //
+  //                 PROTECTED METHODS
+  //
+  //********************************************** 
+
+
+void NoveltyDetectorCodebookLevel::setFeatureExtractor( const bool & _setForTraining )
+{  
+  //be careful with previously allocated memory
+  if (this->featureExtractor != NULL)
+    delete featureExtractor;  
+  
+    //feature stuff
+  // which OpponentSIFT implementation to use {NICE, VANDESANDE}
+  std::string opSiftImpl;  
+  opSiftImpl = this->conf->gS ( "Descriptor", "implementation", "VANDESANDE" );
+  // read features?
+  bool readfeat;
+  readfeat = this->conf->gB ( "Descriptor", "read", true );
+  // write features?
+  bool writefeat;  
+  writefeat = this->conf->gB ( "Descriptor", "write", true );   
+  
+  // Welche Opponentsift Implementierung soll genutzt werden ?
+  LocalFeatureRepresentation *cSIFT = NULL;
+  LocalFeatureRepresentation *writeFeats = NULL;
+  LocalFeatureRepresentation *readFeats = NULL;
+  this->featureExtractor = NULL;
+  if ( opSiftImpl == "NICE" )
+  {
+    if ( _setForTraining )
+      cSIFT = new OBJREC::LFonHSG ( this->conf, "HSGtrain" );
+    else
+      cSIFT = new OBJREC::LFonHSG ( this->conf, "HSGtest" );
+  }
+  else if ( opSiftImpl == "VANDESANDE" )
+  {
+    if ( _setForTraining )
+      cSIFT = new OBJREC::LFColorSande ( this->conf, "LFColorSandeTrain" );
+    else
+      cSIFT = new OBJREC::LFColorSande ( this->conf, "LFColorSandeTest" );
+  }
+  else
+  {
+    fthrow ( Exception, "feattype: %s not yet supported" << opSiftImpl );
+  }
+
+  this->featureExtractor = cSIFT;
+  
+  if ( writefeat )
+  {
+    // write the features to a file, if there isn't any to read
+    writeFeats = new LFWriteCache ( this->conf, cSIFT );
+    this->featureExtractor = writeFeats;
+  }
+
+  if ( readfeat )
+  {
+    // read the features from a file
+    if ( writefeat )
+    {
+      readFeats = new LFReadCache ( this->conf, writeFeats, -1 );
+    }
+    else
+    {
+      readFeats = new LFReadCache ( this->conf, cSIFT, -1 );
+    }
+    this->featureExtractor = readFeats; 
+  }  
+  
+  //only set feature stuff to NULL, deletion of the underlying object is done in the destructor
+  if ( cSIFT != NULL )
+    cSIFT = NULL;
+  if ( writeFeats != NULL )
+    writeFeats = NULL;
+  if ( readFeats != NULL )
+    readFeats  = NULL ;   
+}
+
+
+
+bool NoveltyDetectorCodebookLevel::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 NoveltyDetectorCodebookLevel::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
+  //
+  //********************************************** 
+
+
+NoveltyDetectorCodebookLevel::NoveltyDetectorCodebookLevel ( const Config *_conf,
+                               const MultiDataset *_md, const std::string & _section )
+    : NoveltyDetector ( _conf, _section )
+{ 
+  // define the distance function to be used
+  std::string distFunctionString = conf->gS(section, "distFunction", "euclidian");    
+  
+  
+  //**********************************************
+  //
+  //      SET UP VARIABLES AND METHODS
+  //             - FEATURE TYPE
+  //             - DISTANCE FUNCTION
+  //             - ...
+  //
+  //**********************************************  
+  
+  
+  if (distFunctionString.compare("euclidian") == 0)
+  {
+    distFunction = new NICE::EuclidianDistance<double>();
+  }
+  else
+  {
+    std::cerr << "Unknown vector distance selected, use euclidian instead" << std::endl;
+    distFunction = new NICE::EuclidianDistance<double>();
+  }    
+
+  //feature extraction for comparison against codebook
+  this->featureExtractor = NULL;
+  //feature extraction for unseen images
+  this->setFeatureExtractor( false /* set for training */ );    
+  
+  this->prototypes = NULL;  
+}
+
+NoveltyDetectorCodebookLevel::~NoveltyDetectorCodebookLevel()
+{
+  // clean-up
+  if ( distFunction != NULL )
+    delete distFunction;
+  if ( featureExtractor != NULL )
+    delete featureExtractor; 
+  if ( ( ! externalCodebook ) && (prototypes != NULL) )
+    delete prototypes;
+}
+
+void NoveltyDetectorCodebookLevel::setCodebook( NICE::VVector * _prototypes)
+{
+  std::cerr << "aim to set codebook" << std::endl;
+  // if we had no codebook so far, we simply store the pointer and remember, that we use the external one
+  // such that we do not have to delete it lateron
+  if ( this->prototypes == NULL)
+  {
+    std::cerr << "previous codebook is null -- set it as external codebook" << std::endl;
+    this->prototypes = _prototypes;
+    this->externalCodebook = true;
+  }
+  else
+  {
+    std::cerr << "previous codebook is not null - do we want to treat the new one as external?" << std::endl;
+    //in case of an internal codebook, we need to copy the whole thing
+    if ( ! externalCodebook )
+    {
+      *(this->prototypes) = *_prototypes;
+      std::cerr << "new codebook treated as internal codebook" << std::endl;
+    }
+    //if we use a pointer to an external codebook, we simply renew the pointer
+    else
+    {
+      this->prototypes = _prototypes;
+      std::cerr << "new codebook treated as external codebook " << std::endl;
+    }
+  }
+}

+ 125 - 0
noveltyDetection/NoveltyDetectorCodebookLevel.h

@@ -0,0 +1,125 @@
+/**
+ * @file NoveltyDetectorCodebookLevel.h
+ * @brief Compute novelty of images based on relations between extracted features and current codebook
+ * @author Alexander Freytag
+ * @date 02-05-2013 (dd-mm-yyyy)
+*/
+#ifndef _INCLUDENOVELTYDETECTIONCODEBOOKLEVEL
+#define _INCLUDENOVELTYDETECTIONCODEBOOKLEVEL
+
+#include "NoveltyDetector.h"
+
+//core
+#include <core/vector/Distance.h>
+#include <core/vector/VVector.h>
+
+// vislearning
+#include <vislearning/cbaselib/MultiDataset.h>
+// 
+#include <vislearning/features/localfeatures/GenericLocalFeatureSelection.h>
+// 
+#include <vislearning/math/cluster/ClusterAlgorithm.h>
+
+
+
+
+namespace OBJREC
+{
+
+  /**
+   * @class NoveltyDetectorCodebookLevel
+   * @brief Compute novelty of images based on relations between extracted features and current codebook
+   * @author Alexander Freytag
+   * @date 02-05-2013 (dd-mm-yyyy)
+  */  
+  
+  class NoveltyDetectorCodebookLevel : public NoveltyDetector
+  {
+
+    protected:
+      
+      /************************
+       * 
+       *   protected variables
+       * 
+       **************************/       
+             
+      NICE::VectorDistance<double> * distFunction;
+
+      LocalFeatureRepresentation * featureExtractor;
+      
+      //! The currently known "cluster" centers, which are used for BoW histogram computation  (i.e., the codebook)
+      NICE::VVector * prototypes;   
+      
+      //! was an initial codebook already computed?
+      bool b_loadInitialCodebook;
+      //!shall the initially computed codebook be stored somewhere?
+      bool  b_saveInitialCodebook; 
+      //! where should an initial codebook be located, i.e., read from and written to?
+      std::string cacheInitialCodebook;  
+      
+      //! did we compute a codebook on our own or do we use simply a pointer to an external codebook?
+      bool externalCodebook;
+                       
+      
+      /************************
+       * 
+       *   protected methods
+       * 
+       **************************/
+            
+      void setFeatureExtractor( const bool & _setForTraining = false);
+      
+      void extractFeaturesFromTrainingImages(  const OBJREC::MultiDataset *_md,  NICE::VVector & examplesTraining  ); 
+      
+      virtual bool loadInitialCodebook ( );
+      virtual bool writeInitialCodebook ( );      
+
+
+    public:
+
+      /** constructor
+        * @param _conf needs a configfile
+        * @param _md and a MultiDataset (contains images and other things)
+        * @param _section section information for parsing config files
+        */
+      NoveltyDetectorCodebookLevel ( const NICE::Config *_conf, const OBJREC::MultiDataset *_md , const std::string & _section = "noveltyDetector");
+
+      /** simple destructor */
+      virtual ~NoveltyDetectorCodebookLevel();
+      
+      /** 
+       * @brief  Evaluate whether or not the image of the specified filename is novel with respect to the current known examples or not
+       * @author Alexander Freytag
+       * @date 02-05-2013 (dd-mm-yyyy)
+       * @param _filename of the new image 
+       * @return bool (true: class/content/... of image is novel, false: class/content/... of image is known)
+       * @note This function has to be overloaded by all subclasses!          
+      */
+      virtual bool evaluateNoveltyOfImage ( const std::string & _filename ) = 0;
+      
+      /** 
+       * @brief  Evaluate whether or not the image of the specified filename is novel with respect to the current known examples or not
+       * @author Alexander Freytag
+       * @date 02-05-2013 (dd-mm-yyyy)
+       * @param _noveltyImage contains min distances of features to the current codebook
+       * @return bool (true: class/content/... of image is novel, false: class/content/... of image is known)
+      */      
+      virtual bool evaluateNoveltyOfImage ( const NICE::FloatImage & _noveltyImage ) = 0;
+      
+      /** 
+       * @brief  Hand over a previously computed codebook
+       * @author Alexander Freytag
+       * @date 02-05-2013 (dd-mm-yyyy)
+       * @param _prototypes pointer to a codebook
+       * @note we check whether we just use the pointer to the external codebook, or alternatively copy it and have it as internal codebook available lateron
+      */            
+      virtual void setCodebook( NICE::VVector * _prototypes);
+            
+
+  };
+
+
+} // namespace
+
+#endif

+ 6 - 0
noveltyDetection/libdepend.inc

@@ -0,0 +1,6 @@
+$(call PKG_DEPEND_INT,core/)
+$(call PKG_DEPEND_INT,vislearning/baselib/)
+$(call PKG_DEPEND_INT,vislearning/cbaselib/)
+$(call PKG_DEPEND_INT,vislearning/features/)
+$(call PKG_DEPEND_INT,vislearning/math/)
+$(call PKG_DEPEND_INT,segmentation/)

+ 8 - 0
noveltyDetection/progs/Makefile

@@ -0,0 +1,8 @@
+#TARGETS_FROM:=$(notdir $(patsubst %/,%,$(shell pwd)))/$(TARGETS_FROM)
+#$(info recursivly going up: $(TARGETS_FROM) ($(shell pwd)))
+
+all:
+
+%:
+	$(MAKE) TARGETS_FROM=$(notdir $(patsubst %/,%,$(shell pwd)))/$(TARGETS_FROM) -C .. $@
+

+ 5 - 0
noveltyDetection/progs/libdepend.inc

@@ -0,0 +1,5 @@
+$(call PKG_DEPEND_INT,vislearning/baselib/)
+$(call PKG_DEPEND_INT,vislearning/cbaselib/)
+$(call PKG_DEPEND_INT,vislearning/features/)
+$(call PKG_DEPEND_INT,vislearning/math/)
+$(call PKG_DEPEND_INT,segmentation/)

+ 417 - 0
progs/evaluateCompleteBoWPipeline.cpp

@@ -0,0 +1,417 @@
+/**
+* @file evaluateCompleteBoWPipeline.cpp
+* @brief A complete BoW pipeline: feature extraction, codebook creation, vector quantization, classifier training, evaluation on separate test set
+* @author Alexander Freytag
+* @date 10-05-2013
+*/
+
+//STL
+#include <iostream>
+#include <limits>
+
+//core -- basic stuff
+#include <core/basics/Config.h>
+#include <core/basics/ResourceStatistics.h>
+#include <core/basics/Timer.h>
+#include <core/image/Convert.h>
+#include <core/vector/VectorT.h>
+
+//vislearning -- basic stuff
+#include <vislearning/baselib/Globals.h>
+#include <vislearning/baselib/ICETools.h>
+#include <vislearning/cbaselib/MultiDataset.h>
+#include <vislearning/cbaselib/Example.h>
+#include <vislearning/cbaselib/ClassificationResult.h>
+#include <vislearning/cbaselib/ClassificationResults.h>
+//
+// vislearning -- classifier
+#include <vislearning/classifier/classifierbase/VecClassifier.h>
+#include <vislearning/classifier/genericClassifierSelection.h>
+//
+// vislearning -- BoW codebooks
+#include "vislearning/features/simplefeatures/CodebookPrototypes.h"
+#include "vislearning/features/simplefeatures/BoWFeatureConverter.h"
+// 
+// vislearning -- local features
+#include <vislearning/features/localfeatures/LFonHSG.h>
+#include <vislearning/features/localfeatures/LFColorSande.h>
+#include <vislearning/features/localfeatures/LFColorWeijer.h>
+#include <vislearning/features/localfeatures/LFReadCache.h>
+#include <vislearning/features/localfeatures/LFWriteCache.h>
+#include <vislearning/features/localfeatures/GenericLFSelection.h>
+//
+// vislearning -- clustering methods
+#include <vislearning/math/cluster/ClusterAlgorithm.h>
+#include "vislearning/math/cluster/RandomClustering.h"
+#include <vislearning/math/cluster/KMeans.h>
+#include <vislearning/math/cluster/KMedian.h>
+#include <vislearning/math/cluster/GMM.h>
+//
+
+using namespace std;
+using namespace NICE;
+using namespace OBJREC;
+
+
+LocalFeatureRepresentation * setFeatureExtractor( const Config * _conf )
+{  
+  LocalFeatureRepresentation * featureExtractor;
+   
+    //feature stuff
+  // which OpponentSIFT implementation to use {NICE, VANDESANDE}
+  std::string opSiftImpl;  
+  opSiftImpl = _conf->gS ( "Descriptor", "implementation", "VANDESANDE" );
+  // read features?
+  bool readfeat;
+  readfeat = _conf->gB ( "Descriptor", "read", true );
+  // write features?
+  bool writefeat;  
+  writefeat = _conf->gB ( "Descriptor", "write", true );   
+  
+  // Welche Opponentsift Implementierung soll genutzt werden ?
+  LocalFeatureRepresentation *cSIFT = NULL;
+  LocalFeatureRepresentation *writeFeats = NULL;
+  LocalFeatureRepresentation *readFeats = NULL;
+  featureExtractor = NULL;
+  if ( opSiftImpl == "NICE" )
+  {
+     cSIFT = new OBJREC::LFonHSG ( _conf, "HSG" );
+  }
+  else if ( opSiftImpl == "VANDESANDE" )
+  {
+    cSIFT = new OBJREC::LFColorSande ( _conf, "LFColorSande" );
+  }
+  else
+  {
+    fthrow ( Exception, "feattype: %s not yet supported" << opSiftImpl );
+  }
+
+  featureExtractor = cSIFT;
+  
+  if ( writefeat )
+  {
+    // write the features to a file, if there isn't any to read
+    writeFeats = new LFWriteCache ( _conf, cSIFT );
+    featureExtractor = writeFeats;
+  }
+
+  if ( readfeat )
+  {
+    // read the features from a file
+    if ( writefeat )
+    {
+      readFeats = new LFReadCache ( _conf, writeFeats, -1 );
+    }
+    else
+    {
+      readFeats = new LFReadCache ( _conf, cSIFT, -1 );
+    }
+    featureExtractor = readFeats; 
+  }  
+  
+  //only set feature stuff to NULL, deletion of the underlying object is done in the destructor
+  if ( cSIFT != NULL )
+    cSIFT = NULL;
+  if ( writeFeats != NULL )
+    writeFeats = NULL;
+  if ( readFeats != NULL )
+    readFeats  = NULL ;   
+  
+  return featureExtractor;
+}
+
+OBJREC::ClusterAlgorithm * setClusterAlgo( const Config * _conf )
+{ 
+ std::string section ( "clusteringStuff" );
+  // define the initial number of clusters our codebook shall contain
+  int noClusters = _conf->gI(section, "noClusters", 10);
+  
+  // define the clustering algorithm to be used
+  std::string clusterAlgoString = _conf->gS(section, "clusterAlgo", "kmeans");  
+  
+  OBJREC::ClusterAlgorithm * clusterAlgo;
+  
+  if (clusterAlgoString.compare("kmeans") == 0)
+  {
+    clusterAlgo = new OBJREC::KMeans(noClusters);
+  }
+  else if (clusterAlgoString.compare("kmedian") == 0)
+  {
+    clusterAlgo = new OBJREC::KMedian(noClusters);
+  }  
+  else if (clusterAlgoString.compare("GMM") == 0) 
+  {
+    clusterAlgo = new OBJREC::GMM( _conf, noClusters );
+  }
+  else if ( clusterAlgoString.compare("RandomClustering") == 0 )   
+  {
+    clusterAlgo = new OBJREC::RandomClustering( _conf, section );
+  }
+  else
+  {
+    std::cerr << "Unknown cluster algorithm selected, use random clustering instead" << std::endl;
+    clusterAlgo = new OBJREC::RandomClustering( _conf, section );
+  }    
+  
+  return clusterAlgo;
+}
+
+
+/**
+ a complete BoW pipeline
+ 
+ possibly, we can make use of objrec/progs/testClassifier.cpp
+*/
+int main( int argc, char **argv )
+{
+  std::set_terminate( __gnu_cxx::__verbose_terminate_handler );
+
+  Config * conf = new Config ( argc, argv );
+  
+  const bool writeClassificationResults = conf->gB( "main", "writeClassificationResults", true );
+  const std::string resultsfile = conf->gS( "main", "resultsfile", "/tmp/results.txt" );
+  
+  ResourceStatistics rs;
+  
+  // ========================================================================
+  //                            TRAINING STEP
+  // ========================================================================
+
+  MultiDataset md( conf );
+  const LabeledSet *trainFiles = md["train"];  
+   
+  //**********************************************
+  //
+  //     FEATURE EXTRACTION FOR TRAINING IMAGES
+  //
+  //**********************************************  
+  
+  std::cerr << "FEATURE EXTRACTION FOR TRAINING IMAGES" << std::endl;
+  
+  OBJREC::LocalFeatureRepresentation * featureExtractor = OBJREC::GenericLFSelection::selectLocalFeatureRep ( conf, "features", OBJREC::GenericLFSelection::TRAINING );
+//   LocalFeatureRepresentation * featureExtractor = setFeatureExtractor( conf );
+  
+  //collect features in a single data structure
+  NICE::VVector featuresFromAllTrainingImages;  
+  featuresFromAllTrainingImages.clear();
+  
+  //determine how many training images we actually use to easily allocate the correct amount of memory afterwards
+  int numberOfTrainingImages ( 0 );
+  for(LabeledSet::const_iterator classIt = trainFiles->begin() ; classIt != trainFiles->end() ; classIt++)
+  {
+    numberOfTrainingImages += classIt->second.size();
+    std::cerr << "number of examples for this class: " << classIt->second.size() << std::endl;
+  }
+  
+  
+  //okay, this is redundant - but I see no way to do it more easy right now...
+  std::vector<NICE::VVector> featuresOfImages ( numberOfTrainingImages );
+  //this again is somehow redundant, but we need the labels lateron for easy access - change this to a better solution :)
+  NICE::VectorT<int> labelsTrain ( numberOfTrainingImages, 0 );
+  
+  //TODO replace the nasty makro by a suitable for-loop to make it omp-ready (parallelization)
+  int imgCnt ( 0 );
+   
+  // the corresponding nasty makro: LOOP_ALL_S( *trainFiles )
+  for(LabeledSet::const_iterator classIt = trainFiles->begin() ; classIt != trainFiles->end() ; classIt++)
+  {
+    for ( std::vector<ImageInfo *>::const_iterator imgIt = classIt->second.begin(); 
+          imgIt != classIt->second.end(); 
+          imgIt++, imgCnt++
+        ) 
+    {
+      // the corresponding nasty makro: EACH_INFO( classno, info );
+      int classno ( classIt->first );
+      const ImageInfo imgInfo = *(*imgIt);      
+      
+      std::string filename = imgInfo.img();      
+      
+      NICE::ColorImage img( filename );
+  
+      //compute features
+      
+      //variables to store feature information
+      NICE::VVector features;
+      NICE::VVector positions;
+  
+      Globals::setCurrentImgFN ( filename );
+      featureExtractor->extractFeatures ( img, features, positions );  
+              
+      //normalization :)
+      for ( NICE::VVector::iterator i = features.begin();
+            i != features.end();
+            i++)
+      {              
+        i->normalizeL1();
+      }  
+            
+      //collect them all in a larger data structure
+      featuresFromAllTrainingImages.append( features );
+      
+      //and store it as well in the data struct that additionally keeps the information which features belong to which image
+      //TODO this can be made more clever!
+//       featuresOfImages.push_back( features );
+      featuresOfImages[imgCnt] = features;
+      labelsTrain[imgCnt] = classno;
+    }
+  }
+  
+  
+  
+  //**********************************************
+  //
+  //             CODEBOOK CREATION
+  //
+  //**********************************************    
+  
+  std::cerr << "CODEBOOK CREATION" << std::endl;
+  OBJREC::ClusterAlgorithm * clusterAlgo = setClusterAlgo( conf );
+   
+  NICE::VVector prototypes;
+  
+  std::vector<double> weights;
+  std::vector<int> assignments;
+  
+  std::cerr << "call cluster of cluster algo " << std::endl;
+  clusterAlgo->cluster( featuresFromAllTrainingImages, prototypes, weights, assignments );
+  
+  std::cerr << "create new codebook with the computed prototypes" << std::endl;
+  OBJREC::CodebookPrototypes * codebook = new OBJREC::CodebookPrototypes ( prototypes );
+  
+  
+  //**********************************************
+  //
+  //             VECTOR QUANTIZATION OF
+  //           FEATURES OF TRAINING IMAGES
+  //
+  //**********************************************  
+  
+  OBJREC::BoWFeatureConverter * bowConverter = new OBJREC::BoWFeatureConverter ( conf, codebook );  
+  
+  OBJREC::LabeledSetVector trainSet;
+  
+  NICE::VVector histograms ( featuresOfImages.size() /* number of vectors*/, 0 /* dimension of vectors*/ ); //the internal vectors will be resized within calcHistogram
+  NICE::VVector::iterator histogramIt = histograms.begin();
+  NICE::VectorT<int>::const_iterator labelsIt = labelsTrain.begin();
+  
+  for (std::vector<NICE::VVector>::const_iterator imgIt = featuresOfImages.begin(); imgIt != featuresOfImages.end(); imgIt++, histogramIt++, labelsIt++)
+  {
+    bowConverter->calcHistogram ( *imgIt, *histogramIt );
+    bowConverter->normalizeHistogram ( *histogramIt );
+    
+    //NOTE perhaps we should use add_reference here
+    trainSet.add( *labelsIt, *histogramIt );
+  }
+  
+  
+  //**********************************************
+  //
+  //             CLASSIFIER TRAINING
+  //
+  //**********************************************  
+  
+  std::string classifierType = conf->gS( "main", "classifierType", "GPHIK" );
+  OBJREC::VecClassifier * classifier = OBJREC::GenericClassifierSelection::selectVecClassifier( conf, classifierType );
+  
+  //TODO integrate GP-HIK-NICE into vislearning and add it into genericClassifierSelection
+  
+  //this method adds the training data to the temporary knowledge of our classifier
+  classifier->teach( trainSet );
+  //now the actual training step starts (e.g., parameter estimation, ... )
+  classifier->finishTeaching();
+  
+  
+  // ========================================================================
+  //                            TEST STEP
+  // ========================================================================
+  
+  const LabeledSet *testFiles = md["test"];
+
+  delete featureExtractor;
+  featureExtractor = OBJREC::GenericLFSelection::selectLocalFeatureRep ( conf, "features", OBJREC::GenericLFSelection::TESTING );  
+  
+  NICE::Matrix confusionMat ( trainFiles->size() /* number of classes in training */, testFiles->size() /* number of classes for testing*/, 0.0 );
+  NICE::Timer t;
+  
+  ClassificationResults results;
+  
+  LOOP_ALL_S( *testFiles )
+  {
+      EACH_INFO( classno, info );
+      std::string filename = info.img();
+  
+      //**********************************************
+      //
+      //     FEATURE EXTRACTION FOR TEST IMAGES
+      //
+      //**********************************************  
+      
+      NICE::ColorImage img( filename );
+  
+      //compute features
+      
+      //variables to store feature information
+      NICE::VVector features;
+      NICE::VVector positions;
+  
+      Globals::setCurrentImgFN ( filename );
+      featureExtractor->extractFeatures ( img, features, positions );  
+        
+      //normalization :)
+      for ( NICE::VVector::iterator i = features.begin();
+            i != features.end();
+            i++)
+      {              
+        i->normalizeL1();
+      }        
+      
+      //**********************************************
+      //
+      //             VECTOR QUANTIZATION OF
+      //           FEATURES OF TEST IMAGES
+      //
+      //********************************************** 
+      
+      NICE::Vector histogramOfCurrentImg;
+      bowConverter->calcHistogram ( features, histogramOfCurrentImg );
+      bowConverter->normalizeHistogram ( histogramOfCurrentImg );
+      
+      //**********************************************
+      //
+      //             CLASSIFIER EVALUATION
+      //
+      //**********************************************   
+            
+      uint classno_groundtruth = classno;
+      
+      t.start();
+      ClassificationResult r = classifier->classify ( histogramOfCurrentImg );
+      t.stop();
+      uint classno_estimated = r.classno;
+      r.classno_groundtruth = classno_groundtruth;
+
+      //if we like to store the classification results for external post processing, uncomment this
+      if ( writeClassificationResults )
+      {
+        results.push_back( r );
+      }      
+      
+      confusionMat( classno_estimated, classno_groundtruth ) += 1;
+  }
+  
+  confusionMat.normalizeColumnsL1();
+  std::cerr << confusionMat << std::endl;
+
+  std::cerr << "average recognition rate: " << confusionMat.trace()/confusionMat.rows() << std::endl;  
+  
+  if ( writeClassificationResults )
+  {
+    double avgRecogResults  ( results.getAverageRecognitionRate () );    
+    std::cerr << "average recognition rate according to classificationResults: " << avgRecogResults << std::endl;
+    results.writeWEKA ( resultsfile, 0 );
+  }     
+  
+ 
+  
+   return 0;
+}