/** * @file PCA.cpp * @brief Computation of a PCA by Eigenvalue Decomposition, Karhunen Loewe Transform * @author Michael Koch * @date 5/27/2008 */ /** \example testPCA.cpp * This is an example of how to use the Principal Component Analysis (PCA) class. */ //#define DEBUG #undef DEBUG #include #include #include #include #include "core/algebra/GenericMatrix.h" #include "core/algebra/EigValues.h" #include "core/algebra/EigValuesTRLAN.h" // #include "vislearning/fourier/FourierLibrary.h" #include "PCA.h" #include "vislearning/baselib/Gnuplot.h" using namespace OBJREC; using namespace std; using namespace NICE; PCA::PCA ( uint dim, uint maxiteration, double mindelta ) { init ( dim, maxiteration, mindelta ); } PCA::PCA ( void ) { init(); } PCA::~PCA() { } void PCA::init ( uint dim, uint maxiteration, double mindelta ) { this->targetDimension = dim; this->maxiteration = maxiteration; this->mindelta = mindelta; } void PCA::restore ( istream & is, int format ) { is >> basis; is >> normalization; is >> mean; is >> targetDimension; } void PCA::store ( ostream & os, int format ) const { os << basis << normalization << mean << targetDimension; } void PCA::clear() { } void PCA::calculateBasis ( const NICE::Matrix &features, const uint targetDimension, const uint mode ) { calculateBasis ( features, targetDimension, false ); } void PCA::calculateMean ( const NICE::Matrix &features, NICE::Vector & mean ) { // data vectors are put row-wise in the matrix mean.resize ( features.cols() ); mean.set(0); for ( uint i = 0 ; i < features.rows(); i++ ) mean = mean + features.getRow ( i ); mean /= features.rows(); } void PCA::calculateBasis ( const NICE::Matrix &features, const uint targetDimension, const bool adaptive, const double targetRatio ) { this->targetDimension = targetDimension; // dimension of the feature vectors uint srcFeatureSize = features.cols(); uint mindimension = std::min ( this->targetDimension, srcFeatureSize ); NICE::Matrix eigenvectors; NICE::Vector eigenvalue; calculateMean ( features, mean ); #ifdef NICE_USELIB_TRLAN EigValues *eig; if(mindimension < 12) eig = new EVArnoldi();//Arnoldi for (srcFeatureSizegetEigenvalues ( C, eigenvalue, eigenvectors, srcFeatureSize ); } else { eig->getEigenvalues ( C, eigenvalue, eigenvectors, mindimension ); } #ifdef DEBUG fprintf ( stderr, "Eigenvalue Decomposition ready \n" ); cerr << eigenvectors << endl; cerr << eigenvalue << endl; //sort values fprintf ( stderr, "Eigenvector-Rows:%i Eigenvector-Cols:%i\n", ( int ) eigenvectors.rows(), ( int ) eigenvectors.cols() ); #endif multimap map; double sumeigen = 0.0; NICE::Vector ratio ( eigenvectors.cols() ); for ( uint i = 0; i < eigenvectors.cols(); i++ ) //every eigenvector { NICE::Vector eigenvector ( srcFeatureSize ); for ( uint k = 0; k < srcFeatureSize; k++ ) { eigenvector[k] = eigenvectors ( k, i ); } map.insert ( pair ( eigenvalue[i], eigenvector ) ); sumeigen += eigenvalue[i]; } //compute basis size if ( adaptive ) { //compute target dimension uint dimensioncount = 0; double addedratio = 0.0; multimap::reverse_iterator it = map.rbegin(); while ( addedratio <= targetRatio && it != map.rend() ) { //calc ratio ratio[dimensioncount] = ( *it ).first / sumeigen; addedratio += ratio[dimensioncount]; dimensioncount++; it++; } this->targetDimension = dimensioncount; } mindimension = std::min ( this->targetDimension, srcFeatureSize ); this->targetDimension = mindimension; basis = NICE::Matrix ( srcFeatureSize, mindimension ); //get sorted values uint count = 0; multimap::reverse_iterator it = map.rbegin(); while ( count < this->targetDimension && it != map.rend() ) { NICE::Vector eigenvector = ( *it ).second; //put eigenvector into column for ( uint k = 0; k < srcFeatureSize; k++ ) { basis ( k, count ) = eigenvector[k]; } //calc ratio ratio[count] = ( *it ).first / sumeigen; count++; it++; } //normalization matrix / modify variance to 1 for all eigenvectors normalization = NICE::Matrix ( mindimension, mindimension, 0 ); for ( uint k = 0; k < mindimension; k++ ) { normalization ( k, k ) = 1.0 / sqrt ( eigenvalue[k] ); } #ifdef DEBUG cout << "Eigenvalue-absolute:" << eigenvalue << endl; cout << "Eigenvalue-ratio:" << ratio << endl; #endif } NICE::Vector PCA::getFeatureVector ( const NICE::Vector &data, const bool normalize ) { //free data of mean if ( normalize ) { NICE::Vector meanfree ( data ); meanfree -= mean; //Y=W^t * B^T if ( normbasis.rows() == 0 ) normbasis.multiply ( normalization, basis, false, true ); NICE::Vector tmp; tmp.multiply ( normbasis, meanfree ); return tmp; } else { NICE::Vector meanfree ( data ); meanfree -= mean; //Y=W^t * B^T NICE::Vector tmp; tmp.multiply ( basis, meanfree, true ); return tmp; } } NICE::Vector PCA::getMean() { return mean; } NICE::Matrix PCA::getBasis() { return basis; } int PCA::getTargetDim() { return targetDimension; }