cmtkTypedArrayNoiseEstimatorBrummer.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 2008-2011 SRI International
00004 //
00005 //  This file is part of the Computational Morphometry Toolkit.
00006 //
00007 //  http://www.nitrc.org/projects/cmtk/
00008 //
00009 //  The Computational Morphometry Toolkit is free software: you can
00010 //  redistribute it and/or modify it under the terms of the GNU General Public
00011 //  License as published by the Free Software Foundation, either version 3 of
00012 //  the License, or (at your option) any later version.
00013 //
00014 //  The Computational Morphometry Toolkit is distributed in the hope that it
00015 //  will be useful, but WITHOUT ANY WARRANTY; without even the implied
00016 //  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 //  GNU General Public License for more details.
00018 //
00019 //  You should have received a copy of the GNU General Public License along
00020 //  with the Computational Morphometry Toolkit.  If not, see
00021 //  <http://www.gnu.org/licenses/>.
00022 //
00023 //  $Revision: 2752 $
00024 //
00025 //  $LastChangedDate: 2011-01-17 11:33:31 -0800 (Mon, 17 Jan 2011) $
00026 //
00027 //  $LastChangedBy: torstenrohlfing $
00028 //
00029 */
00030 
00031 #include "cmtkTypedArrayNoiseEstimatorBrummer.h"
00032 
00033 namespace
00034 cmtk
00035 {
00036 
00037 /*   Brummer estimation of the noise variance.
00038  *   (As described in the Sijbers paper.)
00039  */
00040 TypedArrayNoiseEstimatorBrummer::TypedArrayNoiseEstimatorBrummer
00041 ( const TypedArray* data, const size_t histogramBins )
00042 {
00043   UNUSED(histogramBins);
00044 
00045   const size_t histogramBinsActual = 255;
00046   Histogram<unsigned int>::SmartPtr histogram( data->GetHistogram( histogramBinsActual ) );
00047 
00048 //  int numBinsToUse;
00049 
00050   double sampleMean = 0;
00051   for ( size_t i = 0; i < histogramBinsActual; i++ )
00052     {
00053       sampleMean += (*histogram)[i] * histogram->BinToValue(i);
00054     }
00055   sampleMean /= histogram->SampleCount();
00056   std::cout << " sampleMean: " << sampleMean << std::endl;
00057   std::cout << " sampleCount: " << histogram->SampleCount() << std::endl;
00058   std::cout << " 1 / sampleCount: " << (1.0 / histogram->SampleCount()) << std::endl;
00059   
00060   double sumSquaredSampleDiffs = 0;
00061   for ( size_t i = 0; i < histogramBinsActual; i++ )
00062     {
00063       sumSquaredSampleDiffs += (*histogram)[i] * pow( ( histogram->BinToValue(i) - sampleMean ), 2 );
00064     }
00065   std::cout << " sumSquaredSampleDiffs: " << sumSquaredSampleDiffs << std::endl;
00066   
00067   const double sampleStdDev = sqrt( ( 1.0 / histogram->SampleCount() ) * sumSquaredSampleDiffs );
00068   std::cout << " sampleStdDev: " << sampleStdDev << std::endl;
00069   std::cout << " npow: " << pow( (double)histogram->SampleCount(), 1/5 ) << std::endl;
00070 
00071   /* Find an initial estimate of sigma using the Chang method.
00072    * (Chang L-C et al 2005 "An automatic method for estimating noise-induced signal 
00073    *  variance in magnitude-reconstructed magnetic resonance images")
00074    */
00075   const double changH = 1.0 / ( 1.06 * sampleStdDev * pow( (double)histogram->SampleCount(), 1/5 ) );
00076   double changSigma = 0;
00077   double sigmaUpperBound = 100;
00078   double valueToMaximize = std::numeric_limits<double>::min(); 
00079   for ( double maybeChangSigma = 1.0; maybeChangSigma < sigmaUpperBound; maybeChangSigma += 1.5 )
00080     {
00081 
00082     double changSum = 0;
00083     for ( size_t i = 0; i < histogramBinsActual; i++ )
00084       {
00085       const double valToAdd = ( (*histogram)[i]  
00086                                 * ( 1.0 / sqrt(2.0 * M_PI) )
00087                                 * exp( ( -1.0 * log( pow( (double)( maybeChangSigma - histogram->BinToValue(i) ) / changH, 2 )
00088                                                        / 2.0) ) ) );
00089         changSum += valToAdd;
00090         // std::cout << " valToAdd: " << valToAdd << std::endl;
00091       }
00092 
00093     std::cout << " changSum: " << changSum << std::endl;
00094 
00095     double curValueToMaximize = ( 1.0 / ( histogram->SampleCount() * changH ) ) * changSum;
00096     
00097     std::cout << " curValueToMaximize: " << curValueToMaximize << std::endl;
00098 
00099     if ( curValueToMaximize > valueToMaximize )
00100       {
00101       valueToMaximize = curValueToMaximize;
00102       changSigma = maybeChangSigma;
00103       std::cout << "New max: " << curValueToMaximize << " New changSigma: " << changSigma << std::endl;
00104       }
00105 
00106     }
00107 
00108   /*  Using the Chang sigma as a starting estimate, use
00109    *  the Brummer method to get a final sigma estimate.
00110    */
00111   valueToMaximize = std::numeric_limits<double>::min();
00112   double sigma0 = changSigma;
00113   //double sigma0 = 27.0;
00114   double maxAmplitude = 2000;
00115   double brummerSigma = changSigma;
00116   for ( double maybeSigma = 0; maybeSigma < histogramBinsActual; maybeSigma += 1.0 )
00117     {
00118     for ( double maybeAmplitude = 0; maybeAmplitude < maxAmplitude; maybeAmplitude += 10.0 )
00119       {
00120       double brummerSum = 0;
00121       for ( int i = 0; i <= 2 * sigma0; i++ )
00122         {
00123         brummerSum += log( pow( (double)(*histogram)[i] 
00124                                 - maybeAmplitude 
00125                                 * ( histogram->BinToValue(i) / pow( maybeSigma, 2) )
00126                                 * exp( -1 * ( pow( (double)histogram->BinToValue(i),2) / (2 * pow( maybeSigma, 2 ) ) ) ) 
00127                                 , 2 ) );
00128         }
00129       std::cout << " sigma: " << maybeSigma << " K: " << maybeAmplitude << " brummerSum: " << brummerSum << std::endl;
00130       if ( brummerSum > valueToMaximize )
00131         {
00132           valueToMaximize = brummerSum;
00133           brummerSigma = maybeSigma;
00134           std::cout << "new valueToMaximize: " << valueToMaximize << std::endl;
00135           std::cout << "new brummerSigma: " << brummerSigma << std::endl;
00136         }
00137       }
00138     }
00139   std::cerr << brummerSigma << std::endl;
00140   //return static_cast<Types::DataItem>( maxLikelySigma );
00141   
00142   this->m_NoiseLevelSigma = static_cast<Types::DataItem>( 0 );
00143 }
00144 
00145 double
00146 TypedArrayNoiseEstimatorBrummer::SijbersBiasHat
00147 ( const Histogram<unsigned int>::SmartPtr histogram, const double sigmaHat, const int numBinsToUse )
00148 {
00149 
00150   double K = numBinsToUse;
00151 
00152   /* 
00153    *  Count the number of samples in the partial histogram
00154    *  (bins 0 thru K-1 of histogram)
00155    */
00156   int partialHistCount = 0;
00157   for ( int i = 0; i < K; i++ )
00158     {
00159     partialHistCount += (*histogram)[i];
00160     }
00161 
00162   /* 
00163    *  Pre-calculate some recurring quantities
00164    */
00165   const double twoSigmaSquared = 2 * sigmaHat * sigmaHat;
00166   const double denom = 
00167       exp( -1 * ( log( (double)pow( histogram->BinToValue( 0 ), 2 ) / twoSigmaSquared ) ) )
00168     - exp( -1 * ( log( (double)pow( histogram->BinToValue( K ), 2 ) / twoSigmaSquared ) ) ) ; 
00169   // This will be a denominator below, needs to be != 0             
00170   assert ( denom != 0 );
00171 
00172   /* 
00173    * lambdaStar is composed of two sums: one using
00174    * bins 0 thru K-1 of histogram, and
00175    * one using bins K through the end of the
00176    * histogram.  This loop computes the first sum.
00177    */
00178   double lambdaStar = 0.0;
00179   for ( int i = 1; i < K; i++ )
00180     {
00181     const double quotient = 
00182       ( exp( -1 * ( log( (double)pow( histogram->BinToValue(i-1), 2 ) ) ) / twoSigmaSquared ) 
00183         - exp( -1 * ( log( pow( (double)histogram->BinToValue( i ), 2 ) ) ) / twoSigmaSquared ) )
00184       / denom;
00185     const double f = partialHistCount * quotient;
00186     assert ( f != 0 );
00187     lambdaStar += pow( f - (*histogram)[i], 2 ) / f;
00188     }  
00189 
00190   /* 
00191    *  Second sum for lambdaStar
00192    */
00193   for ( size_t i = static_cast<size_t>( K ); i < histogram->GetNumBins(); i++ )
00194     {
00195     const double quotient = 
00196       ( exp( -1 * ( log( pow( (double)histogram->BinToValue(i-1), 2 ) ) ) / twoSigmaSquared ) 
00197         - exp( -1 * ( log( pow( (double)histogram->BinToValue( i ), 2 ) ) ) / twoSigmaSquared ) )
00198       / denom;
00199     const double f = partialHistCount * quotient;
00200     double numerator = ( f - (*histogram)[i] );
00201     if ( numerator < 0 ) numerator = 0 ;
00202     assert ( f != 0 );
00203     lambdaStar += pow( numerator, 2 ) / f;
00204                       
00205     }
00206 
00207   /* 
00208    *  Compute M (variable name same as in Sijbers)
00209    */
00210   int M = 0;
00211   for ( size_t i = static_cast<size_t>( K ); i < histogram->GetNumBins(); i++ )
00212     {
00213     const double quotient = 
00214       ( exp( -1 * ( log( pow( (double)histogram->BinToValue(i-1), 2 ) ) / twoSigmaSquared ) ) 
00215         - exp( -1 * ( log( pow( (double)histogram->BinToValue( i ), 2 ) ) / twoSigmaSquared ) ) )
00216       / denom;
00217     const double f = partialHistCount * quotient;
00218     const double applyStepFilterTo = ( f - (*histogram)[i] );
00219     M += ( applyStepFilterTo >= 0 ) ? 1 : 0;
00220     }
00221 //  std::cout << "M: " << M << std::endl;
00222 
00223   /* 
00224    *  biasHat is a simple formula using the results 
00225    *  from above.
00226    */
00227   const double biasHat = ( lambdaStar - ( K - 2 + M ) )
00228                    / sqrt( K - 2 + M );
00229   std::cout << "biasHat: " << biasHat << std::endl;
00230 
00231   //return biasHat;
00232   return 0;
00233 }
00234 
00235 double
00236 TypedArrayNoiseEstimatorBrummer::SijbersLogLikelihood
00237 ( const Histogram<unsigned int>::SmartPtr histogram, const double sigma, const int numBinsToUse )
00238 {
00239 
00240   double s2 = 2 * sigma * sigma;
00241   int K = numBinsToUse;
00242 
00243   double logLikelihood = 0.0;
00244   for ( int i = 0; i < K; i++ )
00245     {
00246     const double curCount = (*histogram)[i];
00247     const double quotient =
00248       ( exp( -1 * ( log( pow( (double)histogram->BinToValue(i-1), 2 ) ) / s2 ) )
00249         - exp( -1 * ( log( pow( (double)histogram->BinToValue( i ), 2 ) ) / s2 ) ) )
00250       /
00251       ( exp( -1 * ( log( pow( (double)histogram->BinToValue( 0 ), 2 ) ) / s2 ) )
00252         - exp( -1 * ( log( pow( (double)histogram->BinToValue( K ), 2 ) ) / s2 ) ) );
00253     
00254     std::cout << exp( -1 * ( log( pow( histogram->BinToValue(i-1), 2 ) ) / s2 ) ) << std::endl;
00255     std::cout << exp( -1 * ( log( pow( histogram->BinToValue( i ), 2 ) ) / s2 ) ) << std::endl;
00256     std::cout << exp( -1 * ( log( pow( histogram->BinToValue( 0 ), 2 ) ) / s2 ) ) << std::endl;
00257     std::cout << exp( -1 * ( log( pow( histogram->BinToValue( K ), 2 ) ) / s2 ) ) << std::endl;
00258     
00259     logLikelihood += curCount * quotient;
00260     }
00261   return logLikelihood;
00262 }
00263 
00264 double
00265 TypedArrayNoiseEstimatorBrummer::SijbersVarHat
00266 ( const Histogram<unsigned int>::SmartPtr histogram, const double sigmaHat, const int numBinsToUse )
00267 {
00268 
00269   double h = 0.01;
00270   
00271   const double fXminusH = SijbersLogLikelihood( histogram, sigmaHat - h, numBinsToUse ); 
00272   const double fX = SijbersLogLikelihood( histogram, sigmaHat, numBinsToUse ); 
00273   const double fXplusH = SijbersLogLikelihood( histogram, sigmaHat + h, numBinsToUse ); 
00274 
00275   std::cout << "fXminusH: " << fXminusH << std::endl;
00276   std::cout << "fX: " << fX << std::endl;
00277   std::cout << "fXplusH: " << fXplusH << std::endl;
00278   
00279   const double secondDerivOfLogLikelihood = ( fXminusH - 2 * fX + fXplusH ) / ( h * h );
00280 
00281   const double varHat = -1.0 * 1 / secondDerivOfLogLikelihood;
00282   std::cout << "varHat: " << varHat << std::endl;
00283   //return varHat;
00284   return 0;
00285 }
00286 
00287 double
00288 TypedArrayNoiseEstimatorBrummer::EstimateNumBinsToUse
00289 ( const TypedArray* data, const Histogram<unsigned int>::SmartPtr histogram, const double sigmaHat )
00290 {
00291   UNUSED(data);
00292 
00293   double bestK = 0;
00294   double curKMinimizer;
00295   double minKMinimizer = std::numeric_limits<double>::max(); 
00296  
00297   double maxK = histogram->GetNumBins() / 2;
00298   
00299   for ( int curK = 1; curK < maxK; curK++ )
00300     {
00301     curKMinimizer = SijbersBiasHat( histogram, sigmaHat, curK )+ SijbersVarHat( histogram, sigmaHat, curK );
00302 //std::cout << "minKMinimizer: " << minKMinimizer << std::endl;
00303 //std::cout << "curKMinimizer: " << curKMinimizer << std::endl;
00304     if ( curKMinimizer < minKMinimizer ) 
00305       {
00306       minKMinimizer = curKMinimizer;
00307       bestK = curK;
00308       }
00309     }
00310 
00311 //std::cout << std::endl << "bestK: " << bestK << std::endl;
00312 
00313   return bestK;
00314 }
00315 
00316 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines