LTI-Lib latest version v1.9 - last update 10 Apr 2010

ltiPCA.h

00001 /*
00002  * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006
00003  * Lehrstuhl fuer Technische Informatik, RWTH-Aachen, Germany
00004  *
00005  * This file is part of the LTI-Computer Vision Library (LTI-Lib)
00006  *
00007  * The LTI-Lib is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License (LGPL)
00009  * as published by the Free Software Foundation; either version 2.1 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * The LTI-Lib is distributed in the hope that it will be
00013  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
00014  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with the LTI-Lib; see the file LICENSE.  If
00019  * not, write to the Free Software Foundation, Inc., 59 Temple Place -
00020  * Suite 330, Boston, MA 02111-1307, USA.
00021  */
00022 
00023 
00024 /*----------------------------------------------------------------
00025  * project ....: LTI Digital Image/Signal Processing Library
00026  * file .......: ltiPCA.h
00027  * authors ....: Jochen Wickel
00028  * organization: LTI, RWTH Aachen
00029  * creation ...: 27.11.2000
00030  * revisions ..: $Id: ltiPCA.h,v 1.15 2006/02/08 12:38:50 ltilib Exp $
00031  */
00032 
00033 #ifndef _LTI_P_C_A_H_
00034 #define _LTI_P_C_A_H_
00035 
00036 #include "ltiLinearAlgebraFunctor.h"
00037 #include "ltiEigenSystem.h"
00038 #include "ltiSVD.h"
00039 #include "ltiIoHandler.h"
00040 
00041 #ifdef HAVE_LAPACK
00042 #include "ltiFastEigenSystem.h"
00043 #include "ltiFastSVD.h"
00044 #endif
00045 
00046 namespace lti {
00047   /**
00048    * Functor for computing a principal component analysis.
00049    *
00050    * It receives a set of input vectors in form of a matrix (each row of
00051    * the matrix corresponds to an input vector), which will be transformed
00052    * with PCA.
00053    *
00054    * The first time you use the apply()-method, the transformation
00055    * matrix will be computed.  You can use this transformation matrix
00056    * with other data sets using the transform() methods.
00057    *
00058    * Please note that the eigenvector matrices will contain the
00059    * eigenvector in the COLUMNS and not in the rows, as could be
00060    * expected.  This avoids the requirement of transposing matrices in
00061    * the vector transformations (see also lti::eigenSystem<T>).
00062    *
00063    * For large data matrices is is advisable to use singular value
00064    * decomposition instead of an eigensystem for the PCA. The
00065    * operation will usually be faster and using less memory. To do so
00066    * set parameters::useSVD to true and set parameters::svd to
00067    * singularValueDecomp<T> or one of its subclasses (eg. fastSVD<T>
00068    * if you are using LAPACK)
00069    *
00070    * @ingroup gLinearAlgebra
00071    */
00072   template <class T>
00073   class principalComponents : public linearAlgebraFunctor {
00074   public:
00075     /**
00076      * the parameters for the class principalComponents
00077      */
00078     class parameters : public linearAlgebraFunctor::parameters {
00079     public:
00080       /**
00081        * default constructor
00082        */
00083       parameters() 
00084         : linearAlgebraFunctor::parameters() {
00085         resultDim=3;
00086         autoDim=false;
00087         useCorrelation=false;
00088         whitening=false;
00089         relevance=T(100000);
00090         centerData=true;
00091         useSVD=false;
00092         svd=0;
00093 #ifdef HAVE_LAPACK
00094         eigen=new fastEigenSystem<T>;
00095 #else
00096         eigen=new jacobi<T>;
00097 #endif
00098       }
00099 
00100       /**
00101        * copy constructor
00102        * @param other the parameters object to be copied
00103        */
00104       parameters(const parameters& other) 
00105         : linearAlgebraFunctor::parameters() {
00106         eigen = 0;
00107         svd=0;
00108         copy(other);
00109       }
00110 
00111 
00112       /**
00113        * destructor
00114        */
00115       ~parameters(){
00116         delete eigen;
00117       }
00118 
00119       /**
00120        * returns name of this type
00121        */
00122       const char* getTypeName() const {
00123         return "principalComponents::parameters";
00124       }
00125 
00126       /**
00127        * copy the contents of a parameters object
00128        * @param other the parameters object to be copied
00129        * @return a reference to this parameters object
00130        */
00131       parameters& copy(const parameters& other) {
00132 #     ifndef _LTI_MSC_6
00133         // MS Visual C++ 6 is not able to compile this...
00134         linearAlgebraFunctor::parameters::copy(other);
00135 #     else
00136         // ...so we have to use this workaround.
00137         // Conditional on that, copy may not be virtual.
00138         functor::parameters&
00139           (functor::parameters::* p_copy)
00140           (const functor::parameters&) =
00141           functor::parameters::copy;
00142         (this->*p_copy)(other);
00143 #     endif
00144 
00145         delete eigen;
00146         eigen = 0;
00147         delete svd;
00148         svd = 0;
00149 
00150         resultDim=other.resultDim;
00151         autoDim=other.autoDim;
00152         useCorrelation=other.useCorrelation;
00153         whitening=other.whitening;
00154         relevance=other.relevance;
00155         centerData=other.centerData;
00156         if (other.eigen!=0) {
00157           eigen=dynamic_cast<eigenSystem<T>*>(other.eigen->clone());
00158         } else {
00159           eigen = 0;
00160         }
00161         useSVD=other.useSVD;
00162         if (other.svd!=0) {
00163           svd=dynamic_cast<singularValueDecomp<T>*>(other.svd->clone());
00164         } else {
00165           svd = 0;
00166         }
00167         return *this;
00168       }
00169 
00170 
00171       /**
00172        * Assigns the contents of the other object to this object
00173        */
00174       inline parameters& operator=(const parameters& other) {
00175         copy(other);
00176         return *this;
00177       }
00178 
00179       /**
00180        * returns a pointer to a clone of the parameters
00181        */
00182       virtual functor::parameters* clone() const {
00183         return new parameters(*this);
00184       }
00185 
00186 #     ifndef _LTI_MSC_6
00187       /**
00188        * read the parameters from the given ioHandler
00189        * @param handler the ioHandler to be used
00190        * @param complete if true (the default) the enclosing begin/end will
00191        *        be also read, otherwise only the data block will be read.
00192        * @return true if write was successful
00193        */
00194       virtual bool read(ioHandler &handler, const bool complete=true)
00195 #     else
00196       bool readMS(ioHandler& handler,const bool complete=true)
00197 #     endif
00198       {
00199         bool b=true;
00200 
00201         if (complete) {
00202           b=handler.readBegin();
00203         }
00204 
00205         if (b) {
00206           lti::read(handler,"dim",resultDim);
00207           lti::read(handler,"auto",autoDim);
00208           lti::read(handler,"corr",useCorrelation);
00209           lti::read(handler,"white",whitening);
00210           lti::read(handler, "relevance", relevance);
00211           lti::read(handler, "centerData", centerData);
00212         }
00213 
00214 #     ifndef _LTI_MSC_6
00215         // This is the standard C++ code, which MS Visual C++ 6 is not
00216         // able to compile...
00217         b = b && linearAlgebraFunctor::parameters::read(handler,false);
00218 #     else
00219         bool (functor::parameters::* p_readMS)(ioHandler&,const bool) =
00220           functor::parameters::readMS;
00221         b = b && (this->*p_readMS)(handler,false);
00222 #     endif
00223 
00224         if (complete) {
00225           b=b && handler.readEnd();
00226         }
00227 
00228         return b;
00229       }
00230 
00231 #     ifdef _LTI_MSC_6
00232       /**
00233        * read the parameters from the given ioHandler
00234        * @param handler the ioHandler to be used
00235        * @param complete if true (the default) the enclosing begin/end will
00236        *        be also read, otherwise only the data block will be read.
00237        * @return true if write was successful
00238        */
00239       virtual bool read(ioHandler& handler,const bool complete=true) {
00240         // ...we need this workaround to cope with another really awful MSVC
00241         // bug.
00242         return readMS(handler,complete);
00243       }
00244 #     endif
00245 
00246 #     ifndef _LTI_MSC_6
00247       /**
00248        * write the parameters in the given ioHandler
00249        * @param handler the ioHandler to be used
00250        * @param complete if true (the default) the enclosing begin/end will
00251        *        be also written, otherwise only the data block will be
00252        *        written.
00253        * @return true if write was successful
00254        */
00255       virtual bool write(ioHandler& handler,const bool complete=true) const
00256 #     else
00257       bool writeMS(ioHandler& handler,const bool complete=true) const
00258 #     endif
00259       {
00260         bool b=true;
00261 
00262         if (complete) {
00263           b=handler.writeBegin();
00264         }
00265 
00266         if (b) {
00267           lti::write(handler,"dim",resultDim);
00268           lti::write(handler,"auto",autoDim);
00269           lti::write(handler,"corr",useCorrelation);
00270           lti::write(handler,"white",whitening);
00271           lti::write(handler, "relevance", relevance);
00272           lti::write(handler, "centerData", centerData);
00273         }
00274 
00275 #       ifndef _LTI_MSC_6
00276         // This is the standard C++ code, which MS Visual C++ 6 is not
00277         // able to compile...
00278         b = b && linearAlgebraFunctor::parameters::write(handler,false);
00279 #       else
00280         bool
00281         (functor::parameters::* p_writeMS)(ioHandler&,const bool) const =
00282           functor::parameters::writeMS;
00283         b = b && (this->*p_writeMS)(handler,false);
00284 #       endif
00285 
00286         if (complete) {
00287           b=b && handler.writeEnd();
00288         }
00289 
00290         return b;
00291       }
00292 
00293 #     ifdef _LTI_MSC_6
00294       /**
00295        * write the parameters to the given ioHandler
00296        * @param handler the ioHandler to be used
00297        * @param complete if true (the default) the enclosing begin/end will
00298        *        be also writen, otherwise only the data block will be writen.
00299        * @return true if write was successful
00300        */
00301       virtual bool write(ioHandler& handler,const bool complete=true) {
00302         // ...we need this workaround to cope with another really awful MSVC
00303         // bug.
00304         return writeMS(handler,complete);
00305       }
00306 #     endif
00307 
00308 
00309       /**
00310        * This is the dimension of the reduced vectors.
00311        *
00312        * Default value: 3
00313        */
00314       int resultDim;
00315 
00316       /**
00317        * This flag determines, if the functor should determine a
00318        * maximum allowable dimension itself. "Maximum dimension" means
00319        * that the dimension is equal to the number of eigenvalues of
00320        * the covariance matrix which are larger than zero.
00321        *
00322        * Default value: false
00323        */
00324       bool autoDim;
00325 
00326       /**
00327        * This flag determines if the functor should use the
00328        * correlation coefficient matrix (flag is true) for eigenvector
00329        * computation or the covariance matrix (flag is false).
00330        *
00331        * The default is false.
00332        */
00333       bool useCorrelation;
00334 
00335       /**
00336        * This flag determines if the functor should perform a
00337        * whitening transform of the data. Whitening means that
00338        * 1. A PCA is performed, using the covariance matrix for
00339        *    eigenvector computation
00340        * 2. A scaling of the transformed data by the inverse of the
00341        *    square root of the eigenvalues.
00342        *
00343        * You should set useCorrelation to false if you use whitening.
00344        *
00345        * The default is false.
00346        */
00347       bool whitening;
00348 
00349       /**
00350        * The eigensystem functor used for computing eigenvectors.
00351        *
00352        * Default with LAPACK: lti::fastEigenSystem<T>
00353        * Default without LAPACK: lti::jacobi<T>
00354        */
00355       eigenSystem<T> *eigen;
00356 
00357       /**
00358        * The factor which determines relevant eigenvectors. An eigenvector
00359        * is considered relevant if its eigenvalue is at least as large
00360        * as the largest eigenvalue divided by this number. Usually,
00361        * it takes values between 1e4 and 1e6.
00362        *
00363        * Default: T(100000)
00364        */
00365       T relevance;
00366 
00367       /**
00368        * This flag denotes if the transformed data should be
00369        * centered around zero. This is the usual behaviour of
00370        * the PCA, but for some special operations it may be
00371        * necessary to disable this. If the flag is false,
00372        * the mean of the transformed data is moved to the transformed
00373        * mean of the source data.
00374        *
00375        * Default: true
00376        */
00377       bool centerData;
00378 
00379       /**
00380        * When true singular value decomposition instead of eigensystem
00381        * solution is used to calculate the eigenvectors and
00382        * eigenvalues. This can be much faster and less memory consuming.
00383        * Default is false.
00384        */
00385       bool useSVD;
00386 
00387       /**
00388        * Singular value decomposition to be used when useSVD is true.
00389        *
00390        * Default is null. If LAPACK is used and data matrices are
00391        * large consider using lti::fastSVD<T>.
00392        */
00393       singularValueDecomp<T> *svd;
00394     };
00395 
00396     /**
00397      * default constructor
00398      * @param createDefaultParams if true (default) a default parameters
00399      *                            object will be created.
00400      */
00401     principalComponents(const bool createDefaultParams = true);
00402 
00403     /**
00404      * default constructor with parameters
00405      * 
00406      */
00407     principalComponents(const parameters& par);
00408 
00409     /**
00410      * copy constructor
00411      * @param other the object to be copied
00412      */
00413     principalComponents(const principalComponents& other);
00414 
00415     /**
00416      * destructor
00417      */
00418     virtual ~principalComponents();
00419 
00420     /**
00421      * returns the name of this type ("principalComponents")
00422      */
00423     virtual const char* getTypeName() const;
00424 
00425     /**
00426      * Computes the principal components of the data matrix
00427      * and transforms it according to the new coordinate system.
00428      * The result is the transformed matrix.
00429      * Data and result must not be references to the same matrix.
00430      * Data points are expected to be in the rows of the data matrix.
00431      *
00432      * This method uses the eigenvector functor given in the parameters.
00433      * By default, it uses ltilib's own jacobi functor. However, you can
00434      * speed it up considerably by using the eigensystem functor in
00435      * the lamath directory.
00436      *
00437      * If you don't need to transform the input data, and just want to
00438      * use the input matrix to compute the principal components you
00439      * can use the method computeTransformMatrix().  If you just need
00440      * to transform the data, without computing the transformation matrix, you
00441      * can use the method transform().
00442      *
00443      * @param data matrix<T> with the source data.
00444      * @param result matrix<T> with the result data.
00445      * @return true if the PCA could be computed, false otherwise
00446      */
00447     virtual bool apply(const matrix<T>& data, matrix<T>& result);
00448 
00449     /**
00450      * On-Place version of the transformation.
00451      *
00452      * If you don't need to transform the input data, and just want to
00453      * use the input matrix to compute the principal components you
00454      * can use the method computeTransformMatrix().  If you just need
00455      * to transform the data, without computing the transformation matrix, you
00456      * can use the method transform().
00457      *
00458      * @param srcdest matrix<T> with the source data, which will also contain
00459      *        the result.
00460      * @return a reference to <code>srcdest</code>.
00461      */
00462     virtual bool apply(matrix<T>& srcdest);
00463 
00464     /**
00465      * Transforms a single vector according to a previously computed
00466      * transformation matrix. (this is an alias for the transform() method)
00467      */
00468     virtual inline bool apply(const vector<T> &src, vector<T>& result) {
00469       return transform(src,result);
00470     }
00471 
00472     /**
00473      * Pass the covariance matrix and the mean values directly
00474      * to the functor to generate the transform matrix.
00475      *
00476      * If you know the mean and covariance of your data, you can use this
00477      * method to speed up the computations of the transformation matrix.
00478      * Otherwise, just call one of the apply() methods with your data vectors
00479      * in the rows of the matrix.  The covariance and mean vectors will be
00480      * computed there automatically.
00481      */
00482     bool setCovarianceAndMean(const matrix<T>& coVar,
00483                               const vector<T>& meanVec);
00484 
00485     /**
00486      * Transforms a single vector according to a previously computed
00487      * transformation matrix.
00488      * @param src the data vector
00489      * @param result the vector which will receive the transformed data
00490      * @return a reference to <code>result</code>
00491      */
00492     virtual bool transform(const vector<T> &src, vector<T>& result) const;
00493 
00494     /**
00495      * Transform an entire matrix according to a previously computed
00496      * transformation matrix. Unfortunately, we must choose a name
00497      * different from apply.
00498      * @param src the data matrix
00499      * @param result the matrix which will receive the transformed data
00500      * @return true if successful, false otherwise.
00501      */
00502     virtual bool transform(const matrix<T> &src, matrix<T>& result) const;
00503 
00504     /**
00505      * Transform an entire matrix according to a previously computed
00506      * transformation matrix. Unfortunately, we must choose a name
00507      * different from apply.
00508      * @param srcdest the data matrix.  The result will be left here too.
00509      * @return true if successful, false otherwise.
00510      */
00511     virtual bool transform(matrix<T> &srcdest) const;
00512 
00513     /**
00514      * Compute the transformation matrix.  Similar to the apply() method,
00515      * but it does not transform the given data (this saves some time).
00516      *
00517      * @param src the matrix with the input data to be analysed.
00518      * @return true if transformation matrix could be computed, false otherwise
00519      */
00520     virtual bool computeTransformMatrix(const matrix<T>& src);
00521 
00522     /**
00523      * Alias for computeTransformMatrix()
00524      */
00525     virtual bool train(const matrix<T>& src);
00526 
00527     /**
00528      * Reconstructs a data vector \c dest from the given coefficients
00529      * \c coeff, using the transformMatrix found by
00530      * computeTransformMatrix() or apply() and the appropriate offset.
00531      * @param coeff PCA coefficients for reconstruction.
00532      * @param dest reconstructed data vector.
00533      * @return true if reconstruction was successful
00534      */
00535     virtual bool reconstruct(const vector<T>& coeff, vector<T>& dest) const;
00536 
00537     /**
00538      * Reconstructs a set of data vectors \c dest from the given
00539      * coefficients \c coeff, using the transformMatrix found by
00540      * computeTransformMatrix() or apply() and the appropriate
00541      * offset. As usual \c coeff as well as \c dest contain one data
00542      * vector per row.
00543      * @param coeff each row contains PCA coefficients for reconstruction.
00544      * @param dest each row is one reconstructed data vector.
00545      * @return true if reconstruction was successful
00546      */
00547     virtual bool reconstruct(const matrix<T>& coeff, matrix<T>& dest) const;
00548 
00549     /**
00550      * Returns the previously computed transform matrix.
00551      * @param result the matrix which will receive the transformation
00552      *        matrix.
00553      * @return true if the matrix could be computed, false otherwise.
00554      */
00555     virtual bool getTransformMatrix(matrix<T>& result) const;
00556 
00557     /**
00558      * Returns the previously computed transform matrix.
00559      * @return a const reference to the last computed or used transformation
00560      *           matrix.
00561      */
00562     virtual const matrix<T>& getTransformMatrix() const;
00563 
00564     /**
00565      * Returns the previously computed offset vector, which corresponds to the
00566      * mean of the data.
00567      *
00568      * @param result the offset vector will be written here.
00569      * @return true if the matrix could be computed, false otherwise.
00570      */
00571     virtual bool getOffsetVector(vector<T>& result) const;
00572 
00573     /**
00574      * Returns the previously computed offset vector, which corresponds to the
00575      * mean of the data.
00576      *
00577      * @return a const reference to the last computed or used offset vector.
00578      */
00579     virtual const vector<T>& getOffsetVector() const;
00580 
00581     /**
00582      * Returns the previously computed eigenvalues of the covariance
00583      * matrix.
00584      * @param result the vector which will receive the eigenvalues.
00585      * @return true if the values could be obtained, false otherwise.
00586      */
00587     virtual bool getEigenValues(vector<T>& result) const;
00588 
00589     /**
00590      * Returns the previously computed eigenvalues of the covariance
00591      * matrix.
00592      *
00593      * @return a const reference to the last computed eigenvalues
00594      */
00595     virtual const vector<T>& getEigenValues() const;
00596 
00597     /**
00598      * Returns the previously computed eigenvectors of the covariance
00599      * matrix.
00600      * @param result the matrix which will receive the eigenvectors.
00601      *        Each column of the matrix contains one eigenvector.
00602      * @return true if the vectors could be obtained, false otherwise
00603      */
00604     virtual bool getEigenVectors(matrix<T>& result) const;
00605 
00606     /**
00607      * Returns the previously computed eigenvectors of the covariance
00608      * matrix.
00609      *
00610      * This method will call the normal getEigenVectors() methods and
00611      * after that will transpose the obtained matrix, i.e. it is faster
00612      * to get the eigenvectors in the columns.
00613      *
00614      * @param result the matrix which will receive the eigenvectors.
00615      *        Each row of the matrix contains one eigenvector.
00616      * @return true if the vectors could be obtained, false otherwise
00617      */
00618     virtual bool getEigenVectorsInRows(matrix<T>& result) const;
00619 
00620     /**
00621      * Returns the previously computed eigenvectors of the covariance
00622      * matrix.
00623      * @return a const reference to the last computed eigenvectors
00624      */
00625     virtual const matrix<T>& getEigenVectors() const;
00626 
00627     /**
00628      * Set the dimension to which the vectors should be reduced. This is just considered when 
00629      * computing the transformation matrix. After this matrix is determined the (destination) 
00630      * dimension is fixed and just can be changed by recalculating the transformation matrix.
00631      */
00632     virtual void setDimension(int k);
00633 
00634     /**
00635      * copy data of "other" functor.
00636      * @param other the functor to be copied
00637      * @return a reference to this functor object
00638      */
00639     principalComponents& copy(const principalComponents& other);
00640 
00641     /**
00642      * Alias for copy.
00643      */
00644     principalComponents& operator=(const principalComponents& other);
00645 
00646     /**
00647      * returns a pointer to a clone of this functor.
00648      */
00649     virtual functor* clone() const;
00650 
00651     /**
00652      * returns used parameters
00653      */
00654     const parameters& getParameters() const;
00655 
00656     /**
00657      * Update functor's parameters.
00658      *
00659      * This member initializes some internal data according to the values in
00660      * the parameters.
00661      *
00662      * @return true if successful, false otherwise
00663      */
00664     virtual bool updateParameters();
00665 
00666     /**
00667      * Reads this functor from the given handler.
00668      */
00669     virtual bool read(ioHandler &handler, const bool complete=true);
00670 
00671     /**
00672      * Writes this functor to the given handler.
00673      */
00674     virtual bool write(ioHandler &handler, const bool complete=true) const;
00675 
00676     /**
00677      * Number of dimensions considered in the transformation
00678      * @return the number of dimensions used for the transformation.  It
00679      *         is always less or equal the number of dimensions of the input
00680      *         vectors.
00681      */
00682     int getUsedDimension() const {
00683       return usedDimensionality;
00684     }
00685 
00686   protected:
00687     /**
00688      * Determines the intrinsic dimensionality of the data set if the
00689      * user specify autoDim, otherwise return parameters::resultDim.
00690      * The member usedDimensionality will be set with the returned value
00691      */
00692     int checkDim();
00693 
00694     /**
00695      * Resets all private members to size 0. Used when an error occurs
00696      * in the calculation of the transform matrix.
00697      */
00698     void reset();
00699 
00700   protected:
00701     matrix<T> orderedEigVec;
00702     matrix<T> transformMatrix;
00703     vector<T> eigValues;
00704 
00705     vector<T> offset;
00706     vector<T> transformedOffset;
00707     vector<T> scale;
00708     vector<T> whiteScale;
00709 
00710     /**
00711      * dimensionality being used.
00712      */
00713     int usedDimensionality;
00714   };
00715 
00716   template <class T>
00717   bool read(ioHandler& handler,
00718             principalComponents<T>& pca,
00719             const bool complete=true) {
00720     return pca.read(handler,complete);
00721   }
00722 
00723 
00724   template <class T>
00725   bool write(ioHandler& handler,
00726              const principalComponents<T>& pca,
00727              const bool complete=true) {
00728     return pca.write(handler,complete);
00729   }
00730 
00731 }
00732 
00733 #endif

Generated on Sat Apr 10 15:25:57 2010 for LTI-Lib by Doxygen 1.6.1