Przeglądaj źródła

Clustering - added GenericSelection, improved constructors for config support of derived classes, improved commentations and structure

Alexander Freytag 12 lat temu
rodzic
commit
2546beb89d

+ 20 - 20
math/cluster/ClusterAlgorithm.h

@@ -16,27 +16,27 @@
 
 namespace OBJREC {
 
-/** Interface for Cluster-Algorithms */
-class ClusterAlgorithm
-{
-
-    protected:
-
-    public:
-  
-	/** simple constructor */
-	ClusterAlgorithm();
+  /** Interface for Cluster-Algorithms */
+  class ClusterAlgorithm
+  {
+
+      protected:
+
+      public:
+    
+    /** simple constructor */
+    ClusterAlgorithm();
+        
+    /** simple destructor */
+    virtual ~ClusterAlgorithm();
+
+    virtual void cluster ( 
+            const NICE::VVector & features,
+            NICE::VVector & prototypes,
+            std::vector<double> & weights,
+            std::vector<int>    & assignment ) = 0;
       
-	/** simple destructor */
-	virtual ~ClusterAlgorithm();
-
-	virtual void cluster ( 
-		       const NICE::VVector & features,
-		       NICE::VVector & prototypes,
-		       std::vector<double> & weights,
-		       std::vector<int>    & assignment ) = 0;
-     
-};
+  };
 
 
 } // namespace

+ 15 - 13
math/cluster/GSCluster.cpp

@@ -23,23 +23,25 @@ using namespace NICE;
 
 
 
-GSCluster::GSCluster( const Config *conf ) : GenerateSignature ( conf )
+GSCluster::GSCluster( const Config *conf, const std::string & _section ) : GenerateSignature ( conf )
 {
-    c_no_clusters = conf->gI("GSCluster", "clusters", 20);
-    // refactor-nice.pl: check this substitution
-    // old: string cluster_method = conf->gS("GSCluster", "cluster_method", "kmeans");
     std::string cluster_method = conf->gS("GSCluster", "cluster_method", "kmeans");
     if ( cluster_method == "kmeans" )
     {
-	clualg = new KMeans ( c_no_clusters );
-    } else if ( cluster_method == "spectral" ) {
-	double alpha = conf->gD("GSCluster", "spectral_alpha", 1.0);
-	clualg = new SpectralCluster ( c_no_clusters, alpha );
-    } else if ( cluster_method == "kmeans_matlab" ) {
-	clualg = new KMeansMatlab ( conf, c_no_clusters );
-    } else {
-	fprintf (stderr, "FATAL ERROR GSCluster: cluster method %s unknown !\n", cluster_method.c_str());
-	exit(-1);
+      clualg = new OBJREC::KMeans ( conf );
+    }
+    else if ( cluster_method == "spectral" )
+    {
+      clualg = new SpectralCluster ( conf );
+    }
+    else if ( cluster_method == "kmeans_matlab" )
+    {
+      clualg = new KMeansMatlab ( conf );
+    }
+    else
+    {
+      fprintf (stderr, "FATAL ERROR GSCluster: cluster method %s unknown !\n", cluster_method.c_str());
+      exit(-1);
     }
 }
 

+ 25 - 16
math/cluster/GSCluster.h

@@ -17,26 +17,35 @@
 
 namespace OBJREC {
 
-/** Generate Signature by Clustering Local Features */
-class GSCluster : public GenerateSignature
-{
+    /** 
+     * @class GSCluster
+     * @brief Generate Signature by Clustering Local Features (image-specific codebook!).
+     * The class can be used for the EMD kernel, but not for usual BoV approaches.
+     * @author Erik Rodner
+     */
+    class GSCluster : public GenerateSignature
+    {
 
-    protected:
-	int c_no_clusters;
+        protected:
+          int c_no_clusters;
 
-	ClusterAlgorithm *clualg;
+          ClusterAlgorithm *clualg;
 
-    public:
-  
-	/** simple constructor */
-	GSCluster( const NICE::Config *conf );
+        public:
       
-	/** simple destructor */
-	virtual ~GSCluster();
-     
-	void signature ( const NICE::VVector & features,
-			 NICE::Vector & signature );
-};
+          /** standard  constructor
+           * @param conf config file specifying all relevant variable settings.  You should specify in "section" the string "cluster_method"
+           * @param _section name of the section within the configfile where the settings can be found. Default: GSCluster
+           * @param[in] 
+           */
+          GSCluster( const NICE::Config *conf, const std::string & _section = "GSCluster" );
+              
+          /** simple destructor */
+          virtual ~GSCluster();
+            
+          void signature ( const NICE::VVector & features,
+              NICE::Vector & signature );
+    };
 
 
 } // namespace

+ 94 - 0
math/cluster/GenericClusterAlgorithmSelection.h

@@ -0,0 +1,94 @@
+/** 
+* @file GenericClusterAlgorithmSelection.h
+* @brief This class provides a generic chooser function for different clustering techniques
+* @date 14-06-2013 (dd-mm-yyyy)
+* @author Alexander Freytag
+*/
+
+#ifndef _NICE_GENERICCLUSTERALGORITHMSELECTION_INCLUDE
+#define _NICE_GENERICCLUSTERALGORITHMSELECTION_INCLUDE
+
+#include <iostream>
+
+//abstract base class
+#include "vislearning/math/cluster/ClusterAlgorithm.h"
+//derived specializations
+#include "vislearning/math/cluster/GMM.h"
+#include "vislearning/math/cluster/KMeans.h"
+#include "vislearning/math/cluster/KMeansHeuristic.h"
+#include "vislearning/math/cluster/KMeansMatlab.h"
+#include "vislearning/math/cluster/KMedian.h"
+#include "vislearning/math/cluster/RandomClustering.h"
+#include "vislearning/math/cluster/SpectralCluster.h"
+
+namespace OBJREC {
+
+  /** @class GenericClusterAlgorithmSelection
+  * @brief This class provides a generic chooser function for different clustering techniques
+  * The abstract base class is ClusterAlgorithm
+  * @date 14-06-2013 (dd-mm-yyyy)
+  * @author Alexander Freytag
+  */
+  class GenericClusterAlgorithmSelection
+  {
+      public:
+
+        /** 
+        * @brief This methode switches between the different clustering techniques. 
+        * @param[in] conf - A pointer to the given configfile, which should contain "section" - "clusterTechnique"
+        * @param[in] section - This string defines the value for "section" in the configfile.
+        * @return ClusterAlgorithm* - The ClusterAlgorithm to cluster samples according to the selected clustering technique.
+        * @date 14-06-2013 (dd-mm-yyyy)
+        * @author Alexander Freytag
+        */
+        static
+        OBJREC::ClusterAlgorithm *selectClusterAlgo ( const NICE::Config *conf, std::string section = "clustering" )
+        {
+          // return value
+          OBJREC::ClusterAlgorithm *clusterAlgo = NULL;
+        
+          // string which defines the clustering technique
+          std::string clusterTechnique = conf->gS(section, "clusterTechnique", "");
+        
+          if ( clusterTechnique == "kmeans" )
+          {
+            clusterAlgo = new OBJREC::KMeans ( conf );
+          }        
+          else if ( clusterTechnique == "kmeansHeuristic" )
+          {
+            clusterAlgo = new OBJREC::KMeansHeuristic ( conf );
+          }   
+          else if ( clusterTechnique == "kmeansMatlab" )
+          {
+            clusterAlgo = new OBJREC::KMeansMatlab ( conf );
+          }            
+          else if ( clusterTechnique == "kmedian" )
+          {
+            clusterAlgo = new OBJREC::KMedian ( conf );
+          }     
+          else if ( clusterTechnique == "GMM" )
+          {
+            clusterAlgo = new OBJREC::GMM ( conf );
+          } 
+          else if ( clusterTechnique == "spectral" )
+          {
+            clusterAlgo = new OBJREC::SpectralCluster ( conf );
+          }           
+          else if ( clusterTechnique == "random" )
+          {
+            clusterAlgo = new OBJREC::RandomClustering ( conf );
+          }            
+          else 
+          {
+            //default: random clustering - it is easy, fast, does not need extra memory, and is still better than a NULL pointer
+            std::cerr << "Unknown cluster algorithm selected, use random clustering instead" << std::endl;
+            clusterAlgo = new OBJREC::RandomClustering ( conf );
+          }
+            
+          return clusterAlgo;
+        }
+  };
+
+}
+
+#endif

+ 17 - 9
math/cluster/KMeansHeuristic.cpp

@@ -18,11 +18,19 @@ using namespace NICE;
 
 #undef DEBUG_KMeansHeuristic
 
-KMeansHeuristic::KMeansHeuristic(int _noClasses, string _distanceType) :
-	noClasses(_noClasses), distanceType(_distanceType)
+KMeansHeuristic::KMeansHeuristic(int _noClusters, string _distanceType) :
+	noClusters(_noClusters), distanceType(_distanceType)
 {
-	//srand(time(NULL));
-	distancefunction = GenericDistanceSelection::selectDistance(distanceType);
+  //srand(time(NULL));
+  distancefunction = GenericDistanceSelection::selectDistance(distanceType);
+}
+
+KMeansHeuristic::KMeansHeuristic( const NICE::Config *conf, const std::string & _section)
+{       
+  this->distanceType = conf->gS( _section, "distanceType", "euclidean" );
+  this->distancefunction = GenericDistanceSelection::selectDistance(distanceType);
+  
+  this->noClusters = conf->gI( _section, "noClusters", 20);
 }
 
 KMeansHeuristic::~KMeansHeuristic()
@@ -200,7 +208,7 @@ double KMeansHeuristic::compute_assignments(const VVector & features,
 double KMeansHeuristic::compute_weights(const VVector & features, std::vector<
 		double> & weights, std::vector<int> & assignment)
 {
-	for (int k = 0; k < noClasses; k++)
+	for (int k = 0; k < noClusters; k++)
 		weights[k] = 0;
 
 	int j = 0;
@@ -211,7 +219,7 @@ double KMeansHeuristic::compute_weights(const VVector & features, std::vector<
 		weights[k]++;
 	}
 
-	for (int k = 0; k < noClasses; k++)
+	for (int k = 0; k < noClusters; k++)
 		weights[k] = weights[k] / features.size();
 
 	return 0.0;
@@ -225,12 +233,12 @@ void KMeansHeuristic::cluster(const VVector & features, VVector & prototypes,
 	prototypes.clear();
 	weights.clear();
 	assignment.clear();
-	weights.resize(noClasses, 0);
+	weights.resize(noClusters, 0);
 	assignment.resize(features.size(), 0);
 
 	int dimension;
 
-	if ((int) features.size() >= noClasses)
+	if ((int) features.size() >= noClusters)
 		dimension = features[0].size();
 	else
 	{
@@ -239,7 +247,7 @@ void KMeansHeuristic::cluster(const VVector & features, VVector & prototypes,
 		exit(-1);
 	}
 
-	for (int k = 0; k < noClasses; k++)
+	for (int k = 0; k < noClusters; k++)
 	{
 		prototypes.push_back(Vector(dimension));
 		prototypes[k].set(0);

+ 37 - 26
math/cluster/KMeansHeuristic.h

@@ -8,50 +8,61 @@
 #ifndef KMeansHeuristicINCLUDE
 #define KMeansHeuristicINCLUDE
 
+#include <core/basics/Config.h>
 #include <core/vector/Distance.h>
-#include "ClusterAlgorithm.h"
-
 #include "core/vector/VectorT.h"
 #include "core/vector/MatrixT.h"
 
+#include "ClusterAlgorithm.h"
+
 
 namespace OBJREC
 {
 
-/** K-Means */
-class KMeansHeuristic: public ClusterAlgorithm
-{
+  /** K-Means (but what does Heuristic actually mean? )*/
+  class KMeansHeuristic: public ClusterAlgorithm
+  {
 
-protected:
-	int noClasses;
-	std::string distanceType;
-	NICE::VectorDistance<double> *distancefunction;
-	double compute_assignments(const NICE::VVector & features,
-			const NICE::VVector & prototypes, std::vector<int> & assignment);
+  protected:
+    int noClusters;
+    std::string distanceType;
+    NICE::VectorDistance<double> *distancefunction;
+    
+    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);
+    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);
+    double compute_delta(const NICE::VVector & oldprototypes,
+        const NICE::VVector & prototypes);
 
 
-	void initial_guess(const NICE::VVector & features, NICE::VVector & prototypes);
+    void initial_guess(const NICE::VVector & features, NICE::VVector & prototypes);
 
-	void print_iteration(int iterations, NICE::VVector & prototypes, double delta);
-	int robust_prototypes(const NICE::VVector &features, NICE::VVector &prototypes, std::vector<
-			double> & weights, const std::vector<int> & assignment);
-public:
+    void print_iteration(int iterations, NICE::VVector & prototypes, double delta);
+    int robust_prototypes(const NICE::VVector &features, NICE::VVector &prototypes, std::vector<
+        double> & weights, const std::vector<int> & assignment);
+  public:
 
-	/** simple constructor */
-	KMeansHeuristic(int noClasses, std::string distanceMode = "euclidean");
+    /** simple constructor */
+    KMeansHeuristic(int noClusters, 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: KMeansHeuristic)
+    * @date 14-06-2013 (dd-mm-yyyy)
+    * @author Alexander Freytag
+    */
+    KMeansHeuristic( const NICE::Config *conf, const std::string & _section = "KMeansHeuristic");    
 
-	/** simple destructor */
-	virtual ~KMeansHeuristic();
+    /** simple destructor */
+    virtual ~KMeansHeuristic();
 
-	void cluster(const NICE::VVector & features, NICE::VVector & prototypes, std::vector<double> & weights, std::vector<int> & assignment);
+    void cluster(const NICE::VVector & features, NICE::VVector & prototypes, std::vector<double> & weights, std::vector<int> & assignment);
 
-};
+  };
 
 } // namespace
 

+ 18 - 18
math/cluster/KMeansMatlab.cpp

@@ -19,23 +19,23 @@ using namespace NICE;
 
 #undef DEBUG_KMeansMatlab
 
-KMeansMatlab::KMeansMatlab( const Config *conf, 
-			    int _noClasses ) : noClasses(_noClasses)
+KMeansMatlab::KMeansMatlab( const Config *conf, const std::string & _section )
 {
-    kmeansDir = conf->gS("matlab", "source_root", "/home/rodner/osl/labor/cvs/osl/") + "/kernel/";
-    inputFN   = conf->gS("KMeansMatlab", "tmpInput", "/tmp/KMeansMatlab.input" );
-    outputFN   = conf->gS("KMeansMatlab", "tmpoutput", "/tmp/KMeansMatlab.output" );
-
-    matlabExec = conf->gS("matlab", "matlab_exec", "matlab");
-    matlabArgs = conf->gS("matlab", "matlab_args", "-nosplash -nojvm -nodesktop");
-    
+  this->noClusters = conf->gI( _section, "noClusters", 20);
+  
+    kmeansDir = conf->gS(_section, "source_root", "/home/rodner/osl/labor/cvs/osl/") + "/kernel/";
+    inputFN   = conf->gS(_section, "tmpInput", "/tmp/KMeansMatlab.input" );
+    outputFN   = conf->gS(_section, "tmpOutput", "/tmp/KMeansMatlab.output" );
+
+    matlabExec = conf->gS(_section, "matlab_exec", "matlab");
+    matlabArgs = conf->gS(_section, "matlab_args", "-nosplash -nojvm -nodesktop");   
     
 }
 
 KMeansMatlab::~KMeansMatlab()
 {
-    if ( matlabPipe != NULL )
-	pclose (matlabPipe);
+  if ( matlabPipe != NULL )
+    pclose (matlabPipe);
 }
 
 
@@ -45,8 +45,8 @@ int KMeansMatlab::compute_prototypes ( const VVector & features,
 				  const std::vector<int>    & assignment )
 {
     int j = 0;
-    // fprintf (stderr, "KMeansMatlab::compute_prototypes: init noClasses=%d\n", noClasses);
-    for ( int k = 0 ; k < noClasses ; k++ )
+    // fprintf (stderr, "KMeansMatlab::compute_prototypes: init noClusters=%d\n", noClusters);
+    for ( int k = 0 ; k < noClusters ; k++ )
     {
 	prototypes[k].set(0);
 	weights[k] = 0;
@@ -81,7 +81,7 @@ int KMeansMatlab::compute_prototypes ( const VVector & features,
     }
 
     // fprintf (stderr, "KMeansMatlab::compute_prototypes: scaling\n");
-    for ( int k = 0 ; k < noClasses ; k++ )
+    for ( int k = 0 ; k < noClusters ; k++ )
     {
 	// refactor-nice.pl: check this substitution
 	// old: Vector & p = prototypes[k];
@@ -117,7 +117,7 @@ void KMeansMatlab::cluster ( const VVector & features,
     prototypes.clear();
     weights.clear();
     assignment.clear ();
-    weights.resize ( noClasses, 0 );
+    weights.resize ( noClusters, 0 );
     assignment.resize ( features.size(), 0 );
 
     // ----------- routine argument -------------
@@ -127,7 +127,7 @@ void KMeansMatlab::cluster ( const VVector & features,
 
     int dimension;
 
-    if ( (int)features.size() >= noClasses )
+    if ( (int)features.size() >= noClusters )
 	dimension = features[0].size();
     else {
 	fprintf (stderr, "FATAL ERROR: Not enough feature vectors provided for KMeansMatlab\n");
@@ -140,7 +140,7 @@ void KMeansMatlab::cluster ( const VVector & features,
 	fprintf (stderr, "KMeansMatlab: FATAL ERROR cannot write features!\n");
 	exit(-1);
     }
-    fwrite (&noClasses, sizeof(int), 1, fi );
+    fwrite (&noClusters, sizeof(int), 1, fi );
     int n = features.size();
     fwrite (&n, sizeof(int), 1, fi );
     int d = features[0].size();
@@ -191,7 +191,7 @@ void KMeansMatlab::cluster ( const VVector & features,
     fclose(g);
 
 
-    for ( int k = 0 ; k < noClasses ; k++ )
+    for ( int k = 0 ; k < noClusters ; k++ )
     {
 	// fprintf (stderr, "KMeansMatlab::cluster prototype init constructor\n");
 	prototypes.push_back( Vector( dimension ) );

+ 55 - 46
math/cluster/KMeansMatlab.h

@@ -1,6 +1,6 @@
 /** 
 * @file KMeansMatlab.h
-* @brief K-Means
+* @brief K-Means using a matlab implementation
 * @author Erik Rodner
 * @date 10/29/2007
 
@@ -17,51 +17,60 @@
 
 namespace OBJREC {
 
-/** K-Means */
-class KMeansMatlab : public ClusterAlgorithm
-{
-
-    protected:
-	int noClasses;
-	// refactor-nice.pl: check this substitution
-	// old: string kmeansDir;
-	std::string kmeansDir;
-	// refactor-nice.pl: check this substitution
-	// old: string matlabExec;
-	std::string matlabExec;
-	// refactor-nice.pl: check this substitution
-	// old: string matlabArgs;
-	std::string matlabArgs;
-
-	// refactor-nice.pl: check this substitution
-	// old: string inputFN;
-	std::string inputFN;
-	// refactor-nice.pl: check this substitution
-	// old: string outputFN;
-	std::string outputFN;
-
-	FILE *matlabPipe;
-	
-	int compute_prototypes ( const NICE::VVector & features,
-				  NICE::VVector & prototypes,
-				  std::vector<double> & weights,
-				  const std::vector<int>    & assignment );
-
-
-    public:
-  
-	/** simple constructor */
-	KMeansMatlab( const NICE::Config *conf, int noClasses );
-      
-	/** simple destructor */
-	virtual ~KMeansMatlab();
-     
-	void cluster ( const NICE::VVector & features,
-		       NICE::VVector & prototypes,
-		       std::vector<double> & weights,
-		       std::vector<int>    & assignment );
-
-};
+  /** 
+  * @class KMeansMatlab
+  * @brief K-Means using a matlab implementation
+  * @author Erik Rodner
+  */
+
+  class KMeansMatlab : public ClusterAlgorithm
+  {
+
+      protected:
+        int noClusters;
+        // refactor-nice.pl: check this substitution
+        // old: string kmeansDir;
+        std::string kmeansDir;
+        // refactor-nice.pl: check this substitution
+        // old: string matlabExec;
+        std::string matlabExec;
+        // refactor-nice.pl: check this substitution
+        // old: string matlabArgs;
+        std::string matlabArgs;
+
+        // refactor-nice.pl: check this substitution
+        // old: string inputFN;
+        std::string inputFN;
+        // refactor-nice.pl: check this substitution
+        // old: string outputFN;
+        std::string outputFN;
+
+        FILE *matlabPipe;
+        
+        int compute_prototypes ( const NICE::VVector & features,
+                NICE::VVector & prototypes,
+                std::vector<double> & weights,
+                const std::vector<int>    & assignment );
+
+
+      public:
+    
+        /** 
+        * @brief simple constructor
+        * @author Erik Rodner, Alexander Freytag
+        * Among others, you can specify for "section" the following attributes: "source_root", "tmpInput", "tmpOutput", "matlab_exec", "matlab_args"
+        */
+        KMeansMatlab( const NICE::Config *conf,  const std::string & _section = "KMeansMatlab" );
+            
+        /** simple destructor */
+        virtual ~KMeansMatlab();
+          
+        void cluster ( const NICE::VVector & features,
+                NICE::VVector & prototypes,
+                std::vector<double> & weights,
+                std::vector<int>    & assignment );
+
+  };
 
 
 } // namespace

+ 4 - 4
math/cluster/RandomClustering.h

@@ -71,16 +71,16 @@ namespace OBJREC {
     
         /**
         * @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)* 
+        * @param[in] _noClasses the number of clusters to be computed
+        * @param[in] _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)
+        * @param[in] conf config file specifying all relevant variable settings
+        * @param[in] _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");

+ 118 - 93
math/cluster/SpectralCluster.cpp

@@ -18,18 +18,24 @@ using namespace OBJREC;
 using namespace std;
 using namespace NICE;
 
-SpectralCluster::SpectralCluster ( int _noClasses, double alpha ) : noClasses(_noClasses), kmeans(_noClasses)
+SpectralCluster::SpectralCluster ( int _noClusters, double alpha ) : noClusters(_noClusters), kmeans(_noClusters)
 {
     this->alpha = alpha;
 }
 
+SpectralCluster::SpectralCluster( const NICE::Config *conf, const std::string & _section) : kmeans(conf)
+{  
+  this->noClusters = conf->gI( _section, "noClusters", 20);
+  this->alpha = conf->gD( _section, "alpha", 1.0);
+}
+
 SpectralCluster::~SpectralCluster()
 {
 }
 
 void SpectralCluster::getSimilarityMatrix ( const VVector & features, 
-					    NICE::Matrix & laplacian,
-					    double alpha )
+                                            NICE::Matrix & laplacian,
+                                            double alpha )
 {
     NICE::Matrix distances ( laplacian );
     double mindist = numeric_limits<double>::max();
@@ -41,48 +47,53 @@ void SpectralCluster::getSimilarityMatrix ( const VVector & features,
 
     for ( int i = 0 ; i < (int)features.size() ; i++ )
     {
-	const NICE::Vector & xi = features[i];
-	for ( int j = i ; j < (int)features.size() ; j++ )
-	{
-	    const NICE::Vector & xj = features[j];
-	    // double sim = xi * xj;
-	    double dist = distance.calculate ( xi, xj );
-	    distances(i, j) = dist;
-
-	    if ( dist < mindist )
-		mindist = dist;
-	    if ( dist > maxdist )
-		maxdist = dist;
-
-	    count++;
-	    mean += dist;
-	}
+      const NICE::Vector & xi = features[i];
+      for ( int j = i ; j < (int)features.size() ; j++ )
+      {
+          const NICE::Vector & xj = features[j];
+          // double sim = xi * xj;
+          double dist = distance.calculate ( xi, xj );
+          distances(i, j) = dist;
+
+          if ( dist < mindist )
+        mindist = dist;
+          if ( dist > maxdist )
+        maxdist = dist;
+
+          count++;
+          mean += dist;
+      }
     }
     mean /= count;
 
     for ( int i = 0 ; i < (int)features.size() ; i++ )
-	for ( int j = i ; j < (int)features.size() ; j++ )
-	{
-	    double d = ( mean - distances(i, j) );
-	    stddev += d*d;
-	}
+    {
+      for ( int j = i ; j < (int)features.size() ; j++ )
+      {
+          double d = ( mean - distances(i, j) );
+          stddev += d*d;
+      }
+    }
 
     stddev /= count;
 
     double norm = alpha / (2.0 * stddev);
 
     for ( int i = 0 ; i < (int)features.size() ; i++ )
-	for ( int j = i ; j < (int)features.size() ; j++ )
-	{
-	    double sim = exp(-distances(i, j) * norm );
-	    
-	    laplacian(i, j) = - sim;
-	    laplacian(j, i) = - sim;
-	}
+    {
+      for ( int j = i ; j < (int)features.size() ; j++ )
+      {
+          double sim = exp(-distances(i, j) * norm );
+          
+          laplacian(i, j) = - sim;
+          laplacian(j, i) = - sim;
+      }
+    }
 }
 
-void SpectralCluster::computeLaplacian ( const VVector & features,
-					 NICE::Matrix & laplacian, int method, double alpha )
+void SpectralCluster::computeLaplacian ( const NICE::VVector & features,
+                                         NICE::Matrix & laplacian,
+                                         int method, double alpha )
 {
     laplacian.set(0.0);
     getSimilarityMatrix(features, laplacian, alpha);
@@ -91,8 +102,10 @@ void SpectralCluster::computeLaplacian ( const VVector & features,
     d.set(0.0);
     for ( int i = 0 ; i < (int)laplacian.rows(); i++ )
     {
-		for ( int j = 0 ; j < (int)laplacian.cols(); j++ )
-	    	d[i] -=laplacian(i, j);
+      for ( int j = 0 ; j < (int)laplacian.cols(); j++ )
+      {
+          d[i] -=laplacian(i, j);
+      }
     }
 
     // Now we got the negative weight matrix laplacian : -W
@@ -102,31 +115,40 @@ void SpectralCluster::computeLaplacian ( const VVector & features,
     // D^-1 * L
     if ( method == L_RW_NORMALIZED ) 
     {
-	// L = D^-1 L_unnormalized = I - D^-1*W
-		for ( int i = 0 ; i < (int)laplacian.rows() ; i++ )
-			for ( int j = 0 ; j < (int)laplacian.cols() ; j++ )
-				laplacian(i, j) *= (1.0/d[i]);
-
-		for ( int i = 0 ; i < (int)laplacian.rows() ; i++ )
-	    	laplacian(i, i) += 1.0;
-
-    } else if ( method == L_UNNORMALIZED ) {
-	// unnormalized version
-	// L = D - W
-		for ( int i = 0 ; i < (int)laplacian.rows() ; i++ )
-	    	laplacian(i, i) += d[i];
+    // L = D^-1 L_unnormalized = I - D^-1*W
+      for ( int i = 0 ; i < (int)laplacian.rows() ; i++ )
+      {
+        for ( int j = 0 ; j < (int)laplacian.cols() ; j++ )
+        {
+          laplacian(i, j) *= (1.0/d[i]);
+        }
+      }
+
+      for ( int i = 0 ; i < (int)laplacian.rows() ; i++ )
+      {
+          laplacian(i, i) += 1.0;
+      }
+
+    }
+    else if ( method == L_UNNORMALIZED )
+    {
+    // unnormalized version
+    // L = D - W
+      for ( int i = 0 ; i < (int)laplacian.rows() ; i++ )
+      {
+          laplacian(i, i) += d[i];
+      }
     }
 }
 
-void SpectralCluster::cluster ( const VVector & features,
-				VVector & prototypes,
-				std::vector<double> & weights,
-				std::vector<int>    & assignment )
+void SpectralCluster::cluster ( const NICE::VVector & features,
+                                NICE::VVector & prototypes,
+                                std::vector<double> & weights,
+                                std::vector<int>    & assignment )
 {
-
     if ( features.size() <= 0 ) {
-	fprintf (stderr, "FATAL ERROR: not enough features vectors provided\n");
-	exit(-1);
+      fprintf (stderr, "FATAL ERROR: not enough features vectors provided\n");
+      exit(-1);
     }
 
     const NICE::Vector & x = features[0];
@@ -141,67 +163,70 @@ void SpectralCluster::cluster ( const VVector & features,
     NICE::Vector eigvals;
     try {
       NICE::eigenvectorvalues ( laplacian, eigvect, eigvals );
-    } catch (...) {
-      cerr << "You should adjust the alpha parameter, or the features are somehow weird" << endl;
-      cerr << "Laplacian = " << laplacian(0, 0, min((int)laplacian.rows()-1, 5), min((int)laplacian.cols()-1, 5)) << endl;
+    }
+    catch (...)
+    {
+      std::cerr << "You should adjust the alpha parameter, or the features are somehow weird" << std::endl;
+      std::cerr << "Laplacian = " << laplacian(0, 0, min((int)laplacian.rows()-1, 5), min((int)laplacian.cols()-1, 5)) << std::endl;
       throw Exception ("Laplacian matrix is singular or not well-conditioned!");
     }
 
     std::map<double, int> eigvals_sorted;
-	for ( int i = 0 ; i < (int)eigvals.size(); i++ ) 
+    for ( int i = 0 ; i < (int)eigvals.size(); i++ ) 
     {
-	eigvals_sorted.insert ( make_pair( eigvals[i], i ) );
+      eigvals_sorted.insert ( make_pair( eigvals[i], i ) );
     }
 
-    VVector spectral_features;
+    NICE::VVector spectral_features;
 
-	for ( int i = 0 ; i < (int)eigvect.rows() ; i++ )
+    for ( int i = 0 ; i < (int)eigvect.rows() ; i++ )
     {
-	NICE::Vector eigvec_k ( noClasses );
-	map<double, int>::const_iterator k = eigvals_sorted.begin();
-	for ( int j = 0 ; j < noClasses ; j++ )
-	{
-	    int eigval_index = k->second;
-	    eigvec_k[j] = eigvect(i, eigval_index ) ;
-	    k++;
-	}
-
-	spectral_features.push_back ( eigvec_k );
+      NICE::Vector eigvec_k ( noClusters );
+      map<double, int>::const_iterator k = eigvals_sorted.begin();
+      for ( int j = 0 ; j < noClusters ; j++ )
+      {
+        int eigval_index = k->second;
+        eigvec_k[j] = eigvect(i, eigval_index ) ;
+        k++;
+      }
+
+      spectral_features.push_back ( eigvec_k );
     }
 
-    kmeans.cluster ( spectral_features, prototypes, weights, assignment ); 
+    this->kmeans.cluster ( spectral_features, prototypes, weights, assignment ); 
 
     // recompute prototypes
 
-    for ( int k = 0 ; k < noClasses ; k++ )
+    for ( int k = 0 ; k < noClusters ; k++ )
     {
-	prototypes[k].resize( dimension );
-	prototypes[k].set(0);
-	weights[k] = 0;
+      prototypes[k].resize( dimension );
+      prototypes[k].set(0);
+      weights[k] = 0;
     }
 
     int j = 0;
     for ( VVector::const_iterator i  = features.begin();
-                                              i != features.end();
-	                                      i++, j++ )
+                                  i != features.end();
+                                  i++, j++ )
     {
-	int k = assignment[j];
-	
-	NICE::Vector & p = prototypes[k];
-	const NICE::Vector & x = *i;
-	p += x;
-	weights[k]++;
+      int k = assignment[j];
+      
+      NICE::Vector & p = prototypes[k];
+      const NICE::Vector & x = *i;
+      p += x;
+      weights[k]++;
     }
 
-    for ( int k = 0 ; k < noClasses ; k++ )
+    for ( int k = 0 ; k < noClusters ; k++ )
     {
-	NICE::Vector & p = prototypes[k];
-	if ( weights[k] <= 0 ) {
-	    fprintf (stderr, "FATAL ERROR: spectral clustering produced empty cluster !\n");
-	    exit(-1);
-	}
-	p *= ( 1.0 / weights[k] );
-	weights[k] = weights[k] / features.size();
+      NICE::Vector & p = prototypes[k];
+      if ( weights[k] <= 0 )
+      {
+          fprintf (stderr, "FATAL ERROR: spectral clustering produced empty cluster !\n");
+          exit(-1);
+      }
+      p *= ( 1.0 / weights[k] );
+      weights[k] = weights[k] / features.size();
     }
 }
 

+ 47 - 38
math/cluster/SpectralCluster.h

@@ -8,8 +8,8 @@
 #ifndef SPECTRALCLUSTERINCLUDE
 #define SPECTRALCLUSTERINCLUDE
 
-#include "core/vector/VectorT.h"
-#include "core/vector/MatrixT.h"
+#include <core/vector/VectorT.h>
+#include <core/vector/MatrixT.h>
 
 #include "ClusterAlgorithm.h"
 #include "KMeans.h"
@@ -17,42 +17,51 @@
 
 namespace OBJREC {
 
-/** spectral clustering by kmeans-clustering of eigenvectors */
-class SpectralCluster : public ClusterAlgorithm
-{
-
-    protected:
-	int noClasses;
-	double alpha;
-
-	KMeans kmeans;
-
-	enum {
-	    L_UNNORMALIZED  = 0,
-	    L_RW_NORMALIZED 
-	};
-
-	virtual void computeLaplacian ( const NICE::VVector & features,
-					 NICE::Matrix & laplacian,
-					 int method, double alpha );
-
-
-	virtual void getSimilarityMatrix ( const NICE::VVector & features, 
-					    NICE::Matrix & laplacian, double alpha );
-    public:
-  
-	/** simple constructor */
-	SpectralCluster (int _noClasses, double alpha);
-      
-	/** simple destructor */
-	virtual ~SpectralCluster();
-     
-	void cluster ( const NICE::VVector & features,
-		       NICE::VVector & prototypes,
-		       std::vector<double> & weights,
-		       std::vector<int>    & assignment );
-
-};
+  /** spectral clustering by kmeans-clustering of eigenvectors */
+  class SpectralCluster : public ClusterAlgorithm
+  {
+
+      protected:
+        int noClusters;
+        double alpha;
+
+        KMeans kmeans;
+
+        enum {
+            L_UNNORMALIZED  = 0,
+            L_RW_NORMALIZED 
+        };
+
+        virtual void computeLaplacian ( const NICE::VVector & features,
+                NICE::Matrix & laplacian,
+                int method, double alpha );
+
+
+        virtual void getSimilarityMatrix ( const NICE::VVector & features, 
+                    NICE::Matrix & laplacian, double alpha );
+      public:
+    
+        /** simple constructor */
+        SpectralCluster (int _noClusters, double alpha);
+        
+        /**
+        * @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: SpectralCluster)
+        * @date 14-06-2013 (dd-mm-yyyy)
+        * @author Alexander Freytag
+        */
+        SpectralCluster( const NICE::Config *conf, const std::string & _section = "SpectralCluster");     
+            
+        /** simple destructor */
+        virtual ~SpectralCluster();
+          
+        void cluster ( const NICE::VVector & features,
+                NICE::VVector & prototypes,
+                std::vector<double> & weights,
+                std::vector<int>    & assignment );
+
+  };
 
 
 } // namespace

+ 116 - 113
progs/evaluateCompleteBoWPipeline.cpp

@@ -33,19 +33,20 @@
 #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/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>
+#include <vislearning/math/cluster/GenericClusterAlgorithmSelection.h>
+// #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;
@@ -53,108 +54,108 @@ 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;
-}
+// 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;
+// }
 
 
 /**
@@ -265,7 +266,9 @@ int main( int argc, char **argv )
   //**********************************************    
   
   std::cerr << "CODEBOOK CREATION" << std::endl;
-  OBJREC::ClusterAlgorithm * clusterAlgo = setClusterAlgo( conf );
+//   OBJREC::ClusterAlgorithm * clusterAlgo = setClusterAlgo( conf );
+  
+  OBJREC::ClusterAlgorithm * clusterAlgo = OBJREC::GenericClusterAlgorithmSelection::selectClusterAlgo ( conf );
    
   NICE::VVector prototypes;