/** @file */ /* Copyright (c) 2008, Naotoshi Seo. All rights reserved. * * The program is free to use for non-commercial academic purposes, * but for course works, you must understand what is going inside to * use. The program can be used, modified, or re-distributed for any * purposes only if you or one of your group understand not only * programming codes but also theory and math behind (if any). * Please contact the authors if you are interested in using the * program without meeting the above conditions. */ #include "cv.h" #include "cvaux.h" #include #include #define _USE_MATH_DEFINES #include #ifndef CV_PCADIFFS_INCLUDED #define CV_PCADIFFS_INCLUDED #ifndef NO_DOXYGEN CVAPI(void) cvMatPcaDiffs( const CvMat* samples, const CvMat* avg, const CvMat* eigenvalues, const CvMat* eigenvectors, CvMat* probs, int normalize, bool logprob ); CV_INLINE double cvPcaDiffs( const CvMat* sample, const CvMat* avg, const CvMat* eigenvalues, const CvMat* eigenvectors, int normalize, bool logprob ); #endif /** * PCA Distance "in" and "from" feature space [1] * * This computes a distance between a point to a PCA subspace as sum of * distance-from-feature space (DFFS) and distance-in-feature-space * (DIFS). The DFFS is essentially a reconstruction error and the * DIFS measures (mahalanobis) distance between projected point and * origin of PCA subpsace. DFFS and DIFS can be used to measure * approximated likelihood Gaussian density distribution. * See [1] for more details. * * @param samples D x N sample vectors * @param avg D x 1 mean vector * @param eigenvalues nEig x 1 eigen values * @param eigenvectors M x D or D x M (automatically adjusted) eigen vectors * @param probs 1 x N computed likelihood probabilities * @param normalize Compute normalization term or not * 0 - nothing * 1 - normalization term * 2 - normalize so that sum becomes 1.0 * @param logprob Log probability or not * @see CVAPI(void) cvCalcPCA( const CvArr* data, CvArr* avg, CvArr* eigenvalues, CvArr* eigenvectors, int flags ); * @see CVAPI(void) cvProjectPCA( const CvArr* data, const CvArr* avg, const CvArr* eigenvectors, CvArr* result ); * @see CVAPI(void) cvBackProjectPCA( const CvArr* proj, const CvArr* avg,const CvArr* eigenvects, CvArr* result ); * * References * @verbatim * [1] @INPROCEEDINGS{Moghaddam95probabilisticvisual, * author = {Baback Moghaddam and Alex Pentl}, * title = {Probabilistic visual learning for object detection}, * booktitle = {}, * year = {1995}, * pages = {786--793} * } * [2] @ARTICLE{Moghaddam02principalmanifolds, * author = {Baback Moghaddam}, * title = {Principal manifolds and probabilistic subspaces for visual recognition}, * journal = {IEEE Transactions on Pattern Analysis and Machine Intelligence}, * year = {2002}, * volume = {24}, * pages = {780--788} * @endverbatim */ CVAPI(void) cvMatPcaDiffs( const CvMat* samples, const CvMat* avg, const CvMat* eigenvalues, const CvMat* eigenvectors, CvMat* probs, int normalize CV_DEFAULT(1), bool logprob CV_DEFAULT(false) ) { int D = samples->rows; int N = samples->cols; int M = (eigenvectors->rows == D) ? eigenvectors->cols : eigenvectors->rows; int type = samples->type; int nEig = eigenvalues->rows; int d, n; double normterm = 0; CvMat *_eigenvectors; // cvProjectPCA requires M x D vec CvMat *proj = cvCreateMat( N, M, type ); CvMat *subproj, subprojhdr; CvMat *sclsubproj = cvCreateMat( 1, M, type ); CvMat *pLambda, pLambdaHdr; CvMat *rLambda, rLambdaHdr; CvMat *sqrt_pLambda = cvCreateMat( M, 1, type ); CvMat *DIFS = cvCreateMat( 1, N, type ); CvMat *DFFS = cvCreateMat( 1, N, type ); CvMat *samples0 = cvCreateMat( D, N, type ); // mean subtracted samples CvMat *subsamples0, subsamples0hdr; CvScalar rho; CvMat *_proj; CV_FUNCNAME( "cvMatPcaDiffs" ); __CV_BEGIN__; CV_ASSERT( CV_IS_MAT(samples) ); CV_ASSERT( CV_IS_MAT(avg) ); CV_ASSERT( CV_IS_MAT(eigenvalues) ); CV_ASSERT( CV_IS_MAT(eigenvectors) ); CV_ASSERT( D == eigenvectors->rows || D == eigenvectors->cols ); CV_ASSERT( D == avg->rows && 1 == avg->cols ); CV_ASSERT( 1 == probs->rows && N == probs->cols ); cvZero( DIFS ); cvZero( DFFS ); if( D == eigenvectors->rows ) { _eigenvectors = cvCreateMat( M, D, type ); cvT( eigenvectors, _eigenvectors ); } else { _eigenvectors = (CvMat*)eigenvectors; } //cvProjectPCA( samples, avg, _eigenvectors, proj ); for( n = 0; n < N; n++ ) { // want mean subtracted samples for laster too for( d = 0; d < D; d++ ) { cvmSet( samples0, d, n, cvmGet( samples, d, n ) - cvmGet( avg, d, 0 ) ); } } _proj = cvCreateMat( M, N, type ); cvMatMul( _eigenvectors, samples0, _proj ); cvT( _proj, proj ); cvReleaseMat( &_proj ); // distance in feature space if( M > 0 ) { pLambda = cvGetRows( eigenvalues, &pLambdaHdr, 0, M ); cvPow( pLambda, sqrt_pLambda, 0.5 ); for( n = 0; n < N; n++ ) { subproj = cvGetRow( proj, &subprojhdr, n ); for( d = 0; d < M; d++ ) { cvmSet( sclsubproj, 0, d, cvmGet( subproj, 0, d ) / cvmGet( sqrt_pLambda, d, 0 ) ); } cvmSet( DIFS, 0, n, pow(cvNorm( sclsubproj, NULL, CV_L2 ), 2) ); } if( normalize == 1 ) { //cvLog( sqrt_pLambda, sqrt_pLambda ); //cvSum( sqrt_pLambda ); for( d = 0; d < M; d++ ) { normterm += log( cvmGet( sqrt_pLambda, d, 0 ) ); } normterm += log(2*M_PI)*(M/2.0); } } // distance from feature space if( nEig > M ) { rLambda = cvGetRows( eigenvalues, &rLambdaHdr, M, nEig ); rho = cvAvg( rLambda ); for( n = 0; n < N; n++ ) { subsamples0 = cvGetCol( samples0, &subsamples0hdr, n ); subproj = cvGetRow( proj, &subprojhdr, n ); cvmSet( DFFS, 0, n, pow(cvNorm( subsamples0, NULL, CV_L2 ),2) - pow(cvNorm( subproj, NULL, CV_L2 ),2) ); } cvScale( DFFS, DFFS, 1.0/rho.val[0] ); if( normalize == 1 ) { normterm += log(2*M_PI*rho.val[0]) * ((nEig - M)/2.0); } } // sum DIFS and DFFS in log form for( n = 0; n < N; n++ ) { cvmSet( probs, 0, n, cvmGet( DIFS, 0, n ) / (-2) + cvmGet( DFFS, 0, n) / (-2) - normterm ); } // normalization and so on if( normalize == 2 ) { double minval, maxval; cvMinMaxLoc( probs, &minval, &maxval ); cvSubS( probs, cvScalar( maxval ), probs ); } if( !logprob || normalize == 2 ) { for( n = 0; n < N; n++ ) { cvmSet( probs, 0, n, exp(cvmGet( probs, 0, n )) ); } if( normalize == 2 ) { CvScalar sumprob = cvSum( probs ); cvScale( probs, probs, 1.0 / sumprob.val[0] ); } } if( logprob && normalize == 2 ) { for( n = 0; n < N; n++ ) { cvmSet( probs, 0, n, log(cvmGet( probs, 0, n )) ); } } cvReleaseMat( &proj ); cvReleaseMat( &sclsubproj ); cvReleaseMat( &sqrt_pLambda ); cvReleaseMat( &DIFS ); cvReleaseMat( &DFFS ); cvReleaseMat( &samples0 ); if( D == eigenvectors->rows ) { cvReleaseMat( &_eigenvectors ); } __CV_END__; } /** * PCA Distance "in" and "from" feature space * * @param sample D x 1 feature vector * @param avg D x 1 mean vector * @param eigenvalues nEig x 1 eigen values * @param eigenvectors M x D eigen vectors * @param normalize Compute normalization term or not * 0 - nothing * 1 - normalization term * 2 - normalize so that sum becomes 1.0 * @param logprob Log probability or not * @return double likelihood */ CV_INLINE double cvPcaDiffs( const CvMat* sample, const CvMat* avg, const CvMat* eigenvalues, const CvMat* eigenvectors, int normalize CV_DEFAULT(1), bool logprob CV_DEFAULT(false) ) { double prob; CvMat *_probs = cvCreateMat( 1, 1, sample->type ); cvMatPcaDiffs( sample, avg, eigenvalues, eigenvectors, _probs, normalize, logprob ); prob = cvmGet(_probs, 0, 0); cvReleaseMat( &_probs ); return prob; } #endif