cmtkActiveShapeModel.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 1997-2009 Torsten Rohlfing
00004 //
00005 //  Copyright 2004-2010 SRI International
00006 //
00007 //  This file is part of the Computational Morphometry Toolkit.
00008 //
00009 //  http://www.nitrc.org/projects/cmtk/
00010 //
00011 //  The Computational Morphometry Toolkit is free software: you can
00012 //  redistribute it and/or modify it under the terms of the GNU General Public
00013 //  License as published by the Free Software Foundation, either version 3 of
00014 //  the License, or (at your option) any later version.
00015 //
00016 //  The Computational Morphometry Toolkit is distributed in the hope that it
00017 //  will be useful, but WITHOUT ANY WARRANTY; without even the implied
00018 //  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 //  GNU General Public License for more details.
00020 //
00021 //  You should have received a copy of the GNU General Public License along
00022 //  with the Computational Morphometry Toolkit.  If not, see
00023 //  <http://www.gnu.org/licenses/>.
00024 //
00025 //  $Revision: 2398 $
00026 //
00027 //  $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $
00028 //
00029 //  $LastChangedBy: torstenrohlfing $
00030 //
00031 */
00032 
00033 #include <Base/cmtkActiveShapeModel.h>
00034 
00035 #include <Base/cmtkMatrix.h>
00036 #include <Base/cmtkMathUtil.h>
00037 
00038 #include <System/cmtkConsole.h>
00039 
00040 #include <math.h>
00041 #include <algorithm>
00042 
00043 namespace
00044 cmtk
00045 {
00046 
00049 
00050 ActiveShapeModel::ActiveShapeModel
00051 ( CoordinateVector::SmartPtr& mean, DirectionSet::SmartPtr& modes, CoordinateVector::SmartPtr& modeVariances ) :
00052   Mean( mean ), Modes( modes ), ModeVariances( modeVariances )
00053 {
00054   NumberOfPoints = Mean->Dim;
00055   NumberOfModes = Modes->GetNumberOfDirections();
00056 }
00057 
00058 float 
00059 ActiveShapeModel::Construct
00060 ( const Types::Coordinate *const* trainingSet, const unsigned int numberOfSamples,
00061   const unsigned int numberOfPoints, const unsigned int numberOfModes )
00062 {
00063   if ( numberOfSamples < numberOfModes ) 
00064     {
00065     StdErr << "WARNING: number of modes of an ASM can be no higher than number of training samples.\n";
00066     this->Allocate( numberOfPoints, numberOfSamples );
00067     } 
00068   else
00069     {
00070     this->Allocate( numberOfPoints, numberOfModes );
00071     }
00072   
00073 
00074   // first, compute mean shape
00075   Types::Coordinate *meanPtr = Mean->Elements;
00076   for ( unsigned int point = 0; point < NumberOfPoints; ++point, ++meanPtr ) 
00077     {
00078     Types::Coordinate mean = trainingSet[0][point];
00079     for ( unsigned int sample = 1; sample < numberOfSamples; ++sample ) 
00080       {
00081       mean += trainingSet[sample][point];
00082       }
00083     (*meanPtr) = (mean / numberOfSamples );
00084     }
00085   
00086   // now generate covariance matrix; actually, we're using a slightly
00087   // modified approach following Cootes' 1995 CVIU paper. This is much
00088   // more efficient when the number of samples is smaller than the
00089   // number of data dimensions.
00090   Matrix2D<Types::Coordinate> cc( numberOfSamples, numberOfSamples );
00091   
00092   for ( unsigned int sampleY = 0; sampleY < numberOfSamples; ++sampleY )
00093     for ( unsigned int sampleX = 0; sampleX < numberOfSamples; ++sampleX ) {
00094 
00095       // compute upper half of matrix, use symmetry to fill lower half
00096       if ( sampleY <= sampleX ) 
00097         {
00098         Types::Coordinate ccXY = 0;
00099         
00100         const Types::Coordinate* meanPtr = Mean->Elements;
00101         for ( unsigned int point = 0; point < NumberOfPoints; ++point, ++meanPtr )
00102           {
00103           ccXY += ( trainingSet[sampleX][point] - (*meanPtr) ) * ( trainingSet[sampleY][point] - (*meanPtr) );
00104           }
00105         cc[sampleX][sampleY] = ccXY / numberOfSamples;
00106         }
00107       else
00108         {
00109         cc[sampleX][sampleY] = cc[sampleY][sampleX];
00110         }
00111       
00112     }
00113   
00114   // here comes the hard part: compute Eigenvectors of cc...
00115   // we do this in a separate routine, for clarity.
00116   Matrix2D<Types::Coordinate> eigensystem( numberOfSamples, numberOfSamples );
00117   std::vector<Types::Coordinate> eigenvalues( numberOfSamples );
00118 
00119   MathUtil::ComputeEigensystem( cc, eigensystem, eigenvalues );
00120 
00121   // determine permutation that orders eigenvectors by descending eigenvalues
00122   std::vector<unsigned int> permutation( numberOfSamples );
00123   // initialize permutation array
00124   for ( unsigned int i = 0; i < numberOfSamples; ++i )
00125     permutation[i] = i;
00126   
00127   // now do a simple bubble sort
00128   bool sorted = false;
00129   while ( ! sorted ) 
00130     {
00131     sorted = true;
00132     for ( unsigned int i = 0; i < numberOfSamples-1; ++i )
00133       if ( eigenvalues[permutation[i]] < eigenvalues[permutation[i+1]] ) 
00134         {
00135         std::swap( permutation[i], permutation[i+1] );
00136         sorted = false;
00137         }
00138     }
00139   
00140   // now, we need to convert the eigenvectors of the simplified matrix
00141   // back to those of the actual covariance matrix. Again, this follows
00142   // Cootes et al., CVIU 1995
00143   for ( unsigned int mode = 0; mode < NumberOfModes; ++mode ) 
00144     {    
00145     ModeVariances->Elements[mode] = eigenvalues[permutation[mode]];
00146 
00147     Types::Coordinate* modePtr = (*Modes)[mode]->Elements;
00148     for ( unsigned int point = 0; point < NumberOfPoints; ++point, ++modePtr ) 
00149       {
00150       unsigned int fromMode = permutation[mode];
00151       Types::Coordinate meanValue = Mean->Elements[point];
00152       
00153       *modePtr = 0;
00154       for ( unsigned int sample = 0; sample < numberOfSamples; ++sample )
00155         *modePtr += (eigensystem[sample][fromMode] * (trainingSet[sample][point] - meanValue) );
00156       }
00157     
00158     // finally, normalize mode vectors... if Geremy is right ;)
00159     (*(*Modes)[mode]) *= (sqrt( eigenvalues[permutation[mode]] ) / (*Modes)[mode]->EuclidNorm());
00160     }
00161   
00162   return 0;
00163 }
00164 
00165 Types::Coordinate*
00166 ActiveShapeModel::Generate
00167 ( Types::Coordinate *const instance, const Types::Coordinate* modeWeights ) const
00168 {
00169   Types::Coordinate* target = instance;
00170   if ( !target )
00171     target = Memory::AllocateArray<Types::Coordinate>( NumberOfPoints );
00172 
00173   memcpy( target, Mean->Elements, sizeof( *target ) * NumberOfPoints );
00174 
00175   if ( modeWeights )
00176     {
00177     for ( unsigned int mode = 0; mode < NumberOfModes; ++mode ) 
00178       {
00179       Types::Coordinate modeWeight = modeWeights[mode];
00180       
00181       Types::Coordinate* targetPtr = target;
00182       const Types::Coordinate* modePtr = (*Modes)[mode]->Elements;
00183       
00184       for ( unsigned int point = 0; point < NumberOfPoints; ++point, ++targetPtr, ++modePtr )
00185         (*targetPtr) += ( modeWeight * (*modePtr) );
00186       }
00187     }
00188   
00189   return target;
00190 }
00191 
00192 float
00193 ActiveShapeModel::Decompose
00194 ( const CoordinateVector* input, Types::Coordinate *const weights ) const
00195 {
00196   std::vector<Types::Coordinate> w( this->NumberOfModes );
00197   CoordinateVector deviation( *input );
00198   deviation -= *(this->Mean);
00199 
00200 #define RETURN_PDF
00201 #ifdef RETURN_PDF
00202   float pdf = 1.0;
00203   for ( size_t mode = 0; mode < this->NumberOfModes; ++mode ) 
00204     {
00205     const CoordinateVector* thisMode = (*this->Modes)[mode];
00206     // since Modes are orthogonal basis, we can decompose using scalar product
00207     w[mode] = (deviation * *thisMode) / thisMode->EuclidNorm();
00208     
00209     const Types::Coordinate variance = (*(this->ModeVariances))[mode];
00210     pdf *= exp( -(w[mode]*w[mode]) / (2.0 * variance) ) / sqrt( 2.0 * M_PI * variance);
00211     }
00212 #else
00213   float distance = 0.0;
00214   for ( size_t mode = 0; mode < this->NumberOfModes; ++mode ) 
00215     {
00216     const CoordinateVector* thisMode = (*this->Modes)[mode];
00217     // since Modes are orthogonal basis, we can decompose using scalar product
00218     w[mode] = (deviation * *thisMode) / thisMode->EuclidNorm();
00219     
00220     const Types::Coordinate variance = (*(this->ModeVariances))[mode];
00221     distance += w[mode] * w[mode] / variance;
00222     }
00223   distance = sqrt( distance );
00224 #endif
00225   
00226   if ( weights )
00227     memcpy( weights, &w[0], this->NumberOfModes * sizeof( *weights ) );
00228   
00229 #ifdef RETURN_PDF
00230   return pdf;
00231 #else
00232   return distance;
00233 #endif
00234 }
00235 
00236 void
00237 ActiveShapeModel::Allocate
00238 ( const unsigned int numberOfPoints, const unsigned int numberOfModes )
00239 {
00240   NumberOfModes = numberOfModes;
00241   NumberOfPoints = numberOfPoints;
00242 
00243   Modes = DirectionSet::SmartPtr( new DirectionSet( NumberOfPoints ) );
00244   for ( unsigned int mode = 0; mode < NumberOfModes; ++mode )
00245     Modes->push_back( CoordinateVector::SmartPtr( new CoordinateVector( NumberOfPoints ) ) );
00246   ModeVariances = CoordinateVector::SmartPtr( new CoordinateVector( NumberOfModes ) );
00247   Mean = CoordinateVector::SmartPtr( new CoordinateVector( NumberOfPoints ) );
00248 }
00249 
00250 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines