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: ltiSerialPCA.h,v 1.10 2007/09/08 20:04:04 alvarado Exp $ 00031 */ 00032 00033 #ifndef _LTI_SERIAL_PCA_H_ 00034 #define _LTI_SERIAL_PCA_H_ 00035 00036 #include "ltiLinearAlgebraFunctor.h" 00037 #include "ltiEigenSystem.h" 00038 #include "ltiIoHandler.h" 00039 #include "ltiSerialVectorStats.h" 00040 00041 namespace lti { 00042 /** 00043 * Functor for sequentially computing a principal component analysis. 00044 * 00045 * It receives a set of input vectors in form of a matrix (each row of 00046 * the matrix corresponds to an input vector), which will be transformed 00047 * with PCA. 00048 * 00049 * The first time you use the apply()-method, the transformation 00050 * matrix will be computed. You can use this transformation matrix 00051 * with other data sets using the transform() methods. 00052 * 00053 * Please note that the eigenvector matrices will contain the 00054 * eigenvector in the COLUMNS and not in the rows, as could be 00055 * expected. This avoids the requirement of transposing matrices in 00056 * the vector transformations (see also lti::eigenSystem<T>). 00057 * 00058 * @ingroup gLinearAlgebra 00059 * \see lti::principalComponents 00060 */ 00061 template <class T> 00062 class serialPCA : public linearAlgebraFunctor { 00063 public: 00064 /** 00065 * the parameters for the class serialPCA 00066 */ 00067 class parameters : public linearAlgebraFunctor::parameters { 00068 public: 00069 /** 00070 * default constructor 00071 */ 00072 parameters() 00073 : linearAlgebraFunctor::parameters() { 00074 resultDim=3; 00075 autoDim=false; 00076 useCorrelation=false; 00077 whitening=false; 00078 00079 eigen=new jacobi<T>; 00080 } 00081 00082 /** 00083 * copy constructor 00084 * @param other the parameters object to be copied 00085 */ 00086 parameters(const parameters& other) 00087 : linearAlgebraFunctor::parameters() { 00088 eigen = 0; 00089 copy(other); 00090 } 00091 00092 00093 /** 00094 * destructor 00095 */ 00096 ~parameters(){ 00097 delete eigen; 00098 } 00099 00100 /** 00101 * returns name of this type 00102 */ 00103 const char* getTypeName() const { 00104 return "serialPCA::parameters"; 00105 } 00106 00107 /** 00108 * copy the contents of a parameters object 00109 * @param other the parameters object to be copied 00110 * @return a reference to this parameters object 00111 */ 00112 parameters& copy(const parameters& other) { 00113 # ifndef _LTI_MSC_6 00114 // MS Visual C++ 6 is not able to compile this... 00115 linearAlgebraFunctor::parameters::copy(other); 00116 # else 00117 // ...so we have to use this workaround. 00118 // Conditional on that, copy may not be virtual. 00119 functor::parameters& 00120 (functor::parameters::* p_copy) 00121 (const functor::parameters&) = 00122 functor::parameters::copy; 00123 (this->*p_copy)(other); 00124 # endif 00125 00126 delete eigen; 00127 eigen = 0; 00128 00129 resultDim=other.resultDim; 00130 autoDim=other.autoDim; 00131 useCorrelation=other.useCorrelation; 00132 whitening=other.whitening; 00133 eigen=dynamic_cast<eigenSystem<T>*>(other.eigen->clone()); 00134 return *this; 00135 } 00136 00137 00138 /** 00139 * Assigns the contents of the other object to this object 00140 */ 00141 inline parameters& operator=(const parameters& other) { 00142 copy(other); 00143 return *this; 00144 } 00145 00146 /** 00147 * returns a pointer to a clone of the parameters 00148 */ 00149 virtual functor::parameters* clone() const { 00150 return new parameters(*this); 00151 } 00152 00153 # ifndef _LTI_MSC_6 00154 /** 00155 * read the parameters from the given ioHandler 00156 * @param handler the ioHandler to be used 00157 * @param complete if true (the default) the enclosing begin/end will 00158 * be also read, otherwise only the data block will be read. 00159 * @return true if write was successful 00160 */ 00161 virtual bool read(ioHandler &handler, const bool complete=true) 00162 # else 00163 bool readMS(ioHandler& handler,const bool complete=true) 00164 # endif 00165 { 00166 bool b=true; 00167 00168 if (complete) { 00169 b=handler.readBegin(); 00170 } 00171 00172 if (b) { 00173 lti::read(handler,"dim",resultDim); 00174 lti::read(handler,"auto",autoDim); 00175 lti::read(handler,"corr",useCorrelation); 00176 lti::read(handler,"white",whitening); 00177 } 00178 00179 # ifndef _LTI_MSC_6 00180 // This is the standard C++ code, which MS Visual C++ 6 is not 00181 // able to compile... 00182 b = b && linearAlgebraFunctor::parameters::read(handler,false); 00183 # else 00184 bool (functor::parameters::* p_readMS)(ioHandler&,const bool) = 00185 functor::parameters::readMS; 00186 b = b && (this->*p_readMS)(handler,false); 00187 # endif 00188 00189 if (complete) { 00190 b=b && handler.readEnd(); 00191 } 00192 00193 return b; 00194 } 00195 00196 # ifdef _LTI_MSC_6 00197 /** 00198 * read the parameters from the given ioHandler 00199 * @param handler the ioHandler to be used 00200 * @param complete if true (the default) the enclosing begin/end will 00201 * be also read, otherwise only the data block will be read. 00202 * @return true if write was successful 00203 */ 00204 virtual bool read(ioHandler& handler,const bool complete=true) { 00205 // ...we need this workaround to cope with another really awful MSVC 00206 // bug. 00207 return readMS(handler,complete); 00208 } 00209 # endif 00210 00211 # ifndef _LTI_MSC_6 00212 /** 00213 * write the parameters in the given ioHandler 00214 * @param handler the ioHandler to be used 00215 * @param complete if true (the default) the enclosing begin/end will 00216 * be also written, otherwise only the data block will be 00217 * written. 00218 * @return true if write was successful 00219 */ 00220 virtual bool write(ioHandler& handler,const bool complete=true) const 00221 # else 00222 bool writeMS(ioHandler& handler,const bool complete=true) const 00223 # endif 00224 { 00225 bool b=true; 00226 00227 if (complete) { 00228 b=handler.writeBegin(); 00229 } 00230 00231 if (b) { 00232 lti::write(handler,"dim",resultDim); 00233 lti::write(handler,"auto",autoDim); 00234 lti::write(handler,"corr",useCorrelation); 00235 lti::write(handler,"white",whitening); 00236 } 00237 00238 # ifndef _LTI_MSC_6 00239 // This is the standard C++ code, which MS Visual C++ 6 is not 00240 // able to compile... 00241 b = b && linearAlgebraFunctor::parameters::write(handler,false); 00242 # else 00243 bool 00244 (functor::parameters::* p_writeMS)(ioHandler&,const bool) const = 00245 functor::parameters::writeMS; 00246 b = b && (this->*p_writeMS)(handler,false); 00247 # endif 00248 00249 if (complete) { 00250 b=b && handler.writeEnd(); 00251 } 00252 00253 return b; 00254 } 00255 00256 # ifdef _LTI_MSC_6 00257 /** 00258 * write the parameters to the given ioHandler 00259 * @param handler the ioHandler to be used 00260 * @param complete if true (the default) the enclosing begin/end will 00261 * be also writen, otherwise only the data block will be writen. 00262 * @return true if write was successful 00263 */ 00264 virtual bool write(ioHandler& handler,const bool complete=true) { 00265 // ...we need this workaround to cope with another really awful MSVC 00266 // bug. 00267 return writeMS(handler,complete); 00268 } 00269 # endif 00270 00271 00272 /** 00273 * This is the dimension of the reduced vectors. 00274 * 00275 * Default value: 3 00276 */ 00277 int resultDim; 00278 00279 /** 00280 * This flag determines, if the functor should determine a 00281 * maximum allowable dimension itself. "Maximum dimension" means 00282 * that the dimension is equal to the number of eigenvalues of 00283 * the covariance matrix which are larger than zero. 00284 * 00285 * Default value: false 00286 */ 00287 bool autoDim; 00288 00289 /** 00290 * This flag determines if the functor should use the 00291 * correlation coefficient matrix (flag is true) for eigenvector 00292 * computation or the covariance matrix (flag is false). 00293 * 00294 * The default is false. 00295 */ 00296 bool useCorrelation; 00297 00298 /** 00299 * This flag determines if the functor should perform a 00300 * whitening transform of the data. Whitening means that 00301 * 1. A PCA is performed, using the covariance matrix for 00302 * eigenvector computation 00303 * 2. A scaling of the transformed data by the inverse of the 00304 * square root of the eigenvalues. 00305 * 00306 * You should set useCorrelation to false if you use whitening. 00307 * 00308 * The default is false. 00309 */ 00310 bool whitening; 00311 00312 /** 00313 * The eigensystem functor used for computing eigenvectors 00314 */ 00315 eigenSystem<T> *eigen; 00316 00317 }; 00318 00319 /** 00320 * default constructor 00321 */ 00322 serialPCA(); 00323 00324 /** 00325 * copy constructor 00326 * @param other the object to be copied 00327 */ 00328 serialPCA(const serialPCA& other); 00329 00330 /** 00331 * destructor 00332 */ 00333 virtual ~serialPCA(); 00334 00335 /** 00336 * returns the name of this type ("serialPCA") 00337 */ 00338 virtual const char* getTypeName() const; 00339 00340 /** 00341 * Consider the given data for the computation of a principal components 00342 * transformation matrix. 00343 * 00344 * You can later call the transform() method or the apply method to 00345 * transform the data. 00346 * 00347 * Each time you call this method, the current transformation matrix is 00348 * invalidated and later recomputed when you call a transform method. 00349 * Then, all data considered so far will be used in the 00350 * covariance/correlation matrix estimation. 00351 * 00352 * @param data matrix<T> with the source data. 00353 * @return true if the PCA could be computed, false otherwise 00354 */ 00355 bool consider(const matrix<T>& data); 00356 00357 /** 00358 * Consider a vector in the estimation of the covariance/correlation 00359 * matrix. 00360 * 00361 * You can later call the transform() method or the apply method to 00362 * transform the data. 00363 * 00364 * Each time you call this method, the current transformation matrix is 00365 * invalidated and later recomputed when you call a transform method. 00366 * Then, all data considered so far will be used in the 00367 * covariance/correlation matrix estimation. 00368 * 00369 * @param data matrix<T> with the source data. 00370 * @return true if the PCA could be computed, false otherwise 00371 */ 00372 bool consider(const vector<T>& data); 00373 00374 /** 00375 * Reset the data 00376 * 00377 * Removes all data considered until now and set the functor as in the 00378 * initial empty state. 00379 */ 00380 bool reset(); 00381 00382 00383 /** 00384 * Computes the principal components of the previously 00385 * considered data points and applies it to the given data 00386 * matrix. The result is the transformed matrix. 00387 * data and result must not be references to the same matrix. 00388 * This method uses the eigenvector functor given in the parameters. 00389 * By default, it uses ltilib's own jacobi functor. However, you can 00390 * speed it up considerably by using the eigensystem functor in 00391 * the lamath directory. 00392 * 00393 * \warning This method does not consider the given \a src data to compute 00394 * the transformation, unless it is the very first time you call this 00395 * functor. The normal way of using apply() is previously having called 00396 * the \a consider() method. 00397 * 00398 * @param src matrix<T> with the source data. 00399 * @param dest matrix<T> with the result data. 00400 * @return true if the PCA could be computed, false otherwise 00401 */ 00402 bool apply(const matrix<T>& src, matrix<T>& dest); 00403 00404 00405 /** 00406 * On-Place version of the transformation. 00407 * 00408 * Computes the principal components of the previously 00409 * considered data points and applies it to the given data 00410 * matrix. The result is the transformed matrix. 00411 * This method uses the eigenvector functor given in the parameters. 00412 * By default, it uses ltilib's own jacobi functor. However, you can 00413 * speed it up considerably by using the eigensystem functor in 00414 * the lamath directory. 00415 * 00416 * \warning This method does not consider the given \a srcdest data to 00417 * compute the transformation, unless it is the very first time you call 00418 * this functor. The normal way of using apply() is previously having 00419 * called the \a consider() method. 00420 * 00421 * @param srcdest matrix<T> with the source data, which will also contain 00422 * the result. 00423 * @return a reference to <code>srcdest</code>. 00424 */ 00425 bool apply(matrix<T>& srcdest); 00426 00427 /** 00428 * Transforms a single vector according to a previously computed 00429 * transformation matrix. 00430 */ 00431 inline bool apply(const vector<T> &src, vector<T>& result) { 00432 return transform(src,result); 00433 } 00434 00435 /** 00436 * Transforms a single vector according to a previously computed 00437 * transformation matrix. 00438 * @param src the data vector 00439 * @param result the vector which will receive the transformed data 00440 * @return a reference to <code>result</code> 00441 */ 00442 bool transform(const vector<T> &src, vector<T>& result); 00443 00444 /** 00445 * Transform an entire matrix according to a previously computed 00446 * transformation matrix. Unfortunately, we must choose a name 00447 * different from apply. 00448 * @param src the data matrix 00449 * @param result the matrix which will receive the transformed data 00450 * @return a reference to <code>result</code> 00451 */ 00452 bool transform(const matrix<T> &src, matrix<T>& result); 00453 00454 /** 00455 * Returns the previously computed transform matrix. 00456 * @param result the matrix which will receive the transformation 00457 * matrix. 00458 * @return true if the matrix could be computed, false otherwise. 00459 */ 00460 bool getTransformMatrix(matrix<T>& result) const; 00461 00462 /** 00463 * Returns the previously computed transform matrix. 00464 * @return a const reference to the last computed or used transformation 00465 * matrix. 00466 */ 00467 const matrix<T>& getTransformMatrix() const; 00468 00469 /** 00470 * Returns the previously computed eigenvalues of the covariance 00471 * matrix. 00472 * @param result the vector which will receive the eigenvalues. 00473 * @return true if the values could be obtained, false otherwise. 00474 */ 00475 bool getEigenValues(vector<T>& result) const; 00476 00477 /** 00478 * Returns the previously computed eigenvalues of the covariance 00479 * matrix. 00480 * 00481 * @return a const reference to the last computed eigenvalues 00482 */ 00483 const vector<T>& getEigenValues() const; 00484 00485 /** 00486 * Returns the previously computed eigenvectors of the covariance 00487 * matrix. 00488 * @param result the matrix which will receive the eigenvectors. 00489 * Each column of the matrix contains one eigenvector. 00490 * @return true if the vectors could be obtained, false otherwise 00491 */ 00492 bool getEigenVectors(matrix<T>& result) const; 00493 00494 /** 00495 * Returns the previously computed eigenvectors of the covariance 00496 * matrix. 00497 * 00498 * This method will call the normal getEigenVectors() methods and 00499 * after that will transpose the obtained matrix, i.e. it is faster 00500 * to get the eigenvectors in the columns. 00501 * 00502 * @param result the matrix which will receive the eigenvectors. 00503 * Each row of the matrix contains one eigenvector. 00504 * @return true if the vectors could be obtained, false otherwise 00505 */ 00506 bool getEigenVectorsInRows(matrix<T>& result) const; 00507 00508 00509 /** 00510 * Returns the previously computed eigenvectors of the covariance 00511 * matrix. 00512 * @return a const reference to the last computed eigenvectors 00513 */ 00514 const matrix<T>& getEigenVectors() const; 00515 00516 /** 00517 * Set the dimension to which the vectors should be reduced. 00518 */ 00519 void setDimension(int k); 00520 00521 /** 00522 * copy data of "other" functor. 00523 * @param other the functor to be copied 00524 * @return a reference to this functor object 00525 */ 00526 serialPCA& copy(const serialPCA& other); 00527 00528 /** 00529 * returns a pointer to a clone of this functor. 00530 */ 00531 virtual functor* clone() const; 00532 00533 /** 00534 * returns used parameters 00535 */ 00536 const parameters& getParameters() const; 00537 00538 /** 00539 * Update functor's parameters. 00540 * 00541 * Initialize some internal data according to the parameters. 00542 * 00543 * @return true if successful, false otherwise 00544 */ 00545 virtual bool updateParameters(); 00546 00547 /** 00548 * Reads this functor from the given handler. 00549 */ 00550 virtual bool read(ioHandler &handler, const bool complete=true); 00551 00552 /** 00553 * Writes this functor to the given handler. 00554 */ 00555 virtual bool write(ioHandler &handler, const bool complete=true) const; 00556 00557 int getUsedDimension() const { 00558 return usedDimensionality; 00559 } 00560 00561 protected: 00562 /** 00563 * Determines the intrinsic dimensionality of the data set if the 00564 * user specify autoDim, otherwise return parameters::resultDim. 00565 * The member usedDimensionality will be set with the returned value 00566 */ 00567 int checkDim(); 00568 00569 00570 private: 00571 00572 bool buildTransform(); 00573 00574 matrix<T> orderedEigVec; 00575 matrix<T> transformMatrix; 00576 vector<T> eigValues; 00577 00578 vector<T> offset; 00579 vector<T> scale; 00580 vector<T> whiteScale; 00581 00582 /** 00583 * This attribute accumulates the statistics of data being considered. 00584 */ 00585 serialVectorStats<T> vstat; 00586 00587 /** 00588 * Flag that indicates if new data has been considered, such that the 00589 * covariance matrix needs to be recomputed, as well as its eigenvalues and 00590 * eigenvectors. 00591 */ 00592 bool isValid; 00593 00594 /** 00595 * Dimensionality of the final space being used. 00596 */ 00597 int usedDimensionality; 00598 }; 00599 00600 template <class T> 00601 bool read(ioHandler& handler, 00602 serialPCA<T>& pca, 00603 const bool complete=true) { 00604 return pca.read(handler,complete); 00605 } 00606 00607 00608 template <class T> 00609 bool write(ioHandler& handler, 00610 const serialPCA<T>& pca, 00611 const bool complete=true) { 00612 return pca.write(handler,complete); 00613 } 00614 00615 } 00616 00617 #endif