latest version v1.9 - last update 10 Apr 2010 |
00001 /* 00002 * Copyright (C) 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-Lib: Image Processing and Computer Vision Library 00026 * file .......: ltiFastICA.h 00027 * authors ....: Arnd Hannemann 00028 * organization: LTI, RWTH Aachen 00029 * creation ...: 3.3.2004 00030 * revisions ..: $Id: ltiFastICA.h,v 1.8 2006/02/08 12:20:58 ltilib Exp $ 00031 */ 00032 00033 #ifndef _LTI_FAST_I_C_A_H_ 00034 #define _LTI_FAST_I_C_A_H_ 00035 00036 #include "ltiLinearAlgebraFunctor.h" 00037 #include "ltiUniformDist.h" 00038 #include "ltiPCA.h" 00039 00040 namespace lti { 00041 /** 00042 * This class implements a fast fixed point algorithm for the Independent 00043 * Component Analysis (ICA). 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 ICA. (apply() methods) 00048 * 00049 * If you only want to compute the transformation matrix you can use 00050 * the computeTransformMatrix() methods. 00051 * 00052 * Once the transformationMatrix is computed (by an apply, 00053 * or computeTransformMatrix method) you can use the transform() methods to 00054 * %transform other datasets with the same transformation matrix. 00055 * 00056 * Note that you have to subtract the offset vector from data you want to 00057 * %transform with the transformation matrix if \b not 00058 * using the transform() methods. 00059 */ 00060 template<class T> 00061 class fastICA : public linearAlgebraFunctor { 00062 public: 00063 /** 00064 * The parameters for the class fastICA 00065 */ 00066 class parameters : public linearAlgebraFunctor::parameters { 00067 public: 00068 00069 /** 00070 * Enumeration of orthogonalization methods. 00071 */ 00072 enum eOrthogonalizationMethod { 00073 Deflationary, /**< The ICs are computed one after another and 00074 * orthogonalization is done dependent on the 00075 * already computed ICs (first component has 00076 * a free choice of direction) 00077 */ 00078 Symmetric /**< The ICs are computed in parallel and 00079 * orthogonalization is done after each 00080 * iteration step considering all ICs. 00081 */ 00082 }; 00083 00084 /** 00085 * Enumeration of negentropyApproximation methods. 00086 * 00087 * In fact the first and second derivation of the stated functions are 00088 * used in the algorithm. 00089 */ 00090 enum eNegentropyApproximation { 00091 Exp, /**< using 00092 * \f$G(x)=-\frac{1}{\alpha_{exp}}*exp(-\frac{\alpha_{exp}}{2}*u^2) 00093 * \f$ as negentropy approximation function 00094 */ 00095 Tanh, /**< using 00096 * \f$G(u)=\frac{1}{\alpha_{tanh}}*log (cosh(\alpha_{tanh}*u))\f$ 00097 * as negentropy approximation function 00098 */ 00099 Kurt /**< using 00100 * \f$G(u)=u^4 \f$ as negentropy Approximation function 00101 */ 00102 }; 00103 00104 /** 00105 * Default constructor 00106 */ 00107 parameters() : linearAlgebraFunctor::parameters() { 00108 //TODO: Initialize your parameter values! 00109 // If you add more parameters manually, do not forget to do following: 00110 // 1. indicate in the default constructor the default values 00111 // 2. make sure that the copy member also copy your new parameters 00112 // 3. make sure that the read and write members also read and 00113 // write your parameters 00114 00115 // maxSteps 00116 // delta 00117 maxSteps = 1000; 00118 orthogonalizationMethod = Deflationary; 00119 negentropyApproximation = Exp; 00120 epsilon = static_cast<T>(0.0001f); 00121 expAlpha = 1; 00122 tanhAlpha = 1; 00123 }; 00124 00125 /** 00126 * Copy constructor 00127 * @param other the parameters object to be copied 00128 */ 00129 parameters(const parameters& other) : linearAlgebraFunctor::parameters() { 00130 copy(other); 00131 } 00132 00133 /** 00134 * Destructor 00135 */ 00136 ~parameters() { 00137 }; 00138 00139 /** 00140 * Returns name of this type 00141 */ 00142 const char* getTypeName() const { 00143 return "fastICA::parameters"; 00144 }; 00145 00146 /** 00147 * Copy the contents of a parameters object 00148 * @param other the parameters object to be copied 00149 * @return a reference to this parameters object 00150 */ 00151 parameters& copy(const parameters& other) { 00152 # ifndef _LTI_MSC_6 00153 // MS Visual C++ 6 is not able to compile this... 00154 linearAlgebraFunctor::parameters::copy(other); 00155 # else 00156 // ...so we have to use this workaround. 00157 // Conditional on that, copy may not be virtual. 00158 linearAlgebraFunctor::parameters& (linearAlgebraFunctor::parameters::* p_copy) 00159 (const linearAlgebraFunctor::parameters&) = 00160 linearAlgebraFunctor::parameters::copy; 00161 (this->*p_copy)(other); 00162 # endif 00163 00164 maxSteps = other.maxSteps; 00165 orthogonalizationMethod = other.orthogonalizationMethod; 00166 negentropyApproximation = other.negentropyApproximation; 00167 expAlpha = other.expAlpha; 00168 tanhAlpha = other.tanhAlpha; 00169 epsilon = other.epsilon; 00170 return *this; 00171 }; 00172 00173 /** 00174 * Copy the contents of a parameters object 00175 * @param other the parameters object to be copied 00176 * @return a reference to this parameters object 00177 */ 00178 parameters& operator=(const parameters& other) { 00179 return copy(other); 00180 }; 00181 00182 /** 00183 * Returns a pointer to a clone of the parameters 00184 */ 00185 virtual functor::parameters* clone() const { 00186 return new parameters(*this); 00187 }; 00188 00189 # ifndef _LTI_MSC_6 00190 /** 00191 * Write the parameters in the given ioHandler 00192 * @param handler the ioHandler to be used 00193 * @param complete if true (the default) the enclosing begin/end will 00194 * be also written, otherwise only the data block will be written. 00195 * @return true if write was successful 00196 */ 00197 virtual bool write(ioHandler& handler,const bool complete=true) const 00198 # else 00199 /** 00200 * This function is required by MSVC only, as a workaround for a 00201 * very awful bug, which exists since MSVC V.4.0, and still by 00202 * V.6.0 with all bugfixes (so called "service packs") remains 00203 * there... This method is also public due to another bug, so please 00204 * NEVER EVER call this method directly: use write() instead 00205 */ 00206 bool writeMS(ioHandler& handler,const bool complete=true) const 00207 # endif 00208 { 00209 bool b = true; 00210 if (complete) { 00211 b = handler.writeBegin(); 00212 } 00213 00214 if (b) { 00215 00216 std::string str1,str2; 00217 switch (orthogonalizationMethod) { 00218 case Deflationary: { 00219 str1 = "Deflationary"; 00220 break; 00221 } 00222 case Symmetric: { 00223 str1 = "Symmetric"; 00224 break; 00225 } 00226 default: { 00227 str1 = "Deflationary"; 00228 break; 00229 } 00230 } 00231 switch (negentropyApproximation) { 00232 case Exp: { 00233 str2 = "Exp"; 00234 break; 00235 } 00236 case Tanh: { 00237 str2 = "Tanh"; 00238 break; 00239 } 00240 case Kurt: { 00241 str2 = "Kurt"; 00242 break; 00243 } 00244 } 00245 00246 lti::write(handler,"maxSteps",maxSteps); 00247 lti::write(handler,"epsilon",epsilon); 00248 lti::write(handler,"expAlpha",expAlpha); 00249 lti::write(handler,"tanhAlpha",tanhAlpha); 00250 lti::write(handler,"orthogonalizationMethod",str1); 00251 lti::write(handler,"negentropyApproximation",str2); 00252 } 00253 00254 # ifndef _LTI_MSC_6 00255 // This is the standard C++ code, which MS Visual C++ 6 is not able to 00256 // compile... 00257 b = b && linearAlgebraFunctor::parameters::write(handler,false); 00258 # else 00259 bool (linearAlgebraFunctor::parameters::* p_writeMS)(ioHandler&, 00260 const bool) const = 00261 linearAlgebraFunctor::parameters::writeMS; 00262 b = b && (this->*p_writeMS)(handler,false); 00263 # endif 00264 00265 if (complete) { 00266 b = b && handler.writeEnd(); 00267 } 00268 00269 return b; 00270 } 00271 00272 # ifdef _LTI_MSC_6 00273 /** 00274 * Write the parameters in the given ioHandler 00275 * @param handler the ioHandler to be used 00276 * @param complete if true (the default) the enclosing begin/end will 00277 * be also written, otherwise only the data block will be written. 00278 * @return true if write was successful 00279 */ 00280 bool write(ioHandler& handler, 00281 const bool complete=true) const { 00282 // ...we need this workaround to cope with another really 00283 // awful MSVC bug. 00284 return writeMS(handler,complete); 00285 } 00286 # endif 00287 00288 00289 # ifndef _LTI_MSC_6 00290 /** 00291 * Read the parameters from the given ioHandler 00292 * @param handler the ioHandler to be used 00293 * @param complete if true (the default) the enclosing begin/end will 00294 * be also written, otherwise only the data block will be written. 00295 * @return true if read was successful 00296 */ 00297 virtual bool read(ioHandler& handler,const bool complete=true) 00298 # else 00299 /** 00300 * This function is required by MSVC only, as a workaround for a 00301 * very awful bug, which exists since MSVC V.4.0, and still by 00302 * V.6.0 with all bugfixes (so called "service packs") remains 00303 * there... This method is also public due to another bug, so please 00304 * NEVER EVER call this method directly: use read() instead 00305 */ 00306 bool readMS(ioHandler& handler,const bool complete=true) 00307 # endif 00308 { 00309 bool b = true; 00310 if (complete) { 00311 b = handler.readBegin(); 00312 } 00313 00314 if (b) { 00315 std::string str1,str2; 00316 lti::write(handler,"maxSteps",maxSteps); 00317 lti::write(handler,"epsilon",epsilon); 00318 lti::write(handler,"expAlpha",expAlpha); 00319 lti::write(handler,"tanhAlpha",tanhAlpha); 00320 lti::write(handler,"orthogonalizationMethod",str1); 00321 lti::write(handler,"negentropyApproximation",str2); 00322 if (str1=="Symmetric") { 00323 orthogonalizationMethod = Symmetric; 00324 } // default 00325 else { 00326 orthogonalizationMethod = Deflationary; 00327 } 00328 00329 if (str2=="Tanh") { 00330 negentropyApproximation = Tanh; 00331 } else if (str2=="Kurt") { 00332 negentropyApproximation = Kurt; 00333 } //default 00334 else { 00335 negentropyApproximation = Exp; 00336 } 00337 00338 } 00339 00340 # ifndef _LTI_MSC_6 00341 // This is the standard C++ code, which MS Visual C++ 6 is not 00342 // able to compile... 00343 b = b && linearAlgebraFunctor::parameters::read(handler,false); 00344 # else 00345 bool (linearAlgebraFunctor::parameters::* p_readMS)(ioHandler&, 00346 const bool) = 00347 linearAlgebraFunctor::parameters::readMS; 00348 b = b && (this->*p_readMS)(handler,false); 00349 # endif 00350 00351 if (complete) { 00352 b = b && handler.readEnd(); 00353 } 00354 00355 return b; 00356 } 00357 00358 # ifdef _LTI_MSC_6 00359 /** 00360 * Read the parameters from the given ioHandler 00361 * @param handler the ioHandler to be used 00362 * @param complete if true (the default) the enclosing begin/end will 00363 * be also written, otherwise only the data block will be written. 00364 * @return true if read was successful 00365 */ 00366 bool read(ioHandler& handler,const bool complete=true) { 00367 // ...we need this workaround to cope with another really awful MSVC 00368 // bug. 00369 return readMS(handler,complete); 00370 } 00371 # endif 00372 00373 // ------------------------------------------------ 00374 // the parameters 00375 // ------------------------------------------------ 00376 00377 /** 00378 * maximum number of iterations to spend on one component 00379 * 00380 * Default: 1000 00381 */ 00382 int maxSteps; 00383 00384 /** 00385 * at which epsilon the vector is assumed to be converged 00386 * 00387 * Default: 0.0001 00388 */ 00389 T epsilon; 00390 00391 /** 00392 * orthogonalization method used 00393 * 00394 * Default: Deflationary 00395 */ 00396 eOrthogonalizationMethod orthogonalizationMethod; 00397 00398 /** 00399 * negentropy approximation 00400 * 00401 * Default: Exp 00402 */ 00403 eNegentropyApproximation negentropyApproximation; 00404 00405 /** 00406 * parameter for tuning the exponential negentropy approximation 00407 * 00408 * Default: 1 00409 */ 00410 T expAlpha; 00411 00412 /** 00413 * parameter for tuning the tanh negentropy approximation 00414 * 00415 * Default: 1 00416 */ 00417 T tanhAlpha; 00418 00419 }; 00420 00421 /** 00422 * Default constructor 00423 */ 00424 fastICA(); 00425 00426 /** 00427 * Construct a functor using the given parameters 00428 */ 00429 fastICA(const parameters& par); 00430 00431 /** 00432 * Copy constructor 00433 * @param other the object to be copied 00434 */ 00435 fastICA(const fastICA& other); 00436 00437 /** 00438 * Destructor 00439 */ 00440 virtual ~fastICA(); 00441 00442 /** 00443 * Returns the name of this type ("fastICA") 00444 */ 00445 virtual const char* getTypeName() const; 00446 00447 00448 /** 00449 * operates on the given %parameter. 00450 * @param srcdest matrix with the source data. The result 00451 * will be left here too. 00452 * @return true if apply successful or false otherwise. 00453 */ 00454 virtual bool apply(matrix<T>& srcdest); 00455 00456 /** 00457 * This computes a transformation matrix and transforms the given data. 00458 * @param src matrix with the source data. Each row represents a dataset. 00459 * @param dest matrix where the result will be left. 00460 * @return true if apply successful or false otherwise. 00461 */ 00462 virtual bool apply(const matrix<T>& src,matrix<T>& dest); 00463 00464 00465 /** 00466 * only computes the transformation matrix. 00467 * @param src matrix with the sourc data. Each row represents a dataset 00468 * @return true if apply successful or false otherwise. 00469 */ 00470 bool computeTransformMatrix(const matrix<T>& src); 00471 00472 /** 00473 * Returns the saved rotation matrix, which was computed with ICA. 00474 * @return a const reference to the last computed or used 00475 * rotation matrix. 00476 */ 00477 const matrix<T>& getRotationMatrix() const; 00478 00479 /** 00480 * Returns the saved rotation matrix, which was computed with ICA. 00481 * @param dest last computed or used rotation matrix will be stored here. 00482 */ 00483 bool getRotationMatrix(matrix<T>& dest) const; 00484 00485 /** 00486 * Returns the saved whitening matrix, which was computed with PCA 00487 * @return a const reference to the last computed or used 00488 * whitening matrix. 00489 */ 00490 const matrix<T>& getWhiteningMatrix() const; 00491 00492 /** 00493 * Returns the saved whitening matrix, which was computed with ICA. 00494 * @param dest last computed or used whitening matrix will be stored here. 00495 */ 00496 bool getWhiteningMatrix(matrix<T>& dest) const; 00497 00498 /** 00499 * Returns the saved transformation matrix. 00500 * This is only the product of (whitening matrix) * (rotation matrix). 00501 * 00502 * \b Note: If you want to use the transformation matrix to transform 00503 * datasets, you should subtract the offset vector from your data first. 00504 * 00505 * @return a const reference to the last computed or used 00506 * transform matrix. 00507 */ 00508 const matrix<T>& getTransformMatrix() const; 00509 00510 00511 /** 00512 * Returns the saved transformation matrix. 00513 * @param dest last computed or used transformation matrix will be stored 00514 * here. 00515 */ 00516 bool getTransformMatrix(matrix<T>& dest) const; 00517 00518 /** 00519 * Returns the previously computed offset vector, which corresponds to the 00520 * mean of the data. 00521 * 00522 * @param result the offset vector will be written here. 00523 * @return true if the matrix could be computed, false otherwise. 00524 */ 00525 bool getOffsetVector(vector<T>& result) const; 00526 00527 /** 00528 * Returns the previously computed offset vector, which corresponds to the 00529 * mean of the data. 00530 * 00531 * @return a const reference to the last computed or used offset vector. 00532 */ 00533 const vector<T>& getOffsetVector() const; 00534 00535 /** 00536 * Transform an entire matrix according to a previosly computed transformation 00537 * matrix. 00538 * @param src the input data 00539 * @param dest here the transformed data will be left 00540 * 00541 * @return true if operation was succesfull and false otherwise 00542 */ 00543 bool transform(const matrix<T>& src, matrix<T>& dest) const; 00544 00545 /** 00546 * Transform an entire matrix according to a previosly computed transformation 00547 * matrix. 00548 * @param srcdest the input data, the result will be left here, too 00549 * 00550 * @return true if operation was succesfull and false otherwise 00551 */ 00552 bool transform(matrix<T>& srcdest) const; 00553 00554 00555 /** 00556 * Transforms a single vector according to a previously computed 00557 * transformation matrix. 00558 * @param src the data vector 00559 * @param dest the transformed vector will be left here 00560 */ 00561 bool transform(const vector<T>& src, vector<T>& dest) const; 00562 00563 /** 00564 * Transforms a single vector according to a previously computed 00565 * transformation matrix. 00566 * @param srcdest the input data, the result will be left here too 00567 * @return true if operation was succesfull and false otherwise 00568 */ 00569 bool transform(vector<T>& srcdest) const; 00570 00571 /** 00572 * Copy data of "other" functor. 00573 * @param other the functor to be copied 00574 * @return a reference to this functor object 00575 */ 00576 fastICA& copy(const fastICA& other); 00577 00578 /** 00579 * Alias for copy member 00580 * @param other the functor to be copied 00581 * @return a reference to this functor object 00582 */ 00583 fastICA& operator=(const fastICA& other); 00584 00585 /** 00586 * Returns a pointer to a clone of this functor. 00587 */ 00588 virtual functor* clone() const; 00589 00590 /** 00591 * Returns used parameters 00592 * @return const reference to the parameter object of this functor. 00593 */ 00594 const parameters& getParameters() const; 00595 00596 00597 /** 00598 * Update functor's parameters. 00599 * This member updates the internal state of the functor according to the parameter set. 00600 * @return true if successful, false otherwise 00601 */ 00602 virtual bool updateParameters(); 00603 00604 00605 protected: 00606 /** 00607 * PCA functor which is applied before ICA with Whitening 00608 */ 00609 principalComponents<T> pca; 00610 00611 /** 00612 * method to center the data 00613 */ 00614 bool centerData(matrix<T>& srcdest) const; 00615 00616 /** 00617 * ICA with deflationary approach 00618 */ 00619 bool icaDeflationary(const matrix<T>& src, matrix<T>& dest, const parameters& par) const; 00620 00621 /** 00622 * ICA with symmetric approach 00623 */ 00624 bool icaSymmetric(const matrix<T>& src, matrix<T>& dest, const parameters& par) const; 00625 00626 00627 /** 00628 * method to determine if a vector is close enough to another 00629 */ 00630 bool closeEnough(const vector<T>& a, 00631 const vector<T>& b, 00632 const T epsilon) const; 00633 00634 /** 00635 * method to determine if a vector is close enough to another 00636 */ 00637 bool closeEnough(const matrix<T>& a, 00638 const matrix<T>& b, 00639 const T epsilon) const; 00640 00641 /** 00642 * Function pointer to the non-linearity function which should be used. 00643 * This pointer is set in updateParameters dependent on the 00644 * negentropyAproximation setting. 00645 * 00646 * Default: negentropyApproximation 00647 */ 00648 T (fastICA<T>::*g) (const T x) const; 00649 00650 /** 00651 * Function pointer to derivation of above g. 00652 */ 00653 T (fastICA<T>::*gd) (const T x) const; 00654 00655 /** 00656 * Constants for tuning negentropyApproximation functions are copied 00657 * from parameters in setParameters to this variable. 00658 */ 00659 T a1; 00660 00661 /** 00662 * Used to choose initial random vectors. 00663 */ 00664 uniformDistribution uniDist; 00665 00666 /** 00667 * First derivation of Exp negentropyApproximation. 00668 */ 00669 T expo1(const T x) const; 00670 00671 /** 00672 * Second derivation of Exp negentropyApproximation. 00673 */ 00674 T expo2(const T x) const; 00675 00676 /** 00677 * First derivation of Tanh negentropyApproximation. 00678 */ 00679 T tanh1(const T x) const; 00680 00681 /** 00682 * Second derivation of Tanh negentropyApproximation. 00683 */ 00684 T tanh2(const T x) const; 00685 00686 /** 00687 * First derivation of Pow4 negentropyApproximation (Kurtosis). 00688 */ 00689 T kurt1(const T x) const; 00690 00691 /** 00692 * Second derivation of Pow4 negentropyApproximation (Kurtosis). 00693 */ 00694 T kurt2(const T x) const; 00695 00696 /** 00697 * Matrix which is used to save the transform matrix. 00698 */ 00699 matrix<T> transformMatrix; 00700 00701 /** 00702 * Matrix which is used to save the whitening matrix. 00703 */ 00704 matrix<T> whiteningMatrix; 00705 00706 /** 00707 * Matrix which is used to save the whitening matrix. 00708 */ 00709 matrix<T> rotationMatrix; 00710 00711 }; 00712 } 00713 00714 #endif