latest version v1.9 - last update 10 Apr 2010 |
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